[
  {
    "path": ".gitignore",
    "content": "# pyc\n*.pyc\n\n# Compiled Object files\n*.lo\n*.o\n\n# Compiled Dynamic libraries\n*.so\n\n# Compiled Static libraries\n*.la\n*.a\n\n# Compiled misc\n*.dep\n*.gcda\n*.gcno\n*.gcov\n\n# Packages\n*.tar.gz\n*.tar.bz2\n\n# Logs\n*.log\n\n# vire\n*.swp\n*.~\n*.project\n*.cproject\n\n# Core and executable\ncore*\nvire\n\n# extracted jemalloc\n!/dep/jemalloc-*\n\n# Autotools\n.deps\n.libs\n\n/aclocal.m4\n/autom4te.cache\n/stamp-h1\n/autoscan.log\n/libtool\n\n/config/config.guess\n/config/config.sub\n/config/depcomp\n/config/install-sh\n/config/ltmain.sh\n/config/missing\n/config\n\n/config.h\n/config.h.in\n/config.h.in~\n/config.log\n/config.status\n/configure.scan\n/configure\n\nMakefile\nMakefile.in\n"
  },
  {
    "path": "ChangeLog",
    "content": " 2016-10-25  deep011  <diguo58@gmail.com>\n    * vire: version 1.0.0 release\n      vire (pronounced \"vip-redis\") is a multithread redis(based on redis-3.2.0) maintains in vipshop.\n      multi-threads support.\n      command type CONNECTION supported: ping,quit,echo,select,auth,admin.\n      command type SERVER supported: info,flushall,flushdb,time,dbsize,command,config,client,slowlog.\n      command type KEY supported: del,exists,ttl,pttl,expire,expireat,pexpire,pexpireat,persist,randomkey,type,keys,scan,object.\n      command type STRING supported: get,set,setnx,setex,psetex,incr,decr,incrby,decrby,append,strlen,getset,incrbyfloat,setbit,getbit,setrange,getrange,bitcount,bitpos,mget,mset.\n      command type HASH supported: hset,hget,hlen,hdel,hexists,hkeys,hvals,hgetall,hincrby,hincrbyfloat,hmget,hmset,hsetnx,hstrlen,hscan.\n      command type LIST supported: rpush,lpush,lrange,rpop,lpop,llen,lrem,ltrim,lindex,lset.\n      command type SET supported: sadd,smembers,scard,srem,spop,sismember,sscan,sunion,sunionstore,sdiff,sdiffstore,sinter,sinterstore.\n      command type SORTEDSET supported: zadd,zincrby,zrange,zrevrange,zrem,zcard,zcount,zrangebyscore,zrevrangebyscore,zrank,zrevrank,zscore,zremrangebyscore,zremrangebyrank,zremrangebylex,zscan.\n      command type HYPERLOGLOG supported: pfadd,pfcount.\n      config option added(used for config file and 'config get/set' command): port,databases,internal-dbs-per-databases,requirepass,adminpass,commands-need-adminpass,maxclients,maxmemory,maxmemory-policy,maxmemory-samples,max-time-complexity-limit,slowlog-log-slower-than,slowlog-max-len.\n      viretest added that is for unit test.\n      vireabtest added that is for compare command execution and data consistency with redis-3.2.0.\n      vire-benchmark added that is modified from redis-benchmark but multi-threads supported and pressure test vire."
  },
  {
    "path": "LICENSE",
    "content": "\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS"
  },
  {
    "path": "Makefile.am",
    "content": "MAINTAINERCLEANFILES = Makefile.in aclocal.m4 configure config.h.in config.h.in~ stamp-h.in\n\nACLOCAL_AMFLAGS = -I m4\n\nSUBDIRS = dep src tests\n\nEXTRA_DIST = README.md NOTICE LICENSE ChangeLog conf scripts notes\n"
  },
  {
    "path": "NOTICE",
    "content": ""
  },
  {
    "path": "README.md",
    "content": "# vire\n\n**vire** (pronounced \"vip-redis\") is a multithread redis(based on redis-3.2.0) maintains in vipshop.\n\n### QQ交流群：276406429\n\n## Dependence\n\nPlease install automake, libtool, autoconf and bzip2 at first.\n\n## Build\n\nTo build vire from source with _debug logs enabled_ and _assertions enabled_:\n\n    $ git clone  https://github.com/vipshop/vire.git\n    $ cd vire\n    $ autoreconf -fvi\n    $ ./configure --enable-debug=full\n    $ make\n    $ src/vire -h\n\nA quick checklist:\n\n+ Use newer version of gcc (older version of gcc has problems)\n+ Use CFLAGS=\"-O1\" ./configure && make\n+ Use CFLAGS=\"-O3 -fno-strict-aliasing\" ./configure && make\n+ `autoreconf -fvi && ./configure` needs `automake` and `libtool` to be installed\n\n## Run\n\n    $ src/vire -c conf/vire.conf -o log -T 6 -d\n\n## Features\n\n+ Multithread.\n+ Fast.\n+ Works with Linux, *BSD, OS X and SmartOS (Solaris)\n\n## Help\n\n    Usage: vire [-?hVdt] [-v verbosity level] [-o output file]\n                [-c conf file] [-p pid file]\n                [-T worker threads number]\n    \n    Options:\n    -h, --help             : this help\n    -V, --version          : show version and exit\n    -t, --test-conf        : test configuration for syntax errors and exit\n    -d, --daemonize        : run as a daemon\n    -v, --verbose=N        : set logging level (default: 5, min: 0, max: 11)\n    -o, --output=S         : set logging file (default: stderr)\n    -c, --conf-file=S      : set configuration file (default: conf/vire.conf)\n    -p, --pid-file=S       : set pid file (default: off)\n    -T, --thread_num=N     : set the worker threads number (default: 6)\n\n## Support redis command so far\n\n#### Connection\n\n+ ping\n+ quit\n+ echo\n+ select\n+ auth\n+ admin\n\n#### Server\n\n+ info\n+ flushall\n+ flushdb\n+ time\n+ dbsize\n+ command\n+ config\n+ client\n+ slowlog\n\n#### Key\n\n+ del\n+ exists\n+ ttl\n+ pttl\n+ expire\n+ expireat\n+ pexpire\n+ pexpireat\n+ persist\n+ randomkey\n+ type\n+ keys\n+ scan\n+ object\n\n#### String\n\n+ get\n+ set\n+ setnx\n+ setex\n+ psetex\n+ incr\n+ decr\n+ incrby\n+ decrby\n+ append\n+ strlen\n+ getset\n+ incrbyfloat\n+ setbit\n+ getbit\n+ setrange\n+ getrange\n+ bitcount\n+ bitpos\n+ mget\n+ mset\n\n#### Hash\n\n+ hset\n+ hget\n+ hlen\n+ hdel\n+ hexists\n+ hkeys\n+ hvals\n+ hgetall\n+ hincrby\n+ hincrbyfloat\n+ hmget\n+ hmset\n+ hsetnx\n+ hstrlen\n+ hscan\n\n#### List\n\n+ rpush\n+ lpush\n+ lrange\n+ rpop\n+ lpop\n+ llen\n+ lrem\n+ ltrim\n+ lindex\n+ lset\n\n#### Set\n\n+ sadd\n+ smembers\n+ scard\n+ srem\n+ spop\n+ sismember\n+ sscan\n+ sunion\n+ sunionstore\n+ sdiff\n+ sdiffstore\n+ sinter\n+ sinterstore\n\n#### SortedSet\n\n+ zadd\n+ zincrby\n+ zrange\n+ zrevrange\n+ zrem\n+ zcard\n+ zcount\n+ zrangebyscore\n+ zrevrangebyscore\n+ zrank\n+ zrevrank\n+ zscore\n+ zremrangebyscore\n+ zremrangebyrank\n+ zremrangebylex\n+ zscan\n\n#### HyperLogLog\n\n+ pfadd\n+ pfcount\n\n## License\n\nCopyright © 2016 VIPSHOP Inc.\n\nLicensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0\n"
  },
  {
    "path": "conf/vire.conf",
    "content": "################################## NETWORK #####################################\n\n# By default, if no \"bind\" configuration directive is specified, Vire listens\n# for connections from all the network interfaces available on the server.\n# It is possible to listen to just one or multiple selected interfaces using\n# the \"bind\" configuration directive, followed by one or more IP addresses.\n#\n# Examples:\n#\n# bind 192.168.1.100 10.0.0.1\n# bind 127.0.0.1 ::1\n#\n# ~~~ WARNING ~~~ If the computer running Vire is directly exposed to the\n# internet, binding to all the interfaces is dangerous and will expose the\n# instance to everybody on the internet. So by default we uncomment the\n# following bind directive, that will force Vire to listen only into\n# the IPv4 lookback interface address (this means Vire will be able to\n# accept connections only from clients running into the same computer it\n# is running).\n#\n# IF YOU ARE SURE YOU WANT YOUR INSTANCE TO LISTEN TO ALL THE INTERFACES\n# JUST COMMENT THE FOLLOWING LINE.\n# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n# bind 127.0.0.1\n\n# Accept connections on the specified port, default is 55555.\n# If port 0 is specified Vire will not listen on a TCP socket.\nport 55555\n\n################################# GENERAL #####################################\n\n# Set the number of databases. The default database is DB 0, you can select\n# a different one on a per-connection basis using SELECT <dbid> where\n# dbid is a number between 0 and 'databases'-1\ndatabases 6\n\n# Set the number of internal dbs for every databases. This is used for\n# multi-threads avoid too much locker competition.\ninternal-dbs-per-databases 6\n\n################################## SECURITY ###################################\n\n# Require clients to issue AUTH <PASSWORD> before processing any other\n# commands.  This might be useful in environments in which you do not trust\n# others with access to the host running redis-server.\n#\n# This should stay commented out for backward compatibility and because most\n# people do not need auth (e.g. they run their own servers).\n#\n# Warning: since Redis is pretty fast an outside user can try up to\n# 150k passwords per second against a good box. This means that you should\n# use a very strong password otherwise it will be very easy to break.\n#\n# requirepass foobared\n\n# Require clients to issue ADMIN <PASSWORD> before processing any other\n# needed admin right commands. This might be useful to prevent users from \n# doing some dangerous actions to the host running Vire.\n#\n# This should stay commented out for backward compatibility and because most\n# people do not need auth (e.g. they run their own servers).\n#\n# Warning: since Redis is pretty fast an outside user can try up to\n# 150k passwords per second against a good box. This means that you should\n# use a very strong password otherwise it will be very easy to break.\n#\n# adminpass iamadmin\n\n# Make some commands need adminpass to execute. Those commands just allowed \n# administrator to execute. This might be useful to prevent users from \n# doing some dangerous actions to the host running Vire.\n#\n# commands-need-adminpass flushall flushdb keys config\n\n################################### CLIENTS ####################################\n\n# Set the max number of connected clients at the same time. By default\n# this limit is set to 10000 clients, however if the Redis server is not\n# able to configure the process file limit to allow for the specified limit\n# the max number of allowed clients is set to the current file limit\n# minus 32 (as Redis reserves a few file descriptors for internal uses).\n#\n# Once the limit is reached Redis will close all the new connections sending\n# an error 'max number of clients reached'.\n#\n# maxclients 10000\n\n############################## MEMORY MANAGEMENT ################################\n\n# Don't use more memory than the specified amount of bytes.\n# When the memory limit is reached Vire will try to remove keys\n# according to the eviction policy selected (see maxmemory-policy).\n#\n# If Vire can't remove keys according to the policy, or if the policy is\n# set to 'noeviction', Vire will start to reply with errors to commands\n# that would use more memory, like SET, LPUSH, and so on, and will continue\n# to reply to read-only commands like GET.\n#\n# This option is usually useful when using Vire as an LRU cache, or to set\n# a hard memory limit for an instance (using the 'noeviction' policy).\n#\n# WARNING: If you have slaves attached to an instance with maxmemory on,\n# the size of the output buffers needed to feed the slaves are subtracted\n# from the used memory count, so that network problems / resyncs will\n# not trigger a loop where keys are evicted, and in turn the output\n# buffer of slaves is full with DELs of keys evicted triggering the deletion\n# of more keys, and so forth until the database is completely emptied.\n#\n# In short... if you have slaves attached it is suggested that you set a lower\n# limit for maxmemory so that there is some free RAM on the system for slave\n# output buffers (but this is not needed if the policy is 'noeviction').\n#\n# maxmemory <bytes>\n\n# MAXMEMORY POLICY: how Vire will select what to remove when maxmemory\n# is reached. You can select among five behaviors:\n#\n# volatile-lru -> remove the key with an expire set using an LRU algorithm, not support now\n# allkeys-lru -> remove any key according to the LRU algorithm, not support now\n# volatile-random -> remove a random key with an expire set\n# allkeys-random -> remove a random key, any key\n# volatile-ttl -> remove the key with the nearest expire time (minor TTL)\n# noeviction -> don't expire at all, just return an error on write operations\n#\n# Note: with any of the above policies, Vire will return an error on write\n#       operations, when there are no suitable keys for eviction.\n#\n#       At the date of writing these commands are: set setnx setex append\n#       incr decr rpush lpush rpushx lpushx linsert lset rpoplpush sadd\n#       sinter sinterstore sunion sunionstore sdiff sdiffstore zadd zincrby\n#       zunionstore zinterstore hset hsetnx hmset hincrby incrby decrby\n#       getset mset msetnx exec sort\n#\n# The default is:\n#\n# maxmemory-policy noeviction\n\n# LRU and minimal TTL algorithms are not precise algorithms but approximated\n# algorithms (in order to save memory), so you can tune it for speed or\n# accuracy. For default Vire will check five keys and pick the one that was\n# used less recently, you can change the sample size using the following\n# configuration directive.\n#\n# The default of 5 produces good enough results. 10 Approximates very closely\n# true LRU but costs a bit more CPU. 3 is very fast but not very accurate.\n#\n# maxmemory-samples 5\n\n# Max time complexity limit for the commands that their time complexity is O(n).\n#\n# If n is bigger than max-time-complexity-limit, an error is returned for the client.\n# The default of 0 means unlimited.\n#\n#       At the date of affected commands are: keys\n# \n# max-time-complexity-limit 0\n\n################################## SLOW LOG ###################################\n\n# The Vire Slow Log is a system to log queries that exceeded a specified\n# execution time. The execution time does not include the I/O operations\n# like talking with the client, sending the reply and so forth,\n# but just the time needed to actually execute the command (this is the only\n# stage of command execution where the thread is blocked and can not serve\n# other requests in the meantime).\n#\n# You can configure the slow log with two parameters: one tells Vire\n# what is the execution time, in microseconds, to exceed in order for the\n# command to get logged, and the other parameter is the length of the\n# slow log. When a new command is logged the oldest one is removed from the\n# queue of logged commands.\n\n# The following time is expressed in microseconds, so 1000000 is equivalent\n# to one second. Note that a negative number disables the slow log, while\n# a value of zero forces the logging of every command.\nslowlog-log-slower-than 10000\n\n# There is no limit to this length. Just be aware that it will consume memory.\n# You can reclaim memory used by the slow log with SLOWLOG RESET.\nslowlog-max-len 128"
  },
  {
    "path": "configure.ac",
    "content": "# Define the package version numbers and the bug reporting address\nm4_define([VR_MAJOR], 1)\nm4_define([VR_MINOR], 0)\nm4_define([VR_PATCH], 0)\nm4_define([VR_BUGS], [diguo58@gmail.com])\n\n# Initialize autoconf\nAC_PREREQ([2.63])\nAC_INIT([vire], [VR_MAJOR.VR_MINOR.VR_PATCH], [VR_BUGS])\nAC_CONFIG_SRCDIR([src/vr.c])\nAC_CONFIG_AUX_DIR([config])\nAC_CONFIG_HEADERS([config.h:config.h.in])\nAC_CONFIG_MACRO_DIR([m4])\n\n# Initialize automake\nAM_INIT_AUTOMAKE([1.9 foreign])\n\n# Define macro variables for the package version numbers\nAC_DEFINE(VR_VERSION_MAJOR, VR_MAJOR, [Define the major version number])\nAC_DEFINE(VR_VERSION_MINOR, VR_MINOR, [Define the minor version number])\nAC_DEFINE(VR_VERSION_PATCH, VR_PATCH, [Define the patch version number])\nAC_DEFINE(VR_VERSION_STRING, \"VR_MAJOR.VR_MINOR.VR_PATCH\", [Define the version string])\n\n# Checks for language\nAC_LANG([C])\n\n# Checks for programs\nAC_PROG_AWK\nAC_PROG_CC\nAC_PROG_CPP\nAC_PROG_CXX\nAC_PROG_INSTALL\nAC_PROG_LN_S\nAC_PROG_MAKE_SET\nAC_PROG_RANLIB\nAC_PROG_LIBTOOL\n\n# Checks for typedefs, structures, and compiler characteristics\nAC_C_INLINE\nAC_TYPE_INT8_T\nAC_TYPE_INT16_T\nAC_TYPE_INT32_T\nAC_TYPE_INT64_T\nAC_TYPE_INTMAX_T\nAC_TYPE_INTPTR_T\nAC_TYPE_UINT8_T\nAC_TYPE_UINT16_T\nAC_TYPE_UINT32_T\nAC_TYPE_UINT64_T\nAC_TYPE_UINTMAX_T\nAC_TYPE_UINTPTR_T\nAC_TYPE_OFF_T\nAC_TYPE_PID_T\nAC_TYPE_SIZE_T\nAC_TYPE_SSIZE_T\n\nAC_C_BIGENDIAN(\n  [],\n  [AC_DEFINE(HAVE_LITTLE_ENDIAN, 1, [Define to 1 if machine is little endian])],\n  [AC_MSG_ERROR([endianess of this machine is unknown])],\n  [AC_MSG_ERROR([universial endianess not supported])]\n)\n\n# Checks for header files\nAC_HEADER_STDBOOL\nAC_CHECK_HEADERS([fcntl.h float.h limits.h stddef.h stdlib.h string.h unistd.h])\nAC_CHECK_HEADERS([inttypes.h stdint.h])\nAC_CHECK_HEADERS([sys/ioctl.h sys/time.h sys/uio.h])\nAC_CHECK_HEADERS([sys/socket.h sys/un.h netinet/in.h arpa/inet.h netdb.h])\nAC_CHECK_HEADERS([execinfo.h],\n  [AC_DEFINE(HAVE_BACKTRACE, [1], [Define to 1 if backtrace is supported])], [])\nAC_CHECK_HEADERS([sys/epoll.h], [], [])\nAC_CHECK_HEADERS([sys/event.h], [], [])\n\n# Checks for libraries\nAC_CHECK_LIB([m], [pow])\nAC_CHECK_LIB([pthread], [pthread_create])\n\n# Checks for library functions\nAC_FUNC_FORK\nAC_FUNC_MALLOC\nAC_FUNC_REALLOC\nAC_CHECK_FUNCS([dup2 gethostname gettimeofday strerror])\nAC_CHECK_FUNCS([socket])\nAC_CHECK_FUNCS([memchr memmove memset])\nAC_CHECK_FUNCS([strchr strndup strtoul])\n\nAC_CACHE_CHECK([if epoll works], [ac_cv_epoll_works],\n  AC_TRY_RUN([\n#include <stdio.h>\n#include <stdlib.h>\n#include <sys/epoll.h>\nint\nmain(int argc, char **argv)\n{\n    int fd;\n\n    fd = epoll_create(256);\n    if (fd < 0) {\n        perror(\"epoll_create:\");\n        exit(1);\n    }\n    exit(0);\n}\n  ], [ac_cv_epoll_works=yes], [ac_cv_epoll_works=no]))\nAS_IF([test \"x$ac_cv_epoll_works\" = \"xyes\"],\n  [AC_DEFINE([HAVE_EPOLL], [1], [Define to 1 if epoll is supported])], [])\n\nAC_CACHE_CHECK([if kqueue works], [ac_cv_kqueue_works],\n  AC_TRY_RUN([\n#include <stdio.h>\n#include <stdlib.h>\n#include <sys/types.h>\n#include <sys/event.h>\n#include <sys/time.h>\nint\nmain(int argc, char **argv)\n{\n    int fd;\n\n    fd = kqueue();\n    if (fd < 0) {\n        perror(\"kqueue:\");\n        exit(1);\n    }\n    exit(0);\n}\n  ], [ac_cv_kqueue_works=yes], [ac_cv_kqueue_works=no]))\nAS_IF([test \"x$ac_cv_kqueue_works\" = \"xyes\"],\n  [AC_DEFINE([HAVE_KQUEUE], [1], [Define to 1 if kqueue is supported])], [])\n\nAC_CACHE_CHECK([if event ports works], [ac_cv_evports_works],\n  AC_TRY_RUN([\n#include <stdio.h>\n#include <stdlib.h>\n#include <port.h>\nint\nmain(int argc, char **argv)\n{\n    int fd;\n\n    fd = port_create();\n    if (fd < 0) {\n        perror(\"port_create:\");\n        exit(1);\n    }\n    exit(0);\n}\n  ], [ac_cv_evports_works=yes], [ac_cv_evports_works=no]))\nAS_IF([test \"x$ac_cv_evports_works\" = \"xyes\"],\n  [AC_DEFINE([HAVE_EVENT_PORTS], [1], [Define to 1 if event ports is supported])], [])\n\nAS_IF([test \"x$ac_cv_epoll_works\" = \"xno\" &&\n       test \"x$ac_cv_kqueue_works\" = \"xno\" &&\n       test \"x$ac_cv_evports_works\" = \"xno\"],\n  [AC_MSG_ERROR([either epoll or kqueue or event ports support is required])], [])\n\nAM_CONDITIONAL([OS_LINUX], [test \"x$ac_cv_epoll_works\" = \"xyes\"])\nAM_CONDITIONAL([OS_BSD], [test \"x$ac_cv_kqueue_works\" = \"xyes\"])\nAM_CONDITIONAL([OS_SOLARIS], [test \"x$ac_cv_evports_works\" = \"xyes\"])\nAM_CONDITIONAL([OS_FREEBSD], [test \"$(uname -v | cut -c 1-10)\" == \"FreeBSD 10\"])\nAM_CONDITIONAL([OS_DARWIN], [test \"$(uname -v | cut -c 1-6)\" == \"Darwin\"])\n\n# Package options\nAC_MSG_CHECKING([whether to enable debug logs and asserts])\nAC_ARG_ENABLE([debug],\n  [AS_HELP_STRING(\n    [--enable-debug=@<:@full|yes|log|no@:>@],\n    [enable debug logs and asserts @<:@default=no@:>@])\n  ],\n  [],\n  [enable_debug=no])\nAS_CASE([x$enable_debug],\n  [xfull], [AC_DEFINE([HAVE_ASSERT_PANIC], [1],\n                      [Define to 1 if panic on an assert is enabled])\n            AC_DEFINE([HAVE_DEBUG_LOG], [1], [Define to 1 if debug log is enabled])\n           ],\n  [xyes], [AC_DEFINE([HAVE_ASSERT_LOG], [1],\n                     [Define to 1 if log on an assert is enabled])\n           AC_DEFINE([HAVE_DEBUG_LOG], [1], [Define to 1 if debug log is enabled])\n          ],\n  [xlog], [AC_DEFINE([HAVE_DEBUG_LOG], [1], [Define to 1 if debug log is enabled])],\n  [xno], [],\n  [AC_MSG_FAILURE([invalid value ${enable_debug} for --enable-debug])])\nAC_MSG_RESULT($enable_debug)\n\nAC_MSG_CHECKING([whether to disable spinlock])\nAC_ARG_ENABLE([spinlock],\n  [AS_HELP_STRING(\n    [--disable-spinlock],\n    [disable spinlock])\n  ],\n  [disable_spinlock=yes],\n  [disable_spinlock=no])\nAS_IF([test \"x$disable_spinlock\" = xyes],\n  [],\n  [AC_DEFINE([HAVE_SPINLOCK], [1], [Define to 1 if spinlock is not disabled])])\nAC_MSG_RESULT($disable_spinlock)\n\nAC_MSG_CHECKING([whether to use jemalloc])\nAC_ARG_WITH([jemalloc],\n  AS_HELP_STRING([--with-jemalloc@<:@=yes|no@:>@],\n    [use jemalloc(default use jemalloc)]),\n  [\n    if test \"$withval\" = \"no\"; then\n      enable_jemalloc=no\n    else\n\t  enable_jemalloc=yes\n    fi\n  ],\n  [enable_jemalloc=yes])\nAS_IF([test \"x$enable_jemalloc\" = xyes],\n  [AC_DEFINE([HAVE_JEMALLOC], [1], [Define to 1 if jemalloc is used])],\n  [])\nAC_MSG_RESULT($enable_jemalloc)\n\n# Untar the jemalloc-4.2.0.tar.bz2 in dep/ before config.status is rerun\n# Run configure in dep/jemalloc-4.2.0\nAC_CONFIG_COMMANDS_PRE([rm -rf dep/jemalloc-4.2.0])\nAC_CONFIG_COMMANDS_PRE([mkdir dep/jemalloc-4.2.0])\nAC_CONFIG_COMMANDS_PRE([tar xvjf dep/jemalloc-4.2.0.tar.bz2 -C dep])\nAC_CONFIG_COMMANDS_PRE([cd dep/jemalloc-4.2.0])\nAC_CONFIG_COMMANDS_PRE([./configure --with-jemalloc-prefix=je_])\nAC_CONFIG_COMMANDS_PRE([cd ../..])\n\n# Untar the hiredis-0.13.3.tar.gz in dep/ before config.status is rerun\nAC_CONFIG_COMMANDS_PRE([rm -rf dep/hiredis-0.13.3])\nAC_CONFIG_COMMANDS_PRE([mkdir dep/hiredis-0.13.3])\nAC_CONFIG_COMMANDS_PRE([tar zxvf dep/hiredis-0.13.3.tar.gz -C dep])\nAC_CONFIG_COMMANDS_PRE([cd dep/hiredis-0.13.3])\nAC_CONFIG_COMMANDS_PRE([cd ../..])\n\n# Define Makefiles\nAC_CONFIG_FILES([Makefile\n                 dep/Makefile\n                 dep/util/Makefile\n                 dep/dhashkit/Makefile\n                 dep/dmalloc/Makefile\n                 dep/sds/Makefile\n                 dep/ae/Makefile\n                 dep/dlist/Makefile\n                 dep/darray/Makefile\n                 dep/himemcached-0.1.0/Makefile\n                 src/Makefile\n                 tests/Makefile])\n\n# Generate the \"configure\" script\nAC_OUTPUT\n"
  },
  {
    "path": "dep/.gitignore",
    "content": "!*.tar.gz"
  },
  {
    "path": "dep/Makefile.am",
    "content": "SUBDIRS = jemalloc-4.2.0 hiredis-0.13.3 himemcached-0.1.0 util dhashkit dmalloc ae sds dlist darray\n\nEXTRA_DIST = jemalloc-4.2.0.tar.bz2 hiredis-0.13.3.tar.gz\n"
  },
  {
    "path": "dep/ae/Makefile.am",
    "content": "MAINTAINERCLEANFILES = Makefile.in\n\nAM_CPPFLAGS =\nif !OS_SOLARIS\nAM_CPPFLAGS += -D_GNU_SOURCE\nendif\nAM_CPPFLAGS += -I $(top_srcdir)/dep/util\nAM_CPPFLAGS += -I $(top_srcdir)/dep/jemalloc-4.2.0/include\nAM_CPPFLAGS += -I $(top_srcdir)/dep/dmalloc\n\nAM_CFLAGS = -Wall -Wshadow\nAM_CFLAGS += -Wno-unused-parameter -Wno-unused-value\n\nnoinst_LIBRARIES = libae.a\n\nnoinst_HEADERS = ae.h\n\nlibae_a_SOURCES =\t\\\n\tae.c ae.h"
  },
  {
    "path": "dep/ae/ae.c",
    "content": "/* A simple event-driven programming library. Originally I wrote this code\n * for the Jim's event-loop (Jim is a Tcl interpreter) but later translated\n * it in form of a library for easy reuse.\n *\n * Copyright (c) 2006-2010, Salvatore Sanfilippo <antirez at gmail dot com>\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n *   * Redistributions of source code must retain the above copyright notice,\n *     this list of conditions and the following disclaimer.\n *   * Redistributions in binary form must reproduce the above copyright\n *     notice, this list of conditions and the following disclaimer in the\n *     documentation and/or other materials provided with the distribution.\n *   * Neither the name of Redis nor the names of its contributors may be used\n *     to endorse or promote products derived from this software without\n *     specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <stdio.h>\n#include <sys/time.h>\n#include <sys/types.h>\n#include <unistd.h>\n#include <stdlib.h>\n#include <poll.h>\n#include <string.h>\n#include <time.h>\n#include <errno.h>\n\n#include <dmalloc.h>\n\n#include <ae.h>\n\n#ifdef HAVE_CONFIG_H\n# include <config.h>\n#endif\n\n/* Include the best multiplexing layer supported by this system.\n * The following should be ordered by performances, descending. */\n#ifdef HAVE_EVENT_PORTS\n#include \"ae_evport.c\"\n#else\n    #ifdef HAVE_EPOLL\n    #include \"ae_epoll.c\"\n    #else\n        #ifdef HAVE_KQUEUE\n        #include \"ae_kqueue.c\"\n        #else\n        #include \"ae_select.c\"\n        #endif\n    #endif\n#endif\n\naeEventLoop *aeCreateEventLoop(int setsize) {\n    aeEventLoop *eventLoop;\n    int i;\n\n    if ((eventLoop = dalloc(sizeof(*eventLoop))) == NULL) goto err;\n    eventLoop->events = dalloc(sizeof(aeFileEvent)*setsize);\n    eventLoop->fired = dalloc(sizeof(aeFiredEvent)*setsize);\n    if (eventLoop->events == NULL || eventLoop->fired == NULL) goto err;\n    eventLoop->setsize = setsize;\n    eventLoop->lastTime = time(NULL);\n    eventLoop->timeEventHead = NULL;\n    eventLoop->timeEventNextId = 0;\n    eventLoop->stop = 0;\n    eventLoop->maxfd = -1;\n    eventLoop->beforesleep = NULL;\n    eventLoop->bsdata = NULL;\n    if (aeApiCreate(eventLoop) == -1) goto err;\n    /* Events with mask == AE_NONE are not set. So let's initialize the\n     * vector with it. */\n    for (i = 0; i < setsize; i++)\n        eventLoop->events[i].mask = AE_NONE;\n    return eventLoop;\n\nerr:\n    if (eventLoop) {\n        dfree(eventLoop->events);\n        dfree(eventLoop->fired);\n        dfree(eventLoop);\n    }\n    return NULL;\n}\n\n/* Return the current set size. */\nint aeGetSetSize(aeEventLoop *eventLoop) {\n    return eventLoop->setsize;\n}\n\n/* Resize the maximum set size of the event loop.\n * If the requested set size is smaller than the current set size, but\n * there is already a file descriptor in use that is >= the requested\n * set size minus one, AE_ERR is returned and the operation is not\n * performed at all.\n *\n * Otherwise AE_OK is returned and the operation is successful. */\nint aeResizeSetSize(aeEventLoop *eventLoop, int setsize) {\n    int i;\n\n    if (setsize == eventLoop->setsize) return AE_OK;\n    if (eventLoop->maxfd >= setsize) return AE_ERR;\n    if (aeApiResize(eventLoop,setsize) == -1) return AE_ERR;\n\n    eventLoop->events = drealloc(eventLoop->events,sizeof(aeFileEvent)*setsize);\n    eventLoop->fired = drealloc(eventLoop->fired,sizeof(aeFiredEvent)*setsize);\n    eventLoop->setsize = setsize;\n\n    /* Make sure that if we created new slots, they are initialized with\n     * an AE_NONE mask. */\n    for (i = eventLoop->maxfd+1; i < setsize; i++)\n        eventLoop->events[i].mask = AE_NONE;\n    return AE_OK;\n}\n\nvoid aeDeleteEventLoop(aeEventLoop *eventLoop) {\n    aeApiFree(eventLoop);\n    dfree(eventLoop->events);\n    dfree(eventLoop->fired);\n    dfree(eventLoop);\n}\n\nvoid aeStop(aeEventLoop *eventLoop) {\n    eventLoop->stop = 1;\n}\n\nint aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask,\n        aeFileProc *proc, void *clientData)\n{\n    if (fd >= eventLoop->setsize) {\n        if (aeResizeSetSize(eventLoop,fd+1000) != AE_OK) {\n            return AE_ERR;\n        }\n    }\n    aeFileEvent *fe = &eventLoop->events[fd];\n\n    if (aeApiAddEvent(eventLoop, fd, mask) == -1)\n        return AE_ERR;\n    fe->mask |= mask;\n    if (mask & AE_READABLE) fe->rfileProc = proc;\n    if (mask & AE_WRITABLE) fe->wfileProc = proc;\n    fe->clientData = clientData;\n    if (fd > eventLoop->maxfd)\n        eventLoop->maxfd = fd;\n    return AE_OK;\n}\n\nvoid aeDeleteFileEvent(aeEventLoop *eventLoop, int fd, int mask)\n{\n    if (fd >= eventLoop->setsize) return;\n    aeFileEvent *fe = &eventLoop->events[fd];\n    if (fe->mask == AE_NONE) return;\n\n    aeApiDelEvent(eventLoop, fd, mask);\n    fe->mask = fe->mask & (~mask);\n    if (fd == eventLoop->maxfd && fe->mask == AE_NONE) {\n        /* Update the max fd */\n        int j;\n\n        for (j = eventLoop->maxfd-1; j >= 0; j--)\n            if (eventLoop->events[j].mask != AE_NONE) break;\n        eventLoop->maxfd = j;\n    }\n}\n\nint aeGetFileEvents(aeEventLoop *eventLoop, int fd) {\n    if (fd >= eventLoop->setsize) return 0;\n    aeFileEvent *fe = &eventLoop->events[fd];\n\n    return fe->mask;\n}\n\nstatic void aeGetTime(long *seconds, long *milliseconds)\n{\n    struct timeval tv;\n\n    gettimeofday(&tv, NULL);\n    *seconds = tv.tv_sec;\n    *milliseconds = tv.tv_usec/1000;\n}\n\nstatic void aeAddMillisecondsToNow(long long milliseconds, long *sec, long *ms) {\n    long cur_sec, cur_ms, when_sec, when_ms;\n\n    aeGetTime(&cur_sec, &cur_ms);\n    when_sec = cur_sec + milliseconds/1000;\n    when_ms = cur_ms + milliseconds%1000;\n    if (when_ms >= 1000) {\n        when_sec ++;\n        when_ms -= 1000;\n    }\n    *sec = when_sec;\n    *ms = when_ms;\n}\n\nlong long aeCreateTimeEvent(aeEventLoop *eventLoop, long long milliseconds,\n        aeTimeProc *proc, void *clientData,\n        aeEventFinalizerProc *finalizerProc)\n{\n    long long id = eventLoop->timeEventNextId++;\n    aeTimeEvent *te;\n\n    te = dalloc(sizeof(*te));\n    if (te == NULL) return AE_ERR;\n    te->id = id;\n    aeAddMillisecondsToNow(milliseconds,&te->when_sec,&te->when_ms);\n    te->timeProc = proc;\n    te->finalizerProc = finalizerProc;\n    te->clientData = clientData;\n    te->next = eventLoop->timeEventHead;\n    eventLoop->timeEventHead = te;\n    return id;\n}\n\nint aeDeleteTimeEvent(aeEventLoop *eventLoop, long long id)\n{\n    aeTimeEvent *te = eventLoop->timeEventHead;\n    while(te) {\n        if (te->id == id) {\n            te->id = AE_DELETED_EVENT_ID;\n            return AE_OK;\n        }\n        te = te->next;\n    }\n    return AE_ERR; /* NO event with the specified ID found */\n}\n\n/* Search the first timer to fire.\n * This operation is useful to know how many time the select can be\n * put in sleep without to delay any event.\n * If there are no timers NULL is returned.\n *\n * Note that's O(N) since time events are unsorted.\n * Possible optimizations (not needed by Redis so far, but...):\n * 1) Insert the event in order, so that the nearest is just the head.\n *    Much better but still insertion or deletion of timers is O(N).\n * 2) Use a skiplist to have this operation as O(1) and insertion as O(log(N)).\n */\nstatic aeTimeEvent *aeSearchNearestTimer(aeEventLoop *eventLoop)\n{\n    aeTimeEvent *te = eventLoop->timeEventHead;\n    aeTimeEvent *nearest = NULL;\n\n    while(te) {\n        if (!nearest || te->when_sec < nearest->when_sec ||\n                (te->when_sec == nearest->when_sec &&\n                 te->when_ms < nearest->when_ms))\n            nearest = te;\n        te = te->next;\n    }\n    return nearest;\n}\n\n/* Process time events */\nstatic int processTimeEvents(aeEventLoop *eventLoop) {\n    int processed = 0;\n    aeTimeEvent *te, *prev;\n    long long maxId;\n    time_t now = time(NULL);\n\n    /* If the system clock is moved to the future, and then set back to the\n     * right value, time events may be delayed in a random way. Often this\n     * means that scheduled operations will not be performed soon enough.\n     *\n     * Here we try to detect system clock skews, and force all the time\n     * events to be processed ASAP when this happens: the idea is that\n     * processing events earlier is less dangerous than delaying them\n     * indefinitely, and practice suggests it is. */\n    if (now < eventLoop->lastTime) {\n        te = eventLoop->timeEventHead;\n        while(te) {\n            te->when_sec = 0;\n            te = te->next;\n        }\n    }\n    eventLoop->lastTime = now;\n\n    prev = NULL;\n    te = eventLoop->timeEventHead;\n    maxId = eventLoop->timeEventNextId-1;\n    while(te) {\n        long now_sec, now_ms;\n        long long id;\n\n        /* Remove events scheduled for deletion. */\n        if (te->id == AE_DELETED_EVENT_ID) {\n            aeTimeEvent *next = te->next;\n            if (prev == NULL)\n                eventLoop->timeEventHead = te->next;\n            else\n                prev->next = te->next;\n            if (te->finalizerProc)\n                te->finalizerProc(eventLoop, te->clientData);\n            dfree(te);\n            te = next;\n            continue;\n        }\n\n        /* Make sure we don't process time events created by time events in\n         * this iteration. Note that this check is currently useless: we always\n         * add new timers on the head, however if we change the implementation\n         * detail, this check may be useful again: we keep it here for future\n         * defense. */\n        if (te->id > maxId) {\n            te = te->next;\n            continue;\n        }\n        aeGetTime(&now_sec, &now_ms);\n        if (now_sec > te->when_sec ||\n            (now_sec == te->when_sec && now_ms >= te->when_ms))\n        {\n            int retval;\n\n            id = te->id;\n            retval = te->timeProc(eventLoop, id, te->clientData);\n            processed++;\n            if (retval != AE_NOMORE) {\n                aeAddMillisecondsToNow(retval,&te->when_sec,&te->when_ms);\n            } else {\n                te->id = AE_DELETED_EVENT_ID;\n            }\n        }\n        prev = te;\n        te = te->next;\n    }\n    return processed;\n}\n\n/* Process every pending time event, then every pending file event\n * (that may be registered by time event callbacks just processed).\n * Without special flags the function sleeps until some file event\n * fires, or when the next time event occurs (if any).\n *\n * If flags is 0, the function does nothing and returns.\n * if flags has AE_ALL_EVENTS set, all the kind of events are processed.\n * if flags has AE_FILE_EVENTS set, file events are processed.\n * if flags has AE_TIME_EVENTS set, time events are processed.\n * if flags has AE_DONT_WAIT set the function returns ASAP until all\n * the events that's possible to process without to wait are processed.\n *\n * The function returns the number of events processed. */\nint aeProcessEvents(aeEventLoop *eventLoop, int flags)\n{\n    int processed = 0, numevents;\n\n    /* Nothing to do? return ASAP */\n    if (!(flags & AE_TIME_EVENTS) && !(flags & AE_FILE_EVENTS)) return 0;\n\n    /* Note that we want call select() even if there are no\n     * file events to process as long as we want to process time\n     * events, in order to sleep until the next time event is ready\n     * to fire. */\n    if (eventLoop->maxfd != -1 ||\n        ((flags & AE_TIME_EVENTS) && !(flags & AE_DONT_WAIT))) {\n        int j;\n        aeTimeEvent *shortest = NULL;\n        struct timeval tv, *tvp;\n\n        if (flags & AE_TIME_EVENTS && !(flags & AE_DONT_WAIT))\n            shortest = aeSearchNearestTimer(eventLoop);\n        if (shortest) {\n            long now_sec, now_ms;\n\n            aeGetTime(&now_sec, &now_ms);\n            tvp = &tv;\n\n            /* How many milliseconds we need to wait for the next\n             * time event to fire? */\n            long long ms =\n                (shortest->when_sec - now_sec)*1000 +\n                shortest->when_ms - now_ms;\n\n            if (ms > 0) {\n                tvp->tv_sec = ms/1000;\n                tvp->tv_usec = (ms % 1000)*1000;\n            } else {\n                tvp->tv_sec = 0;\n                tvp->tv_usec = 0;\n            }\n        } else {\n            /* If we have to check for events but need to return\n             * ASAP because of AE_DONT_WAIT we need to set the timeout\n             * to zero */\n            if (flags & AE_DONT_WAIT) {\n                tv.tv_sec = tv.tv_usec = 0;\n                tvp = &tv;\n            } else {\n                /* Otherwise we can block */\n                tvp = NULL; /* wait forever */\n            }\n        }\n\n        numevents = aeApiPoll(eventLoop, tvp);\n        for (j = 0; j < numevents; j++) {\n            aeFileEvent *fe = &eventLoop->events[eventLoop->fired[j].fd];\n            int mask = eventLoop->fired[j].mask;\n            int fd = eventLoop->fired[j].fd;\n            int rfired = 0;\n\n\t    /* note the fe->mask & mask & ... code: maybe an already processed\n             * event removed an element that fired and we still didn't\n             * processed, so we check if the event is still valid. */\n            if (fe->mask & mask & AE_READABLE) {\n                rfired = 1;\n                fe->rfileProc(eventLoop,fd,fe->clientData,mask);\n            }\n            if (fe->mask & mask & AE_WRITABLE) {\n                if (!rfired || fe->wfileProc != fe->rfileProc)\n                    fe->wfileProc(eventLoop,fd,fe->clientData,mask);\n            }\n            processed++;\n        }\n    }\n    /* Check time events */\n    if (flags & AE_TIME_EVENTS)\n        processed += processTimeEvents(eventLoop);\n\n    return processed; /* return the number of processed file/time events */\n}\n\n/* Wait for milliseconds until the given file descriptor becomes\n * writable/readable/exception */\nint aeWait(int fd, int mask, long long milliseconds) {\n    struct pollfd pfd;\n    int retmask = 0, retval;\n\n    memset(&pfd, 0, sizeof(pfd));\n    pfd.fd = fd;\n    if (mask & AE_READABLE) pfd.events |= POLLIN;\n    if (mask & AE_WRITABLE) pfd.events |= POLLOUT;\n\n    if ((retval = poll(&pfd, 1, milliseconds))== 1) {\n        if (pfd.revents & POLLIN) retmask |= AE_READABLE;\n        if (pfd.revents & POLLOUT) retmask |= AE_WRITABLE;\n\tif (pfd.revents & POLLERR) retmask |= AE_WRITABLE;\n        if (pfd.revents & POLLHUP) retmask |= AE_WRITABLE;\n        return retmask;\n    } else {\n        return retval;\n    }\n}\n\nvoid aeMain(aeEventLoop *eventLoop) {\n    eventLoop->stop = 0;\n    while (!eventLoop->stop) {\n        if (eventLoop->beforesleep != NULL)\n            eventLoop->beforesleep(eventLoop, eventLoop->bsdata);\n        aeProcessEvents(eventLoop, AE_ALL_EVENTS);\n    }\n}\n\nchar *aeGetApiName(void) {\n    return aeApiName();\n}\n\nvoid aeSetBeforeSleepProc(aeEventLoop *eventLoop, aeBeforeSleepProc *beforesleep, void *private_data) {\n    eventLoop->beforesleep = beforesleep;\n    eventLoop->bsdata = private_data;\n}\n"
  },
  {
    "path": "dep/ae/ae.h",
    "content": "/* A simple event-driven programming library. Originally I wrote this code\n * for the Jim's event-loop (Jim is a Tcl interpreter) but later translated\n * it in form of a library for easy reuse.\n *\n * Copyright (c) 2006-2012, Salvatore Sanfilippo <antirez at gmail dot com>\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n *   * Redistributions of source code must retain the above copyright notice,\n *     this list of conditions and the following disclaimer.\n *   * Redistributions in binary form must reproduce the above copyright\n *     notice, this list of conditions and the following disclaimer in the\n *     documentation and/or other materials provided with the distribution.\n *   * Neither the name of Redis nor the names of its contributors may be used\n *     to endorse or promote products derived from this software without\n *     specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\n#ifndef __AE_H__\n#define __AE_H__\n\n#include <time.h>\n\n#define AE_OK 0\n#define AE_ERR -1\n\n#define AE_NONE 0\n#define AE_READABLE 1\n#define AE_WRITABLE 2\n\n#define AE_FILE_EVENTS 1\n#define AE_TIME_EVENTS 2\n#define AE_ALL_EVENTS (AE_FILE_EVENTS|AE_TIME_EVENTS)\n#define AE_DONT_WAIT 4\n\n#define AE_NOMORE -1\n#define AE_DELETED_EVENT_ID -1\n\n/* Macros */\n#define AE_NOTUSED(V) ((void) V)\n\nstruct aeEventLoop;\n\n/* Types and data structures */\ntypedef void aeFileProc(struct aeEventLoop *eventLoop, int fd, void *clientData, int mask);\ntypedef int aeTimeProc(struct aeEventLoop *eventLoop, long long id, void *clientData);\ntypedef void aeEventFinalizerProc(struct aeEventLoop *eventLoop, void *clientData);\ntypedef void aeBeforeSleepProc(struct aeEventLoop *eventLoop, void *private_data);\n\n/* File event structure */\ntypedef struct aeFileEvent {\n    int mask; /* one of AE_(READABLE|WRITABLE) */\n    aeFileProc *rfileProc;\n    aeFileProc *wfileProc;\n    void *clientData;\n} aeFileEvent;\n\n/* Time event structure */\ntypedef struct aeTimeEvent {\n    long long id; /* time event identifier. */\n    long when_sec; /* seconds */\n    long when_ms; /* milliseconds */\n    aeTimeProc *timeProc;\n    aeEventFinalizerProc *finalizerProc;\n    void *clientData;\n    struct aeTimeEvent *next;\n} aeTimeEvent;\n\n/* A fired event */\ntypedef struct aeFiredEvent {\n    int fd;\n    int mask;\n} aeFiredEvent;\n\n/* State of an event based program */\ntypedef struct aeEventLoop {\n    int maxfd;   /* highest file descriptor currently registered */\n    int setsize; /* max number of file descriptors tracked */\n    long long timeEventNextId;\n    time_t lastTime;     /* Used to detect system clock skew */\n    aeFileEvent *events; /* Registered events */\n    aeFiredEvent *fired; /* Fired events */\n    aeTimeEvent *timeEventHead;\n    int stop;\n    void *apidata; /* This is used for polling API specific data */\n    aeBeforeSleepProc *beforesleep;\n    void *bsdata; /* This is used for beforesleep private data */\n} aeEventLoop;\n\n/* Prototypes */\naeEventLoop *aeCreateEventLoop(int setsize);\nvoid aeDeleteEventLoop(aeEventLoop *eventLoop);\nvoid aeStop(aeEventLoop *eventLoop);\nint aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask,\n        aeFileProc *proc, void *clientData);\nvoid aeDeleteFileEvent(aeEventLoop *eventLoop, int fd, int mask);\nint aeGetFileEvents(aeEventLoop *eventLoop, int fd);\nlong long aeCreateTimeEvent(aeEventLoop *eventLoop, long long milliseconds,\n        aeTimeProc *proc, void *clientData,\n        aeEventFinalizerProc *finalizerProc);\nint aeDeleteTimeEvent(aeEventLoop *eventLoop, long long id);\nint aeProcessEvents(aeEventLoop *eventLoop, int flags);\nint aeWait(int fd, int mask, long long milliseconds);\nvoid aeMain(aeEventLoop *eventLoop);\nchar *aeGetApiName(void);\nvoid aeSetBeforeSleepProc(aeEventLoop *eventLoop, aeBeforeSleepProc *beforesleep, void *private_data);\nint aeGetSetSize(aeEventLoop *eventLoop);\nint aeResizeSetSize(aeEventLoop *eventLoop, int setsize);\n\n#endif\n"
  },
  {
    "path": "dep/ae/ae_epoll.c",
    "content": "/* Linux epoll(2) based ae.c module\n *\n * Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n *   * Redistributions of source code must retain the above copyright notice,\n *     this list of conditions and the following disclaimer.\n *   * Redistributions in binary form must reproduce the above copyright\n *     notice, this list of conditions and the following disclaimer in the\n *     documentation and/or other materials provided with the distribution.\n *   * Neither the name of Redis nor the names of its contributors may be used\n *     to endorse or promote products derived from this software without\n *     specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\n\n#include <sys/epoll.h>\n\ntypedef struct aeApiState {\n    int epfd;\n    struct epoll_event *events;\n} aeApiState;\n\nstatic int aeApiCreate(aeEventLoop *eventLoop) {\n    aeApiState *state = dalloc(sizeof(aeApiState));\n\n    if (!state) return -1;\n    state->events = dalloc(sizeof(struct epoll_event)*eventLoop->setsize);\n    if (!state->events) {\n        dfree(state);\n        return -1;\n    }\n    state->epfd = epoll_create(1024); /* 1024 is just a hint for the kernel */\n    if (state->epfd == -1) {\n        dfree(state->events);\n        dfree(state);\n        return -1;\n    }\n    eventLoop->apidata = state;\n    return 0;\n}\n\nstatic int aeApiResize(aeEventLoop *eventLoop, int setsize) {\n    aeApiState *state = eventLoop->apidata;\n\n    state->events = drealloc(state->events, sizeof(struct epoll_event)*setsize);\n    return 0;\n}\n\nstatic void aeApiFree(aeEventLoop *eventLoop) {\n    aeApiState *state = eventLoop->apidata;\n\n    close(state->epfd);\n    dfree(state->events);\n    dfree(state);\n}\n\nstatic int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask) {\n    aeApiState *state = eventLoop->apidata;\n    struct epoll_event ee = {0}; /* avoid valgrind warning */\n    /* If the fd was already monitored for some event, we need a MOD\n     * operation. Otherwise we need an ADD operation. */\n    int op = eventLoop->events[fd].mask == AE_NONE ?\n            EPOLL_CTL_ADD : EPOLL_CTL_MOD;\n\n    ee.events = 0;\n    mask |= eventLoop->events[fd].mask; /* Merge old events */\n    if (mask & AE_READABLE) ee.events |= EPOLLIN;\n    if (mask & AE_WRITABLE) ee.events |= EPOLLOUT;\n    ee.data.fd = fd;\n    if (epoll_ctl(state->epfd,op,fd,&ee) == -1) return -1;\n    return 0;\n}\n\nstatic void aeApiDelEvent(aeEventLoop *eventLoop, int fd, int delmask) {\n    aeApiState *state = eventLoop->apidata;\n    struct epoll_event ee = {0}; /* avoid valgrind warning */\n    int mask = eventLoop->events[fd].mask & (~delmask);\n\n    ee.events = 0;\n    if (mask & AE_READABLE) ee.events |= EPOLLIN;\n    if (mask & AE_WRITABLE) ee.events |= EPOLLOUT;\n    ee.data.fd = fd;\n    if (mask != AE_NONE) {\n        epoll_ctl(state->epfd,EPOLL_CTL_MOD,fd,&ee);\n    } else {\n        /* Note, Kernel < 2.6.9 requires a non null event pointer even for\n         * EPOLL_CTL_DEL. */\n        epoll_ctl(state->epfd,EPOLL_CTL_DEL,fd,&ee);\n    }\n}\n\nstatic int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp) {\n    aeApiState *state = eventLoop->apidata;\n    int retval, numevents = 0;\n\n    retval = epoll_wait(state->epfd,state->events,eventLoop->setsize,\n            tvp ? (tvp->tv_sec*1000 + tvp->tv_usec/1000) : -1);\n    if (retval > 0) {\n        int j;\n\n        numevents = retval;\n        for (j = 0; j < numevents; j++) {\n            int mask = 0;\n            struct epoll_event *e = state->events+j;\n\n            if (e->events & EPOLLIN) mask |= AE_READABLE;\n            if (e->events & EPOLLOUT) mask |= AE_WRITABLE;\n            if (e->events & EPOLLERR) mask |= AE_WRITABLE;\n            if (e->events & EPOLLHUP) mask |= AE_WRITABLE;\n            eventLoop->fired[j].fd = e->data.fd;\n            eventLoop->fired[j].mask = mask;\n        }\n    }\n    return numevents;\n}\n\nstatic char *aeApiName(void) {\n    return \"epoll\";\n}\n"
  },
  {
    "path": "dep/ae/ae_evport.c",
    "content": "/* ae.c module for illumos event ports.\n *\n * Copyright (c) 2012, Joyent, Inc. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n *   * Redistributions of source code must retain the above copyright notice,\n *     this list of conditions and the following disclaimer.\n *   * Redistributions in binary form must reproduce the above copyright\n *     notice, this list of conditions and the following disclaimer in the\n *     documentation and/or other materials provided with the distribution.\n *   * Neither the name of Redis nor the names of its contributors may be used\n *     to endorse or promote products derived from this software without\n *     specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\n\n#include <assert.h>\n#include <errno.h>\n#include <port.h>\n#include <poll.h>\n\n#include <sys/types.h>\n#include <sys/time.h>\n\n#include <stdio.h>\n\nstatic int evport_debug = 0;\n\n/*\n * This file implements the ae API using event ports, present on Solaris-based\n * systems since Solaris 10.  Using the event port interface, we associate file\n * descriptors with the port.  Each association also includes the set of poll(2)\n * events that the consumer is interested in (e.g., POLLIN and POLLOUT).\n *\n * There's one tricky piece to this implementation: when we return events via\n * aeApiPoll, the corresponding file descriptors become dissociated from the\n * port.  This is necessary because poll events are level-triggered, so if the\n * fd didn't become dissociated, it would immediately fire another event since\n * the underlying state hasn't changed yet.  We must re-associate the file\n * descriptor, but only after we know that our caller has actually read from it.\n * The ae API does not tell us exactly when that happens, but we do know that\n * it must happen by the time aeApiPoll is called again.  Our solution is to\n * keep track of the last fds returned by aeApiPoll and re-associate them next\n * time aeApiPoll is invoked.\n *\n * To summarize, in this module, each fd association is EITHER (a) represented\n * only via the in-kernel association OR (b) represented by pending_fds and\n * pending_masks.  (b) is only true for the last fds we returned from aeApiPoll,\n * and only until we enter aeApiPoll again (at which point we restore the\n * in-kernel association).\n */\n#define MAX_EVENT_BATCHSZ 512\n\ntypedef struct aeApiState {\n    int     portfd;                             /* event port */\n    int     npending;                           /* # of pending fds */\n    int     pending_fds[MAX_EVENT_BATCHSZ];     /* pending fds */\n    int     pending_masks[MAX_EVENT_BATCHSZ];   /* pending fds' masks */\n} aeApiState;\n\nstatic int aeApiCreate(aeEventLoop *eventLoop) {\n    int i;\n    aeApiState *state = dalloc(sizeof(aeApiState));\n    if (!state) return -1;\n\n    state->portfd = port_create();\n    if (state->portfd == -1) {\n        dfree(state);\n        return -1;\n    }\n\n    state->npending = 0;\n\n    for (i = 0; i < MAX_EVENT_BATCHSZ; i++) {\n        state->pending_fds[i] = -1;\n        state->pending_masks[i] = AE_NONE;\n    }\n\n    eventLoop->apidata = state;\n    return 0;\n}\n\nstatic int aeApiResize(aeEventLoop *eventLoop, int setsize) {\n    /* Nothing to resize here. */\n    return 0;\n}\n\nstatic void aeApiFree(aeEventLoop *eventLoop) {\n    aeApiState *state = eventLoop->apidata;\n\n    close(state->portfd);\n    dfree(state);\n}\n\nstatic int aeApiLookupPending(aeApiState *state, int fd) {\n    int i;\n\n    for (i = 0; i < state->npending; i++) {\n        if (state->pending_fds[i] == fd)\n            return (i);\n    }\n\n    return (-1);\n}\n\n/*\n * Helper function to invoke port_associate for the given fd and mask.\n */\nstatic int aeApiAssociate(const char *where, int portfd, int fd, int mask) {\n    int events = 0;\n    int rv, err;\n\n    if (mask & AE_READABLE)\n        events |= POLLIN;\n    if (mask & AE_WRITABLE)\n        events |= POLLOUT;\n\n    if (evport_debug)\n        fprintf(stderr, \"%s: port_associate(%d, 0x%x) = \", where, fd, events);\n\n    rv = port_associate(portfd, PORT_SOURCE_FD, fd, events,\n        (void *)(uintptr_t)mask);\n    err = errno;\n\n    if (evport_debug)\n        fprintf(stderr, \"%d (%s)\\n\", rv, rv == 0 ? \"no error\" : strerror(err));\n\n    if (rv == -1) {\n        fprintf(stderr, \"%s: port_associate: %s\\n\", where, strerror(err));\n\n        if (err == EAGAIN)\n            fprintf(stderr, \"aeApiAssociate: event port limit exceeded.\");\n    }\n\n    return rv;\n}\n\nstatic int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask) {\n    aeApiState *state = eventLoop->apidata;\n    int fullmask, pfd;\n\n    if (evport_debug)\n        fprintf(stderr, \"aeApiAddEvent: fd %d mask 0x%x\\n\", fd, mask);\n\n    /*\n     * Since port_associate's \"events\" argument replaces any existing events, we\n     * must be sure to include whatever events are already associated when\n     * we call port_associate() again.\n     */\n    fullmask = mask | eventLoop->events[fd].mask;\n    pfd = aeApiLookupPending(state, fd);\n\n    if (pfd != -1) {\n        /*\n         * This fd was recently returned from aeApiPoll.  It should be safe to\n         * assume that the consumer has processed that poll event, but we play\n         * it safer by simply updating pending_mask.  The fd will be\n         * re-associated as usual when aeApiPoll is called again.\n         */\n        if (evport_debug)\n            fprintf(stderr, \"aeApiAddEvent: adding to pending fd %d\\n\", fd);\n        state->pending_masks[pfd] |= fullmask;\n        return 0;\n    }\n\n    return (aeApiAssociate(\"aeApiAddEvent\", state->portfd, fd, fullmask));\n}\n\nstatic void aeApiDelEvent(aeEventLoop *eventLoop, int fd, int mask) {\n    aeApiState *state = eventLoop->apidata;\n    int fullmask, pfd;\n\n    if (evport_debug)\n        fprintf(stderr, \"del fd %d mask 0x%x\\n\", fd, mask);\n\n    pfd = aeApiLookupPending(state, fd);\n\n    if (pfd != -1) {\n        if (evport_debug)\n            fprintf(stderr, \"deleting event from pending fd %d\\n\", fd);\n\n        /*\n         * This fd was just returned from aeApiPoll, so it's not currently\n         * associated with the port.  All we need to do is update\n         * pending_mask appropriately.\n         */\n        state->pending_masks[pfd] &= ~mask;\n\n        if (state->pending_masks[pfd] == AE_NONE)\n            state->pending_fds[pfd] = -1;\n\n        return;\n    }\n\n    /*\n     * The fd is currently associated with the port.  Like with the add case\n     * above, we must look at the full mask for the file descriptor before\n     * updating that association.  We don't have a good way of knowing what the\n     * events are without looking into the eventLoop state directly.  We rely on\n     * the fact that our caller has already updated the mask in the eventLoop.\n     */\n\n    fullmask = eventLoop->events[fd].mask;\n    if (fullmask == AE_NONE) {\n        /*\n         * We're removing *all* events, so use port_dissociate to remove the\n         * association completely.  Failure here indicates a bug.\n         */\n        if (evport_debug)\n            fprintf(stderr, \"aeApiDelEvent: port_dissociate(%d)\\n\", fd);\n\n        if (port_dissociate(state->portfd, PORT_SOURCE_FD, fd) != 0) {\n            perror(\"aeApiDelEvent: port_dissociate\");\n            abort(); /* will not return */\n        }\n    } else if (aeApiAssociate(\"aeApiDelEvent\", state->portfd, fd,\n        fullmask) != 0) {\n        /*\n         * ENOMEM is a potentially transient condition, but the kernel won't\n         * generally return it unless things are really bad.  EAGAIN indicates\n         * we've reached an resource limit, for which it doesn't make sense to\n         * retry (counter-intuitively).  All other errors indicate a bug.  In any\n         * of these cases, the best we can do is to abort.\n         */\n        abort(); /* will not return */\n    }\n}\n\nstatic int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp) {\n    aeApiState *state = eventLoop->apidata;\n    struct timespec timeout, *tsp;\n    int mask, i;\n    uint_t nevents;\n    port_event_t event[MAX_EVENT_BATCHSZ];\n\n    /*\n     * If we've returned fd events before, we must re-associate them with the\n     * port now, before calling port_get().  See the block comment at the top of\n     * this file for an explanation of why.\n     */\n    for (i = 0; i < state->npending; i++) {\n        if (state->pending_fds[i] == -1)\n            /* This fd has since been deleted. */\n            continue;\n\n        if (aeApiAssociate(\"aeApiPoll\", state->portfd,\n            state->pending_fds[i], state->pending_masks[i]) != 0) {\n            /* See aeApiDelEvent for why this case is fatal. */\n            abort();\n        }\n\n        state->pending_masks[i] = AE_NONE;\n        state->pending_fds[i] = -1;\n    }\n\n    state->npending = 0;\n\n    if (tvp != NULL) {\n        timeout.tv_sec = tvp->tv_sec;\n        timeout.tv_nsec = tvp->tv_usec * 1000;\n        tsp = &timeout;\n    } else {\n        tsp = NULL;\n    }\n\n    /*\n     * port_getn can return with errno == ETIME having returned some events (!).\n     * So if we get ETIME, we check nevents, too.\n     */\n    nevents = 1;\n    if (port_getn(state->portfd, event, MAX_EVENT_BATCHSZ, &nevents,\n        tsp) == -1 && (errno != ETIME || nevents == 0)) {\n        if (errno == ETIME || errno == EINTR)\n            return 0;\n\n        /* Any other error indicates a bug. */\n        perror(\"aeApiPoll: port_get\");\n        abort();\n    }\n\n    state->npending = nevents;\n\n    for (i = 0; i < nevents; i++) {\n            mask = 0;\n            if (event[i].portev_events & POLLIN)\n                mask |= AE_READABLE;\n            if (event[i].portev_events & POLLOUT)\n                mask |= AE_WRITABLE;\n\n            eventLoop->fired[i].fd = event[i].portev_object;\n            eventLoop->fired[i].mask = mask;\n\n            if (evport_debug)\n                fprintf(stderr, \"aeApiPoll: fd %d mask 0x%x\\n\",\n                    (int)event[i].portev_object, mask);\n\n            state->pending_fds[i] = event[i].portev_object;\n            state->pending_masks[i] = (uintptr_t)event[i].portev_user;\n    }\n\n    return nevents;\n}\n\nstatic char *aeApiName(void) {\n    return \"evport\";\n}\n"
  },
  {
    "path": "dep/ae/ae_kqueue.c",
    "content": "/* Kqueue(2)-based ae.c module\n *\n * Copyright (C) 2009 Harish Mallipeddi - harish.mallipeddi@gmail.com\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n *   * Redistributions of source code must retain the above copyright notice,\n *     this list of conditions and the following disclaimer.\n *   * Redistributions in binary form must reproduce the above copyright\n *     notice, this list of conditions and the following disclaimer in the\n *     documentation and/or other materials provided with the distribution.\n *   * Neither the name of Redis nor the names of its contributors may be used\n *     to endorse or promote products derived from this software without\n *     specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\n\n#include <sys/types.h>\n#include <sys/event.h>\n#include <sys/time.h>\n\ntypedef struct aeApiState {\n    int kqfd;\n    struct kevent *events;\n} aeApiState;\n\nstatic int aeApiCreate(aeEventLoop *eventLoop) {\n    aeApiState *state = dalloc(sizeof(aeApiState));\n\n    if (!state) return -1;\n    state->events = dalloc(sizeof(struct kevent)*eventLoop->setsize);\n    if (!state->events) {\n        dfree(state);\n        return -1;\n    }\n    state->kqfd = kqueue();\n    if (state->kqfd == -1) {\n        dfree(state->events);\n        dfree(state);\n        return -1;\n    }\n    eventLoop->apidata = state;\n    return 0;\n}\n\nstatic int aeApiResize(aeEventLoop *eventLoop, int setsize) {\n    aeApiState *state = eventLoop->apidata;\n\n    state->events = drealloc(state->events, sizeof(struct kevent)*setsize);\n    return 0;\n}\n\nstatic void aeApiFree(aeEventLoop *eventLoop) {\n    aeApiState *state = eventLoop->apidata;\n\n    close(state->kqfd);\n    dfree(state->events);\n    dfree(state);\n}\n\nstatic int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask) {\n    aeApiState *state = eventLoop->apidata;\n    struct kevent ke;\n\n    if (mask & AE_READABLE) {\n        EV_SET(&ke, fd, EVFILT_READ, EV_ADD, 0, 0, NULL);\n        if (kevent(state->kqfd, &ke, 1, NULL, 0, NULL) == -1) return -1;\n    }\n    if (mask & AE_WRITABLE) {\n        EV_SET(&ke, fd, EVFILT_WRITE, EV_ADD, 0, 0, NULL);\n        if (kevent(state->kqfd, &ke, 1, NULL, 0, NULL) == -1) return -1;\n    }\n    return 0;\n}\n\nstatic void aeApiDelEvent(aeEventLoop *eventLoop, int fd, int mask) {\n    aeApiState *state = eventLoop->apidata;\n    struct kevent ke;\n\n    if (mask & AE_READABLE) {\n        EV_SET(&ke, fd, EVFILT_READ, EV_DELETE, 0, 0, NULL);\n        kevent(state->kqfd, &ke, 1, NULL, 0, NULL);\n    }\n    if (mask & AE_WRITABLE) {\n        EV_SET(&ke, fd, EVFILT_WRITE, EV_DELETE, 0, 0, NULL);\n        kevent(state->kqfd, &ke, 1, NULL, 0, NULL);\n    }\n}\n\nstatic int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp) {\n    aeApiState *state = eventLoop->apidata;\n    int retval, numevents = 0;\n\n    if (tvp != NULL) {\n        struct timespec timeout;\n        timeout.tv_sec = tvp->tv_sec;\n        timeout.tv_nsec = tvp->tv_usec * 1000;\n        retval = kevent(state->kqfd, NULL, 0, state->events, eventLoop->setsize,\n                        &timeout);\n    } else {\n        retval = kevent(state->kqfd, NULL, 0, state->events, eventLoop->setsize,\n                        NULL);\n    }\n\n    if (retval > 0) {\n        int j;\n\n        numevents = retval;\n        for(j = 0; j < numevents; j++) {\n            int mask = 0;\n            struct kevent *e = state->events+j;\n\n            if (e->filter == EVFILT_READ) mask |= AE_READABLE;\n            if (e->filter == EVFILT_WRITE) mask |= AE_WRITABLE;\n            eventLoop->fired[j].fd = e->ident;\n            eventLoop->fired[j].mask = mask;\n        }\n    }\n    return numevents;\n}\n\nstatic char *aeApiName(void) {\n    return \"kqueue\";\n}\n"
  },
  {
    "path": "dep/ae/ae_select.c",
    "content": "/* Select()-based ae.c module.\n *\n * Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n *   * Redistributions of source code must retain the above copyright notice,\n *     this list of conditions and the following disclaimer.\n *   * Redistributions in binary form must reproduce the above copyright\n *     notice, this list of conditions and the following disclaimer in the\n *     documentation and/or other materials provided with the distribution.\n *   * Neither the name of Redis nor the names of its contributors may be used\n *     to endorse or promote products derived from this software without\n *     specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\n\n#include <string.h>\n\ntypedef struct aeApiState {\n    fd_set rfds, wfds;\n    /* We need to have a copy of the fd sets as it's not safe to reuse\n     * FD sets after select(). */\n    fd_set _rfds, _wfds;\n} aeApiState;\n\nstatic int aeApiCreate(aeEventLoop *eventLoop) {\n    aeApiState *state = dalloc(sizeof(aeApiState));\n\n    if (!state) return -1;\n    FD_ZERO(&state->rfds);\n    FD_ZERO(&state->wfds);\n    eventLoop->apidata = state;\n    return 0;\n}\n\nstatic int aeApiResize(aeEventLoop *eventLoop, int setsize) {\n    /* Just ensure we have enough room in the fd_set type. */\n    if (setsize >= FD_SETSIZE) return -1;\n    return 0;\n}\n\nstatic void aeApiFree(aeEventLoop *eventLoop) {\n    dfree(eventLoop->apidata);\n}\n\nstatic int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask) {\n    aeApiState *state = eventLoop->apidata;\n\n    if (mask & AE_READABLE) FD_SET(fd,&state->rfds);\n    if (mask & AE_WRITABLE) FD_SET(fd,&state->wfds);\n    return 0;\n}\n\nstatic void aeApiDelEvent(aeEventLoop *eventLoop, int fd, int mask) {\n    aeApiState *state = eventLoop->apidata;\n\n    if (mask & AE_READABLE) FD_CLR(fd,&state->rfds);\n    if (mask & AE_WRITABLE) FD_CLR(fd,&state->wfds);\n}\n\nstatic int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp) {\n    aeApiState *state = eventLoop->apidata;\n    int retval, j, numevents = 0;\n\n    memcpy(&state->_rfds,&state->rfds,sizeof(fd_set));\n    memcpy(&state->_wfds,&state->wfds,sizeof(fd_set));\n\n    retval = select(eventLoop->maxfd+1,\n                &state->_rfds,&state->_wfds,NULL,tvp);\n    if (retval > 0) {\n        for (j = 0; j <= eventLoop->maxfd; j++) {\n            int mask = 0;\n            aeFileEvent *fe = &eventLoop->events[j];\n\n            if (fe->mask == AE_NONE) continue;\n            if (fe->mask & AE_READABLE && FD_ISSET(j,&state->_rfds))\n                mask |= AE_READABLE;\n            if (fe->mask & AE_WRITABLE && FD_ISSET(j,&state->_wfds))\n                mask |= AE_WRITABLE;\n            eventLoop->fired[numevents].fd = j;\n            eventLoop->fired[numevents].mask = mask;\n            numevents++;\n        }\n    }\n    return numevents;\n}\n\nstatic char *aeApiName(void) {\n    return \"select\";\n}\n"
  },
  {
    "path": "dep/darray/Makefile.am",
    "content": "MAINTAINERCLEANFILES = Makefile.in\n\nAM_CPPFLAGS =\nif !OS_SOLARIS\nAM_CPPFLAGS += -D_GNU_SOURCE\nendif\nAM_CPPFLAGS += -I $(top_srcdir)/dep/util\nAM_CPPFLAGS += -I $(top_srcdir)/dep/jemalloc-4.2.0/include\nAM_CPPFLAGS += -I $(top_srcdir)/dep/dmalloc\n\nAM_CFLAGS = -Wall -Wshadow\nAM_CFLAGS += -Wno-unused-parameter -Wno-unused-value\n\nnoinst_LIBRARIES = libdarray.a\n\nnoinst_HEADERS = darray.h\n\nlibdarray_a_SOURCES = \\\n\tdarray.c darray.h"
  },
  {
    "path": "dep/darray/darray.c",
    "content": "#include <stdlib.h>\n\n#include <dmalloc.h>\n\n#include <darray.h>\n\ndarray *\ndarray_create(unsigned long long n, size_t size)\n{\n    darray *a;\n\n    a = dalloc(sizeof(*a));\n    if (a == NULL) {\n        return NULL;\n    }\n\n    a->elem = dalloc(n * size);\n    if (a->elem == NULL) {\n        dfree(a);\n        return NULL;\n    }\n\n    a->nelem = 0;\n    a->size = size;\n    a->nalloc = n;\n\n    return a;\n}\n\nvoid\ndarray_destroy(darray *a)\n{\n    darray_deinit(a);\n    dfree(a);\n}\n\nint\ndarray_init(darray *a, unsigned long long n, size_t size)\n{\n    a->elem = dalloc(n * size);\n    if (a->elem == NULL) {\n        return -1;\n    }\n\n    a->nelem = 0;\n    a->size = size;\n    a->nalloc = n;\n\n    return 0;\n}\n\nvoid\ndarray_deinit(darray *a)\n{\n    if (a->elem != NULL) {\n        dfree(a->elem);\n    }\n}\n\nunsigned long long\ndarray_idx(darray *a, void *elem)\n{\n    char *p, *q;\n    unsigned long long off, idx;\n\n    p = a->elem;\n    q = elem;\n    off = (unsigned long long)(q - p);\n\n    idx = off / (unsigned long long)a->size;\n\n    return idx;\n}\n\nvoid *\ndarray_push(darray *a)\n{\n    void *elem, *new;\n    size_t size;\n\n    if (a->nelem == a->nalloc) {\n\n        /* the array is full; allocate new array */\n        size = a->size * a->nalloc;\n        new = drealloc(a->elem, 2 * size);\n        if (new == NULL) {\n            return NULL;\n        }\n\n        a->elem = new;\n        a->nalloc *= 2;\n    }\n\n    elem = (char *)a->elem + a->size * a->nelem;\n    a->nelem++;\n\n    return elem;\n}\n\nvoid *\ndarray_pop(darray *a)\n{\n    void *elem;\n\n    a->nelem--;\n    elem = (char *)a->elem + a->size * a->nelem;\n\n    return elem;\n}\n\nvoid *\ndarray_get(darray *a, unsigned long long idx)\n{\n    void *elem;\n\n    elem = (char *)a->elem + (a->size * idx);\n\n    return elem;\n}\n\nvoid *\ndarray_top(darray *a)\n{\n    return darray_get(a, a->nelem - 1);\n}\n\nvoid\ndarray_swap(darray *a, darray *b)\n{\n    darray tmp;\n\n    tmp = *a;\n    *a = *b;\n    *b = tmp;\n}\n\n/*\n * Sort nelem elements of the array in ascending order based on the\n * compare comparator.\n */\nvoid\ndarray_sort(darray *a, darray_compare_t compare)\n{\n    qsort(a->elem, a->nelem, a->size, compare);\n}\n\n/*\n * Calls the func once for each element in the array as long as func returns\n * success. On failure short-circuits and returns the error status.\n */\nint\ndarray_each(darray *a, darray_each_t func, void *data)\n{\n    unsigned long long i, nelem;\n\n    for (i = 0, nelem = darray_n(a); i < nelem; i++) {\n        void *elem = darray_get(a, i);\n        int ret;\n\n        ret = func(elem, data);\n        if (ret != 0) {\n            return -1;\n        }\n    }\n\n    return 0;\n}\n"
  },
  {
    "path": "dep/darray/darray.h",
    "content": "#ifndef _DARRAY_H_\n#define _DARRAY_H_\n\ntypedef int (*darray_compare_t)(const void *, const void *);\ntypedef int (*darray_each_t)(void *, void *);\n\ntypedef struct darray {\n    unsigned long long   nelem;  /* # element */\n    void                 *elem;  /* element */\n    size_t               size;   /* element size */\n    unsigned long long   nalloc; /* # allocated element */\n} darray;\n\n#define null_darray { 0, NULL, 0, 0 }\n\nstatic inline void\ndarray_null(darray *a)\n{\n    a->nelem = 0;\n    a->elem = NULL;\n    a->size = 0;\n    a->nalloc = 0;\n}\n\nstatic inline void\ndarray_set(darray *a, void *elem, size_t size, unsigned long long nalloc)\n{\n    a->nelem = 0;\n    a->elem = elem;\n    a->size = size;\n    a->nalloc = nalloc;\n}\n\nstatic inline unsigned long long\ndarray_n(const darray *a)\n{\n    return a->nelem;\n}\n\ndarray *darray_create(unsigned long long n, size_t size);\nvoid darray_destroy(darray *a);\nint darray_init(darray *a, unsigned long long n, size_t size);\nvoid darray_deinit(darray *a);\n\nunsigned long long darray_idx(darray *a, void *elem);\nvoid *darray_push(darray *a);\nvoid *darray_pop(darray *a);\nvoid *darray_get(darray *a, unsigned long long idx);\nvoid *darray_top(darray *a);\nvoid darray_swap(darray *a, darray *b);\nvoid darray_sort(darray *a, darray_compare_t compare);\nint darray_each(darray *a, darray_each_t func, void *data);\n\n#endif\n"
  },
  {
    "path": "dep/dhashkit/Makefile.am",
    "content": "MAINTAINERCLEANFILES = Makefile.in\n\nAM_CFLAGS = -Wall -Wshadow\nAM_CFLAGS += -Wno-unused-parameter -Wno-unused-value\n\nnoinst_LIBRARIES = libdhashkit.a\n\nnoinst_HEADERS = dhashkit.h\n\nlibdhashkit_a_SOURCES =\t\\\n\tdhashkit.h\t\t    \\\n\tdcrc16.c\t\t\t\\\n\tdcrc32.c\t\t\t\\\n\tdfnv.c\t\t\t    \\\n\tdhsieh.c\t\t\t\\\n\tdjenkins.c\t\t    \\\n\tdketama.c\t\t\t\\\n\tdmd5.c\t\t\t    \\\n\tdmodula.c\t\t\t\\\n\tdmurmur.c\t\t\t\\\n\tdone_at_a_time.c\t\\\n\tdrandom.c\t\t\t\\\n\tdsha1.c"
  },
  {
    "path": "dep/dhashkit/dcrc16.c",
    "content": "#include <dhashkit.h>\n\nstatic const uint16_t crc16tab[256] = {\n  0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,\n  0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,\n  0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,\n  0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,\n  0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,\n  0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,\n  0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,\n  0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,\n  0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,\n  0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,\n  0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,\n  0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,\n  0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,\n  0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,\n  0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,\n  0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,\n  0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,\n  0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,\n  0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,\n  0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,\n  0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,\n  0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,\n  0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,\n  0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,\n  0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,\n  0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,\n  0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,\n  0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,\n  0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,\n  0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,\n  0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,\n  0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0,\n};\n\nuint32_t\nhash_crc16(const char *key, size_t key_length)\n{\n    uint64_t x;\n    uint32_t crc = 0;\n\n    for (x=0; x < key_length; x++) {\n        crc = (crc << 8) ^ crc16tab[((crc >> 8) ^ *key++) & 0x00ff];\n    }\n\n    return crc;\n}\n"
  },
  {
    "path": "dep/dhashkit/dcrc32.c",
    "content": "#include <dhashkit.h>\n\nstatic const uint32_t crc32tab[256] = {\n    0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,\n    0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,\n    0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,\n    0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,\n    0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,\n    0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,\n    0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,\n    0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,\n    0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,\n    0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,\n    0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,\n    0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,\n    0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,\n    0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,\n    0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,\n    0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,\n    0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,\n    0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,\n    0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,\n    0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,\n    0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,\n    0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,\n    0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,\n    0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,\n    0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,\n    0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,\n    0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,\n    0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,\n    0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,\n    0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,\n    0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,\n    0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,\n    0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,\n    0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,\n    0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,\n    0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,\n    0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,\n    0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,\n    0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,\n    0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,\n    0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,\n    0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,\n    0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,\n    0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,\n    0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,\n    0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,\n    0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,\n    0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,\n    0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,\n    0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,\n    0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,\n    0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,\n    0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,\n    0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,\n    0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,\n    0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,\n    0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,\n    0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,\n    0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,\n    0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,\n    0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,\n    0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,\n    0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,\n    0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,\n};\n\n/*\n * CRC-32 implementation compatible with libmemcached library. Unfortunately\n * this implementation does not return CRC-32 as per spec.\n */\nuint32_t\nhash_crc32(const char *key, size_t key_length)\n{\n    uint64_t x;\n    uint32_t crc = UINT32_MAX;\n\n    for (x = 0; x < key_length; x++) {\n        crc = (crc >> 8) ^ crc32tab[(crc ^ (uint64_t)key[x]) & 0xff];\n    }\n\n    return ((~crc) >> 16) & 0x7fff;\n}\n\nuint32_t\nhash_crc32a(const char *key, size_t key_length)\n{\n    const uint8_t *p = key;\n    uint32_t crc;\n\n    crc = ~0U;\n    while (key_length--) {\n        crc = crc32tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8);\n    }\n\n    return crc ^ ~0U;\n}\n"
  },
  {
    "path": "dep/dhashkit/dfnv.c",
    "content": "#include <dhashkit.h>\n\nstatic uint64_t FNV_64_INIT = UINT64_C(0xcbf29ce484222325);\nstatic uint64_t FNV_64_PRIME = UINT64_C(0x100000001b3);\nstatic uint32_t FNV_32_INIT = 2166136261UL;\nstatic uint32_t FNV_32_PRIME = 16777619;\n\nuint32_t\nhash_fnv1_64(const char *key, size_t key_length)\n{\n    uint64_t hash = FNV_64_INIT;\n    size_t x;\n\n    for (x = 0; x < key_length; x++) {\n      hash *= FNV_64_PRIME;\n      hash ^= (uint64_t)key[x];\n    }\n\n    return (uint32_t)hash;\n}\n\nuint32_t\nhash_fnv1a_64(const char *key, size_t key_length)\n{\n    uint32_t hash = (uint32_t) FNV_64_INIT;\n    size_t x;\n\n    for (x = 0; x < key_length; x++) {\n      uint32_t val = (uint32_t)key[x];\n      hash ^= val;\n      hash *= (uint32_t) FNV_64_PRIME;\n    }\n\n    return hash;\n}\n\nuint32_t\nhash_fnv1_32(const char *key, size_t key_length)\n{\n    uint32_t hash = FNV_32_INIT;\n    size_t x;\n\n    for (x = 0; x < key_length; x++) {\n      uint32_t val = (uint32_t)key[x];\n      hash *= FNV_32_PRIME;\n      hash ^= val;\n    }\n\n    return hash;\n}\n\nuint32_t\nhash_fnv1a_32(const char *key, size_t key_length)\n{\n    uint32_t hash = FNV_32_INIT;\n    size_t x;\n\n    for (x= 0; x < key_length; x++) {\n      uint32_t val = (uint32_t)key[x];\n      hash ^= val;\n      hash *= FNV_32_PRIME;\n    }\n\n    return hash;\n}\n"
  },
  {
    "path": "dep/dhashkit/dhashkit.h",
    "content": "#ifndef _DHASHKIT_H_\n#define _DHASHKIT_H_\n\n#include <stdint.h>\n#include <stdio.h>\n\n#include <sys/types.h>\n\nstruct continuum {\n    uint32_t index;  /* server index */\n    uint32_t value;  /* hash value */\n};\n\n#define HASH_CODEC(ACTION)                      \\\n    ACTION( HASH_ONE_AT_A_TIME, one_at_a_time ) \\\n    ACTION( HASH_MD5,           md5           ) \\\n    ACTION( HASH_CRC16,         crc16         ) \\\n    ACTION( HASH_CRC32,         crc32         ) \\\n    ACTION( HASH_CRC32A,        crc32a        ) \\\n    ACTION( HASH_FNV1_64,       fnv1_64       ) \\\n    ACTION( HASH_FNV1A_64,      fnv1a_64      ) \\\n    ACTION( HASH_FNV1_32,       fnv1_32       ) \\\n    ACTION( HASH_FNV1A_32,      fnv1a_32      ) \\\n    ACTION( HASH_HSIEH,         hsieh         ) \\\n    ACTION( HASH_MURMUR,        murmur        ) \\\n    ACTION( HASH_JENKINS,       jenkins       ) \\\n\n#define DIST_CODEC(ACTION)                      \\\n    ACTION( DIST_KETAMA,        ketama        ) \\\n    ACTION( DIST_MODULA,        modula        ) \\\n    ACTION( DIST_RANDOM,        random        ) \\\n\n#define DEFINE_ACTION(_hash, _name) _hash,\ntypedef enum hash_type {\n    HASH_CODEC( DEFINE_ACTION )\n    HASH_SENTINEL\n} hash_type_t;\n#undef DEFINE_ACTION\n\n#define DEFINE_ACTION(_dist, _name) _dist,\ntypedef enum dist_type {\n    DIST_CODEC( DEFINE_ACTION )\n    DIST_SENTINEL\n} dist_type_t;\n#undef DEFINE_ACTION\n\nuint32_t hash_one_at_a_time(const char *key, size_t key_length);\nvoid md5_signature(const unsigned char *key, unsigned long length, unsigned char *result);\nuint32_t hash_md5(const char *key, size_t key_length);\nuint32_t hash_crc16(const char *key, size_t key_length);\nuint32_t hash_crc32(const char *key, size_t key_length);\nuint32_t hash_crc32a(const char *key, size_t key_length);\nuint32_t hash_fnv1_64(const char *key, size_t key_length);\nuint32_t hash_fnv1a_64(const char *key, size_t key_length);\nuint32_t hash_fnv1_32(const char *key, size_t key_length);\nuint32_t hash_fnv1a_32(const char *key, size_t key_length);\nuint32_t hash_hsieh(const char *key, size_t key_length);\nuint32_t hash_jenkins(const char *key, size_t length);\nuint32_t hash_murmur(const char *key, size_t length);\n\nuint32_t ketama_dispatch(struct continuum *continuum, uint32_t ncontinuum, uint32_t hash);\nuint32_t modula_dispatch(struct continuum *continuum, uint32_t ncontinuum, uint32_t hash);\nuint32_t random_dispatch(struct continuum *continuum, uint32_t ncontinuum, uint32_t hash);\n\n\n/*SHA-1 in CBy Steve Reid <steve@edmweb.com>100% Public Domain*/\ntypedef struct {\n    uint32_t state[5];    \n    uint32_t count[2];    \n    unsigned char buffer[64];\n} SHA1_CTX;\n\nvoid SHA1Transform(uint32_t state[5], const unsigned char buffer[64]);\nvoid SHA1Init(SHA1_CTX* context);\nvoid SHA1Update(SHA1_CTX* context, const unsigned char* data, uint32_t len);\nvoid SHA1Final(unsigned char digest[20], SHA1_CTX* context);\n\n#endif\n"
  },
  {
    "path": "dep/dhashkit/dhsieh.c",
    "content": "#include <dhashkit.h>\n\n#undef get16bits\n#if (defined(__GNUC__) && defined(__i386__))\n#define get16bits(d) (*((const uint16_t *) (d)))\n#endif\n\n#if !defined (get16bits)\n#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8)\\\n                      +(uint32_t)(((const uint8_t *)(d))[0]) )\n#endif\n\nuint32_t\nhash_hsieh(const char *key, size_t key_length)\n{\n    uint32_t hash = 0, tmp;\n    int rem;\n\n    if (key_length <= 0 || key == NULL) {\n        return 0;\n    }\n\n    rem = key_length & 3;\n    key_length >>= 2;\n\n    /* Main loop */\n    for (;key_length > 0; key_length--) {\n        hash += get16bits (key);\n        tmp = (get16bits (key+2) << 11) ^ hash;\n        hash = (hash << 16) ^ tmp;\n        key += 2*sizeof (uint16_t);\n        hash += hash >> 11;\n    }\n\n    /* Handle end cases */\n    switch (rem) {\n    case 3:\n        hash += get16bits (key);\n        hash ^= hash << 16;\n        hash ^= (uint32_t)key[sizeof (uint16_t)] << 18;\n        hash += hash >> 11;\n        break;\n\n    case 2:\n        hash += get16bits (key);\n        hash ^= hash << 11;\n        hash += hash >> 17;\n        break;\n\n    case 1:\n        hash += (unsigned char)(*key);\n        hash ^= hash << 10;\n        hash += hash >> 1;\n\n    default:\n        break;\n    }\n\n    /* Force \"avalanching\" of final 127 bits */\n    hash ^= hash << 3;\n    hash += hash >> 5;\n    hash ^= hash << 4;\n    hash += hash >> 17;\n    hash ^= hash << 25;\n    hash += hash >> 6;\n\n    return hash;\n}\n"
  },
  {
    "path": "dep/dhashkit/djenkins.c",
    "content": "#include <dhashkit.h>\n\n#define hashsize(n) ((uint32_t)1<<(n))\n#define hashmask(n) (hashsize(n)-1)\n#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k))))\n\n#define mix(a,b,c) \\\n{ \\\n  a -= c;  a ^= rot(c, 4);  c += b; \\\n  b -= a;  b ^= rot(a, 6);  a += c; \\\n  c -= b;  c ^= rot(b, 8);  b += a; \\\n  a -= c;  a ^= rot(c,16);  c += b; \\\n  b -= a;  b ^= rot(a,19);  a += c; \\\n  c -= b;  c ^= rot(b, 4);  b += a; \\\n}\n\n#define final(a,b,c) \\\n{ \\\n  c ^= b; c -= rot(b,14); \\\n  a ^= c; a -= rot(c,11); \\\n  b ^= a; b -= rot(a,25); \\\n  c ^= b; c -= rot(b,16); \\\n  a ^= c; a -= rot(c,4);  \\\n  b ^= a; b -= rot(a,14); \\\n  c ^= b; c -= rot(b,24); \\\n}\n\n#define JENKINS_INITVAL 13\n\n/*\n * jenkins_hash() -- hash a variable-length key into a 32-bit value\n *  k       : the key (the unaligned variable-length array of bytes)\n *  length  : the length of the key, counting by bytes\n *  initval : can be any 4-byte value\n * Returns a 32-bit value.  Every bit of the key affects every bit of\n * the return value.  Two keys differing by one or two bits will have\n * totally different hash values.\n\n * The best hash table sizes are powers of 2.  There is no need to do\n * mod a prime (mod is sooo slow!).  If you need less than 32 bits,\n * use a bitmask.  For example, if you need only 10 bits, do\n *   h = (h & hashmask(10));\n * In which case, the hash table should have hashsize(10) elements.\n */\n\nuint32_t\nhash_jenkins(const char *key, size_t length)\n{\n  uint32_t a,b,c;                                          /* internal state */\n  union { const void *ptr; size_t i; } u;     /* needed for Mac Powerbook G4 */\n\n  /* Set up the internal state */\n  a = b = c = 0xdeadbeef + ((uint32_t)length) + JENKINS_INITVAL;\n\n  u.ptr = key;\n#ifndef WORDS_BIGENDIAN\n  if ((u.i & 0x3) == 0)\n  {\n    const uint32_t *k = (const uint32_t *)key;         /* read 32-bit chunks */\n\n    /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */\n    while (length > 12)\n    {\n      a += k[0];\n      b += k[1];\n      c += k[2];\n      mix(a,b,c);\n      length -= 12;\n      k += 3;\n    }\n\n    /*----------------------------- handle the last (probably partial) block */\n    /*\n     * \"k[2]&0xffffff\" actually reads beyond the end of the string, but\n     * then masks off the part it's not allowed to read.  Because the\n     * string is aligned, the masked-off tail is in the same word as the\n     * rest of the string.  Every machine with memory protection I've seen\n     * does it on word boundaries, so is OK with this.  But VALGRIND will\n     * still catch it and complain.  The masking trick does make the hash\n     * noticeably faster for short strings (like English words).\n     */\n    switch(length)\n    {\n    case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;\n    case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break;\n    case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break;\n    case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break;\n    case 8 : b+=k[1]; a+=k[0]; break;\n    case 7 : b+=k[1]&0xffffff; a+=k[0]; break;\n    case 6 : b+=k[1]&0xffff; a+=k[0]; break;\n    case 5 : b+=k[1]&0xff; a+=k[0]; break;\n    case 4 : a+=k[0]; break;\n    case 3 : a+=k[0]&0xffffff; break;\n    case 2 : a+=k[0]&0xffff; break;\n    case 1 : a+=k[0]&0xff; break;\n    case 0 : return c;              /* zero length strings require no mixing */\n    default: return c;\n    }\n\n  }\n  else if ((u.i & 0x1) == 0)\n  {\n    const uint16_t *k = (const uint16_t *)key;         /* read 16-bit chunks */\n    const uint8_t  *k8;\n\n    /*--------------- all but last block: aligned reads and different mixing */\n    while (length > 12)\n    {\n      a += k[0] + (((uint32_t)k[1])<<16);\n      b += k[2] + (((uint32_t)k[3])<<16);\n      c += k[4] + (((uint32_t)k[5])<<16);\n      mix(a,b,c);\n      length -= 12;\n      k += 6;\n    }\n\n    /*----------------------------- handle the last (probably partial) block */\n    k8 = (const uint8_t *)k;\n    switch(length)\n    {\n    case 12: c+=k[4]+(((uint32_t)k[5])<<16);\n             b+=k[2]+(((uint32_t)k[3])<<16);\n             a+=k[0]+(((uint32_t)k[1])<<16);\n             break;\n    case 11: c+=((uint32_t)k8[10])<<16;     /* fall through */\n    case 10: c+=k[4];\n             b+=k[2]+(((uint32_t)k[3])<<16);\n             a+=k[0]+(((uint32_t)k[1])<<16);\n             break;\n    case 9 : c+=k8[8];                      /* fall through */\n    case 8 : b+=k[2]+(((uint32_t)k[3])<<16);\n             a+=k[0]+(((uint32_t)k[1])<<16);\n             break;\n    case 7 : b+=((uint32_t)k8[6])<<16;      /* fall through */\n    case 6 : b+=k[2];\n             a+=k[0]+(((uint32_t)k[1])<<16);\n             break;\n    case 5 : b+=k8[4];                      /* fall through */\n    case 4 : a+=k[0]+(((uint32_t)k[1])<<16);\n             break;\n    case 3 : a+=((uint32_t)k8[2])<<16;      /* fall through */\n    case 2 : a+=k[0];\n             break;\n    case 1 : a+=k8[0];\n             break;\n    case 0 : return c;                     /* zero length requires no mixing */\n    default: return c;\n    }\n\n  }\n  else\n  {                        /* need to read the key one byte at a time */\n#endif /* little endian */\n    const uint8_t *k = (const uint8_t *)key;\n\n    /*--------------- all but the last block: affect some 32 bits of (a,b,c) */\n    while (length > 12)\n    {\n      a += k[0];\n      a += ((uint32_t)k[1])<<8;\n      a += ((uint32_t)k[2])<<16;\n      a += ((uint32_t)k[3])<<24;\n      b += k[4];\n      b += ((uint32_t)k[5])<<8;\n      b += ((uint32_t)k[6])<<16;\n      b += ((uint32_t)k[7])<<24;\n      c += k[8];\n      c += ((uint32_t)k[9])<<8;\n      c += ((uint32_t)k[10])<<16;\n      c += ((uint32_t)k[11])<<24;\n      mix(a,b,c);\n      length -= 12;\n      k += 12;\n    }\n\n    /*-------------------------------- last block: affect all 32 bits of (c) */\n    switch(length)                   /* all the case statements fall through */\n    {\n    case 12: c+=((uint32_t)k[11])<<24;\n    case 11: c+=((uint32_t)k[10])<<16;\n    case 10: c+=((uint32_t)k[9])<<8;\n    case 9 : c+=k[8];\n    case 8 : b+=((uint32_t)k[7])<<24;\n    case 7 : b+=((uint32_t)k[6])<<16;\n    case 6 : b+=((uint32_t)k[5])<<8;\n    case 5 : b+=k[4];\n    case 4 : a+=((uint32_t)k[3])<<24;\n    case 3 : a+=((uint32_t)k[2])<<16;\n    case 2 : a+=((uint32_t)k[1])<<8;\n    case 1 : a+=k[0];\n             break;\n    case 0 : return c;\n    default : return c;\n    }\n#ifndef WORDS_BIGENDIAN\n  }\n#endif\n\n  final(a,b,c);\n  return c;\n}\n"
  },
  {
    "path": "dep/dhashkit/dketama.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <math.h>\n\n#include <dhashkit.h>\n\n#define KETAMA_CONTINUUM_ADDITION   10  /* # extra slots to build into continuum */\n#define KETAMA_POINTS_PER_SERVER    160 /* 40 points per hash */\n#define KETAMA_MAX_HOSTLEN          86\n\nstatic uint32_t\nketama_hash(const char *key, size_t key_length, uint32_t alignment)\n{\n    unsigned char results[16];\n\n    md5_signature((const unsigned char*)key, (unsigned long)key_length, results);\n\n    return ((uint32_t) (results[3 + alignment * 4] & 0xFF) << 24)\n        | ((uint32_t) (results[2 + alignment * 4] & 0xFF) << 16)\n        | ((uint32_t) (results[1 + alignment * 4] & 0xFF) << 8)\n        | (results[0 + alignment * 4] & 0xFF);\n}\n\nstatic int\nketama_item_cmp(const void *t1, const void *t2)\n{\n    const struct continuum *ct1 = t1, *ct2 = t2;\n\n    if (ct1->value == ct2->value) {\n        return 0;\n    } else if (ct1->value > ct2->value) {\n        return 1;\n    } else {\n        return -1;\n    }\n}\n\nuint32_t\nketama_dispatch(struct continuum *continuum, uint32_t ncontinuum, uint32_t hash)\n{\n    struct continuum *begin, *end, *left, *right, *middle;\n\n    ASSERT(continuum != NULL);\n    ASSERT(ncontinuum != 0);\n\n    begin = left = continuum;\n    end = right = continuum + ncontinuum;\n\n    while (left < right) {\n        middle = left + (right - left) / 2;\n        if (middle->value < hash) {\n          left = middle + 1;\n        } else {\n          right = middle;\n        }\n    }\n\n    if (right == end) {\n        right = begin;\n    }\n\n    return right->index;\n}\n"
  },
  {
    "path": "dep/dhashkit/dmd5.c",
    "content": "#include <string.h>\n\n#include <dhashkit.h>\n\n/*\n * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc.\n * MD5 Message-Digest Algorithm (RFC 1321).\n *\n * Homepage: http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5\n *\n * Author: Alexander Peslyak, better known as Solar Designer <solar at openwall.com>\n */\n\ntypedef unsigned int MD5_u32plus;\n\ntypedef struct {\n    MD5_u32plus lo, hi;\n    MD5_u32plus a, b, c, d;\n    unsigned char buffer[64];\n    MD5_u32plus block[16];\n} MD5_CTX;\n\n/*\n * The basic MD5 functions.\n *\n * F and G are optimized compared to their RFC 1321 definitions for\n * architectures that lack an AND-NOT instruction, just like in Colin Plumb's\n * implementation.\n */\n#define F(x, y, z)\t\t\t((z) ^ ((x) & ((y) ^ (z))))\n#define G(x, y, z)\t\t\t((y) ^ ((z) & ((x) ^ (y))))\n#define H(x, y, z)\t\t\t((x) ^ (y) ^ (z))\n#define I(x, y, z)\t\t\t((y) ^ ((x) | ~(z)))\n\n/*\n * The MD5 transformation for all four rounds.\n */\n#define STEP(f, a, b, c, d, x, t, s)                            \\\n\t(a) += f((b), (c), (d)) + (x) + (t);                        \\\n\t(a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s))));  \\\n\t(a) += (b);\n\n/*\n * SET reads 4 input bytes in little-endian byte order and stores them\n * in a properly aligned word in host byte order.\n *\n * The check for little-endian architectures that tolerate unaligned\n * memory accesses is just an optimization.  Nothing will break if it\n * doesn't work.\n */\n#if defined(__i386__) || defined(__x86_64__) || defined(__vax__)\n#define SET(n) \\\n    (*(MD5_u32plus *)&ptr[(n) * 4])\n#define GET(n) \\\n    SET(n)\n#else\n#define SET(n)                                  \\\n    (ctx->block[(n)] =                          \\\n    (MD5_u32plus)ptr[(n) * 4] |                 \\\n    ((MD5_u32plus)ptr[(n) * 4 + 1] << 8) |      \\\n    ((MD5_u32plus)ptr[(n) * 4 + 2] << 16) |     \\\n    ((MD5_u32plus)ptr[(n) * 4 + 3] << 24))\n#define GET(n) \\\n    (ctx->block[(n)])\n#endif\n\n/*\n * This processes one or more 64-byte data blocks, but does NOT update\n * the bit counters.  There are no alignment requirements.\n */\nstatic void *\nbody(MD5_CTX *ctx, void *data, unsigned long size)\n{\n    unsigned char *ptr;\n    MD5_u32plus a, b, c, d;\n    MD5_u32plus saved_a, saved_b, saved_c, saved_d;\n\n    ptr = data;\n\n    a = ctx->a;\n    b = ctx->b;\n    c = ctx->c;\n    d = ctx->d;\n\n\tdo {\n        saved_a = a;\n        saved_b = b;\n        saved_c = c;\n        saved_d = d;\n\n        /* Round 1 */\n        STEP(F, a, b, c, d, SET(0), 0xd76aa478, 7)\n        STEP(F, d, a, b, c, SET(1), 0xe8c7b756, 12)\n        STEP(F, c, d, a, b, SET(2), 0x242070db, 17)\n        STEP(F, b, c, d, a, SET(3), 0xc1bdceee, 22)\n        STEP(F, a, b, c, d, SET(4), 0xf57c0faf, 7)\n        STEP(F, d, a, b, c, SET(5), 0x4787c62a, 12)\n        STEP(F, c, d, a, b, SET(6), 0xa8304613, 17)\n        STEP(F, b, c, d, a, SET(7), 0xfd469501, 22)\n        STEP(F, a, b, c, d, SET(8), 0x698098d8, 7)\n        STEP(F, d, a, b, c, SET(9), 0x8b44f7af, 12)\n        STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17)\n        STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22)\n        STEP(F, a, b, c, d, SET(12), 0x6b901122, 7)\n        STEP(F, d, a, b, c, SET(13), 0xfd987193, 12)\n        STEP(F, c, d, a, b, SET(14), 0xa679438e, 17)\n        STEP(F, b, c, d, a, SET(15), 0x49b40821, 22)\n\n        /* Round 2 */\n        STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5)\n        STEP(G, d, a, b, c, GET(6), 0xc040b340, 9)\n        STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14)\n        STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20)\n        STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5)\n        STEP(G, d, a, b, c, GET(10), 0x02441453, 9)\n        STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14)\n        STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20)\n        STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5)\n        STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9)\n        STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14)\n        STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20)\n        STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5)\n        STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9)\n        STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14)\n        STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20)\n\n        /* Round 3 */\n        STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4)\n        STEP(H, d, a, b, c, GET(8), 0x8771f681, 11)\n        STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16)\n        STEP(H, b, c, d, a, GET(14), 0xfde5380c, 23)\n        STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4)\n        STEP(H, d, a, b, c, GET(4), 0x4bdecfa9, 11)\n        STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16)\n        STEP(H, b, c, d, a, GET(10), 0xbebfbc70, 23)\n        STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4)\n        STEP(H, d, a, b, c, GET(0), 0xeaa127fa, 11)\n        STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16)\n        STEP(H, b, c, d, a, GET(6), 0x04881d05, 23)\n        STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4)\n        STEP(H, d, a, b, c, GET(12), 0xe6db99e5, 11)\n        STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16)\n        STEP(H, b, c, d, a, GET(2), 0xc4ac5665, 23)\n\n        /* Round 4 */\n        STEP(I, a, b, c, d, GET(0), 0xf4292244, 6)\n        STEP(I, d, a, b, c, GET(7), 0x432aff97, 10)\n        STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15)\n        STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21)\n        STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6)\n        STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10)\n        STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15)\n        STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21)\n        STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6)\n        STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10)\n        STEP(I, c, d, a, b, GET(6), 0xa3014314, 15)\n        STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21)\n        STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6)\n        STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10)\n        STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15)\n        STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21)\n\n        a += saved_a;\n        b += saved_b;\n        c += saved_c;\n        d += saved_d;\n\n        ptr += 64;\n    } while (size -= 64);\n\n    ctx->a = a;\n    ctx->b = b;\n    ctx->c = c;\n    ctx->d = d;\n\n    return ptr;\n}\n\nvoid\nMD5_Init(MD5_CTX *ctx)\n{\n    ctx->a = 0x67452301;\n    ctx->b = 0xefcdab89;\n    ctx->c = 0x98badcfe;\n    ctx->d = 0x10325476;\n\n    ctx->lo = 0;\n    ctx->hi = 0;\n}\n\nvoid\nMD5_Update(MD5_CTX *ctx, void *data, unsigned long size)\n{\n    MD5_u32plus saved_lo;\n    unsigned long used, free;\n\n    saved_lo = ctx->lo;\n    if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo) {\n        ctx->hi++;\n    }\n    ctx->hi += size >> 29;\n\n    used = saved_lo & 0x3f;\n\n    if (used) {\n        free = 64 - used;\n\n        if (size < free) {\n            memcpy(&ctx->buffer[used], data, size);\n            return;\n        }\n\n        memcpy(&ctx->buffer[used], data, free);\n        data = (unsigned char *)data + free;\n        size -= free;\n        body(ctx, ctx->buffer, 64);\n    }\n\n    if (size >= 64) {\n        data = body(ctx, data, size & ~(unsigned long)0x3f);\n        size &= 0x3f;\n    }\n\n    memcpy(ctx->buffer, data, size);\n}\n\nvoid\nMD5_Final(unsigned char *result, MD5_CTX *ctx)\n{\n    unsigned long used, free;\n\n    used = ctx->lo & 0x3f;\n\n    ctx->buffer[used++] = 0x80;\n\n    free = 64 - used;\n\n    if (free < 8) {\n        memset(&ctx->buffer[used], 0, free);\n        body(ctx, ctx->buffer, 64);\n        used = 0;\n        free = 64;\n    }\n\n    memset(&ctx->buffer[used], 0, free - 8);\n\n    ctx->lo <<= 3;\n    ctx->buffer[56] = ctx->lo;\n    ctx->buffer[57] = ctx->lo >> 8;\n    ctx->buffer[58] = ctx->lo >> 16;\n    ctx->buffer[59] = ctx->lo >> 24;\n    ctx->buffer[60] = ctx->hi;\n    ctx->buffer[61] = ctx->hi >> 8;\n    ctx->buffer[62] = ctx->hi >> 16;\n    ctx->buffer[63] = ctx->hi >> 24;\n\n    body(ctx, ctx->buffer, 64);\n\n    result[0] = ctx->a;\n    result[1] = ctx->a >> 8;\n    result[2] = ctx->a >> 16;\n    result[3] = ctx->a >> 24;\n    result[4] = ctx->b;\n    result[5] = ctx->b >> 8;\n    result[6] = ctx->b >> 16;\n    result[7] = ctx->b >> 24;\n    result[8] = ctx->c;\n    result[9] = ctx->c >> 8;\n    result[10] = ctx->c >> 16;\n    result[11] = ctx->c >> 24;\n    result[12] = ctx->d;\n    result[13] = ctx->d >> 8;\n    result[14] = ctx->d >> 16;\n    result[15] = ctx->d >> 24;\n\n    memset(ctx, 0, sizeof(*ctx));\n}\n\n/*\n * Just a simple method for getting the signature\n * result must be == 16\n */\nvoid\nmd5_signature(const unsigned char *key, unsigned long length, unsigned char *result)\n{\n    MD5_CTX my_md5;\n\n    MD5_Init(&my_md5);\n    (void)MD5_Update(&my_md5, key, length);\n    MD5_Final(result, &my_md5);\n}\n\nuint32_t\nhash_md5(const char *key, size_t key_length)\n{\n    unsigned char results[16];\n\n    md5_signature((const unsigned char*)key, (unsigned long)key_length, results);\n\n    return ((uint32_t) (results[3] & 0xFF) << 24) |\n           ((uint32_t) (results[2] & 0xFF) << 16) |\n           ((uint32_t) (results[1] & 0xFF) << 8) |\n           (results[0] & 0xFF);\n}\n"
  },
  {
    "path": "dep/dhashkit/dmodula.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n\n#include <dhashkit.h>\n\n#define MODULA_CONTINUUM_ADDITION   10  /* # extra slots to build into continuum */\n#define MODULA_POINTS_PER_SERVER    1\n\nuint32_t\nmodula_dispatch(struct continuum *continuum, uint32_t ncontinuum, uint32_t hash)\n{\n    struct continuum *c;\n\n    ASSERT(continuum != NULL);\n    ASSERT(ncontinuum != 0);\n\n    c = continuum + hash % ncontinuum;\n\n    return c->index;\n}\n"
  },
  {
    "path": "dep/dhashkit/dmurmur.c",
    "content": "/*\n * \"Murmur\" hash provided by Austin, tanjent@gmail.com\n * http://murmurhash.googlepages.com/\n *\n * Note - This code makes a few assumptions about how your machine behaves -\n *\n * 1. We can read a 4-byte value from any address without crashing\n * 2. sizeof(int) == 4\n *\n * And it has a few limitations -\n * 1. It will not work incrementally.\n * 2. It will not produce the same results on little-endian and big-endian\n *  machines.\n *\n *  Updated to murmur2 hash - BP\n */\n\n#include <dhashkit.h>\n\nuint32_t\nhash_murmur(const char *key, size_t length)\n{\n    /*\n     * 'm' and 'r' are mixing constants generated offline.  They're not\n     * really 'magic', they just happen to work well.\n     */\n\n    const unsigned int m = 0x5bd1e995;\n    const uint32_t seed = (0xdeadbeef * (uint32_t)length);\n    const int r = 24;\n\n\n    /* Initialize the hash to a 'random' value */\n\n    uint32_t h = seed ^ (uint32_t)length;\n\n    /* Mix 4 bytes at a time into the hash */\n\n    const unsigned char * data = (const unsigned char *)key;\n\n    while (length >= 4) {\n        unsigned int k = *(unsigned int *)data;\n\n        k *= m;\n        k ^= k >> r;\n        k *= m;\n\n        h *= m;\n        h ^= k;\n\n        data += 4;\n        length -= 4;\n    }\n\n    /* Handle the last few bytes of the input array */\n\n    switch(length) {\n    case 3:\n        h ^= ((uint32_t)data[2]) << 16;\n\n    case 2:\n        h ^= ((uint32_t)data[1]) << 8;\n\n    case 1:\n        h ^= data[0];\n        h *= m;\n\n    default:\n        break;\n    };\n\n    /*\n     * Do a few final mixes of the hash to ensure the last few bytes are\n     * well-incorporated.\n     */\n\n    h ^= h >> 13;\n    h *= m;\n    h ^= h >> 15;\n\n    return h;\n}\n"
  },
  {
    "path": "dep/dhashkit/done_at_a_time.c",
    "content": "/*\n * HashKit\n * Copyright (C) 2009 Brian Aker\n * All rights reserved.\n *\n * Use and distribution licensed under the BSD license.  See\n * the COPYING file in the parent directory for full text.\n */\n\n/*\n * This has is Jenkin's \"One at A time Hash\".\n * http://en.wikipedia.org/wiki/Jenkins_hash_function\n */\n\n#include <dhashkit.h>\n\nuint32_t\nhash_one_at_a_time(const char *key, size_t key_length)\n{\n    const char *ptr = key;\n    uint32_t value = 0;\n\n    while (key_length--) {\n        uint32_t val = (uint32_t) *ptr++;\n        value += val;\n        value += (value << 10);\n        value ^= (value >> 6);\n    }\n    value += (value << 3);\n    value ^= (value >> 11);\n    value += (value << 15);\n\n    return value;\n}\n"
  },
  {
    "path": "dep/dhashkit/drandom.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n\n#include <dhashkit.h>\n\n#define RANDOM_CONTINUUM_ADDITION   10  /* # extra slots to build into continuum */\n#define RANDOM_POINTS_PER_SERVER    1\n\nuint32_t\nrandom_dispatch(struct continuum *continuum, uint32_t ncontinuum, uint32_t hash)\n{\n    struct continuum *c;\n\n    ASSERT(continuum != NULL);\n    ASSERT(ncontinuum != 0);\n\n    c = continuum + random() % ncontinuum;\n\n    return c->index;\n}\n"
  },
  {
    "path": "dep/dhashkit/dsha1.c",
    "content": "\n/* from valgrind tests */\n\n/* ================ sha1.c ================ */\n/*\nSHA-1 in C\nBy Steve Reid <steve@edmweb.com>\n100% Public Domain\n\nTest Vectors (from FIPS PUB 180-1)\n\"abc\"\n  A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D\n\"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq\"\n  84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1\nA million repetitions of \"a\"\n  34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F\n*/\n\n/* #define LITTLE_ENDIAN * This should be #define'd already, if true. */\n/* #define SHA1HANDSOFF * Copies data before messing with it. */\n\n#define SHA1HANDSOFF\n\n#include <stdio.h>\n#include <string.h>\n#include <stdint.h>\n\n#include <dhashkit.h>\n\n#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))\n\n/* blk0() and blk() perform the initial expand. */\n/* I got the idea of expanding during the round function from SSLeay */\n#ifdef VR_LITTLE_ENDIAN\n#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \\\n    |(rol(block->l[i],8)&0x00FF00FF))\n#else\n#define blk0(i) block->l[i]\n#endif\n#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \\\n    ^block->l[(i+2)&15]^block->l[i&15],1))\n\n/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */\n#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);\n#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);\n#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);\n#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);\n#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);\n\n\n/* Hash a single 512-bit block. This is the core of the algorithm. */\n\nvoid SHA1Transform(uint32_t state[5], const unsigned char buffer[64])\n{\n    uint32_t a, b, c, d, e;\n    typedef union {\n        unsigned char c[64];\n        uint32_t l[16];\n    } CHAR64LONG16;\n#ifdef SHA1HANDSOFF\n    CHAR64LONG16 block[1];  /* use array to appear as a pointer */\n    memcpy(block, buffer, 64);\n#else\n    /* The following had better never be used because it causes the\n     * pointer-to-const buffer to be cast into a pointer to non-const.\n     * And the result is written through.  I threw a \"const\" in, hoping\n     * this will cause a diagnostic.\n     */\n    CHAR64LONG16* block = (const CHAR64LONG16*)buffer;\n#endif\n    /* Copy context->state[] to working vars */\n    a = state[0];\n    b = state[1];\n    c = state[2];\n    d = state[3];\n    e = state[4];\n    /* 4 rounds of 20 operations each. Loop unrolled. */\n    R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);\n    R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);\n    R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);\n    R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);\n    R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);\n    R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);\n    R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);\n    R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);\n    R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);\n    R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);\n    R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);\n    R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);\n    R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);\n    R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);\n    R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);\n    R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);\n    R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);\n    R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);\n    R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);\n    R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);\n    /* Add the working vars back into context.state[] */\n    state[0] += a;\n    state[1] += b;\n    state[2] += c;\n    state[3] += d;\n    state[4] += e;\n    /* Wipe variables */\n    a = b = c = d = e = 0;\n#ifdef SHA1HANDSOFF\n    memset(block, '\\0', sizeof(block));\n#endif\n}\n\n\n/* SHA1Init - Initialize new context */\n\nvoid SHA1Init(SHA1_CTX* context)\n{\n    /* SHA1 initialization constants */\n    context->state[0] = 0x67452301;\n    context->state[1] = 0xEFCDAB89;\n    context->state[2] = 0x98BADCFE;\n    context->state[3] = 0x10325476;\n    context->state[4] = 0xC3D2E1F0;\n    context->count[0] = context->count[1] = 0;\n}\n\n\n/* Run your data through this. */\n\nvoid SHA1Update(SHA1_CTX* context, const unsigned char* data, uint32_t len)\n{\n    uint32_t i, j;\n\n    j = context->count[0];\n    if ((context->count[0] += len << 3) < j)\n        context->count[1]++;\n    context->count[1] += (len>>29);\n    j = (j >> 3) & 63;\n    if ((j + len) > 63) {\n        memcpy(&context->buffer[j], data, (i = 64-j));\n        SHA1Transform(context->state, context->buffer);\n        for ( ; i + 63 < len; i += 64) {\n            SHA1Transform(context->state, &data[i]);\n        }\n        j = 0;\n    }\n    else i = 0;\n    memcpy(&context->buffer[j], &data[i], len - i);\n}\n\n\n/* Add padding and return the message digest. */\n\nvoid SHA1Final(unsigned char digest[20], SHA1_CTX* context)\n{\n    unsigned i;\n    unsigned char finalcount[8];\n    unsigned char c;\n\n#if 0\t/* untested \"improvement\" by DHR */\n    /* Convert context->count to a sequence of bytes\n     * in finalcount.  Second element first, but\n     * big-endian order within element.\n     * But we do it all backwards.\n     */\n    unsigned char *fcp = &finalcount[8];\n\n    for (i = 0; i < 2; i++)\n       {\n        uint32_t t = context->count[i];\n        int j;\n\n        for (j = 0; j < 4; t >>= 8, j++)\n\t          *--fcp = (unsigned char) t;\n    }\n#else\n    for (i = 0; i < 8; i++) {\n        finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]\n         >> ((3-(i & 3)) * 8) ) & 255);  /* Endian independent */\n    }\n#endif\n    c = 0200;\n    SHA1Update(context, &c, 1);\n    while ((context->count[0] & 504) != 448) {\n\tc = 0000;\n        SHA1Update(context, &c, 1);\n    }\n    SHA1Update(context, finalcount, 8);  /* Should cause a SHA1Transform() */\n    for (i = 0; i < 20; i++) {\n        digest[i] = (unsigned char)\n         ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);\n    }\n    /* Wipe variables */\n    memset(context, '\\0', sizeof(*context));\n    memset(&finalcount, '\\0', sizeof(finalcount));\n}\n/* ================ end of sha1.c ================ */\n"
  },
  {
    "path": "dep/dlist/Makefile.am",
    "content": "MAINTAINERCLEANFILES = Makefile.in\n\nAM_CPPFLAGS =\nif !OS_SOLARIS\nAM_CPPFLAGS += -D_GNU_SOURCE\nendif\nAM_CPPFLAGS += -I $(top_srcdir)/dep/util\nAM_CPPFLAGS += -I $(top_srcdir)/dep/jemalloc-4.2.0/include\nAM_CPPFLAGS += -I $(top_srcdir)/dep/dmalloc\n\nAM_CFLAGS = -Wall -Wshadow\nAM_CFLAGS += -Wno-unused-parameter -Wno-unused-value\n\nnoinst_LIBRARIES = libdlist.a\n\nnoinst_HEADERS = dlist.h dmtqueue.h dlockqueue.h\n\nlibdlist_a_SOURCES =\t            \\\n\tdlist.c dlist.h                 \\\n    dmtqueue.c dmtqueue.h           \\\n    dlockqueue.c dlockqueue.h"
  },
  {
    "path": "dep/dlist/dlist.c",
    "content": "#include <stdlib.h>\n\n#include <dmalloc.h>\n\n#include <dlist.h>\n\n/* Create a new list. The created list can be freed with\n * AlFreeList(), but private value of every node need to be freed\n * by the user before to call AlFreeList().\n *\n * On error, NULL is returned. Otherwise the pointer to the new list. */\ndlist *dlistCreate(void)\n{\n    struct dlist *list;\n\n    if ((list = dalloc(sizeof(*list))) == NULL)\n        return NULL;\n    list->head = list->tail = NULL;\n    list->len = 0;\n    list->dup = NULL;\n    list->free = NULL;\n    list->match = NULL;\n    return list;\n}\n\n/* Free the whole list.\n *\n * This function can't fail. */\nvoid dlistRelease(dlist *list)\n{\n    unsigned long len;\n    dlistNode *current, *next;\n\n    current = list->head;\n    len = list->len;\n    while(len--) {\n        next = current->next;\n        if (list->free) list->free(current->value);\n        dfree(current);\n        current = next;\n    }\n    dfree(list);\n}\n\n/* Add a new node to the list, to head, containing the specified 'value'\n * pointer as value.\n *\n * On error, NULL is returned and no operation is performed (i.e. the\n * list remains unaltered).\n * On success the 'list' pointer you pass to the function is returned. */\ndlist *dlistAddNodeHead(dlist *list, void *value)\n{\n    dlistNode *node;\n\n    if ((node = dalloc(sizeof(*node))) == NULL)\n        return NULL;\n    node->value = value;\n    if (list->len == 0) {\n        list->head = list->tail = node;\n        node->prev = node->next = NULL;\n    } else {\n        node->prev = NULL;\n        node->next = list->head;\n        list->head->prev = node;\n        list->head = node;\n    }\n    list->len++;\n    return list;\n}\n\n/* Add a new node to the list, to tail, containing the specified 'value'\n * pointer as value.\n *\n * On error, NULL is returned and no operation is performed (i.e. the\n * list remains unaltered).\n * On success the 'list' pointer you pass to the function is returned. */\ndlist *dlistAddNodeTail(dlist *list, void *value)\n{\n    dlistNode *node;\n\n    if ((node = dalloc(sizeof(*node))) == NULL)\n        return NULL;\n    node->value = value;\n    if (list->len == 0) {\n        list->head = list->tail = node;\n        node->prev = node->next = NULL;\n    } else {\n        node->prev = list->tail;\n        node->next = NULL;\n        list->tail->next = node;\n        list->tail = node;\n    }\n    list->len++;\n    return list;\n}\n\ndlist *dlistInsertNode(dlist *list, dlistNode *old_node, void *value, int after) {\n    dlistNode *node;\n\n    if ((node = dalloc(sizeof(*node))) == NULL)\n        return NULL;\n    node->value = value;\n    if (after) {\n        node->prev = old_node;\n        node->next = old_node->next;\n        if (list->tail == old_node) {\n            list->tail = node;\n        }\n    } else {\n        node->next = old_node;\n        node->prev = old_node->prev;\n        if (list->head == old_node) {\n            list->head = node;\n        }\n    }\n    if (node->prev != NULL) {\n        node->prev->next = node;\n    }\n    if (node->next != NULL) {\n        node->next->prev = node;\n    }\n    list->len++;\n    return list;\n}\n\n/* Remove the specified node from the specified list.\n * It's up to the caller to free the private value of the node.\n *\n * This function can't fail. */\nvoid dlistDelNode(dlist *list, dlistNode *node)\n{\n    if (node->prev)\n        node->prev->next = node->next;\n    else\n        list->head = node->next;\n    if (node->next)\n        node->next->prev = node->prev;\n    else\n        list->tail = node->prev;\n    if (list->free) list->free(node->value);\n    dfree(node);\n    list->len--;\n}\n\n/* Returns a list iterator 'iter'. After the initialization every\n * call to dlistNext() will return the next element of the list.\n *\n * This function can't fail. */\ndlistIter *dlistGetIterator(dlist *list, int direction)\n{\n    dlistIter *iter;\n\n    if ((iter = dalloc(sizeof(*iter))) == NULL) return NULL;\n    if (direction == AL_START_HEAD)\n        iter->next = list->head;\n    else\n        iter->next = list->tail;\n    iter->direction = direction;\n    return iter;\n}\n\n/* Release the iterator memory */\nvoid dlistReleaseIterator(dlistIter *iter) {\n    dfree(iter);\n}\n\n/* Create an iterator in the list private iterator structure */\nvoid dlistRewind(dlist *list, dlistIter *li) {\n    li->next = list->head;\n    li->direction = AL_START_HEAD;\n}\n\nvoid dlistRewindTail(dlist *list, dlistIter *li) {\n    li->next = list->tail;\n    li->direction = AL_START_TAIL;\n}\n\n/* Return the next element of an iterator.\n * It's valid to remove the currently returned element using\n * dlistDelNode(), but not to remove other elements.\n *\n * The function returns a pointer to the next element of the list,\n * or NULL if there are no more elements, so the classical usage patter\n * is:\n *\n * iter = dlistGetIterator(list,<direction>);\n * while ((node = dlistNext(iter)) != NULL) {\n *     doSomethingWith(dlistNodeValue(node));\n * }\n *\n * */\ndlistNode *dlistNext(dlistIter *iter)\n{\n    dlistNode *current = iter->next;\n\n    if (current != NULL) {\n        if (iter->direction == AL_START_HEAD)\n            iter->next = current->next;\n        else\n            iter->next = current->prev;\n    }\n    return current;\n}\n\n/* Duplicate the whole list. On out of memory NULL is returned.\n * On success a copy of the original list is returned.\n *\n * The 'Dup' method set with listSetDupMethod() function is used\n * to copy the node value. Otherwise the same pointer value of\n * the original node is used as value of the copied node.\n *\n * The original list both on success or error is never modified. */\ndlist *dlistDup(dlist *orig)\n{\n    dlist *copy;\n    dlistIter iter;\n    dlistNode *node;\n\n    if ((copy = dlistCreate()) == NULL)\n        return NULL;\n    copy->dup = orig->dup;\n    copy->free = orig->free;\n    copy->match = orig->match;\n    dlistRewind(orig, &iter);\n    while((node = dlistNext(&iter)) != NULL) {\n        void *value;\n\n        if (copy->dup) {\n            value = copy->dup(node->value);\n            if (value == NULL) {\n                dlistRelease(copy);\n                return NULL;\n            }\n        } else\n            value = node->value;\n        if (dlistAddNodeTail(copy, value) == NULL) {\n            dlistRelease(copy);\n            return NULL;\n        }\n    }\n    return copy;\n}\n\n/* Search the list for a node matching a given key.\n * The match is performed using the 'match' method\n * set with listSetMatchMethod(). If no 'match' method\n * is set, the 'value' pointer of every node is directly\n * compared with the 'key' pointer.\n *\n * On success the first matching node pointer is returned\n * (search starts from head). If no matching node exists\n * NULL is returned. */\ndlistNode *dlistSearchKey(dlist *list, void *key)\n{\n    dlistIter iter;\n    dlistNode *node;\n\n    dlistRewind(list, &iter);\n    while((node = dlistNext(&iter)) != NULL) {\n        if (list->match) {\n            if (list->match(node->value, key)) {\n                return node;\n            }\n        } else {\n            if (key == node->value) {\n                return node;\n            }\n        }\n    }\n    return NULL;\n}\n\n/* Return the element at the specified zero-based index\n * where 0 is the head, 1 is the element next to head\n * and so on. Negative integers are used in order to count\n * from the tail, -1 is the last element, -2 the penultimate\n * and so on. If the index is out of range NULL is returned. */\ndlistNode *dlistIndex(dlist *list, long index) {\n    dlistNode *n;\n\n    if (index < 0) {\n        index = (-index)-1;\n        n = list->tail;\n        while(index-- && n) n = n->prev;\n    } else {\n        n = list->head;\n        while(index-- && n) n = n->next;\n    }\n    return n;\n}\n\n/* Rotate the list removing the tail node and inserting it to the head. */\nvoid dlistRotate(dlist *list) {\n    dlistNode *tail = list->tail;\n\n    if (dlistLength(list) <= 1) return;\n\n    /* Detach current tail */\n    list->tail = tail->prev;\n    list->tail->next = NULL;\n    /* Move it as head */\n    list->head->prev = tail;\n    tail->prev = NULL;\n    tail->next = list->head;\n    list->head = tail;\n}\n\ndlist *dlistPush(dlist *list, void *value) {\n    dlistAddNodeTail(list, value);\n    return list;\n}\n\nvoid *dlistPop(dlist *list) {\n    dlistNode *node;\n    void *value;\n    \n    node = dlistFirst(list);\n    if (node == NULL) {\n        return NULL;\n    }\n\n    value = dlistNodeValue(node);\n    dlistDelNode(list, node);\n\n    if (list->free) return NULL;\n    \n    return value;\n}\n"
  },
  {
    "path": "dep/dlist/dlist.h",
    "content": "#ifndef _DLIST_H__\n#define _DLIST_H__\n\n/* Node, List, and Iterator are the only data structures used currently. */\n\ntypedef struct dlistNode {\n    struct dlistNode *prev;\n    struct dlistNode *next;\n    void *value;\n} dlistNode;\n\ntypedef struct dlistIter {\n    dlistNode *next;\n    int direction;\n} dlistIter;\n\ntypedef struct dlist {\n    dlistNode *head;\n    dlistNode *tail;\n    void *(*dup)(void *ptr);\n    void (*free)(void *ptr);\n    int (*match)(void *ptr, void *key);\n    unsigned long len;\n} dlist;\n\n/* Functions implemented as macros */\n#define dlistLength(l) ((l)->len)\n#define dlistFirst(l) ((l)->head)\n#define dlistLast(l) ((l)->tail)\n#define dlistPrevNode(n) ((n)->prev)\n#define dlistNextNode(n) ((n)->next)\n#define dlistNodeValue(n) ((n)->value)\n\n#define dlistSetDupMethod(l,m) ((l)->dup = (m))\n#define dlistSetFreeMethod(l,m) ((l)->free = (m))\n#define dlistSetMatchMethod(l,m) ((l)->match = (m))\n\n#define dlistGetDupMethod(l) ((l)->dup)\n#define dlistGetFree(l) ((l)->free)\n#define dlistGetMatchMethod(l) ((l)->match)\n\n/* Prototypes */\ndlist *dlistCreate(void);\nvoid dlistRelease(dlist *list);\ndlist *dlistAddNodeHead(dlist *list, void *value);\ndlist *dlistAddNodeTail(dlist *list, void *value);\ndlist *dlistInsertNode(dlist *list, dlistNode *old_node, void *value, int after);\nvoid dlistDelNode(dlist *list, dlistNode *node);\ndlistIter *dlistGetIterator(dlist *list, int direction);\ndlistNode *dlistNext(dlistIter *iter);\nvoid dlistReleaseIterator(dlistIter *iter);\ndlist *dlistDup(dlist *orig);\ndlistNode *dlistSearchKey(dlist *list, void *key);\ndlistNode *dlistIndex(dlist *list, long index);\nvoid dlistRewind(dlist *list, dlistIter *li);\nvoid dlistRewindTail(dlist *list, dlistIter *li);\nvoid dlistRotate(dlist *list);\ndlist *dlistPush(dlist *list, void *value);\nvoid *dlistPop(dlist *list);\n\n/* Directions for iterators */\n#define AL_START_HEAD 0\n#define AL_START_TAIL 1\n\n#endif /* __ADLIST_H__ */\n"
  },
  {
    "path": "dep/dlist/dlockqueue.c",
    "content": "#include <pthread.h>\n#include <stdlib.h>\n#include <stdio.h>\n\n#include <dmalloc.h>\n\n#include <dlist.h>\n#include <dmtqueue.h>\n#include <dlockqueue.h>\n\ndlockqueue *dlockqueue_create(void)\n{\n    dlockqueue *lqueue;\n\n    lqueue = dalloc(sizeof(*lqueue));\n    if (lqueue == NULL) {\n        return NULL;\n    }\n\n    lqueue->maxlen = -1;\n    lqueue->maxlen_policy = MAX_LENGTH_POLICY_REJECT;\n    pthread_mutex_init(&lqueue->lmutex,NULL);\n    \n    lqueue->l = dlistCreate();\n    if (lqueue->l == NULL) {\n        dlockqueue_destroy(lqueue);\n        return NULL;\n    }\n\n    return lqueue;\n}\n\nlong long dlockqueue_push(void *q, void *value)\n{\n    dlockqueue *lqueue = q;\n    dlist *list;\n    long long length;\n    \n    pthread_mutex_lock(&lqueue->lmutex);\n    length = (long long)dlistLength(lqueue->l);\n    if (lqueue->maxlen >0 && length >= lqueue->maxlen) {\n        if (lqueue->maxlen_policy == MAX_LENGTH_POLICY_REJECT) {\n            length = -1;\n        } else if (lqueue->maxlen_policy == MAX_LENGTH_POLICY_EVICT_HEAD) {\n            while (length >= lqueue->maxlen) {\n                dlistNode *ln = dlistFirst(lqueue->l);\n                dlistDelNode(lqueue->l,ln);\n                length = (long long)dlistLength(lqueue->l);\n            }\n            list = dlistAddNodeTail(lqueue->l, value);\n            length ++;\n        } else if (lqueue->maxlen_policy == MAX_LENGTH_POLICY_EVICT_END) {\n            while (length >= lqueue->maxlen) {\n                dlistNode *ln = dlistLast(lqueue->l);\n                dlistDelNode(lqueue->l,ln);\n                length = (long long)dlistLength(lqueue->l);\n            }\n            list = dlistAddNodeTail(lqueue->l, value);\n            length ++;\n        }\n    } else {\n        list = dlistAddNodeTail(lqueue->l, value);\n        length ++;\n    }\n    pthread_mutex_unlock(&lqueue->lmutex);\n\n    if (list == NULL) {\n        return -1;\n    }\n\n    return length;\n}\n\nvoid *dlockqueue_pop(void *q)\n{\n    dlockqueue *lqueue = q;\n    dlistNode *node;\n    void *value;\n        \n    if (lqueue == NULL || lqueue->l == NULL) {\n        return NULL;\n    }\n    \n    pthread_mutex_lock(&lqueue->lmutex);\n    \n    node = dlistFirst(lqueue->l);\n    if (node == NULL) {\n        pthread_mutex_unlock(&lqueue->lmutex);\n        return NULL;\n    }\n\n    value = dlistNodeValue(node);\n\n    dlistDelNode(lqueue->l, node);\n\n    pthread_mutex_unlock(&lqueue->lmutex);\n\n    return value;\n}\n\nvoid dlockqueue_destroy(void *q)\n{\n    dlockqueue *lqueue = q;\n    if (lqueue == NULL) {\n        return;\n    }\n\n    if (lqueue->l != NULL) {\n        dlistRelease(lqueue->l);\n    }\n\n    pthread_mutex_destroy(&lqueue->lmutex);\n\n    dfree(lqueue);\n}\n\nlong long dlockqueue_length(void *q)\n{\n    dlockqueue *lqueue = q;\n    long long length;\n    \n    if (lqueue == NULL || lqueue->l == NULL) {\n        return -1;\n    }\n\n    pthread_mutex_lock(&lqueue->lmutex);\n    length = dlistLength(lqueue->l);\n    pthread_mutex_unlock(&lqueue->lmutex);\n    \n    return length;\n}\n"
  },
  {
    "path": "dep/dlist/dlockqueue.h",
    "content": "#ifndef _DLOCKQUEUE_H_\n#define _DLOCKQUEUE_H_\n\nstruct dlist;\n\ntypedef struct dlockqueue{\n    struct dlist *l;\n    long long maxlen;\n    int maxlen_policy;\n    pthread_mutex_t lmutex;\n} dlockqueue;\n\ndlockqueue *dlockqueue_create(void);\nlong long dlockqueue_push(void *q, void *value);\nvoid *dlockqueue_pop(void *q);\nvoid dlockqueue_destroy(void *q);\nlong long dlockqueue_length(void *q);\n\n#endif\n"
  },
  {
    "path": "dep/dlist/dmtqueue.c",
    "content": "#include <stdlib.h>\n\n#include <dmalloc.h>\n\n#include <dlist.h>\n#include <dmtqueue.h>\n#include <dlockqueue.h>\n\n/******** multi-thread safe queue interface ********/\ndmtqueue *dmtqueue_create(void)\n{\n    dmtqueue *q;\n\n    q = dalloc(sizeof(*q));\n    if (q == NULL) {\n        return NULL;\n    }\n\n    q->l = NULL;\n    q->lock_push = NULL;\n    q->lock_pop = NULL;\n    q->destroy = NULL;\n    q->length = NULL;\n    \n    return q;\n}\n\nvoid dmtqueue_destroy(dmtqueue *q)\n{\n    if (q == NULL) {\n        return;\n    }\n\n    if (q->destroy) {\n        q->destroy(q->l);\n    }\n\n    dfree(q);\n}\n\nlong long dmtqueue_push(dmtqueue *q, void *value)\n{\n    if(q == NULL || q->l == NULL\n        || q->lock_push == NULL)\n    {\n        return -1;\n    }\n\n    return q->lock_push(q->l, value);\n}\n\nvoid *dmtqueue_pop(dmtqueue *q)\n{\n    if(q == NULL || q->l == NULL\n        || q->lock_pop == NULL)\n    {\n        return NULL;\n    }\n    \n    return q->lock_pop(q->l);\n}\n\nint dmtqueue_empty(dmtqueue *q)\n{\n    if(q == NULL || q->l == NULL\n        || q->length == NULL)\n    {\n        return -1;\n    }\n\n    if(q->length(q->l) > 0)\n    {\n        return 0;\n    }\n\n    return 1;\n}\n\nlong long dmtqueue_length(dmtqueue *q)\n{\n    if(q == NULL || q->l == NULL\n        || q->length == NULL)\n    {\n        return -1;\n    }\n\n    return q->length(q->l);\n}\n\n/******** multi-thread safe queue implement ********/\n\n/**\n* This is multi-thread safe queue.\n* This lock list's performance is not good, but it is safe.\n*/\nint dmtqueue_init_with_lockqueue(dmtqueue *q, dlockqueue_freefunc freefunc)\n{\n    dlockqueue *lq;\n    \n    if (q == NULL) {\n        return -1;\n    }\n\n    lq = dlockqueue_create();\n    if (lq == NULL) {\n        return -1;\n    }\n\n    lq->l->free = freefunc;\n\n    q->l = lq;\n    q->lock_push = dlockqueue_push;\n    q->lock_pop = dlockqueue_pop;\n    q->destroy = dlockqueue_destroy;\n    q->length = dlockqueue_length;\n    \n    return 0;\n}\n"
  },
  {
    "path": "dep/dlist/dmtqueue.h",
    "content": "#ifndef _DMTQUEUE_H_\n#define _DMTQUEUE_H_\n\n#define MAX_LENGTH_POLICY_REJECT        0\n#define MAX_LENGTH_POLICY_EVICT_HEAD    1\n#define MAX_LENGTH_POLICY_EVICT_END     2\n\n/* Multi-thread safe queue */\ntypedef struct dmtqueue{\n    void *l;\n    long long (*lock_push)(void *q, void *value);\n    void *(*lock_pop)(void *q);\n    void (*destroy)(void *q);\n    long long (*length)(void *q);\n} dmtqueue;\n\n#define dmtqueueSetMaxlength(q,l)        ((q)->l->maxlen = (l))\n#define dmtqueueSetMaxlengthPolicy(q,p)  ((q)->l->maxlen = (p))\n\ntypedef int (*dmtqueue_init)(dmtqueue *);\n\n/******** multi-thread safe list interface ********/\n\ndmtqueue *dmtqueue_create(void);\nvoid dmtqueue_destroy(dmtqueue *q);\nlong long dmtqueue_push(dmtqueue *q, void *value);\nvoid *dmtqueue_pop(dmtqueue *q);\nint dmtqueue_empty(dmtqueue *q);\nlong long dmtqueue_length(dmtqueue *q);\n\n/******** multi-thread safe list implement ********/\n\ntypedef void (*dlockqueue_freefunc)(void *);\nint dmtqueue_init_with_lockqueue(dmtqueue *l, dlockqueue_freefunc freefunc);\n\n#endif\n"
  },
  {
    "path": "dep/dmalloc/Makefile.am",
    "content": "MAINTAINERCLEANFILES = Makefile.in\n\nAM_CPPFLAGS =\nif !OS_SOLARIS\nAM_CPPFLAGS += -D_GNU_SOURCE\nendif\nAM_CPPFLAGS += -I $(top_srcdir)/dep/jemalloc-4.2.0/include\nAM_CPPFLAGS += -I $(top_srcdir)/dep/util\n\nAM_CFLAGS = -Wall -Wshadow\nAM_CFLAGS += -Wno-unused-parameter -Wno-unused-value\n\nnoinst_LIBRARIES = libdmalloc.a\n\nnoinst_HEADERS = dmalloc.h\n\nlibdmalloc_a_SOURCES =      \\\n\tdmalloc.c dmalloc.h"
  },
  {
    "path": "dep/dmalloc/dmalloc.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <errno.h>\n#include <pthread.h>\n\n#include <unistd.h>\n\n#include <dutil.h>\n#include <dlog.h>\n\n#include <dmalloc.h>\n\n/*memory api*/\nstatic size_t used_memory = 0;\npthread_mutex_t used_memory_mutex = PTHREAD_MUTEX_INITIALIZER;\n\n#if defined(__ATOMIC_RELAXED)\n#define update_used_mem_stat_add(__n) __atomic_add_fetch(&used_memory, (__n), __ATOMIC_RELAXED)\n#define update_used_mem_stat_sub(__n) __atomic_sub_fetch(&used_memory, (__n), __ATOMIC_RELAXED)\nchar *malloc_lock_type(void) {return \"__ATOMIC_RELAXED\";}\n#elif defined(HAVE_ATOMIC)\n#define update_used_mem_stat_add(__n) __sync_add_and_fetch(&used_memory, (__n))\n#define update_used_mem_stat_sub(__n) __sync_sub_and_fetch(&used_memory, (__n))\nchar *malloc_lock_type(void) {return \"HAVE_ATOMIC\";}\n#else\n#define update_used_mem_stat_add(__n) do {      \\\n    pthread_mutex_lock(&used_memory_mutex);     \\\n    used_memory += (__n); \\\n    pthread_mutex_unlock(&used_memory_mutex);   \\\n} while(0)\n\n#define update_used_mem_stat_sub(__n) do {      \\\n    pthread_mutex_lock(&used_memory_mutex);     \\\n    used_memory -= (__n); \\\n    pthread_mutex_unlock(&used_memory_mutex);   \\\n} while(0)\n\nchar *malloc_lock_type(void) {return \"pthread_mutex_t\";}\n#endif\n\n#define update_dmalloc_stat_alloc(__n) do {                                 \\\n    size_t _n = (__n);                                                      \\\n    if (_n&(sizeof(long)-1)) _n += sizeof(long)-(_n&(sizeof(long)-1));    \\\n    update_used_mem_stat_add(_n);                                           \\\n} while(0)\n\n#define update_dmalloc_stat_free(__n) do {                                  \\\n    size_t _n = (__n);                                                      \\\n    if (_n&(sizeof(long)-1)) _n += sizeof(long)-(_n&(sizeof(long)-1));    \\\n    update_used_mem_stat_sub(_n);                                           \\\n} while(0)\n\n#ifdef HAVE_MALLOC_SIZE\n#define PREFIX_SIZE (0)\n#else\n#if defined(__sun) || defined(__sparc) || defined(__sparc__)\n#define PREFIX_SIZE (sizeof(long long))\n#else\n#define PREFIX_SIZE (sizeof(size_t))\n#endif\n#endif\n\n/* Provide dmalloc_size() for systems where this function is not provided by\n * malloc itself, given that in that case we store a header with this\n * information as the first bytes of every allocation. */\n#ifndef HAVE_MALLOC_SIZE\nsize_t dmalloc_size(void *ptr) {\n    void *realptr = (char*)ptr-PREFIX_SIZE;\n    size_t size = *((size_t*)realptr);\n    /* Assume at least that all the allocations are padded at sizeof(long) by\n     * the underlying allocator. */\n    if (size&(sizeof(long)-1)) size += sizeof(long)-(size&(sizeof(long)-1));\n    return size+PREFIX_SIZE;\n}\n#endif\n\nvoid *\n_dalloc(size_t size, const char *name, int line)\n{\n    void *p;\n\n    ASSERT(size != 0);\n\n#ifdef DUSE_JEMALLOC\n    p = je_malloc(size+PREFIX_SIZE);\n#else\n    p = malloc(size+PREFIX_SIZE);\n#endif\n    if (p == NULL) {\n        log_error(\"malloc(%zu) failed @ %s:%d\", size, name, line);\n    } else {\n#ifdef HAVE_MALLOC_SIZE\n        update_dmalloc_stat_alloc(dmalloc_size(p));\n        return p;\n#else\n        *((size_t*)p) = size;\n        update_dmalloc_stat_alloc(size+PREFIX_SIZE);\n        return (char*)p+PREFIX_SIZE;\n#endif\n        log_debug(LOG_VVERB, \"malloc(%zu) at %p @ %s:%d\", size, p, name, line);\n    }\n\n    return p;\n}\n\nvoid *\n_dzalloc(size_t size, const char *name, int line)\n{\n    void *p;\n\n    p = _dalloc(size, name, line);\n    if (p != NULL) {\n        memset(p, 0, size);\n    }\n\n    return p;\n}\n\nvoid *\n_dcalloc(size_t nmemb, size_t size, const char *name, int line)\n{\n    return _dzalloc(nmemb * size, name, line);\n}\n\nvoid *\n_drealloc(void *ptr, size_t size, const char *name, int line)\n{\n#ifndef HAVE_MALLOC_SIZE\n    void *realp;\n#endif\n    void *p;\n    size_t oldsize;\n\n    ASSERT(size != 0);\n\n    if (ptr == NULL) return _dalloc(size, name, line);\n\n#ifdef HAVE_MALLOC_SIZE\n    oldsize = dmalloc_size(ptr);\n#ifdef DUSE_JEMALLOC\n    p = je_realloc(ptr, size);\n#else\n    p = realloc(ptr, size);\n#endif\n#else\n    realp = (char*)ptr-PREFIX_SIZE;\n    oldsize = *((size_t*)realp);\n#ifdef DUSE_JEMALLOC\n    p = je_realloc(ptr, size+PREFIX_SIZE);\n#else\n    p = realloc(ptr, size+PREFIX_SIZE);\n#endif\n#endif\n    if (p == NULL) {\n        log_error(\"realloc(%zu) failed @ %s:%d\", size, name, line);\n        return NULL;\n    } else {\n        log_debug(LOG_VVERB, \"realloc(%zu) at %p @ %s:%d\", size, p, name, line);\n#ifdef HAVE_MALLOC_SIZE\n        update_dmalloc_stat_free(oldsize);\n        update_dmalloc_stat_alloc(dmalloc_size(p));\n        return p;\n#else\n        *((size_t*)p) = size;\n        update_dmalloc_stat_free(oldsize);\n        update_dmalloc_stat_alloc(size);\n        return p+PREFIX_SIZE;\n#endif\n    }\n\n    return NULL;\n}\n\nvoid\n_dfree(void *ptr, const char *name, int line)\n{\n#ifndef HAVE_MALLOC_SIZE\n    void *realp;\n    size_t oldsize;\n#endif\n\n    ASSERT(ptr != NULL);\n    log_debug(LOG_VVERB, \"free(%p) @ %s:%d\", ptr, name, line);\n\n#ifdef HAVE_MALLOC_SIZE\n    update_dmalloc_stat_free(dmalloc_size(ptr));\n#ifdef DUSE_JEMALLOC\n    je_free(ptr);\n#else\n    free(ptr);\n#endif\n#else\n    realp = (char*)ptr-PREFIX_SIZE;\n    oldsize = *((size_t*)realp);\n    update_dmalloc_stat_free(oldsize+PREFIX_SIZE);\n    free(realp);\n#ifdef DUSE_JEMALLOC\n    je_free(realp);\n#else\n    free(realp);\n#endif\n#endif\n}\n\nsize_t\ndalloc_used_memory(void)\n{\n    size_t um;\n\n#if defined(__ATOMIC_RELAXED) || defined(HAVE_ATOMIC)\n    um = update_used_mem_stat_add(0);\n#else\n    pthread_mutex_lock(&used_memory_mutex);\n    um = used_memory;\n    pthread_mutex_unlock(&used_memory_mutex);\n#endif\n\n    return um;\n}\n\n/* Returns the size of physical memory (RAM) in bytes.\n * It looks ugly, but this is the cleanest way to achive cross platform results.\n * Cleaned up from:\n *\n * http://nadeausoftware.com/articles/2012/09/c_c_tip_how_get_physical_memory_size_system\n *\n * Note that this function:\n * 1) Was released under the following CC attribution license:\n *    http://creativecommons.org/licenses/by/3.0/deed.en_US.\n * 2) Was originally implemented by David Robert Nadeau.\n * 3) Was modified for Redis by Matt Stancliff.\n * 4) This note exists in order to comply with the original license.\n */\nsize_t dalloc_get_memory_size(void) {\n#if defined(__unix__) || defined(__unix) || defined(unix) || \\\n    (defined(__APPLE__) && defined(__MACH__))\n#if defined(CTL_HW) && (defined(HW_MEMSIZE) || defined(HW_PHYSMEM64))\n    int mib[2];\n    mib[0] = CTL_HW;\n#if defined(HW_MEMSIZE)\n    mib[1] = HW_MEMSIZE;            /* OSX. --------------------- */\n#elif defined(HW_PHYSMEM64)\n    mib[1] = HW_PHYSMEM64;          /* NetBSD, OpenBSD. --------- */\n#endif\n    int64_t size = 0;               /* 64-bit */\n    size_t len = sizeof(size);\n    if (sysctl( mib, 2, &size, &len, NULL, 0) == 0)\n        return (size_t)size;\n    return 0L;          /* Failed? */\n\n#elif defined(_SC_PHYS_PAGES) && defined(_SC_PAGESIZE)\n    /* FreeBSD, Linux, OpenBSD, and Solaris. -------------------- */\n    return (size_t)sysconf(_SC_PHYS_PAGES) * (size_t)sysconf(_SC_PAGESIZE);\n\n#elif defined(CTL_HW) && (defined(HW_PHYSMEM) || defined(HW_REALMEM))\n    /* DragonFly BSD, FreeBSD, NetBSD, OpenBSD, and OSX. -------- */\n    int mib[2];\n    mib[0] = CTL_HW;\n#if defined(HW_REALMEM)\n    mib[1] = HW_REALMEM;        /* FreeBSD. ----------------- */\n#elif defined(HW_PYSMEM)\n    mib[1] = HW_PHYSMEM;        /* Others. ------------------ */\n#endif\n    unsigned int size = 0;      /* 32-bit */\n    size_t len = sizeof(size);\n    if (sysctl(mib, 2, &size, &len, NULL, 0) == 0)\n        return (size_t)size;\n    return 0L;          /* Failed? */\n#endif /* sysctl and sysconf variants */\n\n#else\n    return 0L;          /* Unknown OS. */\n#endif\n}\n\n/* Get the RSS information in an OS-specific way.\n *\n * WARNING: the function zmalloc_get_rss() is not designed to be fast\n * and may not be called in the busy loops where Redis tries to release\n * memory expiring or swapping out objects.\n *\n * For this kind of \"fast RSS reporting\" usages use instead the\n * function RedisEstimateRSS() that is a much faster (and less precise)\n * version of the function. */\n\n#if defined(HAVE_PROC_STAT)\n#include <unistd.h>\n#include <sys/types.h>\n#include <sys/stat.h>\n#include <fcntl.h>\n\nsize_t dalloc_get_rss(void) {\n    int page = sysconf(_SC_PAGESIZE);\n    size_t rss;\n    char buf[4096];\n    char filename[256];\n    int fd, count;\n    char *p, *x;\n\n    snprintf(filename,256,\"/proc/%d/stat\",getpid());\n    if ((fd = open(filename,O_RDONLY)) == -1) return 0;\n    if (read(fd,buf,4096) <= 0) {\n        close(fd);\n        return 0;\n    }\n    close(fd);\n\n    p = buf;\n    count = 23; /* RSS is the 24th field in /proc/<pid>/stat */\n    while(p && count--) {\n        p = strchr(p,' ');\n        if (p) p++;\n    }\n    if (!p) return 0;\n    x = strchr(p,' ');\n    if (!x) return 0;\n    *x = '\\0';\n\n    rss = strtoll(p,NULL,10);\n    rss *= page;\n    return rss;\n}\n#elif defined(HAVE_TASKINFO)\n#include <unistd.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <sys/types.h>\n#include <sys/sysctl.h>\n#include <mach/task.h>\n#include <mach/mach_init.h>\n\nsize_t dalloc_get_rss(void) {\n    task_t task = MACH_PORT_NULL;\n    struct task_basic_info t_info;\n    mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT;\n\n    if (task_for_pid(current_task(), getpid(), &task) != KERN_SUCCESS)\n        return 0;\n    task_info(task, TASK_BASIC_INFO, (task_info_t)&t_info, &t_info_count);\n\n    return t_info.resident_size;\n}\n#else\nsize_t dalloc_get_rss(void) {\n    /* If we can't get the RSS in an OS-specific way for this system just\n     * return the memory usage we estimated in dalloc()..\n     *\n     * Fragmentation will appear to be always 1 (no fragmentation)\n     * of course... */\n    return dalloc_used_memory();\n}\n#endif\n\n/* Fragmentation = RSS / allocated-bytes */\nfloat dalloc_get_fragmentation_ratio(size_t rss) {\n    return (float)rss/dalloc_used_memory();\n}\n"
  },
  {
    "path": "dep/dmalloc/dmalloc.h",
    "content": "#ifndef _DMALLOC_H_\n#define _DMALLOC_H_\n\n#ifdef HAVE_CONFIG_H\n# include <config.h>\n#endif\n\n#include <dspecialconfig.h>\n\n#ifdef HAVE_JEMALLOC\n# define DUSE_JEMALLOC 1\n#endif\n\n/*\n * Memory allocation and free wrappers.\n *\n * These wrappers enables us to loosely detect double free, dangling\n * pointer access and zero-byte alloc.\n */\n#if defined(DUSE_JEMALLOC)\n#define DMALLOC_LIB (\"jemalloc-\" __xstr(JEMALLOC_VERSION_MAJOR) \".\" __xstr(JEMALLOC_VERSION_MINOR) \".\" __xstr(JEMALLOC_VERSION_BUGFIX))\n#include <jemalloc/jemalloc.h>\n#if (JEMALLOC_VERSION_MAJOR == 2 && JEMALLOC_VERSION_MINOR >= 1) || (JEMALLOC_VERSION_MAJOR > 2)\n#define HAVE_MALLOC_SIZE 1\n#define dmalloc_size(p) je_malloc_usable_size(p)\n#else\n#error \"Newer version of jemalloc required\"\n#endif\n#elif defined(__APPLE__)\n#include <malloc/malloc.h>\n#define HAVE_MALLOC_SIZE 1\n#define dmalloc_size(p) malloc_size(p)\n#endif\n    \n#ifndef DMALLOC_LIB\n#define DMALLOC_LIB \"libc\"\n#endif\n\n#define dalloc(_s)                    \\\n    _dalloc((size_t)(_s), __FILE__, __LINE__)\n\n#define dzalloc(_s)                   \\\n    _dzalloc((size_t)(_s), __FILE__, __LINE__)\n\n#define dcalloc(_n, _s)               \\\n    _dcalloc((size_t)(_n), (size_t)(_s), __FILE__, __LINE__)\n\n#define drealloc(_p, _s)              \\\n    _drealloc(_p, (size_t)(_s), __FILE__, __LINE__)\n\n#define dfree(_p) do {                \\\n    _dfree(_p, __FILE__, __LINE__);   \\\n} while (0)\n\nchar *dmalloc_lock_type(void);\n\n#ifndef HAVE_MALLOC_SIZE\nsize_t dmalloc_size(void *ptr);\n#endif\n\nvoid *_dalloc(size_t size, const char *name, int line);\nvoid *_dzalloc(size_t size, const char *name, int line);\nvoid *_dcalloc(size_t nmemb, size_t size, const char *name, int line);\nvoid *_drealloc(void *ptr, size_t size, const char *name, int line);\nvoid _dfree(void *ptr, const char *name, int line);\n\nsize_t dalloc_used_memory(void);\n\nsize_t dalloc_get_memory_size(void);\n\nsize_t dalloc_get_rss(void);\nfloat dalloc_get_fragmentation_ratio(size_t rss);\n\n#endif\n"
  },
  {
    "path": "dep/himemcached-0.1.0/Makefile.am",
    "content": "MAINTAINERCLEANFILES = Makefile.in\n\nAM_CFLAGS = -Wall -Wshadow\nAM_CFLAGS += -Wno-unused-parameter -Wno-unused-value\n\nnoinst_LIBRARIES = libhimemcached.a\n\nnoinst_HEADERS = himemcached.h himcread.h himcdep/sds.h\n\nlibhimemcached_a_SOURCES =          \\\n    himcdep/sds.c himcdep/sds.h     \\\n    himcread.c himcread.h           \\\n\thimemcached.c himemcached.h"
  },
  {
    "path": "dep/himemcached-0.1.0/himcdep/sds.c",
    "content": "/* SDS (Simple Dynamic Strings), A C dynamic strings library.\n *\n * Copyright (c) 2006-2014, Salvatore Sanfilippo <antirez at gmail dot com>\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n *   * Redistributions of source code must retain the above copyright notice,\n *     this list of conditions and the following disclaimer.\n *   * Redistributions in binary form must reproduce the above copyright\n *     notice, this list of conditions and the following disclaimer in the\n *     documentation and/or other materials provided with the distribution.\n *   * Neither the name of Redis nor the names of its contributors may be used\n *     to endorse or promote products derived from this software without\n *     specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <ctype.h>\n#include <assert.h>\n\n#include \"sds.h\"\n\n/* Create a new sds string with the content specified by the 'init' pointer\n * and 'initlen'.\n * If NULL is used for 'init' the string is initialized with zero bytes.\n *\n * The string is always null-termined (all the sds strings are, always) so\n * even if you create an sds string with:\n *\n * mystring = sdsnewlen(\"abc\",3\");\n *\n * You can print the string with printf() as there is an implicit \\0 at the\n * end of the string. However the string is binary safe and can contain\n * \\0 characters in the middle, as the length is stored in the sds header. */\nsds sdsnewlen(const void *init, size_t initlen) {\n    struct sdshdr *sh;\n\n    if (init) {\n        sh = malloc(sizeof *sh+initlen+1);\n    } else {\n        sh = calloc(sizeof *sh+initlen+1,1);\n    }\n    if (sh == NULL) return NULL;\n    sh->len = initlen;\n    sh->free = 0;\n    if (initlen && init)\n        memcpy(sh->buf, init, initlen);\n    sh->buf[initlen] = '\\0';\n    return (char*)sh->buf;\n}\n\n/* Create an empty (zero length) sds string. Even in this case the string\n * always has an implicit null term. */\nsds sdsempty(void) {\n    return sdsnewlen(\"\",0);\n}\n\n/* Create a new sds string starting from a null termined C string. */\nsds sdsnew(const char *init) {\n    size_t initlen = (init == NULL) ? 0 : strlen(init);\n    return sdsnewlen(init, initlen);\n}\n\n/* Duplicate an sds string. */\nsds sdsdup(const sds s) {\n    return sdsnewlen(s, sdslen(s));\n}\n\n/* Free an sds string. No operation is performed if 's' is NULL. */\nvoid sdsfree(sds s) {\n    if (s == NULL) return;\n    free(s-sizeof(struct sdshdr));\n}\n\n/* Set the sds string length to the length as obtained with strlen(), so\n * considering as content only up to the first null term character.\n *\n * This function is useful when the sds string is hacked manually in some\n * way, like in the following example:\n *\n * s = sdsnew(\"foobar\");\n * s[2] = '\\0';\n * sdsupdatelen(s);\n * printf(\"%d\\n\", sdslen(s));\n *\n * The output will be \"2\", but if we comment out the call to sdsupdatelen()\n * the output will be \"6\" as the string was modified but the logical length\n * remains 6 bytes. */\nvoid sdsupdatelen(sds s) {\n    struct sdshdr *sh = (void*) (s-sizeof *sh);\n    int reallen = strlen(s);\n    sh->free += (sh->len-reallen);\n    sh->len = reallen;\n}\n\n/* Modify an sds string on-place to make it empty (zero length).\n * However all the existing buffer is not discarded but set as free space\n * so that next append operations will not require allocations up to the\n * number of bytes previously available. */\nvoid sdsclear(sds s) {\n    struct sdshdr *sh = (void*) (s-sizeof *sh);\n    sh->free += sh->len;\n    sh->len = 0;\n    sh->buf[0] = '\\0';\n}\n\n/* Enlarge the free space at the end of the sds string so that the caller\n * is sure that after calling this function can overwrite up to addlen\n * bytes after the end of the string, plus one more byte for nul term.\n *\n * Note: this does not change the *length* of the sds string as returned\n * by sdslen(), but only the free buffer space we have. */\nsds sdsMakeRoomFor(sds s, size_t addlen) {\n    struct sdshdr *sh, *newsh;\n    size_t free = sdsavail(s);\n    size_t len, newlen;\n\n    if (free >= addlen) return s;\n    len = sdslen(s);\n    sh = (void*) (s-sizeof *sh);\n    newlen = (len+addlen);\n    if (newlen < SDS_MAX_PREALLOC)\n        newlen *= 2;\n    else\n        newlen += SDS_MAX_PREALLOC;\n    newsh = realloc(sh, sizeof *newsh+newlen+1);\n    if (newsh == NULL) return NULL;\n\n    newsh->free = newlen - len;\n    return newsh->buf;\n}\n\n/* Reallocate the sds string so that it has no free space at the end. The\n * contained string remains not altered, but next concatenation operations\n * will require a reallocation.\n *\n * After the call, the passed sds string is no longer valid and all the\n * references must be substituted with the new pointer returned by the call. */\nsds sdsRemoveFreeSpace(sds s) {\n    struct sdshdr *sh;\n\n    sh = (void*) (s-sizeof *sh);\n    sh = realloc(sh, sizeof *sh+sh->len+1);\n    sh->free = 0;\n    return sh->buf;\n}\n\n/* Return the total size of the allocation of the specifed sds string,\n * including:\n * 1) The sds header before the pointer.\n * 2) The string.\n * 3) The free buffer at the end if any.\n * 4) The implicit null term.\n */\nsize_t sdsAllocSize(sds s) {\n    struct sdshdr *sh = (void*) (s-sizeof *sh);\n\n    return sizeof(*sh)+sh->len+sh->free+1;\n}\n\n/* Increment the sds length and decrements the left free space at the\n * end of the string according to 'incr'. Also set the null term\n * in the new end of the string.\n *\n * This function is used in order to fix the string length after the\n * user calls sdsMakeRoomFor(), writes something after the end of\n * the current string, and finally needs to set the new length.\n *\n * Note: it is possible to use a negative increment in order to\n * right-trim the string.\n *\n * Usage example:\n *\n * Using sdsIncrLen() and sdsMakeRoomFor() it is possible to mount the\n * following schema, to cat bytes coming from the kernel to the end of an\n * sds string without copying into an intermediate buffer:\n *\n * oldlen = sdslen(s);\n * s = sdsMakeRoomFor(s, BUFFER_SIZE);\n * nread = read(fd, s+oldlen, BUFFER_SIZE);\n * ... check for nread <= 0 and handle it ...\n * sdsIncrLen(s, nread);\n */\nvoid sdsIncrLen(sds s, int incr) {\n    struct sdshdr *sh = (void*) (s-sizeof *sh);\n\n    assert(sh->free >= incr);\n    sh->len += incr;\n    sh->free -= incr;\n    assert(sh->free >= 0);\n    s[sh->len] = '\\0';\n}\n\n/* Grow the sds to have the specified length. Bytes that were not part of\n * the original length of the sds will be set to zero.\n *\n * if the specified length is smaller than the current length, no operation\n * is performed. */\nsds sdsgrowzero(sds s, size_t len) {\n    struct sdshdr *sh = (void*) (s-sizeof *sh);\n    size_t totlen, curlen = sh->len;\n\n    if (len <= curlen) return s;\n    s = sdsMakeRoomFor(s,len-curlen);\n    if (s == NULL) return NULL;\n\n    /* Make sure added region doesn't contain garbage */\n    sh = (void*)(s-sizeof *sh);\n    memset(s+curlen,0,(len-curlen+1)); /* also set trailing \\0 byte */\n    totlen = sh->len+sh->free;\n    sh->len = len;\n    sh->free = totlen-sh->len;\n    return s;\n}\n\n/* Append the specified binary-safe string pointed by 't' of 'len' bytes to the\n * end of the specified sds string 's'.\n *\n * After the call, the passed sds string is no longer valid and all the\n * references must be substituted with the new pointer returned by the call. */\nsds sdscatlen(sds s, const void *t, size_t len) {\n    struct sdshdr *sh;\n    size_t curlen = sdslen(s);\n\n    s = sdsMakeRoomFor(s,len);\n    if (s == NULL) return NULL;\n    sh = (void*) (s-sizeof *sh);\n    memcpy(s+curlen, t, len);\n    sh->len = curlen+len;\n    sh->free = sh->free-len;\n    s[curlen+len] = '\\0';\n    return s;\n}\n\n/* Append the specified null termianted C string to the sds string 's'.\n *\n * After the call, the passed sds string is no longer valid and all the\n * references must be substituted with the new pointer returned by the call. */\nsds sdscat(sds s, const char *t) {\n    return sdscatlen(s, t, strlen(t));\n}\n\n/* Append the specified sds 't' to the existing sds 's'.\n *\n * After the call, the modified sds string is no longer valid and all the\n * references must be substituted with the new pointer returned by the call. */\nsds sdscatsds(sds s, const sds t) {\n    return sdscatlen(s, t, sdslen(t));\n}\n\n/* Destructively modify the sds string 's' to hold the specified binary\n * safe string pointed by 't' of length 'len' bytes. */\nsds sdscpylen(sds s, const char *t, size_t len) {\n    struct sdshdr *sh = (void*) (s-sizeof *sh);\n    size_t totlen = sh->free+sh->len;\n\n    if (totlen < len) {\n        s = sdsMakeRoomFor(s,len-sh->len);\n        if (s == NULL) return NULL;\n        sh = (void*) (s-sizeof *sh);\n        totlen = sh->free+sh->len;\n    }\n    memcpy(s, t, len);\n    s[len] = '\\0';\n    sh->len = len;\n    sh->free = totlen-len;\n    return s;\n}\n\n/* Like sdscpylen() but 't' must be a null-termined string so that the length\n * of the string is obtained with strlen(). */\nsds sdscpy(sds s, const char *t) {\n    return sdscpylen(s, t, strlen(t));\n}\n\n/* Helper for sdscatlonglong() doing the actual number -> string\n * conversion. 's' must point to a string with room for at least\n * SDS_LLSTR_SIZE bytes.\n *\n * The function returns the lenght of the null-terminated string\n * representation stored at 's'. */\n#define SDS_LLSTR_SIZE 21\nint sdsll2str(char *s, long long value) {\n    char *p, aux;\n    unsigned long long v;\n    size_t l;\n\n    /* Generate the string representation, this method produces\n     * an reversed string. */\n    v = (value < 0) ? -value : value;\n    p = s;\n    do {\n        *p++ = '0'+(v%10);\n        v /= 10;\n    } while(v);\n    if (value < 0) *p++ = '-';\n\n    /* Compute length and add null term. */\n    l = p-s;\n    *p = '\\0';\n\n    /* Reverse the string. */\n    p--;\n    while(s < p) {\n        aux = *s;\n        *s = *p;\n        *p = aux;\n        s++;\n        p--;\n    }\n    return l;\n}\n\n/* Identical sdsll2str(), but for unsigned long long type. */\nint sdsull2str(char *s, unsigned long long v) {\n    char *p, aux;\n    size_t l;\n\n    /* Generate the string representation, this method produces\n     * an reversed string. */\n    p = s;\n    do {\n        *p++ = '0'+(v%10);\n        v /= 10;\n    } while(v);\n\n    /* Compute length and add null term. */\n    l = p-s;\n    *p = '\\0';\n\n    /* Reverse the string. */\n    p--;\n    while(s < p) {\n        aux = *s;\n        *s = *p;\n        *p = aux;\n        s++;\n        p--;\n    }\n    return l;\n}\n\n/* Like sdscatpritf() but gets va_list instead of being variadic. */\nsds sdscatvprintf(sds s, const char *fmt, va_list ap) {\n    va_list cpy;\n    char *buf, *t;\n    size_t buflen = 16;\n\n    while(1) {\n        buf = malloc(buflen);\n        if (buf == NULL) return NULL;\n        buf[buflen-2] = '\\0';\n        va_copy(cpy,ap);\n        vsnprintf(buf, buflen, fmt, cpy);\n        if (buf[buflen-2] != '\\0') {\n            free(buf);\n            buflen *= 2;\n            continue;\n        }\n        break;\n    }\n    t = sdscat(s, buf);\n    free(buf);\n    return t;\n}\n\n/* Append to the sds string 's' a string obtained using printf-alike format\n * specifier.\n *\n * After the call, the modified sds string is no longer valid and all the\n * references must be substituted with the new pointer returned by the call.\n *\n * Example:\n *\n * s = sdsnew(\"Sum is: \");\n * s = sdscatprintf(s,\"%d+%d = %d\",a,b,a+b);\n *\n * Often you need to create a string from scratch with the printf-alike\n * format. When this is the need, just use sdsempty() as the target string:\n *\n * s = sdscatprintf(sdsempty(), \"... your format ...\", args);\n */\nsds sdscatprintf(sds s, const char *fmt, ...) {\n    va_list ap;\n    char *t;\n    va_start(ap, fmt);\n    t = sdscatvprintf(s,fmt,ap);\n    va_end(ap);\n    return t;\n}\n\n/* This function is similar to sdscatprintf, but much faster as it does\n * not rely on sprintf() family functions implemented by the libc that\n * are often very slow. Moreover directly handling the sds string as\n * new data is concatenated provides a performance improvement.\n *\n * However this function only handles an incompatible subset of printf-alike\n * format specifiers:\n *\n * %s - C String\n * %S - SDS string\n * %i - signed int\n * %I - 64 bit signed integer (long long, int64_t)\n * %u - unsigned int\n * %U - 64 bit unsigned integer (unsigned long long, uint64_t)\n * %T - A size_t variable.\n * %% - Verbatim \"%\" character.\n */\nsds sdscatfmt(sds s, char const *fmt, ...) {\n    struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));\n    size_t initlen = sdslen(s);\n    const char *f = fmt;\n    int i;\n    va_list ap;\n\n    va_start(ap,fmt);\n    f = fmt;    /* Next format specifier byte to process. */\n    i = initlen; /* Position of the next byte to write to dest str. */\n    while(*f) {\n        char next, *str;\n        int l;\n        long long num;\n        unsigned long long unum;\n\n        /* Make sure there is always space for at least 1 char. */\n        if (sh->free == 0) {\n            s = sdsMakeRoomFor(s,1);\n            sh = (void*) (s-(sizeof(struct sdshdr)));\n        }\n\n        switch(*f) {\n        case '%':\n            next = *(f+1);\n            f++;\n            switch(next) {\n            case 's':\n            case 'S':\n                str = va_arg(ap,char*);\n                l = (next == 's') ? strlen(str) : sdslen(str);\n                if (sh->free < l) {\n                    s = sdsMakeRoomFor(s,l);\n                    sh = (void*) (s-(sizeof(struct sdshdr)));\n                }\n                memcpy(s+i,str,l);\n                sh->len += l;\n                sh->free -= l;\n                i += l;\n                break;\n            case 'i':\n            case 'I':\n                if (next == 'i')\n                    num = va_arg(ap,int);\n                else\n                    num = va_arg(ap,long long);\n                {\n                    char buf[SDS_LLSTR_SIZE];\n                    l = sdsll2str(buf,num);\n                    if (sh->free < l) {\n                        s = sdsMakeRoomFor(s,l);\n                        sh = (void*) (s-(sizeof(struct sdshdr)));\n                    }\n                    memcpy(s+i,buf,l);\n                    sh->len += l;\n                    sh->free -= l;\n                    i += l;\n                }\n                break;\n            case 'u':\n            case 'U':\n            case 'T':\n                if (next == 'u')\n                    unum = va_arg(ap,unsigned int);\n                else if(next == 'U')\n                    unum = va_arg(ap,unsigned long long);\n                else\n                    unum = (unsigned long long)va_arg(ap,size_t);\n                {\n                    char buf[SDS_LLSTR_SIZE];\n                    l = sdsull2str(buf,unum);\n                    if (sh->free < l) {\n                        s = sdsMakeRoomFor(s,l);\n                        sh = (void*) (s-(sizeof(struct sdshdr)));\n                    }\n                    memcpy(s+i,buf,l);\n                    sh->len += l;\n                    sh->free -= l;\n                    i += l;\n                }\n                break;\n            default: /* Handle %% and generally %<unknown>. */\n                s[i++] = next;\n                sh->len += 1;\n                sh->free -= 1;\n                break;\n            }\n            break;\n        default:\n            s[i++] = *f;\n            sh->len += 1;\n            sh->free -= 1;\n            break;\n        }\n        f++;\n    }\n    va_end(ap);\n\n    /* Add null-term */\n    s[i] = '\\0';\n    return s;\n}\n\n\n/* Remove the part of the string from left and from right composed just of\n * contiguous characters found in 'cset', that is a null terminted C string.\n *\n * After the call, the modified sds string is no longer valid and all the\n * references must be substituted with the new pointer returned by the call.\n *\n * Example:\n *\n * s = sdsnew(\"AA...AA.a.aa.aHelloWorld     :::\");\n * s = sdstrim(s,\"A. :\");\n * printf(\"%s\\n\", s);\n *\n * Output will be just \"Hello World\".\n */\nvoid sdstrim(sds s, const char *cset) {\n    struct sdshdr *sh = (void*) (s-sizeof *sh);\n    char *start, *end, *sp, *ep;\n    size_t len;\n\n    sp = start = s;\n    ep = end = s+sdslen(s)-1;\n    while(sp <= end && strchr(cset, *sp)) sp++;\n    while(ep > start && strchr(cset, *ep)) ep--;\n    len = (sp > ep) ? 0 : ((ep-sp)+1);\n    if (sh->buf != sp) memmove(sh->buf, sp, len);\n    sh->buf[len] = '\\0';\n    sh->free = sh->free+(sh->len-len);\n    sh->len = len;\n}\n\n/* Turn the string into a smaller (or equal) string containing only the\n * substring specified by the 'start' and 'end' indexes.\n *\n * start and end can be negative, where -1 means the last character of the\n * string, -2 the penultimate character, and so forth.\n *\n * The interval is inclusive, so the start and end characters will be part\n * of the resulting string.\n *\n * The string is modified in-place.\n *\n * Example:\n *\n * s = sdsnew(\"Hello World\");\n * sdsrange(s,1,-1); => \"ello World\"\n */\nvoid sdsrange(sds s, int start, int end) {\n    struct sdshdr *sh = (void*) (s-sizeof *sh);\n    size_t newlen, len = sdslen(s);\n\n    if (len == 0) return;\n    if (start < 0) {\n        start = len+start;\n        if (start < 0) start = 0;\n    }\n    if (end < 0) {\n        end = len+end;\n        if (end < 0) end = 0;\n    }\n    newlen = (start > end) ? 0 : (end-start)+1;\n    if (newlen != 0) {\n        if (start >= (signed)len) {\n            newlen = 0;\n        } else if (end >= (signed)len) {\n            end = len-1;\n            newlen = (start > end) ? 0 : (end-start)+1;\n        }\n    } else {\n        start = 0;\n    }\n    if (start && newlen) memmove(sh->buf, sh->buf+start, newlen);\n    sh->buf[newlen] = 0;\n    sh->free = sh->free+(sh->len-newlen);\n    sh->len = newlen;\n}\n\n/* Apply tolower() to every character of the sds string 's'. */\nvoid sdstolower(sds s) {\n    int len = sdslen(s), j;\n\n    for (j = 0; j < len; j++) s[j] = tolower(s[j]);\n}\n\n/* Apply toupper() to every character of the sds string 's'. */\nvoid sdstoupper(sds s) {\n    int len = sdslen(s), j;\n\n    for (j = 0; j < len; j++) s[j] = toupper(s[j]);\n}\n\n/* Compare two sds strings s1 and s2 with memcmp().\n *\n * Return value:\n *\n *     1 if s1 > s2.\n *    -1 if s1 < s2.\n *     0 if s1 and s2 are exactly the same binary string.\n *\n * If two strings share exactly the same prefix, but one of the two has\n * additional characters, the longer string is considered to be greater than\n * the smaller one. */\nint sdscmp(const sds s1, const sds s2) {\n    size_t l1, l2, minlen;\n    int cmp;\n\n    l1 = sdslen(s1);\n    l2 = sdslen(s2);\n    minlen = (l1 < l2) ? l1 : l2;\n    cmp = memcmp(s1,s2,minlen);\n    if (cmp == 0) return l1-l2;\n    return cmp;\n}\n\n/* Split 's' with separator in 'sep'. An array\n * of sds strings is returned. *count will be set\n * by reference to the number of tokens returned.\n *\n * On out of memory, zero length string, zero length\n * separator, NULL is returned.\n *\n * Note that 'sep' is able to split a string using\n * a multi-character separator. For example\n * sdssplit(\"foo_-_bar\",\"_-_\"); will return two\n * elements \"foo\" and \"bar\".\n *\n * This version of the function is binary-safe but\n * requires length arguments. sdssplit() is just the\n * same function but for zero-terminated strings.\n */\nsds *sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count) {\n    int elements = 0, slots = 5, start = 0, j;\n    sds *tokens;\n\n    if (seplen < 1 || len < 0) return NULL;\n\n    tokens = malloc(sizeof(sds)*slots);\n    if (tokens == NULL) return NULL;\n\n    if (len == 0) {\n        *count = 0;\n        return tokens;\n    }\n    for (j = 0; j < (len-(seplen-1)); j++) {\n        /* make sure there is room for the next element and the final one */\n        if (slots < elements+2) {\n            sds *newtokens;\n\n            slots *= 2;\n            newtokens = realloc(tokens,sizeof(sds)*slots);\n            if (newtokens == NULL) goto cleanup;\n            tokens = newtokens;\n        }\n        /* search the separator */\n        if ((seplen == 1 && *(s+j) == sep[0]) || (memcmp(s+j,sep,seplen) == 0)) {\n            tokens[elements] = sdsnewlen(s+start,j-start);\n            if (tokens[elements] == NULL) goto cleanup;\n            elements++;\n            start = j+seplen;\n            j = j+seplen-1; /* skip the separator */\n        }\n    }\n    /* Add the final element. We are sure there is room in the tokens array. */\n    tokens[elements] = sdsnewlen(s+start,len-start);\n    if (tokens[elements] == NULL) goto cleanup;\n    elements++;\n    *count = elements;\n    return tokens;\n\ncleanup:\n    {\n        int i;\n        for (i = 0; i < elements; i++) sdsfree(tokens[i]);\n        free(tokens);\n        *count = 0;\n        return NULL;\n    }\n}\n\n/* Free the result returned by sdssplitlen(), or do nothing if 'tokens' is NULL. */\nvoid sdsfreesplitres(sds *tokens, int count) {\n    if (!tokens) return;\n    while(count--)\n        sdsfree(tokens[count]);\n    free(tokens);\n}\n\n/* Create an sds string from a long long value. It is much faster than:\n *\n * sdscatprintf(sdsempty(),\"%lld\\n\", value);\n */\nsds sdsfromlonglong(long long value) {\n    char buf[32], *p;\n    unsigned long long v;\n\n    v = (value < 0) ? -value : value;\n    p = buf+31; /* point to the last character */\n    do {\n        *p-- = '0'+(v%10);\n        v /= 10;\n    } while(v);\n    if (value < 0) *p-- = '-';\n    p++;\n    return sdsnewlen(p,32-(p-buf));\n}\n\n/* Append to the sds string \"s\" an escaped string representation where\n * all the non-printable characters (tested with isprint()) are turned into\n * escapes in the form \"\\n\\r\\a....\" or \"\\x<hex-number>\".\n *\n * After the call, the modified sds string is no longer valid and all the\n * references must be substituted with the new pointer returned by the call. */\nsds sdscatrepr(sds s, const char *p, size_t len) {\n    s = sdscatlen(s,\"\\\"\",1);\n    while(len--) {\n        switch(*p) {\n        case '\\\\':\n        case '\"':\n            s = sdscatprintf(s,\"\\\\%c\",*p);\n            break;\n        case '\\n': s = sdscatlen(s,\"\\\\n\",2); break;\n        case '\\r': s = sdscatlen(s,\"\\\\r\",2); break;\n        case '\\t': s = sdscatlen(s,\"\\\\t\",2); break;\n        case '\\a': s = sdscatlen(s,\"\\\\a\",2); break;\n        case '\\b': s = sdscatlen(s,\"\\\\b\",2); break;\n        default:\n            if (isprint(*p))\n                s = sdscatprintf(s,\"%c\",*p);\n            else\n                s = sdscatprintf(s,\"\\\\x%02x\",(unsigned char)*p);\n            break;\n        }\n        p++;\n    }\n    return sdscatlen(s,\"\\\"\",1);\n}\n\n/* Helper function for sdssplitargs() that returns non zero if 'c'\n * is a valid hex digit. */\nint is_hex_digit(char c) {\n    return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') ||\n           (c >= 'A' && c <= 'F');\n}\n\n/* Helper function for sdssplitargs() that converts a hex digit into an\n * integer from 0 to 15 */\nint hex_digit_to_int(char c) {\n    switch(c) {\n    case '0': return 0;\n    case '1': return 1;\n    case '2': return 2;\n    case '3': return 3;\n    case '4': return 4;\n    case '5': return 5;\n    case '6': return 6;\n    case '7': return 7;\n    case '8': return 8;\n    case '9': return 9;\n    case 'a': case 'A': return 10;\n    case 'b': case 'B': return 11;\n    case 'c': case 'C': return 12;\n    case 'd': case 'D': return 13;\n    case 'e': case 'E': return 14;\n    case 'f': case 'F': return 15;\n    default: return 0;\n    }\n}\n\n/* Split a line into arguments, where every argument can be in the\n * following programming-language REPL-alike form:\n *\n * foo bar \"newline are supported\\n\" and \"\\xff\\x00otherstuff\"\n *\n * The number of arguments is stored into *argc, and an array\n * of sds is returned.\n *\n * The caller should free the resulting array of sds strings with\n * sdsfreesplitres().\n *\n * Note that sdscatrepr() is able to convert back a string into\n * a quoted string in the same format sdssplitargs() is able to parse.\n *\n * The function returns the allocated tokens on success, even when the\n * input string is empty, or NULL if the input contains unbalanced\n * quotes or closed quotes followed by non space characters\n * as in: \"foo\"bar or \"foo'\n */\nsds *sdssplitargs(const char *line, int *argc) {\n    const char *p = line;\n    char *current = NULL;\n    char **vector = NULL;\n\n    *argc = 0;\n    while(1) {\n        /* skip blanks */\n        while(*p && isspace(*p)) p++;\n        if (*p) {\n            /* get a token */\n            int inq=0;  /* set to 1 if we are in \"quotes\" */\n            int insq=0; /* set to 1 if we are in 'single quotes' */\n            int done=0;\n\n            if (current == NULL) current = sdsempty();\n            while(!done) {\n                if (inq) {\n                    if (*p == '\\\\' && *(p+1) == 'x' &&\n                                             is_hex_digit(*(p+2)) &&\n                                             is_hex_digit(*(p+3)))\n                    {\n                        unsigned char byte;\n\n                        byte = (hex_digit_to_int(*(p+2))*16)+\n                                hex_digit_to_int(*(p+3));\n                        current = sdscatlen(current,(char*)&byte,1);\n                        p += 3;\n                    } else if (*p == '\\\\' && *(p+1)) {\n                        char c;\n\n                        p++;\n                        switch(*p) {\n                        case 'n': c = '\\n'; break;\n                        case 'r': c = '\\r'; break;\n                        case 't': c = '\\t'; break;\n                        case 'b': c = '\\b'; break;\n                        case 'a': c = '\\a'; break;\n                        default: c = *p; break;\n                        }\n                        current = sdscatlen(current,&c,1);\n                    } else if (*p == '\"') {\n                        /* closing quote must be followed by a space or\n                         * nothing at all. */\n                        if (*(p+1) && !isspace(*(p+1))) goto err;\n                        done=1;\n                    } else if (!*p) {\n                        /* unterminated quotes */\n                        goto err;\n                    } else {\n                        current = sdscatlen(current,p,1);\n                    }\n                } else if (insq) {\n                    if (*p == '\\\\' && *(p+1) == '\\'') {\n                        p++;\n                        current = sdscatlen(current,\"'\",1);\n                    } else if (*p == '\\'') {\n                        /* closing quote must be followed by a space or\n                         * nothing at all. */\n                        if (*(p+1) && !isspace(*(p+1))) goto err;\n                        done=1;\n                    } else if (!*p) {\n                        /* unterminated quotes */\n                        goto err;\n                    } else {\n                        current = sdscatlen(current,p,1);\n                    }\n                } else {\n                    switch(*p) {\n                    case ' ':\n                    case '\\n':\n                    case '\\r':\n                    case '\\t':\n                    case '\\0':\n                        done=1;\n                        break;\n                    case '\"':\n                        inq=1;\n                        break;\n                    case '\\'':\n                        insq=1;\n                        break;\n                    default:\n                        current = sdscatlen(current,p,1);\n                        break;\n                    }\n                }\n                if (*p) p++;\n            }\n            /* add the token to the vector */\n            vector = realloc(vector,((*argc)+1)*sizeof(char*));\n            vector[*argc] = current;\n            (*argc)++;\n            current = NULL;\n        } else {\n            /* Even on empty input string return something not NULL. */\n            if (vector == NULL) vector = malloc(sizeof(void*));\n            return vector;\n        }\n    }\n\nerr:\n    while((*argc)--)\n        sdsfree(vector[*argc]);\n    free(vector);\n    if (current) sdsfree(current);\n    *argc = 0;\n    return NULL;\n}\n\n/* Modify the string substituting all the occurrences of the set of\n * characters specified in the 'from' string to the corresponding character\n * in the 'to' array.\n *\n * For instance: sdsmapchars(mystring, \"ho\", \"01\", 2)\n * will have the effect of turning the string \"hello\" into \"0ell1\".\n *\n * The function returns the sds string pointer, that is always the same\n * as the input pointer since no resize is needed. */\nsds sdsmapchars(sds s, const char *from, const char *to, size_t setlen) {\n    size_t j, i, l = sdslen(s);\n\n    for (j = 0; j < l; j++) {\n        for (i = 0; i < setlen; i++) {\n            if (s[j] == from[i]) {\n                s[j] = to[i];\n                break;\n            }\n        }\n    }\n    return s;\n}\n\n/* Join an array of C strings using the specified separator (also a C string).\n * Returns the result as an sds string. */\nsds sdsjoin(char **argv, int argc, char *sep, size_t seplen) {\n    sds join = sdsempty();\n    int j;\n\n    for (j = 0; j < argc; j++) {\n        join = sdscat(join, argv[j]);\n        if (j != argc-1) join = sdscatlen(join,sep,seplen);\n    }\n    return join;\n}\n\n/* Like sdsjoin, but joins an array of SDS strings. */\nsds sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen) {\n    sds join = sdsempty();\n    int j;\n\n    for (j = 0; j < argc; j++) {\n        join = sdscatsds(join, argv[j]);\n        if (j != argc-1) join = sdscatlen(join,sep,seplen);\n    }\n    return join;\n}\n\n#ifdef SDS_TEST_MAIN\n#include <stdio.h>\n#include \"testhelp.h\"\n\nint main(void) {\n    {\n        struct sdshdr *sh;\n        sds x = sdsnew(\"foo\"), y;\n\n        test_cond(\"Create a string and obtain the length\",\n            sdslen(x) == 3 && memcmp(x,\"foo\\0\",4) == 0)\n\n        sdsfree(x);\n        x = sdsnewlen(\"foo\",2);\n        test_cond(\"Create a string with specified length\",\n            sdslen(x) == 2 && memcmp(x,\"fo\\0\",3) == 0)\n\n        x = sdscat(x,\"bar\");\n        test_cond(\"Strings concatenation\",\n            sdslen(x) == 5 && memcmp(x,\"fobar\\0\",6) == 0);\n\n        x = sdscpy(x,\"a\");\n        test_cond(\"sdscpy() against an originally longer string\",\n            sdslen(x) == 1 && memcmp(x,\"a\\0\",2) == 0)\n\n        x = sdscpy(x,\"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk\");\n        test_cond(\"sdscpy() against an originally shorter string\",\n            sdslen(x) == 33 &&\n            memcmp(x,\"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk\\0\",33) == 0)\n\n        sdsfree(x);\n        x = sdscatprintf(sdsempty(),\"%d\",123);\n        test_cond(\"sdscatprintf() seems working in the base case\",\n            sdslen(x) == 3 && memcmp(x,\"123\\0\",4) ==0)\n\n        sdsfree(x);\n        x = sdsnew(\"xxciaoyyy\");\n        sdstrim(x,\"xy\");\n        test_cond(\"sdstrim() correctly trims characters\",\n            sdslen(x) == 4 && memcmp(x,\"ciao\\0\",5) == 0)\n\n        y = sdsdup(x);\n        sdsrange(y,1,1);\n        test_cond(\"sdsrange(...,1,1)\",\n            sdslen(y) == 1 && memcmp(y,\"i\\0\",2) == 0)\n\n        sdsfree(y);\n        y = sdsdup(x);\n        sdsrange(y,1,-1);\n        test_cond(\"sdsrange(...,1,-1)\",\n            sdslen(y) == 3 && memcmp(y,\"iao\\0\",4) == 0)\n\n        sdsfree(y);\n        y = sdsdup(x);\n        sdsrange(y,-2,-1);\n        test_cond(\"sdsrange(...,-2,-1)\",\n            sdslen(y) == 2 && memcmp(y,\"ao\\0\",3) == 0)\n\n        sdsfree(y);\n        y = sdsdup(x);\n        sdsrange(y,2,1);\n        test_cond(\"sdsrange(...,2,1)\",\n            sdslen(y) == 0 && memcmp(y,\"\\0\",1) == 0)\n\n        sdsfree(y);\n        y = sdsdup(x);\n        sdsrange(y,1,100);\n        test_cond(\"sdsrange(...,1,100)\",\n            sdslen(y) == 3 && memcmp(y,\"iao\\0\",4) == 0)\n\n        sdsfree(y);\n        y = sdsdup(x);\n        sdsrange(y,100,100);\n        test_cond(\"sdsrange(...,100,100)\",\n            sdslen(y) == 0 && memcmp(y,\"\\0\",1) == 0)\n\n        sdsfree(y);\n        sdsfree(x);\n        x = sdsnew(\"foo\");\n        y = sdsnew(\"foa\");\n        test_cond(\"sdscmp(foo,foa)\", sdscmp(x,y) > 0)\n\n        sdsfree(y);\n        sdsfree(x);\n        x = sdsnew(\"bar\");\n        y = sdsnew(\"bar\");\n        test_cond(\"sdscmp(bar,bar)\", sdscmp(x,y) == 0)\n\n        sdsfree(y);\n        sdsfree(x);\n        x = sdsnew(\"aar\");\n        y = sdsnew(\"bar\");\n        test_cond(\"sdscmp(bar,bar)\", sdscmp(x,y) < 0)\n\n        sdsfree(y);\n        sdsfree(x);\n        x = sdsnewlen(\"\\a\\n\\0foo\\r\",7);\n        y = sdscatrepr(sdsempty(),x,sdslen(x));\n        test_cond(\"sdscatrepr(...data...)\",\n            memcmp(y,\"\\\"\\\\a\\\\n\\\\x00foo\\\\r\\\"\",15) == 0)\n\n        {\n            int oldfree;\n\n            sdsfree(x);\n            x = sdsnew(\"0\");\n            sh = (void*) (x-(sizeof(struct sdshdr)));\n            test_cond(\"sdsnew() free/len buffers\", sh->len == 1 && sh->free == 0);\n            x = sdsMakeRoomFor(x,1);\n            sh = (void*) (x-(sizeof(struct sdshdr)));\n            test_cond(\"sdsMakeRoomFor()\", sh->len == 1 && sh->free > 0);\n            oldfree = sh->free;\n            x[1] = '1';\n            sdsIncrLen(x,1);\n            test_cond(\"sdsIncrLen() -- content\", x[0] == '0' && x[1] == '1');\n            test_cond(\"sdsIncrLen() -- len\", sh->len == 2);\n            test_cond(\"sdsIncrLen() -- free\", sh->free == oldfree-1);\n        }\n    }\n    test_report()\n    return 0;\n}\n#endif\n"
  },
  {
    "path": "dep/himemcached-0.1.0/himcdep/sds.h",
    "content": "/* SDS (Simple Dynamic Strings), A C dynamic strings library.\n *\n * Copyright (c) 2006-2014, Salvatore Sanfilippo <antirez at gmail dot com>\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n *   * Redistributions of source code must retain the above copyright notice,\n *     this list of conditions and the following disclaimer.\n *   * Redistributions in binary form must reproduce the above copyright\n *     notice, this list of conditions and the following disclaimer in the\n *     documentation and/or other materials provided with the distribution.\n *   * Neither the name of Redis nor the names of its contributors may be used\n *     to endorse or promote products derived from this software without\n *     specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\n#ifndef __SDS_H\n#define __SDS_H\n\n#define SDS_MAX_PREALLOC (1024*1024)\n\n#include <sys/types.h>\n#include <stdarg.h>\n#ifdef _MSC_VER\n#include \"win32.h\"\n#endif\n\ntypedef char *sds;\n\nstruct sdshdr {\n    int len;\n    int free;\n    char buf[];\n};\n\nstatic inline size_t sdslen(const sds s) {\n    struct sdshdr *sh = (struct sdshdr *)(s-sizeof *sh);\n    return sh->len;\n}\n\nstatic inline size_t sdsavail(const sds s) {\n    struct sdshdr *sh = (struct sdshdr *)(s-sizeof *sh);\n    return sh->free;\n}\n\nsds sdsnewlen(const void *init, size_t initlen);\nsds sdsnew(const char *init);\nsds sdsempty(void);\nsize_t sdslen(const sds s);\nsds sdsdup(const sds s);\nvoid sdsfree(sds s);\nsize_t sdsavail(const sds s);\nsds sdsgrowzero(sds s, size_t len);\nsds sdscatlen(sds s, const void *t, size_t len);\nsds sdscat(sds s, const char *t);\nsds sdscatsds(sds s, const sds t);\nsds sdscpylen(sds s, const char *t, size_t len);\nsds sdscpy(sds s, const char *t);\n\nsds sdscatvprintf(sds s, const char *fmt, va_list ap);\n#ifdef __GNUC__\nsds sdscatprintf(sds s, const char *fmt, ...)\n    __attribute__((format(printf, 2, 3)));\n#else\nsds sdscatprintf(sds s, const char *fmt, ...);\n#endif\n\nsds sdscatfmt(sds s, char const *fmt, ...);\nvoid sdstrim(sds s, const char *cset);\nvoid sdsrange(sds s, int start, int end);\nvoid sdsupdatelen(sds s);\nvoid sdsclear(sds s);\nint sdscmp(const sds s1, const sds s2);\nsds *sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count);\nvoid sdsfreesplitres(sds *tokens, int count);\nvoid sdstolower(sds s);\nvoid sdstoupper(sds s);\nsds sdsfromlonglong(long long value);\nsds sdscatrepr(sds s, const char *p, size_t len);\nsds *sdssplitargs(const char *line, int *argc);\nsds sdsmapchars(sds s, const char *from, const char *to, size_t setlen);\nsds sdsjoin(char **argv, int argc, char *sep, size_t seplen);\nsds sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen);\n\n/* Low level functions exposed to the user API */\nsds sdsMakeRoomFor(sds s, size_t addlen);\nvoid sdsIncrLen(sds s, int incr);\nsds sdsRemoveFreeSpace(sds s);\nsize_t sdsAllocSize(sds s);\n\n#endif\n"
  },
  {
    "path": "dep/himemcached-0.1.0/himcread.c",
    "content": "#include <string.h>\n#include <stdlib.h>\n#ifndef _MSC_VER\n#include <unistd.h>\n#endif\n#include <assert.h>\n#include <errno.h>\n#include <ctype.h>\n\n#include \"himcread.h\"\n#include \"himcdep/sds.h\"\n\n#define PARSE_OK    0        /* Parsing ok */\n#define PARSE_ERROR 1        /* Parsing error */\n#define PARSE_AGAIN 3        /* Incomplete -> parse again */\n\n#define RSP_TYPE_UNKNOWN        0\n#define RSP_TYPE_NUM            1\n#define RSP_TYPE_STORED         2\n#define RSP_TYPE_NOT_STORED     3\n#define RSP_TYPE_EXISTS         4\n#define RSP_TYPE_NOT_FOUND      5\n#define RSP_TYPE_END            6\n#define RSP_TYPE_VALUE          7\n#define RSP_TYPE_DELETED        8\n#define RSP_TYPE_ERROR          9\n#define RSP_TYPE_CLIENT_ERROR   10\n#define RSP_TYPE_SERVER_ERROR   11\n\nstatic void memcachedReaderReset(mcReader *r);\n\nstatic void __memcachedReaderSetError(mcReader *r, int type, const char *str) {\n    size_t len;\n\n    memcachedReaderReset(r);\n\n    /* Clear input buffer on errors. */\n    if (r->buf != NULL) {\n        sdsfree(r->buf);\n        r->buf = NULL;\n        r->pos = r->len = 0;\n    }\n\n    /* Set error. */\n    r->err = type;\n    len = strlen(str);\n    len = len < (sizeof(r->errstr)-1) ? len : (sizeof(r->errstr)-1);\n    memcpy(r->errstr,str,len);\n    r->errstr[len] = '\\0';\n}\n\nstatic size_t chrtos(char *buf, size_t size, char byte) \n{\n    size_t len = 0;\n\n    switch(byte) {\n    case '\\\\':\n    case '\"':\n        len = snprintf(buf,size,\"\\\"\\\\%c\\\"\",byte);\n        break;\n    case '\\n': len = snprintf(buf,size,\"\\\"\\\\n\\\"\"); break;\n    case '\\r': len = snprintf(buf,size,\"\\\"\\\\r\\\"\"); break;\n    case '\\t': len = snprintf(buf,size,\"\\\"\\\\t\\\"\"); break;\n    case '\\a': len = snprintf(buf,size,\"\\\"\\\\a\\\"\"); break;\n    case '\\b': len = snprintf(buf,size,\"\\\"\\\\b\\\"\"); break;\n    default:\n        if (isprint(byte))\n            len = snprintf(buf,size,\"\\\"%c\\\"\",byte);\n        else\n            len = snprintf(buf,size,\"\\\"\\\\x%02x\\\"\",(unsigned char)byte);\n        break;\n    }\n\n    return len;\n}\n\nstatic void __memcachedReaderSetErrorProtocolByte(mcReader *r, char byte) {\n    char cbuf[8], sbuf[128];\n\n    chrtos(cbuf,sizeof(cbuf),byte);\n    snprintf(sbuf,sizeof(sbuf),\n        \"Protocol error, got %s as reply type byte\", cbuf);\n    __memcachedReaderSetError(r,MC_ERR_PROTOCOL,sbuf);\n}\n\nstatic void __memcachedReaderSetErrorOOM(mcReader *r) {\n    __memcachedReaderSetError(r,MC_ERR_OOM,\"Out of memory\");\n}\n\nstatic int elementArrayCreate(mcReader *r)\n{\n    assert(r->alloc_len == 0);\n    assert(r->element == NULL);\n    assert(r->elements == 0);\n\n    r->element = malloc(10*sizeof(void*));\n    if (r->element == NULL) {\n        __memcachedReaderSetErrorOOM(r);\n        return MC_ERR;\n    }\n    r->alloc_len = 10;\n    r->elements = 0;\n    \n    return MC_OK;\n}\n\nstatic void elementArrayDestroy(mcReader *r)\n{\n    unsigned int i;\n\n    if (r->element == NULL)\n        return;\n\n    if (r->fn && r->fn->freeObject) {\n        for (i = 0; i < r->elements; i ++) {\n            if (r->element[i])\n                r->fn->freeObject(r->element[i]);\n        }\n    }\n    free(r->element);\n    r->element = NULL;\n    r->elements = 0;\n    r->alloc_len = 0;\n    \n    return MC_OK;\n}\n\n#define EXPAND_MAX_SIZE_PER_TIME 300\nstatic int elementArrayExpand(mcReader *r) \n{\n    size_t new_length;\n    if (r->alloc_len <= 150) {\n        new_length = r->alloc_len*2;\n    } else if (r->alloc_len <= 500) {\n        new_length = r->alloc_len+EXPAND_MAX_SIZE_PER_TIME;\n    }\n    r->element = realloc(r->element,new_length*sizeof(void*));\n    if (r->element == NULL) {\n        __memcachedReaderSetErrorOOM(r);\n        return MC_ERR;\n    }\n    r->alloc_len = new_length;\n\n    return MC_OK;\n}\n\nstatic int elementArrayAdd(mcReader *r, void *reply)\n{\n    assert(r->elements <= r->alloc_len);\n    if (r->elements == r->alloc_len) {\n        if (elementArrayExpand(r) != MC_OK)\n            return MC_ERR;\n    }\n    r->element[r->elements++] = reply;\n\n    return MC_OK;\n}\n\nstatic void memcachedParseResponse(mcReader *r)\n{\n    void *obj;\n    char *p, *m;\n    char ch;\n    enum {\n        SW_START,\n        SW_RSP_NUM,\n        SW_RSP_STR,\n        SW_SPACES_BEFORE_KEY,\n        SW_KEY,\n        SW_SPACES_BEFORE_FLAGS,     /* 5 */\n        SW_FLAGS,\n        SW_SPACES_BEFORE_VLEN,\n        SW_VLEN,\n        SW_RUNTO_VAL,\n        SW_VAL,                     /* 10 */\n        SW_VAL_LF,\n        SW_END,\n        SW_RUNTO_CRLF,\n        SW_CRLF,\n        SW_ALMOST_DONE,             /* 15 */\n        SW_SENTINEL\n    } state;\n\n    state = r->state;\n\n    assert(state >= SW_START && state < SW_SENTINEL);\n\n    /* validate the parsing marker */\n    assert(r->buf != NULL);\n    assert(r->pos < r->len);\n\n    for (p = r->buf+r->pos; p <= r->buf+r->len; p++) {\n        ch = *p;\n\n        switch (state) {\n        case SW_START:\n            if (isdigit(ch)) {\n                state = SW_RSP_NUM;\n            } else {\n                state = SW_RSP_STR;\n            }\n            p = p - 1; /* go back by 1 byte */\n\n            break;\n\n        case SW_RSP_NUM:\n            if (r->token == NULL) {\n                /* rsp_start <- p; type_start <- p */\n                r->token = p;\n            }\n\n            if (isdigit(ch)) {\n                /* num <- num * 10 + (ch - '0') */\n                r->integer = r->integer*10 + (long long)(ch-'0');\n            } else if (ch == ' ' || ch == '\\r') {\n                /* type_end <- p - 1 */\n                r->token = NULL;\n                r->integer = 0;\n                r->type = RSP_TYPE_NUM;\n                p = p - 1; /* go back by 1 byte */\n                state = SW_CRLF;\n            } else {\n                goto error;\n            }\n\n            break;\n\n        case SW_RSP_STR:\n            if (r->token == NULL) {\n                /* rsp_start <- p; type_start <- p */\n                r->token = p;\n            }\n\n            if (ch == ' ' || ch == '\\r') {\n                /* type_end <- p - 1 */\n                m = r->token;\n                /* r->token = NULL; */\n                r->type = RSP_TYPE_UNKNOWN;\n                assert(r->str == NULL && r->strlen == 0);\n                \n                switch (p - m) {\n                case 3:\n                    if (!strncmp(m,\"END\\r\",4)) {\n                        r->type = RSP_TYPE_END;\n                        /* end_start <- m; end_end <- p - 1 */\n                    }\n\n                    break;\n\n                case 5:\n                    if (!strncmp(m,\"VALUE\",5)) {\n                        /*\n                                           * Encompasses responses for 'get', 'gets' and\n                                           * 'cas' command.\n                                           */\n                        r->type = RSP_TYPE_VALUE;\n                        break;\n                    }\n\n                    if (!strncmp(m,\"ERROR\",5)) {\n                        r->type = RSP_TYPE_ERROR;\n                        break;\n                    }\n\n                    break;\n\n                case 6:\n                    if (!strncmp(m,\"STORED\",6)) {\n                        r->type = RSP_TYPE_STORED;\n\n                        r->str = m;\n                        r->strlen = 6;\n                        break;\n                    }\n\n                    if (!strncmp(m,\"EXISTS\",6)) {\n                        r->type = RSP_TYPE_EXISTS;\n\n                        r->str = m;\n                        r->strlen = 6;\n                        break;\n                    }\n\n                    break;\n\n                case 7:\n                    if (!strncmp(m,\"DELETED\",7)) {\n                        r->type = RSP_TYPE_DELETED;\n                        \n                        r->str = m;\n                        r->strlen = 7;\n                        break;\n                    }\n\n                    break;\n\n                case 9:\n                    if (!strncmp(m,\"NOT_FOUND\",9)) {\n                        r->type = RSP_TYPE_NOT_FOUND;\n\n                        r->str = m;\n                        r->strlen = 9;\n                        break;\n                    }\n\n                    break;\n\n                case 10:\n                    if (!strncmp(m,\"NOT_STORED\",10)) {\n                        r->type = RSP_TYPE_NOT_STORED;\n\n                        r->str = m;\n                        r->strlen = 10;\n                        break;\n                    }\n\n                    break;\n\n                case 12:\n                    if (!strncmp(m,\"CLIENT_ERROR\",12)) {\n                        r->type = RSP_TYPE_CLIENT_ERROR;\n                        break;\n                    }\n\n                    if (!strncmp(m,\"SERVER_ERROR\",12)) {\n                        r->type = RSP_TYPE_SERVER_ERROR;\n                        break;\n                    }\n\n                    break;\n                }\n\n                switch (r->type) {\n                case RSP_TYPE_UNKNOWN:\n                    goto error;\n\n                case RSP_TYPE_STORED:\n                case RSP_TYPE_NOT_STORED:\n                case RSP_TYPE_EXISTS:\n                case RSP_TYPE_NOT_FOUND:\n                case RSP_TYPE_DELETED:\n                    state = SW_CRLF;\n                    break;\n\n                case RSP_TYPE_END:\n                    state = SW_CRLF;\n                    break;\n\n                case RSP_TYPE_VALUE:\n                    state = SW_SPACES_BEFORE_KEY;\n                    break;\n\n                case RSP_TYPE_ERROR:\n                    state = SW_CRLF;\n                    break;\n\n                case RSP_TYPE_CLIENT_ERROR:\n                case RSP_TYPE_SERVER_ERROR:\n                    r->token = NULL;\n                    state = SW_RUNTO_CRLF;\n                    break;\n\n                default:\n                    NOT_REACHED();\n                }\n\n                p = p - 1; /* go back by 1 byte */\n            }\n\n            break;\n\n        case SW_SPACES_BEFORE_KEY:\n            if (ch != ' ') {\n                state = SW_KEY;\n                p = p - 1; /* go back by 1 byte */\n                r->token = NULL;\n            }\n\n            break;\n\n        case SW_KEY:\n            if (r->token == NULL) {\n                r->token = p;\n            }\n            \n            if (ch == ' ') {\n                assert(r->str == NULL && r->strlen == 0);\n                m = r->token;\n                r->token = NULL;\n                state = SW_SPACES_BEFORE_FLAGS;\n                r->strlen = p-m;\n                r->str = m;\n            }\n\n            break;\n\n        case SW_SPACES_BEFORE_FLAGS:\n            if (ch != ' ') {\n                if (!isdigit(ch)) {\n                    goto error;\n                }\n                state = SW_FLAGS;\n                p = p - 1; /* go back by 1 byte */\n                r->kflags = 0;\n            }\n\n            break;\n\n        case SW_FLAGS:\n            if (isdigit(ch)) {\n                /* flags <- flags * 10 + (ch - '0') */\n                r->kflags = r->kflags*10 + (int)(ch-'0');\n            } else if (ch == ' ') {\n                /* flags_end <- p - 1 */\n                /* r->token = NULL; */\n                state = SW_SPACES_BEFORE_VLEN;\n            } else {\n                goto error;\n            }\n\n            break;\n\n        case SW_SPACES_BEFORE_VLEN:\n            if (ch != ' ') {\n                if (!isdigit(ch)) {\n                    goto error;\n                }\n                p = p - 1; /* go back by 1 byte */\n                state = SW_VLEN;\n                r->integer = 0;\n            }\n\n            break;\n\n        case SW_VLEN:\n            if (isdigit(ch)) {\n                r->integer = r->integer*10 + (long long)(ch-'0');\n            } else if (ch == ' ' || ch == '\\r') {\n                /* vlen_end <- p - 1 */\n                p = p - 1; /* go back by 1 byte */\n                /* r->token = NULL; */\n                state = SW_RUNTO_CRLF;\n            } else {\n                goto error;\n            }\n\n            break;\n\n        case SW_RUNTO_VAL:\n            switch (ch) {\n            case '\\n':\n                /* val_start <- p + 1 */\n                state = SW_VAL;\n                r->token = NULL;\n                break;\n\n            default:\n                goto error;\n            }\n\n            break;\n\n        case SW_VAL:\n            if (r->token == NULL) {\n                /* flags_start <- p */\n                r->token = p;\n            }\n\n            m = r->token + r->integer;\n            if (m > r->buf+r->len) {\n                p = r->buf + r->len;\n                break;\n            }\n            \n            switch (*m) {\n            case '\\r':\n                /* val_end <- p - 1 */\n                p = m; /* move forward by vlen bytes */\n                state = SW_VAL_LF;\n                break;\n\n            default:\n                goto error;\n            }\n\n            break;\n\n        case SW_VAL_LF:\n            switch (ch) {\n            case '\\n':\n                /* state = SW_END; */\n                if (r->fn && r->fn->createString)\n                    obj = r->fn->createString(MC_REPLY_STRING,r->str,r->strlen,\n                        r->token,r->integer,r->kflags,r->kversion);\n                else\n                    obj = (void*)MC_REPLY_STRING;\n                if (r->element) {\n                    assert(r->subreply == NULL);\n                    elementArrayAdd(r,r->subreply);\n                } else if (r->subreply) {\n                    elementArrayCreate(r);\n                    elementArrayAdd(r,r->subreply);\n                    r->subreply = NULL;\n                    elementArrayAdd(r,obj);\n                } else {\n                    r->subreply = obj;\n                }\n                \n                r->token = NULL;\n                r->str = NULL;\n                r->strlen = 0;\n                r->kflags = 0;\n                r->kversion = -1;\n                state = SW_RSP_STR;\n                break;\n\n            default:\n                goto error;\n            }\n\n            break;\n\n        case SW_END:\n            if (r->token == NULL) {\n                if (ch != 'E') {\n                    goto error;\n                }\n                /* end_start <- p */\n                r->token = p;\n            } else if (ch == '\\r') {\n                /* end_end <- p */\n                m = r->token;\n                r->token = NULL;\n\n                switch (p - m) {\n                case 3:\n                    if (!strncmp(m,\"END\\r\",4)) {\n                        state = SW_ALMOST_DONE;\n                    }\n                    break;\n\n                default:\n                    goto error;\n                }\n            }\n\n            break;\n\n        case SW_RUNTO_CRLF:\n            switch (ch) {\n            case '\\r':\n                if (r->type == RSP_TYPE_VALUE) {\n                    state = SW_RUNTO_VAL;\n                } else {\n                    if (r->type == RSP_TYPE_CLIENT_ERROR || \n                        r->type == RSP_TYPE_SERVER_ERROR) {\n                        m = r->token;\n                        r->token = NULL;\n                        r->strlen = p-m;\n                        r->str = m;\n                    }\n                    state = SW_ALMOST_DONE;\n                }\n\n                break;\n\n            default:\n                break;\n            }\n\n            break;\n\n        case SW_CRLF:\n            switch (ch) {\n            case ' ':\n                break;\n\n            case '\\r':\n                state = SW_ALMOST_DONE;\n                break;\n\n            default:\n                goto error;\n            }\n\n            break;\n\n        case SW_ALMOST_DONE:\n            switch (ch) {\n            case '\\n':\n                /* rsp_end <- p */\n                goto done;\n\n            default:\n                goto error;\n            }\n\n            break;\n\n        case SW_SENTINEL:\n        default:\n            NOT_REACHED();\n            break;\n\n        }\n    }\n\n    assert(p == r->buf+r->len);\n    r->pos = r->len;\n    r->state = state;\n    \n    r->result = PARSE_AGAIN;\n\n    return;\n\ndone:\n    r->pos = p-r->buf+1;\n    assert(r->pos <= r->len);\n    r->state = SW_START;\n    r->token = NULL;\n    r->result = PARSE_OK;\n\n    return;\n\nerror:\n    r->result = PARSE_ERROR;\n    r->state = state;\n    errno = EINVAL;\n}\n\nmcReader *memcachedReaderCreateWithFunctions(mcReplyObjectFunctions *fn) \n{\n    mcReader *r;\n\n    r = calloc(sizeof(mcReader),1);\n    if (r == NULL)\n        return NULL;\n\n    r->err = 0;\n    r->errstr[0] = '\\0';\n    r->buf = sdsempty();\n    r->maxbuf = MC_READER_MAX_BUF;\n    if (r->buf == NULL) {\n        free(r);\n        return NULL;\n    }\n\n    r->subreply = NULL;\n    r->alloc_len = 0;\n    r->elements = 0;\n    r->element = NULL;\n\n    r->state = 0;\n    r->token = NULL;\n\n    r->str = NULL;\n    r->strlen = 0;\n    r->kflags = 0;\n    r->kversion = -1;\n    r->integer = 0;\n    r->type = RSP_TYPE_UNKNOWN;\n    r->result = PARSE_OK;\n\n    r->fn = fn;\n    \n    return r;\n}\n\nvoid memcachedReaderFree(mcReader *r) \n{\n    memcachedReaderReset(r);\n    \n    if (r->buf != NULL)\n        sdsfree(r->buf);\n    free(r);\n}\n\nint memcachedReaderFeed(mcReader *r, const char *buf, size_t len) \n{\n    sds newbuf;\n\n    /* Return early when this reader is in an erroneous state. */\n    if (r->err)\n        return MC_ERR;\n\n    /* Copy the provided buffer. */\n    if (buf != NULL && len >= 1) {\n        /* Destroy internal buffer when it is empty and is quite large. */\n        if (r->len == 0 && r->maxbuf != 0 && sdsavail(r->buf) > r->maxbuf) {\n            sdsfree(r->buf);\n            r->buf = sdsempty();\n            r->pos = 0;\n\n            /* r->buf should not be NULL since we just free'd a larger one. */\n            assert(r->buf != NULL);\n        }\n\n        newbuf = sdscatlen(r->buf,buf,len);\n        if (newbuf == NULL) {\n            __memcachedReaderSetErrorOOM(r);\n            return MC_ERR;\n        }\n\n        r->buf = newbuf;\n        r->len = sdslen(r->buf);\n    }\n\n    return MC_OK;\n}\n\nstatic void memcachedReaderReset(mcReader *r) \n{\n    r->str = NULL;\n    r->strlen = 0;\n    r->kflags = 0;\n    r->kversion = -1;\n\n    r->state = 0;\n    r->token = 0;\n \n    r->integer = 0;\n\n    r->type = RSP_TYPE_UNKNOWN;\n    r->result = PARSE_OK;\n    \n    if (r->subreply != NULL) {\n        if (r->fn && r->fn->freeObject)\n            r->fn->freeObject(r->subreply);\n        \n        r->subreply = NULL;\n    }\n\n    elementArrayDestroy(r);\n\n    r->err = 0;\n    r->errstr[0] = '\\0';\n}\n\nstatic void *getReplyFromReader(mcReader *r)\n{\n    void *reply;\n\n    switch (r->type) {\n        case RSP_TYPE_VALUE:\n            if (r->element) {\n                assert(r->subreply == NULL);\n                if (r->fn && r->fn->createArray) {\n                    reply = r->fn->createArray(r->elements,r->element);\n                    r->element = NULL;\n                    r->elements = 0;\n                    r->alloc_len = 0;\n                } else {\n                    reply = (void*)MC_REPLY_ARRAY;\n                }\n            } else if (r->subreply) {\n                reply = r->subreply;\n            }\n            break;\n        case RSP_TYPE_NUM:\n            if (r->fn && r->fn->createInteger)\n                reply = r->fn->createInteger(r->integer);\n            else\n                reply = (void*)MC_REPLY_INTEGER;\n            break;\n        case RSP_TYPE_END:\n            if (r->fn && r->fn->createNil)\n                reply = r->fn->createNil();\n            else\n                reply = (void*)MC_REPLY_NIL;\n            break;\n        case RSP_TYPE_CLIENT_ERROR:\n        case RSP_TYPE_SERVER_ERROR:\n            if (r->fn && r->fn->createString)\n                reply = r->fn->createString(MC_REPLY_ERROR,\n                    NULL,0,r->str,r->strlen,0,0);\n            else\n                reply = (void*)MC_REPLY_ERROR;\n            break;\n        case RSP_TYPE_ERROR:\n            if (r->fn && r->fn->createString)\n                reply = r->fn->createString(MC_REPLY_ERROR,\n                    NULL,0,\"\",0,0,0);\n            else\n                reply = (void*)MC_REPLY_ERROR;\n            break;\n        case RSP_TYPE_STORED:\n        case RSP_TYPE_NOT_STORED:\n        case RSP_TYPE_EXISTS:\n        case RSP_TYPE_NOT_FOUND:\n        case RSP_TYPE_DELETED:\n            if (r->fn && r->fn->createString)\n                reply = r->fn->createString(MC_REPLY_STATUS,\n                    NULL,0,r->str,r->strlen,0,0);\n            else\n                reply = (void*)MC_REPLY_STATUS;\n            break;\n        default:\n            reply = NULL;\n            break;\n    }\n    \n    return reply;\n}\n\nint memcachedReaderGetReply(mcReader *r, void **reply) {\n    /* Default target pointer to NULL. */\n    if (reply != NULL)\n        *reply = NULL;\n\n    /* Return early when this reader is in an erroneous state. */\n    if (r->err)\n        return MC_ERR;\n\n    /* When the buffer is empty, there will never be a reply. */\n    if (r->len == 0)\n        return MC_OK;\n\n    memcachedParseResponse(r);\n\n    /* Return ASAP when an error occurred. */\n    if (r->err)\n        return MC_ERR;\n\n    /*\n    printf(\"!######### r->result: %d, r->state: %d, r->type: %d, r->pos: %d, r->len: %d,\" \n        \"r->kflags: %d, r->kversion : %lld, r->strlen: %zu, r->integer: %lld r->buf: %s, r->str: %s\\n\", \n        r->result, r->state, r->type, r->pos, r->len, r->kflags, r->kversion, \n        r->strlen, r->integer, r->buf, r->str);\n        */\n    /* Emit a reply when there is one. */\n    if (r->result == PARSE_OK) {\n        if (reply != NULL) {\n            *reply = getReplyFromReader(r);\n        }\n        memcachedReaderReset(r);\n    }\n\n    /* Discard part of the buffer when we've consumed at least 1k, to avoid\n     * doing unnecessary calls to memmove() in sds.c. */\n    if (r->pos >= 1024 && r->token == NULL && r->str == NULL) {\n        sdsrange(r->buf,r->pos,-1);\n        r->pos = 0;\n        r->len = sdslen(r->buf);\n    }\n\n    return MC_OK;\n}\n"
  },
  {
    "path": "dep/himemcached-0.1.0/himcread.h",
    "content": "#ifndef _HIMC_READ_H_\n#define _HIMC_READ_H_\n#include <stdio.h> /* for size_t */\n\n#include <himcread.h>\n\n#define MC_ERR   -1\n#define MC_OK    0\n\n/* When an error occurs, the err flag in a context is set to hold the type of\n * error that occured. REDIS_ERR_IO means there was an I/O error and you\n * should use the \"errno\" variable to find out what is wrong.\n * For other values, the \"errstr\" field will hold a description. */\n#define MC_ERR_IO 1 /* Error in read or write */\n#define MC_ERR_EOF 3 /* End of file */\n#define MC_ERR_PROTOCOL 4 /* Protocol error */\n#define MC_ERR_OOM 5 /* Out of memory */\n#define MC_ERR_OTHER 2 /* Everything else... */\n\n#define MC_REPLY_STRING     1\n#define MC_REPLY_ARRAY      2\n#define MC_REPLY_INTEGER    3\n#define MC_REPLY_NIL        4\n#define MC_REPLY_STATUS     5\n#define MC_REPLY_ERROR      6\n\n#define MC_READER_MAX_BUF (1024*16)  /* Default max unused reader buffer. */\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct mcReplyObjectFunctions {\n    void *(*createString)(int, char*, size_t, char*, size_t, int, long long);\n    void *(*createArray)(size_t, void **);\n    void *(*createInteger)(long long);\n    void *(*createNil)(void);\n    void (*freeObject)(void*);\n} mcReplyObjectFunctions;\n\ntypedef struct mcReader {\n    int err; /* Error flags, 0 when there is no error */\n    char errstr[128]; /* String representation of error when applicable */\n\n    char *buf; /* Read buffer */\n    size_t pos; /* Buffer cursor */\n    size_t len; /* Buffer length */\n    size_t maxbuf; /* Max length of unused buffer */\n\n    void *subreply; /* Temporary reply for array type */\n    size_t alloc_len; /* Temporary reply array alloc length */\n    size_t elements; /* Temporary reply array length */\n    void **element; /* Temporary reply array */\n\n    char *str;\n    size_t strlen;\n    int kflags;  /* Used for key flags (get/gets command reply) */\n    long long kversion;  /* Used for key version (gets command reply) */\n\n    int state;  /* Current parser state */\n    char *token;    /* Token marker */\n\n    long long integer; /* Cache the integer if need */\n\n    int type;   /* Response type */\n    int result; /* Parsing result */\n\n    mcReplyObjectFunctions *fn;\n    void *privdata;\n} mcReader;\n\n/* Public API for the protocol parser. */\nmcReader *memcachedReaderCreateWithFunctions(mcReplyObjectFunctions *fn);\nvoid memcachedReaderFree(mcReader *r);\nint memcachedReaderFeed(mcReader *r, const char *buf, size_t len);\nint memcachedReaderGetReply(mcReader *r, void **reply);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "dep/himemcached-0.1.0/himemcached.c",
    "content": "#include <stdlib.h>\n#include <errno.h>\n#include <assert.h>\n\n#include \"himemcached.h\"\n\n#define REQ_TYPE_UNKNOWN        0\n#define REQ_TYPE_STORAGE        1\n#define REQ_TYPE_CAS            2\n#define REQ_TYPE_RETRIEVAL      3\n#define REQ_TYPE_ARITHMETIC     4\n#define REQ_TYPE_DELETE         5\n\nstatic mcReply *createReplyObject(int type);\nstatic void *createStringObject(int type, char *key, size_t keylen, char *str, size_t len, int flags, long long version);\nstatic void *createArrayObject(size_t elements, void **element);\nstatic void *createIntegerObject(long long value);\nstatic void *createNilObject(void);\n\n/* Default set of functions to build the reply. Keep in mind that such a\n * function returning NULL is interpreted as OOM. */\nstatic mcReplyObjectFunctions defaultFunctions = {\n    createStringObject,\n    createArrayObject,\n    createIntegerObject,\n    createNilObject,\n    freeMcReplyObject\n};\n\n/* Create a reply object */\nstatic mcReply *createReplyObject(int type) {\n    mcReply *r = calloc(1,sizeof(*r));\n\n    if (r == NULL)\n        return NULL;\n\n    r->type = type;\n    \n    return r;\n}\n\n/* Free a reply object */\nvoid freeMcReplyObject(void *reply) {\n    mcReply *r = reply;\n    size_t j;\n\n    if (r == NULL)\n        return;\n\n    switch(r->type) {\n    case MC_REPLY_INTEGER:\n    case MC_REPLY_NIL:\n        break; /* Nothing to free */\n    case MC_REPLY_ARRAY:\n        if (r->element != NULL) {\n            for (j = 0; j < r->elements; j++)\n                if (r->element[j] != NULL)\n                    freeMcReplyObject(r->element[j]);\n            free(r->element);\n        }\n        break;\n    case MC_REPLY_ERROR:\n    case MC_REPLY_STATUS:\n    case MC_REPLY_STRING:\n        if (r->key != NULL)\n            free(r->key);\n        if (r->str != NULL)\n            free(r->str);\n        break;\n    default:\n        assert(0);\n        break;\n    }\n    free(r);\n}\n\nstatic void *createStringObject(int type, char *key, size_t keylen, char *str, size_t len, int flags, long long version) {\n    mcReply *r, *parent;\n    char *buf;\n\n    assert(type == MC_REPLY_ERROR  ||\n           type == MC_REPLY_STATUS ||\n           type == MC_REPLY_STRING);\n\n    r = createReplyObject(type);\n    if (r == NULL)\n        return NULL;\n\n    if (key != NULL) {\n        r->key = malloc(keylen+1);\n        if (r->key == NULL) {\n            freeMcReplyObject(r);\n            return NULL;\n        }\n        if (keylen > 0)\n            /* Copy string value */\n            memcpy(r->key,key,keylen);\n        r->key[keylen] = '\\0';\n        r->keylen = keylen;\n    }\n\n    buf = malloc(len+1);\n    if (buf == NULL) {\n        freeMcReplyObject(r);\n        return NULL;\n    }\n    if (len > 0)\n        /* Copy string value */\n        memcpy(buf,str,len);\n    buf[len] = '\\0';\n    r->str = buf;\n    r->len = len;\n\n    r->flags = flags;\n    r->version = version;\n    \n    return r;\n}\n\nstatic void *createArrayObject(size_t elements, void **element) {\n    mcReply *r;\n\n    r = createReplyObject(MC_REPLY_ARRAY);\n    if (r == NULL)\n        return NULL;\n\n    r->elements = elements;\n    r->element = (mcReply **)element;\n\n    return r;\n}\n\nstatic void *createIntegerObject(long long value) {\n    mcReply *r;\n\n    r = createReplyObject(MC_REPLY_INTEGER);\n    if (r == NULL)\n        return NULL;\n\n    r->integer = value;\n    \n    return r;\n}\n\nstatic void *createNilObject(void) {\n    mcReply *r;\n\n    r = createReplyObject(MC_REPLY_NIL);\n    if (r == NULL)\n        return NULL;\n\n    return r;\n}\n\nvoid __memcachedSetError(mcContext *c, int type, const char *str) {\n    size_t len;\n\n    c->err = type;\n    if (str != NULL) {\n        len = strlen(str);\n        len = len < (sizeof(c->errstr)-1) ? len : (sizeof(c->errstr)-1);\n        memcpy(c->errstr,str,len);\n        c->errstr[len] = '\\0';\n    } else {\n        /* Only REDIS_ERR_IO may lack a description! */\n        assert(type == MC_ERR_IO);\n        //__redis_strerror_r(errno, c->errstr, sizeof(c->errstr));\n    }\n}\n\n\n/* Write the output buffer to the socket.\n *\n * Returns MC_OK when the buffer is empty, or (a part of) the buffer was\n * succesfully written to the socket. When the buffer is empty after the\n * write operation, \"done\" is set to 1 (if given).\n *\n * Returns MC_ERR if an error occured trying to write and sets\n * c->errstr to hold the appropriate error string.\n */\nint memcachedBufferWrite(mcContext *c, int *done) {\n    int nwritten;\n\n    /* Return early when the context has seen an error. */\n    if (c->err)\n        return MC_ERR;\n\n    if (sdslen(c->obuf) > 0) {\n        nwritten = write(c->fd,c->obuf,sdslen(c->obuf));\n        if (nwritten == -1) {\n            if ((errno == EAGAIN && !(c->flags & MC_BLOCK)) || (errno == EINTR)) {\n                /* Try again later */\n            } else {\n                __memcachedSetError(c,MC_ERR_IO,NULL);\n                return MC_ERR;\n            }\n        } else if (nwritten > 0) {\n            if (nwritten == (signed)sdslen(c->obuf)) {\n                sdsfree(c->obuf);\n                c->obuf = sdsempty();\n            } else {\n                sdsrange(c->obuf,nwritten,-1);\n            }\n        }\n    }\n    if (done != NULL) *done = (sdslen(c->obuf) == 0);\n    return MC_OK;\n}\n\n/* Internal helper function to try and get a reply from the reader,\n * or set an error in the context otherwise. */\nint memcachedGetReplyFromReader(mcContext *c, void **reply) {\n    if (memcachedReaderGetReply(c->reader,reply) == MC_ERR) {\n        __memcachedSetError(c,c->reader->err,c->reader->errstr);\n        return MC_ERR;\n    }\n    return MC_OK;\n}\n\nint memcachedGetReply(mcContext *c, void **reply) {\n    int wdone = 0;\n    void *aux = NULL;\n\n    /* Try to read pending replies */\n    if (memcachedGetReplyFromReader(c,&aux) == MC_ERR)\n        return MC_ERR;\n\n    /* For the blocking context, flush output buffer and read reply */\n    if (aux == NULL && c->flags & MC_BLOCK) {\n        /* Write until done */\n        do {\n            if (memcachedBufferWrite(c,&wdone) == MC_ERR)\n                return MC_ERR;\n        } while (!wdone);\n\n        /* Read until there is a reply */\n        do {\n            if (memcachedBufferRead(c) == MC_ERR)\n                return MC_ERR;\n            if (memcachedGetReplyFromReader(c,&aux) == MC_ERR)\n                return MC_ERR;\n        } while (aux == NULL);\n    }\n\n    /* Set reply object */\n    if (reply != NULL) *reply = aux;\n    return MC_OK;\n}\n\nmcReader *memcachedReaderCreate(void) {\n    return memcachedReaderCreateWithFunctions(&defaultFunctions);\n}\n\nmcContext *memcachedContextInit(void) {\n    mcContext *c;\n\n    c = calloc(1,sizeof(mcContext));\n    if (c == NULL)\n        return NULL;\n\n    c->err = 0;\n    c->errstr[0] = '\\0';\n    c->obuf = sdsempty();\n    c->reader = memcachedReaderCreate();\n    c->tcp.host = NULL;\n    c->tcp.source_addr = NULL;\n    c->unix_sock.path = NULL;\n    c->timeout = NULL;\n\n    if (c->obuf == NULL || c->reader == NULL) {\n        memcachedFree(c);\n        return NULL;\n    }\n\n    return c;\n}\n\nvoid memcachedFree(mcContext *c) {\n    if (c == NULL)\n        return;\n    if (c->fd > 0)\n        close(c->fd);\n    if (c->obuf != NULL)\n        sdsfree(c->obuf);\n    if (c->reader != NULL)\n        memcachedReaderFree(c->reader);\n    if (c->tcp.host)\n        free(c->tcp.host);\n    if (c->tcp.source_addr)\n        free(c->tcp.source_addr);\n    if (c->unix_sock.path)\n        free(c->unix_sock.path);\n    if (c->timeout)\n        free(c->timeout);\n    free(c);\n}\n\n/* Use this function to handle a read event on the descriptor. It will try\n * and read some bytes from the socket and feed them to the reply parser.\n *\n * After this function is called, you may use memcachedContextReadReply to\n * see if there is a reply available. */\nint memcachedBufferRead(mcContext *c) {\n    char buf[1024*16];\n    int nread;\n\n    /* Return early when the context has seen an error. */\n    if (c->err)\n        return MC_ERR;\n\n    nread = read(c->fd,buf,sizeof(buf));\n    if (nread == -1) {\n        if ((errno == EAGAIN && !(c->flags & MC_BLOCK)) || (errno == EINTR)) {\n            /* Try again later */\n        } else {\n            __memcachedSetError(c,MC_ERR_IO,NULL);\n            return MC_ERR;\n        }\n    } else if (nread == 0) {\n        __memcachedSetError(c,MC_ERR_EOF,\"Server closed the connection\");\n        return MC_ERR;\n    } else {\n        if (memcachedReaderFeed(c->reader,buf,nread) != MC_OK) {\n            __memcachedSetError(c,c->reader->err,c->reader->errstr);\n            return MC_ERR;\n        }\n    }\n    return MC_OK;\n}\n\nstatic int getRequestTypeFromString(char *str, size_t len)\n{\n    if (str == NULL || len == 0)\n        return -1;\n\n    if (len == 3) {\n        if (!strncasecmp(str,\"set\",3) || \n            !strncasecmp(str,\"add\",3)) {\n            return REQ_TYPE_STORAGE;\n        } else if (!strncasecmp(str,\"cas\",3)) {\n            return REQ_TYPE_CAS;\n        } else if (!strncasecmp(str,\"get\",3)) {\n            return REQ_TYPE_RETRIEVAL;\n        } else {\n            return -1;\n        }\n    } else if (len == 4) {\n        if (!strncasecmp(str,\"gets\",4)) {\n            return REQ_TYPE_RETRIEVAL;\n        } else if (!strncasecmp(str,\"incr\",4) || \n            !strncasecmp(str,\"decr\",4)) {\n            return REQ_TYPE_ARITHMETIC;\n        } else {\n            return -1;\n        }\n    } else if (len == 6) {\n        if (!strncasecmp(str,\"append\",6)) {\n            return REQ_TYPE_STORAGE;\n        } else if (!strncasecmp(str,\"delete\",6)) {\n            return REQ_TYPE_DELETE;\n        } else {\n            return -1;\n        }\n    } else if (len == 7) {\n        if (!strncasecmp(str,\"replace\",7) || \n            !strncasecmp(str,\"prepend\",7)) {\n            return REQ_TYPE_STORAGE;\n        } else {\n            return -1;\n        }\n    }\n\n    return -1;\n}\n\n#define ARGUMENTLEN(_argtype,_argv,_argvlen,_idx) \\\n    (_argtype==0?sdslen(_argv[_idx]):(_argvlen==NULL?strlen(_argv[_idx]):_argvlen[_idx]))\n\n/* argtype==0 : argv is sds array \n * argtype==1 : argv is c-string array and an array with their lengths. \n * If the length array is set to NULL, strlen will be used to compute the\n * argument lengths.\n */\nstatic int checkCmdValidAndGetTotalLen(int cmdtype, int argtype, int argc, char **argv, size_t *argvlen)\n{\n    size_t len;\n    int totlen, j;\n    \n    switch (cmdtype) {\n        case REQ_TYPE_STORAGE:\n            if (argc != 6 && argc != 7) {\n                return -1;\n            }\n            if (argc == 7 && (ARGUMENTLEN(argtype,argv,argvlen,5) != 7 || \n                strncasecmp(argv[5],\"noreply\",7))) {\n                return -1;\n            }\n\n            totlen = 0;\n            for (j = 0; j < argc-1; j ++) {\n                totlen += ARGUMENTLEN(argtype,argv,argvlen,j) + 1;\n            }\n            totlen += 2 + ARGUMENTLEN(argtype,argv,argvlen,argc-1) + 2;\n            break;\n        case REQ_TYPE_CAS:\n            if (argc != 7 && argc != 8) {\n                return -1;\n            }\n            if (argc == 8 && (ARGUMENTLEN(argtype,argv,argvlen,6) != 7 || \n                strncasecmp(argv[6],\"noreply\",7))) {\n                return -1;\n            }\n\n            totlen = 0;\n            for (j = 0; j < argc-1; j ++) {\n                totlen += ARGUMENTLEN(argtype,argv,argvlen,j) + 1;\n            }\n            totlen += 2 + ARGUMENTLEN(argtype,argv,argvlen,argc-1) + 2;\n            break;\n        case REQ_TYPE_ARITHMETIC:\n            if (argc != 3) {\n                return -1;\n            }\n            totlen = ARGUMENTLEN(argtype,argv,argvlen,0) + 1 + \n                ARGUMENTLEN(argtype,argv,argvlen,1) + 1 + \n                ARGUMENTLEN(argtype,argv,argvlen,2) + 2;\n            break;\n        case REQ_TYPE_RETRIEVAL:\n            if (argc <= 1) {\n                return -1;\n            }\n\n            totlen = 0;\n            for (j = 0; j < argc-1; j ++) {\n                totlen += ARGUMENTLEN(argtype,argv,argvlen,j) + 1;\n            }\n            totlen += ARGUMENTLEN(argtype,argv,argvlen,argc-1) + 2;\n            break;\n        case REQ_TYPE_DELETE:\n            if (argc != 2 && argc != 3) {\n                return -1;\n            }\n            \n            totlen = ARGUMENTLEN(argtype,argv,argvlen,0) + 1 + \n                ARGUMENTLEN(argtype,argv,argvlen,1);\n            if (argc == 3) {\n                if (strncasecmp(argv[2],\"noreply\",7)) {\n                    return -1;\n                }\n                totlen += 1 + ARGUMENTLEN(argtype,argv,argvlen,2);\n            }\n            totlen += 2;\n            break;\n        default:\n            totlen = -1;\n            break;\n    }\n\n    return totlen;\n}\n\n/* Like the checkCmdValidAndGetTotalLen() function */\nstatic int genericMemcachedCommand(int cmdtype, char *cmd, int argtype, int argc, char **argv, size_t *argvlen)\n{\n    int j;\n    size_t len;\n    int pos = 0; /* position in final command */\n    \n    switch (cmdtype) {\n        case REQ_TYPE_STORAGE:\n        case REQ_TYPE_CAS:\n            for (j = 0; j < argc-1; j ++) {\n                len = ARGUMENTLEN(argtype,argv,argvlen,j);\n                memcpy(cmd+pos,argv[j],len);\n                pos += (int)len;\n                cmd[pos++] = ' ';\n            }\n            cmd[pos++] = '\\r';\n            cmd[pos++] = '\\n';\n            len = ARGUMENTLEN(argtype,argv,argvlen,argc-1);\n            memcpy(cmd+pos,argv[argc-1],len);\n            pos += (int)len;\n            cmd[pos++] = '\\r';\n            cmd[pos++] = '\\n';\n            break;\n        case REQ_TYPE_ARITHMETIC:\n        case REQ_TYPE_RETRIEVAL:\n        case REQ_TYPE_DELETE:\n            for (j = 0; j < argc-1; j ++) {\n                len = ARGUMENTLEN(argtype,argv,argvlen,j);\n                memcpy(cmd+pos,argv[j],len);\n                pos += len;\n                cmd[pos++] = ' ';\n            }\n            len = ARGUMENTLEN(argtype,argv,argvlen,argc-1);\n            memcpy(cmd+pos,argv[argc-1],len);\n            pos += (int)len;\n            cmd[pos++] = '\\r';\n            cmd[pos++] = '\\n';\n            break;\n        default:\n            pos = -1;\n            break;\n    }\n\n    return pos;\n}\n\n/* Format a command according to the Memcached protocol. This function\n * takes the number of arguments, an array with arguments and an sds array. \n */\nint memcachedFormatCommandSdsArgv(char **target, int argc, const sds *argv) {\n    char *cmd = NULL; /* final command */\n    int pos; /* position in final command */\n    int totlen;\n    int type;\n\n    /* Abort on a NULL target */\n    if (target == NULL || argc < 1)\n        return -1;\n\n    type = getRequestTypeFromString(argv[0], sdslen(argv[0]));\n    if (type < 0)\n        goto format_err;\n\n    totlen = checkCmdValidAndGetTotalLen(type, 0, argc, argv, NULL);\n    if (totlen < 0) {\n        goto format_err;\n    }\n\n    /* Build the command at protocol level */\n    cmd = malloc(totlen+1);\n    if (cmd == NULL) goto memory_err;\n\n    pos = genericMemcachedCommand(type, cmd, 0, argc, argv, NULL);\n    if (pos < 0) goto format_err;\n    \n    assert(pos == totlen);\n    cmd[pos] = '\\0';\n\n    *target = cmd;\n    return totlen;\n\nformat_err:\n    if (cmd) free(cmd);\n    return -2;\n\nmemory_err:\n    return -1;\n}\n\nint memcachedvFormatCommand(char **target, const char *format, va_list ap)\n{\n    const char *c = format;\n    char *cmd = NULL; /* final command */\n    int pos; /* position in final command */\n    sds curarg, newarg; /* current argument */\n    int touched = 0; /* was the current argument touched? */\n    char **curargv = NULL, **newargv = NULL;\n    int argc = 0;\n    int totlen;\n    int error_type = 0; /* 0 = no error; -1 = memory error; -2 = format error */\n    int j;\n\n    /* Abort if there is not target to set */\n    if (target == NULL)\n        return -1;\n\n    /* Build the command string accordingly to protocol */\n    curarg = sdsempty();\n    if (curarg == NULL)\n        return -1;\n\n    while(*c != '\\0') {\n        if (*c != '%' || c[1] == '\\0') {\n            if (*c == ' ') {\n                if (touched) {\n                    newargv = realloc(curargv,sizeof(char*)*(argc+1));\n                    if (newargv == NULL) goto memory_err;\n                    curargv = newargv;\n                    curargv[argc++] = curarg;\n\n                    /* curarg is put in argv so it can be overwritten. */\n                    curarg = sdsempty();\n                    if (curarg == NULL) goto memory_err;\n                    touched = 0;\n                }\n            } else {\n                newarg = sdscatlen(curarg,c,1);\n                if (newarg == NULL) goto memory_err;\n                curarg = newarg;\n                touched = 1;\n            }\n        } else {\n            char *arg;\n            size_t size;\n\n            /* Set newarg so it can be checked even if it is not touched. */\n            newarg = curarg;\n\n            switch(c[1]) {\n            case 's':\n                arg = va_arg(ap,char*);\n                size = strlen(arg);\n                if (size > 0)\n                    newarg = sdscatlen(curarg,arg,size);\n                break;\n            case 'b':\n                arg = va_arg(ap,char*);\n                size = va_arg(ap,size_t);\n                if (size > 0)\n                    newarg = sdscatlen(curarg,arg,size);\n                break;\n            case '%':\n                newarg = sdscat(curarg,\"%\");\n                break;\n            default:\n                /* Try to detect printf format */\n                {\n                    static const char intfmts[] = \"diouxX\";\n                    static const char flags[] = \"#0-+ \";\n                    char _format[16];\n                    const char *_p = c+1;\n                    size_t _l = 0;\n                    va_list _cpy;\n\n                    /* Flags */\n                    while (*_p != '\\0' && strchr(flags,*_p) != NULL) _p++;\n\n                    /* Field width */\n                    while (*_p != '\\0' && isdigit(*_p)) _p++;\n\n                    /* Precision */\n                    if (*_p == '.') {\n                        _p++;\n                        while (*_p != '\\0' && isdigit(*_p)) _p++;\n                    }\n\n                    /* Copy va_list before consuming with va_arg */\n                    va_copy(_cpy,ap);\n\n                    /* Integer conversion (without modifiers) */\n                    if (strchr(intfmts,*_p) != NULL) {\n                        va_arg(ap,int);\n                        goto fmt_valid;\n                    }\n\n                    /* Double conversion (without modifiers) */\n                    if (strchr(\"eEfFgGaA\",*_p) != NULL) {\n                        va_arg(ap,double);\n                        goto fmt_valid;\n                    }\n\n                    /* Size: char */\n                    if (_p[0] == 'h' && _p[1] == 'h') {\n                        _p += 2;\n                        if (*_p != '\\0' && strchr(intfmts,*_p) != NULL) {\n                            va_arg(ap,int); /* char gets promoted to int */\n                            goto fmt_valid;\n                        }\n                        goto fmt_invalid;\n                    }\n\n                    /* Size: short */\n                    if (_p[0] == 'h') {\n                        _p += 1;\n                        if (*_p != '\\0' && strchr(intfmts,*_p) != NULL) {\n                            va_arg(ap,int); /* short gets promoted to int */\n                            goto fmt_valid;\n                        }\n                        goto fmt_invalid;\n                    }\n\n                    /* Size: long long */\n                    if (_p[0] == 'l' && _p[1] == 'l') {\n                        _p += 2;\n                        if (*_p != '\\0' && strchr(intfmts,*_p) != NULL) {\n                            va_arg(ap,long long);\n                            goto fmt_valid;\n                        }\n                        goto fmt_invalid;\n                    }\n\n                    /* Size: long */\n                    if (_p[0] == 'l') {\n                        _p += 1;\n                        if (*_p != '\\0' && strchr(intfmts,*_p) != NULL) {\n                            va_arg(ap,long);\n                            goto fmt_valid;\n                        }\n                        goto fmt_invalid;\n                    }\n\n                fmt_invalid:\n                    va_end(_cpy);\n                    goto format_err;\n\n                fmt_valid:\n                    _l = (_p+1)-c;\n                    if (_l < sizeof(_format)-2) {\n                        memcpy(_format,c,_l);\n                        _format[_l] = '\\0';\n                        newarg = sdscatvprintf(curarg,_format,_cpy);\n\n                        /* Update current position (note: outer blocks\n                         * increment c twice so compensate here) */\n                        c = _p-1;\n                    }\n\n                    va_end(_cpy);\n                    break;\n                }\n            }\n\n            if (newarg == NULL) goto memory_err;\n            curarg = newarg;\n\n            touched = 1;\n            c++;\n        }\n        c++;\n    }\n\n    /* Add the last argument if needed */\n    if (touched) {\n        newargv = realloc(curargv,sizeof(char*)*(argc+1));\n        if (newargv == NULL) goto memory_err;\n        curargv = newargv;\n        curargv[argc++] = curarg;\n    } else {\n        sdsfree(curarg);\n    }\n\n    /* Clear curarg because it was put in curargv or was free'd. */\n    curarg = NULL;\n\n    totlen = memcachedFormatCommandSdsArgv(&cmd, argc,curargv);\n    if (totlen < 0) {\n        error_type = totlen;\n        goto cleanup;\n    }\n\n    free(curargv);\n    *target = cmd;\n    return totlen;\n\nformat_err:\n    error_type = -2;\n    goto cleanup;\n\nmemory_err:\n    error_type = -1;\n    goto cleanup;\n\ncleanup:\n    if (curargv) {\n        while(argc--)\n            sdsfree(curargv[argc]);\n        free(curargv);\n    }\n\n    sdsfree(curarg);\n\n    /* No need to check cmd since it is the last statement that can fail,\n     * but do it anyway to be as defensive as possible. */\n    if (cmd != NULL)\n        free(cmd);\n\n    return error_type;\n}\n\n/* Format a command according to the Memcached protocol. This function\n * takes a format similar to printf:\n *\n * %s represents a C null terminated string you want to interpolate\n * %b represents a binary safe string\n *\n * When using %b you need to provide both the pointer to the string\n * and the length in bytes as a size_t. Examples:\n *\n * len = memcachedFormatCommand(target, \"GET %s\", mykey);\n * len = memcachedFormatCommand(target, \"SET %s %d, %lld %zu %s\", mykey, myflags, myexptime, myvallen, myval);\n */\nint memcachedFormatCommand(char **target, const char *format, ...) {\n    va_list ap;\n    int len;\n    va_start(ap,format);\n    len = memcachedvFormatCommand(target,format,ap);\n    va_end(ap);\n\n    /* The API says \"-1\" means bad result, but we now also return \"-2\" in some\n     * cases.  Force the return value to always be -1. */\n    if (len < 0)\n        len = -1;\n\n    return len;\n}\n\n/* Format a command according to the Redis protocol. This function takes the\n * number of arguments, an array with arguments and an array with their\n * lengths. If the latter is set to NULL, strlen will be used to compute the\n * argument lengths.\n */\nint memcachedFormatCommandArgv(char **target, int argc, const char **argv, const size_t *argvlen) {\n    char *cmd = NULL; /* final command */\n    int pos; /* position in final command */\n    int totlen;\n    int type;\n\n    /* Abort on a NULL target */\n    if (target == NULL || argc < 1)\n        return -1;\n\n    type = getRequestTypeFromString(argv[0], argvlen==NULL?strlen(argv[0]):argvlen[0]);\n    if (type < 0) {\n        goto format_err;\n    }\n\n    totlen = checkCmdValidAndGetTotalLen(type, 1, argc, argv, argvlen);\n    if (totlen < 0) {\n        goto format_err;\n    }\n\n    /* Build the command at protocol level */\n    cmd = malloc(totlen+1);\n    if (cmd == NULL) goto memory_err;\n\n    pos = genericMemcachedCommand(type, cmd, 1, argc, argv, argvlen);\n    if (pos < 0) {\n        goto format_err;\n    }\n    \n    assert(pos == totlen);\n    cmd[pos] = '\\0';\n\n    *target = cmd;\n    return totlen;\n\nformat_err:\n    if (cmd) free(cmd);\n    return -2;\n\nmemory_err:\n    return -1;\n}\n\n"
  },
  {
    "path": "dep/himemcached-0.1.0/himemcached.h",
    "content": "#ifndef _HIMEMCACHED_H_\n#define _HIMEMCACHED_H_\n\n#include \"himcread.h\"\n#include \"himcdep/sds.h\"\n\n#define HIMC_MAJOR 0\n#define HIMC_MINOR 13\n#define HIMC_PATCH 1\n\n/* Connection type can be blocking or non-blocking and is set in the\n * least significant bit of the flags field in redisContext. */\n#define MC_BLOCK 0x1\n\n/* Connection may be disconnected before being free'd. The second bit\n * in the flags field is set when the context is connected. */\n#define MC_CONNECTED 0x2\n\n/* The async API might try to disconnect cleanly and flush the output\n * buffer and read all subsequent replies before disconnecting.\n * This flag means no new commands can come in and the connection\n * should be terminated once all replies have been read. */\n#define MC_DISCONNECTING 0x4\n\n/* Flag specific to the async API which means that the context should be clean\n * up as soon as possible. */\n#define MC_FREEING 0x8\n\n/* Flag that is set when an async callback is executed. */\n#define MC_IN_CALLBACK 0x10\n\n/* Flag that is set when the async context has one or more subscriptions. */\n#define MC_SUBSCRIBED 0x20\n\n/* Flag that is set when monitor mode is active */\n#define MC_MONITORING 0x40\n\n/* Flag that is set when we should set SO_REUSEADDR before calling bind() */\n#define MC_REUSEADDR 0x80\n\n#define MC_KEEPALIVE_INTERVAL 15 /* seconds */\n\n/* number of times we retry to connect in the case of EADDRNOTAVAIL and\n * SO_REUSEADDR is being used. */\n#define MC_CONNECT_RETRIES  10\n\n/* This is the reply object returned by memcachedCommand() */\ntypedef struct mcReply {\n    int type; /* MC_REPLY_* */\n    long long integer; /* The integer when type is MC_REPLY_INTEGER */\n    int keylen; /* Length of key */\n    char *key;  /* Key string */\n    int len; /* Length of string */\n    char *str; /* Used for both REDIS_REPLY_ERROR and MC_REPLY_STRING */\n    int flags;\n    long long version;\n    size_t elements; /* number of elements, for MC_REPLY_ARRAY */\n    struct mcReply **element; /* elements vector for MC_REPLY_ARRAY */\n} mcReply;\n\nmcReader *memcachedReaderCreate(void);\n\n/* Function to free the reply objects hiredis returns by default. */\nvoid freeMcReplyObject(void *reply);\n\nenum mcConnectionType {\n    MC_CONN_TCP,\n    MC_CONN_UNIX,\n};\n\n/* Context for a connection to Memcached */\ntypedef struct mcContext {\n    int err; /* Error flags, 0 when there is no error */\n    char errstr[128]; /* String representation of error when applicable */\n    int fd;\n    int flags;\n    char *obuf; /* Write buffer */\n    mcReader *reader; /* Protocol reader */\n\n    enum mcConnectionType connection_type;\n    struct timeval *timeout;\n\n    struct {\n        char *host;\n        char *source_addr;\n        int port;\n    } tcp;\n\n    struct {\n        char *path;\n    } unix_sock;\n} mcContext;\n\nint memcachedBufferWrite(mcContext *c, int *done);\nint memcachedBufferRead(mcContext *c);\n\nint memcachedGetReplyFromReader(mcContext *c, void **reply);\nint memcachedGetReply(mcContext *c, void **reply);\n\nmcContext *memcachedContextInit(void);\nvoid memcachedFree(mcContext *c) ;\n\nint memcachedFormatCommandSdsArgv(char **target, int argc, const sds *argv);\nint memcachedvFormatCommand(char **target, const char *format, va_list ap);\nint memcachedFormatCommand(char **target, const char *format, ...);\nint memcachedFormatCommandArgv(char **target, int argc, const char **argv, const size_t *argvlen);\n\n#endif /* _HIMEMCACHED_H_ */\n"
  },
  {
    "path": "dep/hiredis-0.13.3/.gitignore",
    "content": "\n"
  },
  {
    "path": "dep/jemalloc-4.2.0/.gitignore",
    "content": "\n"
  },
  {
    "path": "dep/sds/Makefile.am",
    "content": "MAINTAINERCLEANFILES = Makefile.in\n\nAM_CPPFLAGS =\nif !OS_SOLARIS\nAM_CPPFLAGS += -D_GNU_SOURCE\nendif\nAM_CPPFLAGS += -I $(top_srcdir)/dep/util\nAM_CPPFLAGS += -I $(top_srcdir)/dep/jemalloc-4.2.0/include\nAM_CPPFLAGS += -I $(top_srcdir)/dep/dmalloc\n\nAM_CFLAGS = -Wall -Wshadow\nAM_CFLAGS += -Wno-unused-parameter -Wno-unused-value\n\nnoinst_LIBRARIES = libsds.a\n\nnoinst_HEADERS = sds.h sdsalloc.h\n\nlibsds_a_SOURCES =      \\\n    sdsalloc.h          \\\n\tsds.c sds.h"
  },
  {
    "path": "dep/sds/sds.c",
    "content": "/* SDSLib 2.0 -- A C dynamic strings library\n *\n * Copyright (c) 2006-2015, Salvatore Sanfilippo <antirez at gmail dot com>\n * Copyright (c) 2015, Oran Agra\n * Copyright (c) 2015, Redis Labs, Inc\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n *   * Redistributions of source code must retain the above copyright notice,\n *     this list of conditions and the following disclaimer.\n *   * Redistributions in binary form must reproduce the above copyright\n *     notice, this list of conditions and the following disclaimer in the\n *     documentation and/or other materials provided with the distribution.\n *   * Neither the name of Redis nor the names of its contributors may be used\n *     to endorse or promote products derived from this software without\n *     specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <ctype.h>\n#include <assert.h>\n\n#include <sds.h>\n#include <sdsalloc.h>\n\nstatic inline int sdsHdrSize(char type) {\n    switch(type&SDS_TYPE_MASK) {\n        case SDS_TYPE_5:\n            return sizeof(struct sdshdr5);\n        case SDS_TYPE_8:\n            return sizeof(struct sdshdr8);\n        case SDS_TYPE_16:\n            return sizeof(struct sdshdr16);\n        case SDS_TYPE_32:\n            return sizeof(struct sdshdr32);\n        case SDS_TYPE_64:\n            return sizeof(struct sdshdr64);\n    }\n    return 0;\n}\n\nstatic inline char sdsReqType(size_t string_size) {\n    if (string_size < 32)\n        return SDS_TYPE_5;\n    if (string_size < 0xff)\n        return SDS_TYPE_8;\n    if (string_size < 0xffff)\n        return SDS_TYPE_16;\n    if (string_size < 0xffffffff)\n        return SDS_TYPE_32;\n    return SDS_TYPE_64;\n}\n\n/* Create a new sds string with the content specified by the 'init' pointer\n * and 'initlen'.\n * If NULL is used for 'init' the string is initialized with zero bytes.\n *\n * The string is always null-termined (all the sds strings are, always) so\n * even if you create an sds string with:\n *\n * mystring = sdsnewlen(\"abc\",3);\n *\n * You can print the string with printf() as there is an implicit \\0 at the\n * end of the string. However the string is binary safe and can contain\n * \\0 characters in the middle, as the length is stored in the sds header. */\nsds sdsnewlen(const void *init, size_t initlen) {\n    void *sh;\n    sds s;\n    char type = sdsReqType(initlen);\n    /* Empty strings are usually created in order to append. Use type 8\n     * since type 5 is not good at this. */\n    if (type == SDS_TYPE_5 && initlen == 0) type = SDS_TYPE_8;\n    int hdrlen = sdsHdrSize(type);\n    unsigned char *fp; /* flags pointer. */\n\n    sh = s_malloc(hdrlen+initlen+1);\n    if (!init)\n        memset(sh, 0, hdrlen+initlen+1);\n    if (sh == NULL) return NULL;\n    s = (char*)sh+hdrlen;\n    fp = ((unsigned char*)s)-1;\n    switch(type) {\n        case SDS_TYPE_5: {\n            *fp = type | (initlen << SDS_TYPE_BITS);\n            break;\n        }\n        case SDS_TYPE_8: {\n            SDS_HDR_VAR(8,s);\n            sh->len = initlen;\n            sh->alloc = initlen;\n            *fp = type;\n            break;\n        }\n        case SDS_TYPE_16: {\n            SDS_HDR_VAR(16,s);\n            sh->len = initlen;\n            sh->alloc = initlen;\n            *fp = type;\n            break;\n        }\n        case SDS_TYPE_32: {\n            SDS_HDR_VAR(32,s);\n            sh->len = initlen;\n            sh->alloc = initlen;\n            *fp = type;\n            break;\n        }\n        case SDS_TYPE_64: {\n            SDS_HDR_VAR(64,s);\n            sh->len = initlen;\n            sh->alloc = initlen;\n            *fp = type;\n            break;\n        }\n    }\n    if (initlen && init)\n        memcpy(s, init, initlen);\n    s[initlen] = '\\0';\n    return s;\n}\n\n/* Create an empty (zero length) sds string. Even in this case the string\n * always has an implicit null term. */\nsds sdsempty(void) {\n    return sdsnewlen(\"\",0);\n}\n\n/* Create a new sds string starting from a null terminated C string. */\nsds sdsnew(const char *init) {\n    size_t initlen = (init == NULL) ? 0 : strlen(init);\n    return sdsnewlen(init, initlen);\n}\n\n/* Duplicate an sds string. */\nsds sdsdup(const sds s) {\n    return sdsnewlen(s, sdslen(s));\n}\n\n/* Free an sds string. No operation is performed if 's' is NULL. */\nvoid sdsfree(sds s) {\n    if (s == NULL) return;\n    s_free((char*)s-sdsHdrSize(s[-1]));\n}\n\n/* Set the sds string length to the length as obtained with strlen(), so\n * considering as content only up to the first null term character.\n *\n * This function is useful when the sds string is hacked manually in some\n * way, like in the following example:\n *\n * s = sdsnew(\"foobar\");\n * s[2] = '\\0';\n * sdsupdatelen(s);\n * printf(\"%d\\n\", sdslen(s));\n *\n * The output will be \"2\", but if we comment out the call to sdsupdatelen()\n * the output will be \"6\" as the string was modified but the logical length\n * remains 6 bytes. */\nvoid sdsupdatelen(sds s) {\n    int reallen = strlen(s);\n    sdssetlen(s, reallen);\n}\n\n/* Modify an sds string in-place to make it empty (zero length).\n * However all the existing buffer is not discarded but set as free space\n * so that next append operations will not require allocations up to the\n * number of bytes previously available. */\nvoid sdsclear(sds s) {\n    sdssetlen(s, 0);\n    s[0] = '\\0';\n}\n\n/* Enlarge the free space at the end of the sds string so that the caller\n * is sure that after calling this function can overwrite up to addlen\n * bytes after the end of the string, plus one more byte for nul term.\n *\n * Note: this does not change the *length* of the sds string as returned\n * by sdslen(), but only the free buffer space we have. */\nsds sdsMakeRoomFor(sds s, size_t addlen) {\n    void *sh, *newsh;\n    size_t avail = sdsavail(s);\n    size_t len, newlen;\n    char type, oldtype = s[-1] & SDS_TYPE_MASK;\n    int hdrlen;\n\n    /* Return ASAP if there is enough space left. */\n    if (avail >= addlen) return s;\n\n    len = sdslen(s);\n    sh = (char*)s-sdsHdrSize(oldtype);\n    newlen = (len+addlen);\n    if (newlen < SDS_MAX_PREALLOC)\n        newlen *= 2;\n    else\n        newlen += SDS_MAX_PREALLOC;\n\n    type = sdsReqType(newlen);\n\n    /* Don't use type 5: the user is appending to the string and type 5 is\n     * not able to remember empty space, so sdsMakeRoomFor() must be called\n     * at every appending operation. */\n    if (type == SDS_TYPE_5) type = SDS_TYPE_8;\n\n    hdrlen = sdsHdrSize(type);\n    if (oldtype==type) {\n        newsh = s_realloc(sh, hdrlen+newlen+1);\n        if (newsh == NULL) return NULL;\n        s = (char*)newsh+hdrlen;\n    } else {\n        /* Since the header size changes, need to move the string forward,\n         * and can't use realloc */\n        newsh = s_malloc(hdrlen+newlen+1);\n        if (newsh == NULL) return NULL;\n        memcpy((char*)newsh+hdrlen, s, len+1);\n        s_free(sh);\n        s = (char*)newsh+hdrlen;\n        s[-1] = type;\n        sdssetlen(s, len);\n    }\n    sdssetalloc(s, newlen);\n    return s;\n}\n\n/* Reallocate the sds string so that it has no free space at the end. The\n * contained string remains not altered, but next concatenation operations\n * will require a reallocation.\n *\n * After the call, the passed sds string is no longer valid and all the\n * references must be substituted with the new pointer returned by the call. */\nsds sdsRemoveFreeSpace(sds s) {\n    void *sh, *newsh;\n    char type, oldtype = s[-1] & SDS_TYPE_MASK;\n    int hdrlen;\n    size_t len = sdslen(s);\n    sh = (char*)s-sdsHdrSize(oldtype);\n\n    type = sdsReqType(len);\n    hdrlen = sdsHdrSize(type);\n    if (oldtype==type) {\n        newsh = s_realloc(sh, hdrlen+len+1);\n        if (newsh == NULL) return NULL;\n        s = (char*)newsh+hdrlen;\n    } else {\n        newsh = s_malloc(hdrlen+len+1);\n        if (newsh == NULL) return NULL;\n        memcpy((char*)newsh+hdrlen, s, len+1);\n        s_free(sh);\n        s = (char*)newsh+hdrlen;\n        s[-1] = type;\n        sdssetlen(s, len);\n    }\n    sdssetalloc(s, len);\n    return s;\n}\n\n/* Return the total size of the allocation of the specifed sds string,\n * including:\n * 1) The sds header before the pointer.\n * 2) The string.\n * 3) The free buffer at the end if any.\n * 4) The implicit null term.\n */\nsize_t sdsAllocSize(sds s) {\n    size_t alloc = sdsalloc(s);\n    return sdsHdrSize(s[-1])+alloc+1;\n}\n\n/* Return the pointer of the actual SDS allocation (normally SDS strings\n * are referenced by the start of the string buffer). */\nvoid *sdsAllocPtr(sds s) {\n    return (void*) (s-sdsHdrSize(s[-1]));\n}\n\n/* Increment the sds length and decrements the left free space at the\n * end of the string according to 'incr'. Also set the null term\n * in the new end of the string.\n *\n * This function is used in order to fix the string length after the\n * user calls sdsMakeRoomFor(), writes something after the end of\n * the current string, and finally needs to set the new length.\n *\n * Note: it is possible to use a negative increment in order to\n * right-trim the string.\n *\n * Usage example:\n *\n * Using sdsIncrLen() and sdsMakeRoomFor() it is possible to mount the\n * following schema, to cat bytes coming from the kernel to the end of an\n * sds string without copying into an intermediate buffer:\n *\n * oldlen = sdslen(s);\n * s = sdsMakeRoomFor(s, BUFFER_SIZE);\n * nread = read(fd, s+oldlen, BUFFER_SIZE);\n * ... check for nread <= 0 and handle it ...\n * sdsIncrLen(s, nread);\n */\nvoid sdsIncrLen(sds s, int incr) {\n    unsigned char flags = s[-1];\n    size_t len;\n    switch(flags&SDS_TYPE_MASK) {\n        case SDS_TYPE_5: {\n            unsigned char *fp = ((unsigned char*)s)-1;\n            unsigned char oldlen = SDS_TYPE_5_LEN(flags);\n            assert((incr > 0 && oldlen+incr < 32) || (incr < 0 && oldlen >= (unsigned int)(-incr)));\n            *fp = SDS_TYPE_5 | ((oldlen+incr) << SDS_TYPE_BITS);\n            len = oldlen+incr;\n            break;\n        }\n        case SDS_TYPE_8: {\n            SDS_HDR_VAR(8,s);\n            assert((incr >= 0 && sh->alloc-sh->len >= incr) || (incr < 0 && sh->len >= (unsigned int)(-incr)));\n            len = (sh->len += incr);\n            break;\n        }\n        case SDS_TYPE_16: {\n            SDS_HDR_VAR(16,s);\n            assert((incr >= 0 && sh->alloc-sh->len >= incr) || (incr < 0 && sh->len >= (unsigned int)(-incr)));\n            len = (sh->len += incr);\n            break;\n        }\n        case SDS_TYPE_32: {\n            SDS_HDR_VAR(32,s);\n            assert((incr >= 0 && sh->alloc-sh->len >= (unsigned int)incr) || (incr < 0 && sh->len >= (unsigned int)(-incr)));\n            len = (sh->len += incr);\n            break;\n        }\n        case SDS_TYPE_64: {\n            SDS_HDR_VAR(64,s);\n            assert((incr >= 0 && sh->alloc-sh->len >= (uint64_t)incr) || (incr < 0 && sh->len >= (uint64_t)(-incr)));\n            len = (sh->len += incr);\n            break;\n        }\n        default: len = 0; /* Just to avoid compilation warnings. */\n    }\n    s[len] = '\\0';\n}\n\n/* Grow the sds to have the specified length. Bytes that were not part of\n * the original length of the sds will be set to zero.\n *\n * if the specified length is smaller than the current length, no operation\n * is performed. */\nsds sdsgrowzero(sds s, size_t len) {\n    size_t curlen = sdslen(s);\n\n    if (len <= curlen) return s;\n    s = sdsMakeRoomFor(s,len-curlen);\n    if (s == NULL) return NULL;\n\n    /* Make sure added region doesn't contain garbage */\n    memset(s+curlen,0,(len-curlen+1)); /* also set trailing \\0 byte */\n    sdssetlen(s, len);\n    return s;\n}\n\n/* Append the specified binary-safe string pointed by 't' of 'len' bytes to the\n * end of the specified sds string 's'.\n *\n * After the call, the passed sds string is no longer valid and all the\n * references must be substituted with the new pointer returned by the call. */\nsds sdscatlen(sds s, const void *t, size_t len) {\n    size_t curlen = sdslen(s);\n\n    s = sdsMakeRoomFor(s,len);\n    if (s == NULL) return NULL;\n    memcpy(s+curlen, t, len);\n    sdssetlen(s, curlen+len);\n    s[curlen+len] = '\\0';\n    return s;\n}\n\n/* Append the specified null termianted C string to the sds string 's'.\n *\n * After the call, the passed sds string is no longer valid and all the\n * references must be substituted with the new pointer returned by the call. */\nsds sdscat(sds s, const char *t) {\n    return sdscatlen(s, t, strlen(t));\n}\n\n/* Append the specified sds 't' to the existing sds 's'.\n *\n * After the call, the modified sds string is no longer valid and all the\n * references must be substituted with the new pointer returned by the call. */\nsds sdscatsds(sds s, const sds t) {\n    return sdscatlen(s, t, sdslen(t));\n}\n\n/* Destructively modify the sds string 's' to hold the specified binary\n * safe string pointed by 't' of length 'len' bytes. */\nsds sdscpylen(sds s, const char *t, size_t len) {\n    if (sdsalloc(s) < len) {\n        s = sdsMakeRoomFor(s,len-sdslen(s));\n        if (s == NULL) return NULL;\n    }\n    memcpy(s, t, len);\n    s[len] = '\\0';\n    sdssetlen(s, len);\n    return s;\n}\n\n/* Like sdscpylen() but 't' must be a null-termined string so that the length\n * of the string is obtained with strlen(). */\nsds sdscpy(sds s, const char *t) {\n    return sdscpylen(s, t, strlen(t));\n}\n\n/* Helper for sdscatlonglong() doing the actual number -> string\n * conversion. 's' must point to a string with room for at least\n * SDS_LLSTR_SIZE bytes.\n *\n * The function returns the length of the null-terminated string\n * representation stored at 's'. */\n#define SDS_LLSTR_SIZE 21\nint sdsll2str(char *s, long long value) {\n    char *p, aux;\n    unsigned long long v;\n    size_t l;\n\n    /* Generate the string representation, this method produces\n     * an reversed string. */\n    v = (value < 0) ? -value : value;\n    p = s;\n    do {\n        *p++ = '0'+(v%10);\n        v /= 10;\n    } while(v);\n    if (value < 0) *p++ = '-';\n\n    /* Compute length and add null term. */\n    l = p-s;\n    *p = '\\0';\n\n    /* Reverse the string. */\n    p--;\n    while(s < p) {\n        aux = *s;\n        *s = *p;\n        *p = aux;\n        s++;\n        p--;\n    }\n    return l;\n}\n\n/* Identical sdsll2str(), but for unsigned long long type. */\nint sdsull2str(char *s, unsigned long long v) {\n    char *p, aux;\n    size_t l;\n\n    /* Generate the string representation, this method produces\n     * an reversed string. */\n    p = s;\n    do {\n        *p++ = '0'+(v%10);\n        v /= 10;\n    } while(v);\n\n    /* Compute length and add null term. */\n    l = p-s;\n    *p = '\\0';\n\n    /* Reverse the string. */\n    p--;\n    while(s < p) {\n        aux = *s;\n        *s = *p;\n        *p = aux;\n        s++;\n        p--;\n    }\n    return l;\n}\n\n/* Create an sds string from a long long value. It is much faster than:\n *\n * sdscatprintf(sdsempty(),\"%lld\\n\", value);\n */\nsds sdsfromlonglong(long long value) {\n    char buf[SDS_LLSTR_SIZE];\n    int len = sdsll2str(buf,value);\n\n    return sdsnewlen(buf,len);\n}\n\n/* Like sdscatprintf() but gets va_list instead of being variadic. */\nsds sdscatvprintf(sds s, const char *fmt, va_list ap) {\n    va_list cpy;\n    char staticbuf[1024], *buf = staticbuf, *t;\n    size_t buflen = strlen(fmt)*2;\n\n    /* We try to start using a static buffer for speed.\n     * If not possible we revert to heap allocation. */\n    if (buflen > sizeof(staticbuf)) {\n        buf = s_malloc(buflen);\n        if (buf == NULL) return NULL;\n    } else {\n        buflen = sizeof(staticbuf);\n    }\n\n    /* Try with buffers two times bigger every time we fail to\n     * fit the string in the current buffer size. */\n    while(1) {\n        buf[buflen-2] = '\\0';\n        va_copy(cpy,ap);\n        vsnprintf(buf, buflen, fmt, cpy);\n        va_end(cpy);\n        if (buf[buflen-2] != '\\0') {\n            if (buf != staticbuf) s_free(buf);\n            buflen *= 2;\n            buf = s_malloc(buflen);\n            if (buf == NULL) return NULL;\n            continue;\n        }\n        break;\n    }\n\n    /* Finally concat the obtained string to the SDS string and return it. */\n    t = sdscat(s, buf);\n    if (buf != staticbuf) s_free(buf);\n    return t;\n}\n\n/* Append to the sds string 's' a string obtained using printf-alike format\n * specifier.\n *\n * After the call, the modified sds string is no longer valid and all the\n * references must be substituted with the new pointer returned by the call.\n *\n * Example:\n *\n * s = sdsnew(\"Sum is: \");\n * s = sdscatprintf(s,\"%d+%d = %d\",a,b,a+b).\n *\n * Often you need to create a string from scratch with the printf-alike\n * format. When this is the need, just use sdsempty() as the target string:\n *\n * s = sdscatprintf(sdsempty(), \"... your format ...\", args);\n */\nsds sdscatprintf(sds s, const char *fmt, ...) {\n    va_list ap;\n    char *t;\n    va_start(ap, fmt);\n    t = sdscatvprintf(s,fmt,ap);\n    va_end(ap);\n    return t;\n}\n\n/* This function is similar to sdscatprintf, but much faster as it does\n * not rely on sprintf() family functions implemented by the libc that\n * are often very slow. Moreover directly handling the sds string as\n * new data is concatenated provides a performance improvement.\n *\n * However this function only handles an incompatible subset of printf-alike\n * format specifiers:\n *\n * %s - C String\n * %S - SDS string\n * %i - signed int\n * %I - 64 bit signed integer (long long, int64_t)\n * %u - unsigned int\n * %U - 64 bit unsigned integer (unsigned long long, uint64_t)\n * %% - Verbatim \"%\" character.\n */\nsds sdscatfmt(sds s, char const *fmt, ...) {\n    size_t initlen = sdslen(s);\n    const char *f = fmt;\n    int i;\n    va_list ap;\n\n    va_start(ap,fmt);\n    f = fmt;    /* Next format specifier byte to process. */\n    i = initlen; /* Position of the next byte to write to dest str. */\n    while(*f) {\n        char next, *str;\n        size_t l;\n        long long num;\n        unsigned long long unum;\n\n        /* Make sure there is always space for at least 1 char. */\n        if (sdsavail(s)==0) {\n            s = sdsMakeRoomFor(s,1);\n        }\n\n        switch(*f) {\n        case '%':\n            next = *(f+1);\n            f++;\n            switch(next) {\n            case 's':\n            case 'S':\n                str = va_arg(ap,char*);\n                l = (next == 's') ? strlen(str) : sdslen(str);\n                if (sdsavail(s) < l) {\n                    s = sdsMakeRoomFor(s,l);\n                }\n                memcpy(s+i,str,l);\n                sdsinclen(s,l);\n                i += l;\n                break;\n            case 'i':\n            case 'I':\n                if (next == 'i')\n                    num = va_arg(ap,int);\n                else\n                    num = va_arg(ap,long long);\n                {\n                    char buf[SDS_LLSTR_SIZE];\n                    l = sdsll2str(buf,num);\n                    if (sdsavail(s) < l) {\n                        s = sdsMakeRoomFor(s,l);\n                    }\n                    memcpy(s+i,buf,l);\n                    sdsinclen(s,l);\n                    i += l;\n                }\n                break;\n            case 'u':\n            case 'U':\n                if (next == 'u')\n                    unum = va_arg(ap,unsigned int);\n                else\n                    unum = va_arg(ap,unsigned long long);\n                {\n                    char buf[SDS_LLSTR_SIZE];\n                    l = sdsull2str(buf,unum);\n                    if (sdsavail(s) < l) {\n                        s = sdsMakeRoomFor(s,l);\n                    }\n                    memcpy(s+i,buf,l);\n                    sdsinclen(s,l);\n                    i += l;\n                }\n                break;\n            default: /* Handle %% and generally %<unknown>. */\n                s[i++] = next;\n                sdsinclen(s,1);\n                break;\n            }\n            break;\n        default:\n            s[i++] = *f;\n            sdsinclen(s,1);\n            break;\n        }\n        f++;\n    }\n    va_end(ap);\n\n    /* Add null-term */\n    s[i] = '\\0';\n    return s;\n}\n\n/* Remove the part of the string from left and from right composed just of\n * contiguous characters found in 'cset', that is a null terminted C string.\n *\n * After the call, the modified sds string is no longer valid and all the\n * references must be substituted with the new pointer returned by the call.\n *\n * Example:\n *\n * s = sdsnew(\"AA...AA.a.aa.aHelloWorld     :::\");\n * s = sdstrim(s,\"Aa. :\");\n * printf(\"%s\\n\", s);\n *\n * Output will be just \"Hello World\".\n */\nsds sdstrim(sds s, const char *cset) {\n    char *start, *end, *sp, *ep;\n    size_t len;\n\n    sp = start = s;\n    ep = end = s+sdslen(s)-1;\n    while(sp <= end && strchr(cset, *sp)) sp++;\n    while(ep > sp && strchr(cset, *ep)) ep--;\n    len = (sp > ep) ? 0 : ((ep-sp)+1);\n    if (s != sp) memmove(s, sp, len);\n    s[len] = '\\0';\n    sdssetlen(s,len);\n    return s;\n}\n\n/* Turn the string into a smaller (or equal) string containing only the\n * substring specified by the 'start' and 'end' indexes.\n *\n * start and end can be negative, where -1 means the last character of the\n * string, -2 the penultimate character, and so forth.\n *\n * The interval is inclusive, so the start and end characters will be part\n * of the resulting string.\n *\n * The string is modified in-place.\n *\n * Example:\n *\n * s = sdsnew(\"Hello World\");\n * sdsrange(s,1,-1); => \"ello World\"\n */\nvoid sdsrange(sds s, int start, int end) {\n    size_t newlen, len = sdslen(s);\n\n    if (len == 0) return;\n    if (start < 0) {\n        start = len+start;\n        if (start < 0) start = 0;\n    }\n    if (end < 0) {\n        end = len+end;\n        if (end < 0) end = 0;\n    }\n    newlen = (start > end) ? 0 : (end-start)+1;\n    if (newlen != 0) {\n        if (start >= (signed)len) {\n            newlen = 0;\n        } else if (end >= (signed)len) {\n            end = len-1;\n            newlen = (start > end) ? 0 : (end-start)+1;\n        }\n    } else {\n        start = 0;\n    }\n    if (start && newlen) memmove(s, s+start, newlen);\n    s[newlen] = 0;\n    sdssetlen(s,newlen);\n}\n\n/* Apply tolower() to every character of the sds string 's'. */\nvoid sdstolower(sds s) {\n    int len = sdslen(s), j;\n\n    for (j = 0; j < len; j++) s[j] = tolower(s[j]);\n}\n\n/* Apply toupper() to every character of the sds string 's'. */\nvoid sdstoupper(sds s) {\n    int len = sdslen(s), j;\n\n    for (j = 0; j < len; j++) s[j] = toupper(s[j]);\n}\n\n/* Compare two sds strings s1 and s2 with memcmp().\n *\n * Return value:\n *\n *     positive if s1 > s2.\n *     negative if s1 < s2.\n *     0 if s1 and s2 are exactly the same binary string.\n *\n * If two strings share exactly the same prefix, but one of the two has\n * additional characters, the longer string is considered to be greater than\n * the smaller one. */\nint sdscmp(const sds s1, const sds s2) {\n    size_t l1, l2, minlen;\n    int cmp;\n\n    l1 = sdslen(s1);\n    l2 = sdslen(s2);\n    minlen = (l1 < l2) ? l1 : l2;\n    cmp = memcmp(s1,s2,minlen);\n    if (cmp == 0) return l1-l2;\n    return cmp;\n}\n\n/* Split 's' with separator in 'sep'. An array\n * of sds strings is returned. *count will be set\n * by reference to the number of tokens returned.\n *\n * On out of memory, zero length string, zero length\n * separator, NULL is returned.\n *\n * Note that 'sep' is able to split a string using\n * a multi-character separator. For example\n * sdssplit(\"foo_-_bar\",\"_-_\"); will return two\n * elements \"foo\" and \"bar\".\n *\n * This version of the function is binary-safe but\n * requires length arguments. sdssplit() is just the\n * same function but for zero-terminated strings.\n */\nsds *sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count) {\n    int elements = 0, slots = 5, start = 0, j;\n    sds *tokens;\n\n    if (seplen < 1 || len < 0) return NULL;\n\n    tokens = s_malloc(sizeof(sds)*slots);\n    if (tokens == NULL) return NULL;\n\n    if (len == 0) {\n        *count = 0;\n        return tokens;\n    }\n    for (j = 0; j < (len-(seplen-1)); j++) {\n        /* make sure there is room for the next element and the final one */\n        if (slots < elements+2) {\n            sds *newtokens;\n\n            slots *= 2;\n            newtokens = s_realloc(tokens,sizeof(sds)*slots);\n            if (newtokens == NULL) goto cleanup;\n            tokens = newtokens;\n        }\n        /* search the separator */\n        if ((seplen == 1 && *(s+j) == sep[0]) || (memcmp(s+j,sep,seplen) == 0)) {\n            tokens[elements] = sdsnewlen(s+start,j-start);\n            if (tokens[elements] == NULL) goto cleanup;\n            elements++;\n            start = j+seplen;\n            j = j+seplen-1; /* skip the separator */\n        }\n    }\n    /* Add the final element. We are sure there is room in the tokens array. */\n    tokens[elements] = sdsnewlen(s+start,len-start);\n    if (tokens[elements] == NULL) goto cleanup;\n    elements++;\n    *count = elements;\n    return tokens;\n\ncleanup:\n    {\n        int i;\n        for (i = 0; i < elements; i++) sdsfree(tokens[i]);\n        s_free(tokens);\n        *count = 0;\n        return NULL;\n    }\n}\n\n/* Free the result returned by sdssplitlen(), or do nothing if 'tokens' is NULL. */\nvoid sdsfreesplitres(sds *tokens, int count) {\n    if (!tokens) return;\n    while(count--)\n        sdsfree(tokens[count]);\n    s_free(tokens);\n}\n\n/* Append to the sds string \"s\" an escaped string representation where\n * all the non-printable characters (tested with isprint()) are turned into\n * escapes in the form \"\\n\\r\\a....\" or \"\\x<hex-number>\".\n *\n * After the call, the modified sds string is no longer valid and all the\n * references must be substituted with the new pointer returned by the call. */\nsds sdscatrepr(sds s, const char *p, size_t len) {\n    s = sdscatlen(s,\"\\\"\",1);\n    while(len--) {\n        switch(*p) {\n        case '\\\\':\n        case '\"':\n            s = sdscatprintf(s,\"\\\\%c\",*p);\n            break;\n        case '\\n': s = sdscatlen(s,\"\\\\n\",2); break;\n        case '\\r': s = sdscatlen(s,\"\\\\r\",2); break;\n        case '\\t': s = sdscatlen(s,\"\\\\t\",2); break;\n        case '\\a': s = sdscatlen(s,\"\\\\a\",2); break;\n        case '\\b': s = sdscatlen(s,\"\\\\b\",2); break;\n        default:\n            if (isprint(*p))\n                s = sdscatprintf(s,\"%c\",*p);\n            else\n                s = sdscatprintf(s,\"\\\\x%02x\",(unsigned char)*p);\n            break;\n        }\n        p++;\n    }\n    return sdscatlen(s,\"\\\"\",1);\n}\n\n/* Helper function for sdssplitargs() that returns non zero if 'c'\n * is a valid hex digit. */\nint is_hex_digit(char c) {\n    return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') ||\n           (c >= 'A' && c <= 'F');\n}\n\n/* Helper function for sdssplitargs() that converts a hex digit into an\n * integer from 0 to 15 */\nint hex_digit_to_int(char c) {\n    switch(c) {\n    case '0': return 0;\n    case '1': return 1;\n    case '2': return 2;\n    case '3': return 3;\n    case '4': return 4;\n    case '5': return 5;\n    case '6': return 6;\n    case '7': return 7;\n    case '8': return 8;\n    case '9': return 9;\n    case 'a': case 'A': return 10;\n    case 'b': case 'B': return 11;\n    case 'c': case 'C': return 12;\n    case 'd': case 'D': return 13;\n    case 'e': case 'E': return 14;\n    case 'f': case 'F': return 15;\n    default: return 0;\n    }\n}\n\n/* Split a line into arguments, where every argument can be in the\n * following programming-language REPL-alike form:\n *\n * foo bar \"newline are supported\\n\" and \"\\xff\\x00otherstuff\"\n *\n * The number of arguments is stored into *argc, and an array\n * of sds is returned.\n *\n * The caller should free the resulting array of sds strings with\n * sdsfreesplitres().\n *\n * Note that sdscatrepr() is able to convert back a string into\n * a quoted string in the same format sdssplitargs() is able to parse.\n *\n * The function returns the allocated tokens on success, even when the\n * input string is empty, or NULL if the input contains unbalanced\n * quotes or closed quotes followed by non space characters\n * as in: \"foo\"bar or \"foo'\n */\nsds *sdssplitargs(const char *line, int *argc) {\n    const char *p = line;\n    char *current = NULL;\n    char **vector = NULL;\n\n    *argc = 0;\n    while(1) {\n        /* skip blanks */\n        while(*p && isspace(*p)) p++;\n        if (*p) {\n            /* get a token */\n            int inq=0;  /* set to 1 if we are in \"quotes\" */\n            int insq=0; /* set to 1 if we are in 'single quotes' */\n            int done=0;\n\n            if (current == NULL) current = sdsempty();\n            while(!done) {\n                if (inq) {\n                    if (*p == '\\\\' && *(p+1) == 'x' &&\n                                             is_hex_digit(*(p+2)) &&\n                                             is_hex_digit(*(p+3)))\n                    {\n                        unsigned char byte;\n\n                        byte = (hex_digit_to_int(*(p+2))*16)+\n                                hex_digit_to_int(*(p+3));\n                        current = sdscatlen(current,(char*)&byte,1);\n                        p += 3;\n                    } else if (*p == '\\\\' && *(p+1)) {\n                        char c;\n\n                        p++;\n                        switch(*p) {\n                        case 'n': c = '\\n'; break;\n                        case 'r': c = '\\r'; break;\n                        case 't': c = '\\t'; break;\n                        case 'b': c = '\\b'; break;\n                        case 'a': c = '\\a'; break;\n                        default: c = *p; break;\n                        }\n                        current = sdscatlen(current,&c,1);\n                    } else if (*p == '\"') {\n                        /* closing quote must be followed by a space or\n                         * nothing at all. */\n                        if (*(p+1) && !isspace(*(p+1))) goto err;\n                        done=1;\n                    } else if (!*p) {\n                        /* unterminated quotes */\n                        goto err;\n                    } else {\n                        current = sdscatlen(current,p,1);\n                    }\n                } else if (insq) {\n                    if (*p == '\\\\' && *(p+1) == '\\'') {\n                        p++;\n                        current = sdscatlen(current,\"'\",1);\n                    } else if (*p == '\\'') {\n                        /* closing quote must be followed by a space or\n                         * nothing at all. */\n                        if (*(p+1) && !isspace(*(p+1))) goto err;\n                        done=1;\n                    } else if (!*p) {\n                        /* unterminated quotes */\n                        goto err;\n                    } else {\n                        current = sdscatlen(current,p,1);\n                    }\n                } else {\n                    switch(*p) {\n                    case ' ':\n                    case '\\n':\n                    case '\\r':\n                    case '\\t':\n                    case '\\0':\n                        done=1;\n                        break;\n                    case '\"':\n                        inq=1;\n                        break;\n                    case '\\'':\n                        insq=1;\n                        break;\n                    default:\n                        current = sdscatlen(current,p,1);\n                        break;\n                    }\n                }\n                if (*p) p++;\n            }\n            /* add the token to the vector */\n            vector = s_realloc(vector,((*argc)+1)*sizeof(char*));\n            vector[*argc] = current;\n            (*argc)++;\n            current = NULL;\n        } else {\n            /* Even on empty input string return something not NULL. */\n            if (vector == NULL) vector = s_malloc(sizeof(void*));\n            return vector;\n        }\n    }\n\nerr:\n    while((*argc)--)\n        sdsfree(vector[*argc]);\n    s_free(vector);\n    if (current) sdsfree(current);\n    *argc = 0;\n    return NULL;\n}\n\n/* Modify the string substituting all the occurrences of the set of\n * characters specified in the 'from' string to the corresponding character\n * in the 'to' array.\n *\n * For instance: sdsmapchars(mystring, \"ho\", \"01\", 2)\n * will have the effect of turning the string \"hello\" into \"0ell1\".\n *\n * The function returns the sds string pointer, that is always the same\n * as the input pointer since no resize is needed. */\nsds sdsmapchars(sds s, const char *from, const char *to, size_t setlen) {\n    size_t j, i, l = sdslen(s);\n\n    for (j = 0; j < l; j++) {\n        for (i = 0; i < setlen; i++) {\n            if (s[j] == from[i]) {\n                s[j] = to[i];\n                break;\n            }\n        }\n    }\n    return s;\n}\n\n/* Join an array of C strings using the specified separator (also a C string).\n * Returns the result as an sds string. */\nsds sdsjoin(char **argv, int argc, char *sep) {\n    sds join = sdsempty();\n    int j;\n\n    for (j = 0; j < argc; j++) {\n        join = sdscat(join, argv[j]);\n        if (j != argc-1) join = sdscat(join,sep);\n    }\n    return join;\n}\n\n/* Like sdsjoin, but joins an array of SDS strings. */\nsds sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen) {\n    sds join = sdsempty();\n    int j;\n\n    for (j = 0; j < argc; j++) {\n        join = sdscatsds(join, argv[j]);\n        if (j != argc-1) join = sdscatlen(join,sep,seplen);\n    }\n    return join;\n}\n\nint sdsIsNum(sds s) {\n    size_t i;\n    \n    if (s == NULL || sdslen(s) == 0) {\n        return 0;\n    }\n\n    for (i = 0; i < sdslen(s); i ++) {\n        if(*(s+i) < '0' || *(s+i) > '9'){\n            return 0;\n        }\n    }\n\n    return 1;\n}\n\n/* Wrappers to the allocators used by SDS. Note that SDS will actually\n * just use the macros defined into sdsalloc.h in order to avoid to pay\n * the overhead of function calls. Here we define these wrappers only for\n * the programs SDS is linked to, if they want to touch the SDS internals\n * even if they use a different allocator. */\nvoid *sds_malloc(size_t size) { return s_malloc(size); }\nvoid *sds_realloc(void *ptr, size_t size) { return s_realloc(ptr,size); }\nvoid sds_free(void *ptr) { s_free(ptr); }\n\n#if defined(SDS_TEST_MAIN)\n#include <stdio.h>\n#include \"testhelp.h\"\n#include \"limits.h\"\n\n#define UNUSED(x) (void)(x)\nint sdsTest(void) {\n    {\n        sds x = sdsnew(\"foo\"), y;\n\n        test_cond(\"Create a string and obtain the length\",\n            sdslen(x) == 3 && memcmp(x,\"foo\\0\",4) == 0)\n\n        sdsfree(x);\n        x = sdsnewlen(\"foo\",2);\n        test_cond(\"Create a string with specified length\",\n            sdslen(x) == 2 && memcmp(x,\"fo\\0\",3) == 0)\n\n        x = sdscat(x,\"bar\");\n        test_cond(\"Strings concatenation\",\n            sdslen(x) == 5 && memcmp(x,\"fobar\\0\",6) == 0);\n\n        x = sdscpy(x,\"a\");\n        test_cond(\"sdscpy() against an originally longer string\",\n            sdslen(x) == 1 && memcmp(x,\"a\\0\",2) == 0)\n\n        x = sdscpy(x,\"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk\");\n        test_cond(\"sdscpy() against an originally shorter string\",\n            sdslen(x) == 33 &&\n            memcmp(x,\"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk\\0\",33) == 0)\n\n        sdsfree(x);\n        x = sdscatprintf(sdsempty(),\"%d\",123);\n        test_cond(\"sdscatprintf() seems working in the base case\",\n            sdslen(x) == 3 && memcmp(x,\"123\\0\",4) == 0)\n\n        sdsfree(x);\n        x = sdsnew(\"--\");\n        x = sdscatfmt(x, \"Hello %s World %I,%I--\", \"Hi!\", LLONG_MIN,LLONG_MAX);\n        test_cond(\"sdscatfmt() seems working in the base case\",\n            sdslen(x) == 60 &&\n            memcmp(x,\"--Hello Hi! World -9223372036854775808,\"\n                     \"9223372036854775807--\",60) == 0)\n        printf(\"[%s]\\n\",x);\n\n        sdsfree(x);\n        x = sdsnew(\"--\");\n        x = sdscatfmt(x, \"%u,%U--\", UINT_MAX, ULLONG_MAX);\n        test_cond(\"sdscatfmt() seems working with unsigned numbers\",\n            sdslen(x) == 35 &&\n            memcmp(x,\"--4294967295,18446744073709551615--\",35) == 0)\n\n        sdsfree(x);\n        x = sdsnew(\" x \");\n        sdstrim(x,\" x\");\n        test_cond(\"sdstrim() works when all chars match\",\n            sdslen(x) == 0)\n\n        sdsfree(x);\n        x = sdsnew(\" x \");\n        sdstrim(x,\" \");\n        test_cond(\"sdstrim() works when a single char remains\",\n            sdslen(x) == 1 && x[0] == 'x')\n\n        sdsfree(x);\n        x = sdsnew(\"xxciaoyyy\");\n        sdstrim(x,\"xy\");\n        test_cond(\"sdstrim() correctly trims characters\",\n            sdslen(x) == 4 && memcmp(x,\"ciao\\0\",5) == 0)\n\n        y = sdsdup(x);\n        sdsrange(y,1,1);\n        test_cond(\"sdsrange(...,1,1)\",\n            sdslen(y) == 1 && memcmp(y,\"i\\0\",2) == 0)\n\n        sdsfree(y);\n        y = sdsdup(x);\n        sdsrange(y,1,-1);\n        test_cond(\"sdsrange(...,1,-1)\",\n            sdslen(y) == 3 && memcmp(y,\"iao\\0\",4) == 0)\n\n        sdsfree(y);\n        y = sdsdup(x);\n        sdsrange(y,-2,-1);\n        test_cond(\"sdsrange(...,-2,-1)\",\n            sdslen(y) == 2 && memcmp(y,\"ao\\0\",3) == 0)\n\n        sdsfree(y);\n        y = sdsdup(x);\n        sdsrange(y,2,1);\n        test_cond(\"sdsrange(...,2,1)\",\n            sdslen(y) == 0 && memcmp(y,\"\\0\",1) == 0)\n\n        sdsfree(y);\n        y = sdsdup(x);\n        sdsrange(y,1,100);\n        test_cond(\"sdsrange(...,1,100)\",\n            sdslen(y) == 3 && memcmp(y,\"iao\\0\",4) == 0)\n\n        sdsfree(y);\n        y = sdsdup(x);\n        sdsrange(y,100,100);\n        test_cond(\"sdsrange(...,100,100)\",\n            sdslen(y) == 0 && memcmp(y,\"\\0\",1) == 0)\n\n        sdsfree(y);\n        sdsfree(x);\n        x = sdsnew(\"foo\");\n        y = sdsnew(\"foa\");\n        test_cond(\"sdscmp(foo,foa)\", sdscmp(x,y) > 0)\n\n        sdsfree(y);\n        sdsfree(x);\n        x = sdsnew(\"bar\");\n        y = sdsnew(\"bar\");\n        test_cond(\"sdscmp(bar,bar)\", sdscmp(x,y) == 0)\n\n        sdsfree(y);\n        sdsfree(x);\n        x = sdsnew(\"aar\");\n        y = sdsnew(\"bar\");\n        test_cond(\"sdscmp(bar,bar)\", sdscmp(x,y) < 0)\n\n        sdsfree(y);\n        sdsfree(x);\n        x = sdsnewlen(\"\\a\\n\\0foo\\r\",7);\n        y = sdscatrepr(sdsempty(),x,sdslen(x));\n        test_cond(\"sdscatrepr(...data...)\",\n            memcmp(y,\"\\\"\\\\a\\\\n\\\\x00foo\\\\r\\\"\",15) == 0)\n\n        {\n            unsigned int oldfree;\n            char *p;\n            int step = 10, j, i;\n\n            sdsfree(x);\n            sdsfree(y);\n            x = sdsnew(\"0\");\n            test_cond(\"sdsnew() free/len buffers\", sdslen(x) == 1 && sdsavail(x) == 0);\n\n            /* Run the test a few times in order to hit the first two\n             * SDS header types. */\n            for (i = 0; i < 10; i++) {\n                int oldlen = sdslen(x);\n                x = sdsMakeRoomFor(x,step);\n                int type = x[-1]&SDS_TYPE_MASK;\n\n                test_cond(\"sdsMakeRoomFor() len\", sdslen(x) == oldlen);\n                if (type != SDS_TYPE_5) {\n                    test_cond(\"sdsMakeRoomFor() free\", sdsavail(x) >= step);\n                    oldfree = sdsavail(x);\n                }\n                p = x+oldlen;\n                for (j = 0; j < step; j++) {\n                    p[j] = 'A'+j;\n                }\n                sdsIncrLen(x,step);\n            }\n            test_cond(\"sdsMakeRoomFor() content\",\n                memcmp(\"0ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJ\",x,101) == 0);\n            test_cond(\"sdsMakeRoomFor() final length\",sdslen(x)==101);\n\n            sdsfree(x);\n        }\n    }\n    test_report()\n    return 0;\n}\n#endif\n\n#ifdef SDS_TEST_MAIN\nint main(void) {\n    return sdsTest();\n}\n#endif\n"
  },
  {
    "path": "dep/sds/sds.h",
    "content": "/* SDSLib 2.0 -- A C dynamic strings library\n *\n * Copyright (c) 2006-2015, Salvatore Sanfilippo <antirez at gmail dot com>\n * Copyright (c) 2015, Oran Agra\n * Copyright (c) 2015, Redis Labs, Inc\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n *   * Redistributions of source code must retain the above copyright notice,\n *     this list of conditions and the following disclaimer.\n *   * Redistributions in binary form must reproduce the above copyright\n *     notice, this list of conditions and the following disclaimer in the\n *     documentation and/or other materials provided with the distribution.\n *   * Neither the name of Redis nor the names of its contributors may be used\n *     to endorse or promote products derived from this software without\n *     specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\n#ifndef __SDS_H\n#define __SDS_H\n\n#define SDS_MAX_PREALLOC (1024*1024)\n\n#include <sys/types.h>\n#include <stdarg.h>\n#include <stdint.h>\n\ntypedef char *sds;\n\n/* Note: sdshdr5 is never used, we just access the flags byte directly.\n * However is here to document the layout of type 5 SDS strings. */\nstruct __attribute__ ((__packed__)) sdshdr5 {\n    unsigned char flags; /* 3 lsb of type, and 5 msb of string length */\n    char buf[];\n};\nstruct __attribute__ ((__packed__)) sdshdr8 {\n    uint8_t len; /* used */\n    uint8_t alloc; /* excluding the header and null terminator */\n    unsigned char flags; /* 3 lsb of type, 5 unused bits */\n    char buf[];\n};\nstruct __attribute__ ((__packed__)) sdshdr16 {\n    uint16_t len; /* used */\n    uint16_t alloc; /* excluding the header and null terminator */\n    unsigned char flags; /* 3 lsb of type, 5 unused bits */\n    char buf[];\n};\nstruct __attribute__ ((__packed__)) sdshdr32 {\n    uint32_t len; /* used */\n    uint32_t alloc; /* excluding the header and null terminator */\n    unsigned char flags; /* 3 lsb of type, 5 unused bits */\n    char buf[];\n};\nstruct __attribute__ ((__packed__)) sdshdr64 {\n    uint64_t len; /* used */\n    uint64_t alloc; /* excluding the header and null terminator */\n    unsigned char flags; /* 3 lsb of type, 5 unused bits */\n    char buf[];\n};\n\n#define SDS_TYPE_5  0\n#define SDS_TYPE_8  1\n#define SDS_TYPE_16 2\n#define SDS_TYPE_32 3\n#define SDS_TYPE_64 4\n#define SDS_TYPE_MASK 7\n#define SDS_TYPE_BITS 3\n#define SDS_HDR_VAR(T,s) struct sdshdr##T *sh = (void*)((s)-(sizeof(struct sdshdr##T)));\n#define SDS_HDR(T,s) ((struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T))))\n#define SDS_TYPE_5_LEN(f) ((f)>>SDS_TYPE_BITS)\n\nstatic inline size_t sdslen(const sds s) {\n    unsigned char flags = s[-1];\n    switch(flags&SDS_TYPE_MASK) {\n        case SDS_TYPE_5:\n            return SDS_TYPE_5_LEN(flags);\n        case SDS_TYPE_8:\n            return SDS_HDR(8,s)->len;\n        case SDS_TYPE_16:\n            return SDS_HDR(16,s)->len;\n        case SDS_TYPE_32:\n            return SDS_HDR(32,s)->len;\n        case SDS_TYPE_64:\n            return SDS_HDR(64,s)->len;\n    }\n    return 0;\n}\n\nstatic inline size_t sdsavail(const sds s) {\n    unsigned char flags = s[-1];\n    switch(flags&SDS_TYPE_MASK) {\n        case SDS_TYPE_5: {\n            return 0;\n        }\n        case SDS_TYPE_8: {\n            SDS_HDR_VAR(8,s);\n            return sh->alloc - sh->len;\n        }\n        case SDS_TYPE_16: {\n            SDS_HDR_VAR(16,s);\n            return sh->alloc - sh->len;\n        }\n        case SDS_TYPE_32: {\n            SDS_HDR_VAR(32,s);\n            return sh->alloc - sh->len;\n        }\n        case SDS_TYPE_64: {\n            SDS_HDR_VAR(64,s);\n            return sh->alloc - sh->len;\n        }\n    }\n    return 0;\n}\n\nstatic inline void sdssetlen(sds s, size_t newlen) {\n    unsigned char flags = s[-1];\n    switch(flags&SDS_TYPE_MASK) {\n        case SDS_TYPE_5:\n            {\n                unsigned char *fp = ((unsigned char*)s)-1;\n                *fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS);\n            }\n            break;\n        case SDS_TYPE_8:\n            SDS_HDR(8,s)->len = newlen;\n            break;\n        case SDS_TYPE_16:\n            SDS_HDR(16,s)->len = newlen;\n            break;\n        case SDS_TYPE_32:\n            SDS_HDR(32,s)->len = newlen;\n            break;\n        case SDS_TYPE_64:\n            SDS_HDR(64,s)->len = newlen;\n            break;\n    }\n}\n\nstatic inline void sdsinclen(sds s, size_t inc) {\n    unsigned char flags = s[-1];\n    switch(flags&SDS_TYPE_MASK) {\n        case SDS_TYPE_5:\n            {\n                unsigned char *fp = ((unsigned char*)s)-1;\n                unsigned char newlen = SDS_TYPE_5_LEN(flags)+inc;\n                *fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS);\n            }\n            break;\n        case SDS_TYPE_8:\n            SDS_HDR(8,s)->len += inc;\n            break;\n        case SDS_TYPE_16:\n            SDS_HDR(16,s)->len += inc;\n            break;\n        case SDS_TYPE_32:\n            SDS_HDR(32,s)->len += inc;\n            break;\n        case SDS_TYPE_64:\n            SDS_HDR(64,s)->len += inc;\n            break;\n    }\n}\n\n/* sdsalloc() = sdsavail() + sdslen() */\nstatic inline size_t sdsalloc(const sds s) {\n    unsigned char flags = s[-1];\n    switch(flags&SDS_TYPE_MASK) {\n        case SDS_TYPE_5:\n            return SDS_TYPE_5_LEN(flags);\n        case SDS_TYPE_8:\n            return SDS_HDR(8,s)->alloc;\n        case SDS_TYPE_16:\n            return SDS_HDR(16,s)->alloc;\n        case SDS_TYPE_32:\n            return SDS_HDR(32,s)->alloc;\n        case SDS_TYPE_64:\n            return SDS_HDR(64,s)->alloc;\n    }\n    return 0;\n}\n\nstatic inline void sdssetalloc(sds s, size_t newlen) {\n    unsigned char flags = s[-1];\n    switch(flags&SDS_TYPE_MASK) {\n        case SDS_TYPE_5:\n            /* Nothing to do, this type has no total allocation info. */\n            break;\n        case SDS_TYPE_8:\n            SDS_HDR(8,s)->alloc = newlen;\n            break;\n        case SDS_TYPE_16:\n            SDS_HDR(16,s)->alloc = newlen;\n            break;\n        case SDS_TYPE_32:\n            SDS_HDR(32,s)->alloc = newlen;\n            break;\n        case SDS_TYPE_64:\n            SDS_HDR(64,s)->alloc = newlen;\n            break;\n    }\n}\n\nsds sdsnewlen(const void *init, size_t initlen);\nsds sdsnew(const char *init);\nsds sdsempty(void);\nsds sdsdup(const sds s);\nvoid sdsfree(sds s);\nsds sdsgrowzero(sds s, size_t len);\nsds sdscatlen(sds s, const void *t, size_t len);\nsds sdscat(sds s, const char *t);\nsds sdscatsds(sds s, const sds t);\nsds sdscpylen(sds s, const char *t, size_t len);\nsds sdscpy(sds s, const char *t);\n\nsds sdscatvprintf(sds s, const char *fmt, va_list ap);\n#ifdef __GNUC__\nsds sdscatprintf(sds s, const char *fmt, ...)\n    __attribute__((format(printf, 2, 3)));\n#else\nsds sdscatprintf(sds s, const char *fmt, ...);\n#endif\n\nsds sdscatfmt(sds s, char const *fmt, ...);\nsds sdstrim(sds s, const char *cset);\nvoid sdsrange(sds s, int start, int end);\nvoid sdsupdatelen(sds s);\nvoid sdsclear(sds s);\nint sdscmp(const sds s1, const sds s2);\nsds *sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count);\nvoid sdsfreesplitres(sds *tokens, int count);\nvoid sdstolower(sds s);\nvoid sdstoupper(sds s);\nsds sdsfromlonglong(long long value);\nsds sdscatrepr(sds s, const char *p, size_t len);\nsds *sdssplitargs(const char *line, int *argc);\nsds sdsmapchars(sds s, const char *from, const char *to, size_t setlen);\nsds sdsjoin(char **argv, int argc, char *sep);\nsds sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen);\n\nint sdsIsNum(sds s);\n\n/* Low level functions exposed to the user API */\nsds sdsMakeRoomFor(sds s, size_t addlen);\nvoid sdsIncrLen(sds s, int incr);\nsds sdsRemoveFreeSpace(sds s);\nsize_t sdsAllocSize(sds s);\nvoid *sdsAllocPtr(sds s);\n\n/* Export the allocator used by SDS to the program using SDS.\n * Sometimes the program SDS is linked to, may use a different set of\n * allocators, but may want to allocate or free things that SDS will\n * respectively free or allocate. */\nvoid *sds_malloc(size_t size);\nvoid *sds_realloc(void *ptr, size_t size);\nvoid sds_free(void *ptr);\n\n#ifdef REDIS_TEST\nint sdsTest(int argc, char *argv[]);\n#endif\n\n#endif\n"
  },
  {
    "path": "dep/sds/sdsalloc.h",
    "content": "/* SDSLib 2.0 -- A C dynamic strings library\n *\n * Copyright (c) 2006-2015, Salvatore Sanfilippo <antirez at gmail dot com>\n * Copyright (c) 2015, Redis Labs, Inc\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n *   * Redistributions of source code must retain the above copyright notice,\n *     this list of conditions and the following disclaimer.\n *   * Redistributions in binary form must reproduce the above copyright\n *     notice, this list of conditions and the following disclaimer in the\n *     documentation and/or other materials provided with the distribution.\n *   * Neither the name of Redis nor the names of its contributors may be used\n *     to endorse or promote products derived from this software without\n *     specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\n/* SDS allocator selection.\n *\n * This file is used in order to change the SDS allocator at compile time.\n * Just define the following defines to what you want to use. Also add\n * the include of your alternate allocator if needed (not needed in order\n * to use the default libc allocator). */\n\n#include <dmalloc.h>\n\n#define s_malloc    dalloc\n#define s_realloc   drealloc\n#define s_free      dfree\n"
  },
  {
    "path": "dep/util/Makefile.am",
    "content": "MAINTAINERCLEANFILES = Makefile.in\n\nAM_CFLAGS = -Wall -Wshadow\nAM_CFLAGS += -Wno-unused-parameter -Wno-unused-value\n\nnoinst_LIBRARIES = libdutil.a\n\nnoinst_HEADERS = dspecialconfig.h dutil.h dlog.h\n\nlibdutil_a_SOURCES =    \\\n    dspecialconfig.h    \\\n\tdutil.c dutil.h     \\\n    dlog.c dlog.h"
  },
  {
    "path": "dep/util/dlog.c",
    "content": "#include <stdlib.h>\n#include <stdarg.h>\n#include <unistd.h>\n#include <ctype.h>\n#include <time.h>\n#include <sys/stat.h>\n#include <fcntl.h>\n#include <errno.h>\n\n#include <dutil.h>\n#include <dlog.h>\n\nstatic struct logger logger;\n\nint\nlog_init(int level, char *name)\n{\n    struct logger *l = &logger;\n\n    l->level = MAX(LOG_EMERG, MIN(level, LOG_PVERB));\n    l->name = name;\n    if (name == NULL || !strlen(name)) {\n        l->fd = STDERR_FILENO;\n    } else {\n        l->fd = open(name, O_WRONLY | O_APPEND | O_CREAT, 0644);\n        if (l->fd < 0) {\n            log_stderr(\"opening log file '%s' failed: %s\", name,\n                       strerror(errno));\n            return -1;\n        }\n    }\n\n    return 0;\n}\n\nvoid\nlog_deinit(void)\n{\n    struct logger *l = &logger;\n\n    if (l->fd < 0 || l->fd == STDERR_FILENO) {\n        return;\n    }\n\n    close(l->fd);\n}\n\nvoid\nlog_reopen(void)\n{\n    struct logger *l = &logger;\n\n    if (l->fd != STDERR_FILENO) {\n        close(l->fd);\n        l->fd = open(l->name, O_WRONLY | O_APPEND | O_CREAT, 0644);\n        if (l->fd < 0) {\n            log_stderr_safe(\"reopening log file '%s' failed, ignored: %s\", l->name,\n                       strerror(errno));\n        }\n    }\n}\n\nvoid\nlog_level_up(void)\n{\n    struct logger *l = &logger;\n\n    if (l->level < LOG_PVERB) {\n        l->level++;\n        log_safe(\"up log level to %d\", l->level);\n    }\n}\n\nvoid\nlog_level_down(void)\n{\n    struct logger *l = &logger;\n\n    if (l->level > LOG_EMERG) {\n        l->level--;\n        log_safe(\"down log level to %d\", l->level);\n    }\n}\n\nvoid\nlog_level_set(int level)\n{\n    struct logger *l = &logger;\n\n    l->level = MAX(LOG_EMERG, MIN(level, LOG_PVERB));\n    loga(\"set log level to %d\", l->level);\n}\n\nvoid\nlog_stacktrace(void)\n{\n    struct logger *l = &logger;\n\n    if (l->fd < 0) {\n        return;\n    }\n    dstacktrace_fd(l->fd);\n}\n\nint\nlog_loggable(int level)\n{\n    struct logger *l = &logger;\n\n    if (level > l->level) {\n        return 0;\n    }\n\n    return 1;\n}\n\nvoid\n_log(const char *file, int line, int level, int panic, const char *fmt, ...)\n{\n    struct logger *l = &logger;\n    int len, size, errno_save;\n    char buf[LOG_MAX_LEN];\n    va_list args;\n    ssize_t n;\n    struct timeval tv;\n\n    if (l->fd < 0) {\n        return;\n    }\n\n    errno_save = errno;\n    len = 0;            /* length of output buffer */\n    size = LOG_MAX_LEN; /* size of output buffer */\n\n    gettimeofday(&tv, NULL);\n    buf[len++] = '[';\n    len += dstrftime(buf + len, size - len, \"%Y-%m-%d %H:%M:%S.\", localtime(&tv.tv_sec));\n    len += dscnprintf(buf + len, size - len, \"%03ld\", tv.tv_usec/1000);\n    len += dscnprintf(buf + len, size - len, \"] %s:%d \", file, line);\n\n    va_start(args, fmt);\n    len += dvscnprintf(buf + len, size - len, fmt, args);\n    va_end(args);\n\n    buf[len++] = '\\n';\n\n    n = write(l->fd, buf, len);\n    if (n < 0) {\n        l->nerror++;\n    }\n\n    errno = errno_save;\n\n    if (panic) {\n        abort();\n    }\n}\n\nvoid\n_log_stderr(const char *fmt, ...)\n{\n    struct logger *l = &logger;\n    int len, size, errno_save;\n    char buf[4 * LOG_MAX_LEN];\n    va_list args;\n    ssize_t n;\n\n    errno_save = errno;\n    len = 0;                /* length of output buffer */\n    size = 4 * LOG_MAX_LEN; /* size of output buffer */\n\n    va_start(args, fmt);\n    len += dvscnprintf(buf, size, fmt, args);\n    va_end(args);\n\n    buf[len++] = '\\n';\n\n    n = write(STDERR_FILENO, buf, len);\n    if (n < 0) {\n        l->nerror++;\n    }\n\n    errno = errno_save;\n}\n\nvoid\n_log_stdout(const char *fmt, ...)\n{\n    struct logger *l = &logger;\n    int len, size, errno_save;\n    char buf[4 * LOG_MAX_LEN];\n    va_list args;\n    ssize_t n;\n\n    errno_save = errno;\n    len = 0;                /* length of output buffer */\n    size = 4 * LOG_MAX_LEN; /* size of output buffer */\n\n    va_start(args, fmt);\n    len += dvscnprintf(buf, size, fmt, args);\n    va_end(args);\n\n    buf[len++] = '\\n';\n\n    n = write(STDOUT_FILENO, buf, len);\n    if (n < 0) {\n        l->nerror++;\n    }\n\n    errno = errno_save;\n}\n\n/*\n * Hexadecimal dump in the canonical hex + ascii display\n * See -C option in man hexdump\n */\nvoid\n_log_hexdump(const char *file, int line, char *data, int datalen,\n             const char *fmt, ...)\n{\n    struct logger *l = &logger;\n    char buf[8 * LOG_MAX_LEN];\n    int i, off, len, size, errno_save;\n    ssize_t n;\n\n    if (l->fd < 0) {\n        return;\n    }\n\n    /* log hexdump */\n    errno_save = errno;\n    off = 0;                  /* data offset */\n    len = 0;                  /* length of output buffer */\n    size = 8 * LOG_MAX_LEN;   /* size of output buffer */\n\n    while (datalen != 0 && (len < size - 1)) {\n        char *save, *str;\n        unsigned char c;\n        int savelen;\n\n        len += dscnprintf(buf + len, size - len, \"%08x  \", off);\n\n        save = data;\n        savelen = datalen;\n\n        for (i = 0; datalen != 0 && i < 16; data++, datalen--, i++) {\n            c = (unsigned char)(*data);\n            str = (i == 7) ? \"  \" : \" \";\n            len += dscnprintf(buf + len, size - len, \"%02x%s\", c, str);\n        }\n        for ( ; i < 16; i++) {\n            str = (i == 7) ? \"  \" : \" \";\n            len += dscnprintf(buf + len, size - len, \"  %s\", str);\n        }\n\n        data = save;\n        datalen = savelen;\n\n        len += dscnprintf(buf + len, size - len, \"  |\");\n\n        for (i = 0; datalen != 0 && i < 16; data++, datalen--, i++) {\n            c = (unsigned char)(isprint(*data) ? *data : '.');\n            len += dscnprintf(buf + len, size - len, \"%c\", c);\n        }\n        len += dscnprintf(buf + len, size - len, \"|\\n\");\n\n        off += 16;\n    }\n\n    n = write(l->fd, buf, len);\n    if (n < 0) {\n        l->nerror++;\n    }\n\n    if (len >= size - 1) {\n        n = write(l->fd, \"\\n\", 1);\n        if (n < 0) {\n            l->nerror++;\n        }\n    }\n\n    errno = errno_save;\n}\n\nvoid\n_log_safe(const char *fmt, ...)\n{\n    struct logger *l = &logger;\n    int len, size, errno_save;\n    char buf[LOG_MAX_LEN];\n    va_list args;\n    ssize_t n;\n\n    if (l->fd < 0) {\n        return;\n    }\n\n    errno_save = errno;\n    len = 0;            /* length of output buffer */\n    size = LOG_MAX_LEN; /* size of output buffer */\n\n    len += dsafe_snprintf(buf + len, size - len, \"[.......................] \");\n\n    va_start(args, fmt);\n    len += dsafe_vsnprintf(buf + len, size - len, fmt, args);\n    va_end(args);\n\n    buf[len++] = '\\n';\n\n    n = write(l->fd, buf, len);\n    if (n < 0) {\n        l->nerror++;\n    }\n\n    errno = errno_save;\n}\n\nvoid\n_log_stderr_safe(const char *fmt, ...)\n{\n    struct logger *l = &logger;\n    int len, size, errno_save;\n    char buf[LOG_MAX_LEN];\n    va_list args;\n    ssize_t n;\n\n    errno_save = errno;\n    len = 0;            /* length of output buffer */\n    size = LOG_MAX_LEN; /* size of output buffer */\n\n    len += dsafe_snprintf(buf + len, size - len, \"[.......................] \");\n\n    va_start(args, fmt);\n    len += dsafe_vsnprintf(buf + len, size - len, fmt, args);\n    va_end(args);\n\n    buf[len++] = '\\n';\n\n    n = write(STDERR_FILENO, buf, len);\n    if (n < 0) {\n        l->nerror++;\n    }\n\n    errno = errno_save;\n}\n\nvoid log_write_len(char *str, size_t len)\n{\n    struct logger *l = &logger;\n    int errno_save;\n    ssize_t n;\n\n    if (l->fd < 0) {\n        return;\n    }\n\n    errno_save = errno;\n    n = write(l->fd, str, len);\n    if (n < 0) {\n        l->nerror++;\n    }\n\n    errno = errno_save;\n}\n"
  },
  {
    "path": "dep/util/dlog.h",
    "content": "#ifndef _DLOG_H_\n#define _DLOG_H_\n\n#ifdef HAVE_CONFIG_H\n# include <config.h>\n#endif\n\nstruct logger {\n    char *name;  /* log file name */\n    int  level;  /* log level */\n    int  fd;     /* log file descriptor */\n    int  nerror; /* # log error */\n};\n\n#define LOG_EMERG   0   /* system in unusable */\n#define LOG_ALERT   1   /* action must be taken immediately */\n#define LOG_CRIT    2   /* critical conditions */\n#define LOG_ERR     3   /* error conditions */\n#define LOG_WARN    4   /* warning conditions */\n#define LOG_NOTICE  5   /* normal but significant condition (default) */\n#define LOG_INFO    6   /* informational */\n#define LOG_DEBUG   7   /* debug messages */\n#define LOG_VERB    8   /* verbose messages */\n#define LOG_VVERB   9   /* verbose messages on crack */\n#define LOG_VVVERB  10  /* verbose messages on ganga */\n#define LOG_PVERB   11  /* periodic verbose messages on crack */\n\n#define LOG_MAX_LEN 256 /* max length of log message */\n\n/*\n * log_stderr   - log to stderr\n * loga         - log always\n * loga_hexdump - log hexdump always\n * log_error    - error log messages\n * log_warn     - warning log messages\n * log_panic    - log messages followed by a panic\n * ...\n * log_debug    - debug log messages based on a log level\n * log_hexdump  - hexadump -C of a log buffer\n */\n#ifdef HAVE_DEBUG_LOG\n\n#define log_debug(_level, ...) do {                                         \\\n    if (log_loggable(_level) != 0) {                                        \\\n        _log(__FILE__, __LINE__, _level, 0, __VA_ARGS__);                   \\\n    }                                                                       \\\n} while (0)\n\n#else\n\n#define log_debug(_level, ...)\n\n#endif\n\n#define log_hexdump(_level, _data, _datalen, ...) do {                      \\\n    if (log_loggable(_level) != 0) {                                        \\\n        _log(__FILE__, __LINE__, _level, 0, __VA_ARGS__);                   \\\n        _log_hexdump(__FILE__, __LINE__, (char *)(_data), (int)(_datalen),  \\\n                     __VA_ARGS__);                                          \\\n    }                                                                       \\\n} while (0)\n\n#define log_stderr(...) do {                                                \\\n    _log_stderr(__VA_ARGS__);                                               \\\n} while (0)\n\n#define log_stdout(...) do {                                                \\\n    _log_stdout(__VA_ARGS__);                                               \\\n} while (0)\n\n#define log_safe(...) do {                                                  \\\n    _log_safe(__VA_ARGS__);                                                 \\\n} while (0)\n\n#define log_stderr_safe(...) do {                                           \\\n    _log_stderr_safe(__VA_ARGS__);                                          \\\n} while (0)\n\n#define loga(...) do {                                                      \\\n    _log(__FILE__, __LINE__, LOG_EMERG, 0, __VA_ARGS__);                    \\\n} while (0)\n\n#define loga_hexdump(_data, _datalen, ...) do {                             \\\n    _log(__FILE__, __LINE__, LOG_EMERG, 0, __VA_ARGS__);                    \\\n    _log_hexdump(__FILE__, __LINE__, (char *)(_data), (int)(_datalen),      \\\n                 __VA_ARGS__);                                              \\\n} while (0)                                                                 \\\n\n#define log_error(...) do {                                                 \\\n    if (log_loggable(LOG_ERR) != 0) {                                       \\\n        _log(__FILE__, __LINE__, LOG_ERR, 0, __VA_ARGS__);                  \\\n    }                                                                       \\\n} while (0)\n\n#define log_warn(...) do {                                                  \\\n    if (log_loggable(LOG_WARN) != 0) {                                      \\\n        _log(__FILE__, __LINE__, LOG_WARN, 0, __VA_ARGS__);                 \\\n    }                                                                       \\\n} while (0)\n\n#define log_notice(...) do {                                                \\\n    if (log_loggable(LOG_NOTICE) != 0) {                                    \\\n        _log(__FILE__, __LINE__, LOG_NOTICE, 0, __VA_ARGS__);               \\\n    }                                                                       \\\n} while (0)\n\n#define log_panic(...) do {                                                 \\\n    if (log_loggable(LOG_EMERG) != 0) {                                     \\\n        _log(__FILE__, __LINE__, LOG_EMERG, 1, __VA_ARGS__);                \\\n    }                                                                       \\\n} while (0)\n\nint log_init(int level, char *filename);\nvoid log_deinit(void);\nvoid log_level_up(void);\nvoid log_level_down(void);\nvoid log_level_set(int level);\nvoid log_stacktrace(void);\nvoid log_reopen(void);\nint log_loggable(int level);\n\nvoid _log(const char *file, int line, int level, int panic, const char *fmt, ...);\nvoid _log_stderr(const char *fmt, ...);\nvoid _log_stdout(const char *fmt, ...);\nvoid _log_safe(const char *fmt, ...);\nvoid _log_stderr_safe(const char *fmt, ...);\nvoid _log_hexdump(const char *file, int line, char *data, int datalen, const char *fmt, ...);\n\nvoid log_write_len(char * str, size_t len);\n\n#endif\n"
  },
  {
    "path": "dep/util/dspecialconfig.h",
    "content": "#ifndef _DSPECIALCONFIG_H_\n#define _DSPECIALCONFIG_H_\n\n#ifdef __APPLE__\n#include <AvailabilityMacros.h>\n#endif\n\n#ifdef __linux__\n#include <linux/version.h>\n#include <features.h>\n#endif\n\n#if (__i386 || __amd64 || __powerpc__) && __GNUC__\n#define GNUC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)\n#if defined(__clang__)\n#define HAVE_ATOMIC\n#endif\n#if (defined(__GLIBC__) && defined(__GLIBC_PREREQ))\n#if (GNUC_VERSION >= 40100 && __GLIBC_PREREQ(2, 6))\n#define HAVE_ATOMIC\n#endif\n#endif\n#endif\n\n\n#if defined(__sun)\n#if defined(__GNUC__)\n#include <math.h>\n#undef isnan\n#define isnan(x) \\\n     __extension__({ __typeof (x) __x_a = (x); \\\n     __builtin_expect(__x_a != __x_a, 0); })\n\n#undef isfinite\n#define isfinite(x) \\\n     __extension__ ({ __typeof (x) __x_f = (x); \\\n     __builtin_expect(!isnan(__x_f - __x_f), 1); })\n\n#undef isinf\n#define isinf(x) \\\n     __extension__ ({ __typeof (x) __x_i = (x); \\\n     __builtin_expect(!isnan(__x_i) && !isfinite(__x_i), 0); })\n\n#define u_int uint\n#define u_int32_t uint32_t\n#endif /* __GNUC__ */\n#endif /* __sun */\n\n\n/* Test for proc filesystem */\n#ifdef __linux__\n#define HAVE_PROC_STAT 1\n#define HAVE_PROC_MAPS 1\n#define HAVE_PROC_SMAPS 1\n#define HAVE_PROC_SOMAXCONN 1\n#endif\n\n/* Test for task_info() */\n#if defined(__APPLE__)\n#define HAVE_TASKINFO 1\n#endif\n\n\n#endif\n"
  },
  {
    "path": "dep/util/dutil.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <stdarg.h>\n#include <string.h>\n#include <unistd.h>\n#include <fcntl.h>\n#include <netdb.h>\n#include <errno.h>\n\n#include <sys/time.h>\n#include <sys/types.h>\n#include <sys/socket.h>\n#include <sys/ioctl.h>\n\n#include <netinet/in.h>\n#include <netinet/tcp.h>\n\n#ifdef HAVE_CONFIG_H\n# include <config.h>\n#endif\n\n#ifdef HAVE_BACKTRACE\n# include <execinfo.h>\n#endif\n\n#include <dlog.h>\n#include <dutil.h>\n\n/* GCC version >= 4.7 */\n#if defined(__ATOMIC_RELAXED)\n/* GCC version >= 4.1 */\n#elif defined(HAVE_ATOMIC)\n#else\npthread_mutex_t atomic_locker = PTHREAD_MUTEX_INITIALIZER;\n#endif\n\nvoid\ndassert(const char *cond, const char *file, int line, int panic)\n{\n    log_error(\"assert '%s' failed @ (%s, %d)\", cond, file, line);\n\n    if (panic) {\n        dstacktrace(1);\n        abort();\n    }\n}\n\nvoid\ndstacktrace(int skip_count)\n{\n#ifdef HAVE_BACKTRACE\n    void *stack[64];\n    char **symbols;\n    int size, i, j;\n\n    size = backtrace(stack, 64);\n    symbols = backtrace_symbols(stack, size);\n    if (symbols == NULL) {\n        return;\n    }\n\n    skip_count++; /* skip the current frame also */\n\n    for (i = skip_count, j = 0; i < size; i++, j++) {\n        loga(\"[%d] %s\", j, symbols[i]);\n    }\n\n    free(symbols);\n#endif\n}\n\nvoid\ndstacktrace_fd(int fd)\n{\n#ifdef HAVE_BACKTRACE\n    void *stack[64];\n    int size;\n\n    size = backtrace(stack, 64);\n    backtrace_symbols_fd(stack, size, fd);\n#endif\n}\n\nint\n_dvscnprintf(char *buf, size_t size, const char *fmt, va_list args)\n{\n    int n;\n\n    n = vsnprintf(buf, size, fmt, args);\n\n    /*\n     * The return value is the number of characters which would be written\n     * into buf not including the trailing '\\0'. If size is == 0 the\n     * function returns 0.\n     *\n     * On error, the function also returns 0. This is to allow idiom such\n     * as len += _vscnprintf(...)\n     *\n     * See: http://lwn.net/Articles/69419/\n     */\n    if (n <= 0) {\n        return 0;\n    }\n\n    if (n < (int) size) {\n        return n;\n    }\n\n    return (int)(size - 1);\n}\n\nint\n_dscnprintf(char *buf, size_t size, const char *fmt, ...)\n{\n    va_list args;\n    int n;\n\n    va_start(args, fmt);\n    n = _dvscnprintf(buf, size, fmt, args);\n    va_end(args);\n\n    return n;\n}\n\nstatic char *\n_safe_utoa(int _base, uint64_t val, char *buf)\n{\n    char hex[] = \"0123456789abcdef\";\n    uint32_t base = (uint32_t) _base;\n    *buf-- = 0;\n    do {\n        *buf-- = hex[val % base];\n    } while ((val /= base) != 0);\n    return buf + 1;\n}\n\nstatic char *\n_safe_itoa(int base, int64_t val, char *buf)\n{\n    char hex[] = \"0123456789abcdef\";\n    char *orig_buf = buf;\n    const int32_t is_neg = (val < 0);\n    *buf-- = 0;\n\n    if (is_neg) {\n        val = -val;\n    }\n    if (is_neg && base == 16) {\n        int ix;\n        val -= 1;\n        for (ix = 0; ix < 16; ++ix)\n            buf[-ix] = '0';\n    }\n\n    do {\n        *buf-- = hex[val % base];\n    } while ((val /= base) != 0);\n\n    if (is_neg && base == 10) {\n        *buf-- = '-';\n    }\n\n    if (is_neg && base == 16) {\n        int ix;\n        buf = orig_buf - 1;\n        for (ix = 0; ix < 16; ++ix, --buf) {\n            /* *INDENT-OFF* */\n            switch (*buf) {\n            case '0': *buf = 'f'; break;\n            case '1': *buf = 'e'; break;\n            case '2': *buf = 'd'; break;\n            case '3': *buf = 'c'; break;\n            case '4': *buf = 'b'; break;\n            case '5': *buf = 'a'; break;\n            case '6': *buf = '9'; break;\n            case '7': *buf = '8'; break;\n            case '8': *buf = '7'; break;\n            case '9': *buf = '6'; break;\n            case 'a': *buf = '5'; break;\n            case 'b': *buf = '4'; break;\n            case 'c': *buf = '3'; break;\n            case 'd': *buf = '2'; break;\n            case 'e': *buf = '1'; break;\n            case 'f': *buf = '0'; break;\n            }\n            /* *INDENT-ON* */\n        }\n    }\n    return buf + 1;\n}\n\nstatic const char *\n_safe_check_longlong(const char *fmt, int *have_longlong)\n{\n    *have_longlong = 0;\n    if (*fmt == 'l') {\n        fmt++;\n        if (*fmt != 'l') {\n            *have_longlong = (sizeof(long) == sizeof(long long));\n        } else {\n            fmt++;\n            *have_longlong = 1;\n        }\n    }\n    return fmt;\n}\n\nint\n_safe_vsnprintf(char *to, size_t size, const char *format, va_list ap)\n{\n    char *start = to;\n    char *end = start + size - 1;\n    for (; *format; ++format) {\n        int have_longlong = 0;\n        if (*format != '%') {\n            if (to == end) {    /* end of buffer */\n                break;\n            }\n            *to++ = *format;    /* copy ordinary char */\n            continue;\n        }\n        ++format;               /* skip '%' */\n\n        format = _safe_check_longlong(format, &have_longlong);\n\n        switch (*format) {\n        case 'd':\n        case 'i':\n        case 'u':\n        case 'x':\n        case 'p':\n            {\n                int64_t ival = 0;\n                uint64_t uval = 0;\n                if (*format == 'p')\n                    have_longlong = (sizeof(void *) == sizeof(uint64_t));\n                if (have_longlong) {\n                    if (*format == 'u') {\n                        uval = va_arg(ap, uint64_t);\n                    } else {\n                        ival = va_arg(ap, int64_t);\n                    }\n                } else {\n                    if (*format == 'u') {\n                        uval = va_arg(ap, uint32_t);\n                    } else {\n                        ival = va_arg(ap, int32_t);\n                    }\n                }\n\n                {\n                    char buff[22];\n                    const int base = (*format == 'x' || *format == 'p') ? 16 : 10;\n\n\t\t            /* *INDENT-OFF* */\n                    char *val_as_str = (*format == 'u') ?\n                        _safe_utoa(base, uval, &buff[sizeof(buff) - 1]) :\n                        _safe_itoa(base, ival, &buff[sizeof(buff) - 1]);\n\t\t            /* *INDENT-ON* */\n\n                    /* Strip off \"ffffffff\" if we have 'x' format without 'll' */\n                    if (*format == 'x' && !have_longlong && ival < 0) {\n                        val_as_str += 8;\n                    }\n\n                    while (*val_as_str && to < end) {\n                        *to++ = *val_as_str++;\n                    }\n                    continue;\n                }\n            }\n        case 's':\n            {\n                const char *val = va_arg(ap, char *);\n                if (!val) {\n                    val = \"(null)\";\n                }\n                while (*val && to < end) {\n                    *to++ = *val++;\n                }\n                continue;\n            }\n        }\n    }\n    *to = 0;\n    return (int)(to - start);\n}\n\nint\n_safe_snprintf(char *to, size_t n, const char *fmt, ...)\n{\n    int result;\n    va_list args;\n    va_start(args, fmt);\n    result = _safe_vsnprintf(to, n, fmt, args);\n    va_end(args);\n    return result;\n}\n\n/*\n * Return the current time in microseconds since Epoch\n */\nlong long\ndusec_now(void)\n{\n    struct timeval now;\n    int64_t usec;\n    int status;\n\n    status = gettimeofday(&now, NULL);\n    if (status < 0) {\n        log_error(\"gettimeofday failed: %s\", strerror(errno));\n        return -1;\n    }\n\n    usec = (int64_t)now.tv_sec * 1000000LL + (int64_t)now.tv_usec;\n\n    return usec;\n}\n\n/*\n * Return the current time in milliseconds since Epoch\n */\nlong long\ndmsec_now(void)\n{\n    return dusec_now() / 1000LL;\n}\n\n/*\n * Return the current time in seconds since Epoch\n */\nlong long\ndsec_now(void)\n{\n    return dusec_now() / 1000000LL;\n}\n\n/* Glob-style pattern matching. */\nint string_match_len(const char *pattern, int patternLen,\n        const char *string, int stringLen, int nocase)\n{\n    while(patternLen) {\n        switch(pattern[0]) {\n        case '*':\n            while (pattern[1] == '*') {\n                pattern++;\n                patternLen--;\n            }\n            if (patternLen == 1)\n                return 1; /* match */\n            while(stringLen) {\n                if (string_match_len(pattern+1, patternLen-1,\n                            string, stringLen, nocase))\n                    return 1; /* match */\n                string++;\n                stringLen--;\n            }\n            return 0; /* no match */\n            break;\n        case '?':\n            if (stringLen == 0)\n                return 0; /* no match */\n            string++;\n            stringLen--;\n            break;\n        case '[':\n        {\n            int not, match;\n\n            pattern++;\n            patternLen--;\n            not = pattern[0] == '^';\n            if (not) {\n                pattern++;\n                patternLen--;\n            }\n            match = 0;\n            while(1) {\n                if (pattern[0] == '\\\\') {\n                    pattern++;\n                    patternLen--;\n                    if (pattern[0] == string[0])\n                        match = 1;\n                } else if (pattern[0] == ']') {\n                    break;\n                } else if (patternLen == 0) {\n                    pattern--;\n                    patternLen++;\n                    break;\n                } else if (pattern[1] == '-' && patternLen >= 3) {\n                    int start = pattern[0];\n                    int end = pattern[2];\n                    int c = string[0];\n                    if (start > end) {\n                        int t = start;\n                        start = end;\n                        end = t;\n                    }\n                    if (nocase) {\n                        start = tolower(start);\n                        end = tolower(end);\n                        c = tolower(c);\n                    }\n                    pattern += 2;\n                    patternLen -= 2;\n                    if (c >= start && c <= end)\n                        match = 1;\n                } else {\n                    if (!nocase) {\n                        if (pattern[0] == string[0])\n                            match = 1;\n                    } else {\n                        if (tolower((int)pattern[0]) == tolower((int)string[0]))\n                            match = 1;\n                    }\n                }\n                pattern++;\n                patternLen--;\n            }\n            if (not)\n                match = !match;\n            if (!match)\n                return 0; /* no match */\n            string++;\n            stringLen--;\n            break;\n        }\n        case '\\\\':\n            if (patternLen >= 2) {\n                pattern++;\n                patternLen--;\n            }\n            /* fall through */\n        default:\n            if (!nocase) {\n                if (pattern[0] != string[0])\n                    return 0; /* no match */\n            } else {\n                if (tolower((int)pattern[0]) != tolower((int)string[0]))\n                    return 0; /* no match */\n            }\n            string++;\n            stringLen--;\n            break;\n        }\n        pattern++;\n        patternLen--;\n        if (stringLen == 0) {\n            while(*pattern == '*') {\n                pattern++;\n                patternLen--;\n            }\n            break;\n        }\n    }\n    if (patternLen == 0 && stringLen == 0)\n        return 1;\n    return 0;\n}\n\nint string_match(const char *pattern, const char *string, int nocase) {\n    return string_match_len(pattern,strlen(pattern),string,strlen(string),nocase);\n}\n"
  },
  {
    "path": "dep/util/dutil.h",
    "content": "#ifndef _DUTIL_H_\n#define _DUTIL_H_\n\n#include <stdarg.h>\n\n#include <dspecialconfig.h>\n\n#define UNUSED(x) (void)(x)\n\n#define LF                  (uint8_t) 10\n#define CR                  (uint8_t) 13\n#define CRLF                \"\\x0d\\x0a\"\n#define CRLF_LEN            (sizeof(\"\\x0d\\x0a\") - 1)\n\n#define NELEMS(a)           ((sizeof(a)) / sizeof((a)[0]))\n\n#define MIN(a, b)           ((a) < (b) ? (a) : (b))\n#define MAX(a, b)           ((a) > (b) ? (a) : (b))\n\n#define SQUARE(d)           ((d) * (d))\n#define VAR(s, s2, n)       (((n) < 2) ? 0.0 : ((s2) - SQUARE(s)/(n)) / ((n) - 1))\n#define STDDEV(s, s2, n)    (((n) < 2) ? 0.0 : sqrt(VAR((s), (s2), (n))))\n\n/*\n * Wrappers for defining custom assert based on whether macro\n * RMT_ASSERT_PANIC or RMT_ASSERT_LOG was defined at the moment\n * ASSERT was called.\n */\n#ifdef HAVE_ASSERT_PANIC\n\n#define ASSERT(_x) do {                         \\\n    if (!(_x)) {                                \\\n        dassert(#_x, __FILE__, __LINE__, 1);  \\\n    }                                           \\\n} while (0)\n\n#define NOT_REACHED() ASSERT(0)\n\n#elif HAVE_ASSERT_LOG\n\n#define ASSERT(_x) do {                         \\\n    if (!(_x)) {                                \\\n        dassert(#_x, __FILE__, __LINE__, 0);  \\\n    }                                           \\\n} while (0)\n\n#define NOT_REACHED() ASSERT(0)\n\n#else\n\n#define ASSERT(_x)\n\n#define NOT_REACHED()\n\n#endif\n\nvoid dassert(const char *cond, const char *file, int line, int panic);\nvoid dstacktrace(int skip_count);\nvoid dstacktrace_fd(int fd);\n\nint _dscnprintf(char *buf, size_t size, const char *fmt, ...);\nint _dvscnprintf(char *buf, size_t size, const char *fmt, va_list args);\nlong long dusec_now(void);\nlong long dmsec_now(void);\nlong long dsec_now(void);\n\n/*\n * A (very) limited version of snprintf\n * @param   to   Destination buffer\n * @param   n    Size of destination buffer\n * @param   fmt  printf() style format string\n * @returns Number of bytes written, including terminating '\\0'\n * Supports 'd' 'i' 'u' 'x' 'p' 's' conversion\n * Supports 'l' and 'll' modifiers for integral types\n * Does not support any width/precision\n * Implemented with simplicity, and async-signal-safety in mind\n */\nint _safe_vsnprintf(char *to, size_t size, const char *format, va_list ap);\nint _safe_snprintf(char *to, size_t n, const char *fmt, ...);\n\n#define dsafe_snprintf(_s, _n, ...)       \\\n    _safe_snprintf((char *)(_s), (size_t)(_n), __VA_ARGS__)\n\n#define dsafe_vsnprintf(_s, _n, _f, _a)   \\\n    _safe_vsnprintf((char *)(_s), (size_t)(_n), _f, _a)\n\n/*\n * snprintf(s, n, ...) will write at most n - 1 of the characters printed into\n * the output string; the nth character then gets the terminating `\\0'; if\n * the return value is greater than or equal to the n argument, the string\n * was too short and some of the printed characters were discarded; the output\n * is always null-terminated.\n *\n * Note that, the return value of snprintf() is always the number of characters\n * that would be printed into the output string, assuming n were limited not\n * including the trailing `\\0' used to end output.\n *\n * scnprintf(s, n, ...) is same as snprintf() except, it returns the number\n * of characters printed into the output string not including the trailing '\\0'\n */\n#define dsnprintf(_s, _n, ...)        \\\n    snprintf((char *)(_s), (size_t)(_n), __VA_ARGS__)\n\n#define dscnprintf(_s, _n, ...)       \\\n    _dscnprintf((char *)(_s), (size_t)(_n), __VA_ARGS__)\n\n#define dvsnprintf(_s, _n, _f, _a)    \\\n    vsnprintf((char *)(_s), (size_t)(_n), _f, _a)\n\n#define dvscnprintf(_s, _n, _f, _a)   \\\n    _dvscnprintf((char *)(_s), (size_t)(_n), _f, _a)\n\n#define dstrftime(_s, _n, fmt, tm)        \\\n    (int)strftime((char *)(_s), (size_t)(_n), fmt, tm)\n\nint string_match_len(const char *pattern, int patternLen, const char *string, int stringLen, int nocase);\nint string_match(const char *pattern, const char *string, int nocase);\n\n\n/* Atomic API */\n/* GCC version >= 4.7 */\n#if defined(__ATOMIC_RELAXED)\n#define atomic_add(_value, _n) __atomic_add_fetch(&_value, (_n), __ATOMIC_RELAXED)\n#define atomic_sub(_value, _n) __atomic_sub_fetch(&_value, (_n), __ATOMIC_RELAXED)\n#define atomic_set(_value, _n) __atomic_store_n(&_value, (_n), __ATOMIC_RELAXED)\n#define atomic_get(_value, _v) do {                 \\\n    __atomic_load(&_value, _v, __ATOMIC_RELAXED);   \\\n} while(0)\n\n#define ATOMIC_LOCK_TYPE \"__ATOMIC_RELAXED\"\n/* GCC version >= 4.1 */\n#elif defined(HAVE_ATOMIC)\n#define atomic_add(_value, _n) __sync_add_and_fetch(&_value, (_n))\n#define atomic_sub(_value, _n) __sync_sub_and_fetch(&_value, (_n))\n#define atomic_set(_value, _n) __sync_lock_test_and_set(&_value, (_n))\n#define atomic_get(_value, _v) do {                 \\\n    (*_v) = __sync_add_and_fetch(&_value, 0);       \\\n} while(0)\n\n#define ATOMIC_LOCK_TYPE \"HAVE_ATOMIC\"\n#else\nextern pthread_mutex_t atomic_locker;\n\n#define atomic_add(_value, _n) do {         \\\n    pthread_mutex_lock(&atomic_locker);     \\\n    _value += (_n);                         \\\n    pthread_mutex_unlock(&atomic_locker);   \\\n} while(0)\n\n#define atomic_sub(_value, _n) do {         \\\n    pthread_mutex_lock(&atomic_locker);     \\\n    _value -= (_n);                         \\\n    pthread_mutex_unlock(&atomic_locker);   \\\n} while(0)\n\n#define atomic_set(_value, _n) do {         \\\n    pthread_mutex_lock(&atomic_locker);     \\\n    _value = (_n);                          \\\n    pthread_mutex_unlock(&atomic_locker);   \\\n} while(0)\n\n#define atomic_get(_value, _v) do {         \\\n    pthread_mutex_lock(&atomic_locker);     \\\n    (*_v) = _value;                         \\\n    pthread_mutex_unlock(&atomic_locker);   \\\n} while(0)\n\n#define ATOMIC_LOCK_TYPE \"pthread_mutex_lock\"\n#endif\n\n#endif\n"
  },
  {
    "path": "m4/.gitignore",
    "content": "# Ignore everything\n*\n\n# Except me\n!.gitignore\n"
  },
  {
    "path": "notes/c-styleguide.txt",
    "content": "- No literal tabs. Expand tabs to 4 spaces.\n- Indentation is 4 spaces.\n- No more than 3 levels of indentation, otherwise you should think about\n  refactoring your code.\n- Use one statement per line.\n- Make sure that your editor does not leave space at the end of the line.\n- snake_case for variable, function and file names.\n- Use your own judgment when naming variables and functions. Be as Spartan\n  as possible. Eg: Using name like this_variable_is_a_temporary_counter\n  will usually be frowned upon.\n- Don’t use local variables or parameters that shadow global identifiers.\n  GCC’s ‘-Wshadow’ option can help you to detect this problem.\n\n- Avoid using int, char, short, long. Instead use int8_t uint8_t, int16_t,\n  uint16_t, int32_t, uint32_t, int64_t, uint64_t, which are available in\n  <stdint.h>. However, when interfacing with system calls and libraries\n  you cannot get away from using int and char.\n- Use bool for boolean variables. You have to include <stdbool.h>\n- Avoid using a bool as type for struct member names. Instead use unsigned\n  1-bit bit field. Eg:\n  struct foo {\n      unsigned is_bar:1;\n  };\n- Always use size_t type when dealing with sizes of objects or memory ranges.\n- Your code should be 64-bit and 32-bit friendly. Bear in mind problems\n  of printing, comparisons, and structure alignment. You have to include\n  <intyptes.h> to get generic format specifier macros for printing.\n\n- 80 column line limit.\n- If you have to wrap a long statement (> 80 column), put the operator at the\n  end of the line and indent the next line at the same column as the arguments\n  in the previous column. Eg:\n    while (cnt < 20 && this_variable_name_is_too_long &&\n           ep != NULL) {\n        z = a + really + long + statement + that + needs + three + lines +\n            gets + indented + on + the + same + column + as + the +\n            previous + column\n    }\n\n    and:\n\n    int a = function(param_a, param_b, param_c, param_d, param_e, param_f,\n                     param_g, param_h, param_i, param_j, param_k, param_l);\n\n- Always use braces for all conditional blocks (if, switch, for, while, do).\n  This holds good even for single statement conditional blocks. Eg:\n    if (cond) {\n        stmt;\n    }\n- Placement of braces for non-function statement blocks - put opening brace\n  last on the line and closing brace first. Eg:\n    if (x is true) {\n        we do y\n    }\n- Placement of brace for functions - put the opening brace at the beginning\n  of the next line and closing brace first. This is useful because several\n  tools look for opening brace in column one to find beginning of C\n  functions. Eg:\nint\nfunction(int x)\n{\n    body of the function\n}\n\n- Closing brace is empty on a line of its own, except in cases where it is\n  followed by a continuation of the same statement, i.e. a \"while\" in a\n  do-statement or an \"else\" in an if-statement, like this:\n    do {\n        body of do-loop\n    } while (condition);\n\n    and,\n\n    if (x == y) {\n        ..\n    } else if (x > y) {\n        ...\n    } else {\n        ....\n    }\n\n- Column align switch keyword and the corresponding case/default keyword. Eg:\n    switch (alphabet) {\n    case 'a':\n    case 'b':\n        printf(\"I am a or b\\n\");\n        break;\n\n    default:\n        break;\n    }\n\n- Forever loops are done with for, and not while. Eg:\n    for (;;) {\n        stmt;\n    }\n\n- Don't use a space after a function name.\n- Do not needlessly surround the return expression with parentheses.\n- Use space after keywords. Exceptions are sizeof, typeof, alignof and\n  __attribute__, which look like functions.\n- Do not add spaces around (inside) parenthesized expressions.\n    s = sizeof( sizeof(*p)) ); /* bad example */\n    s = sizeof(sizeof(*p));    /* good example */\n- Casts should not be followed by space. Eg:\n  int q = *(int *)&p\n- There is no need to type cast when assigning a void pointer to a non-void\n  pointer, or vice versa.\n- Avoid using goto statements. However there are some exceptions to this rule\n  when a single goto label within a function and one or more goto statements\n  come in handy when a function exits from multiple locations and some common\n  work such as cleanup has to be done. Eg:\nint\nfun(void)\n{\n    int result = 0;\n    char *buffer;\n\n    buffer = malloc(1024);\n    if (buffer == NULL) {\n        return -1;\n    }\n\n    if (condition1) {\n        while (loop1) {\n            ...\n        }\n        result = 1;\n        goto out;\n    }\n\n    ...\nout:\n    free(buffer);\n    return result;\n}\n- When declaring pointer data, use '*' adjacent to the data name and not\n  adjacent to the type name. Eg:\n    int\n    function(int *p)\n    {\n        char *p;\n        <body of the function>\n    }\n- Use one space around (on each side of) most binary and ternary operators,\n  such as any of these:\n    =  +  -  <  >  *  /  %  |  &  ^  <=  >=  ==  !=  ?  :\n  but no space after unary operators:\n    &  *  +  -  ~  !  sizeof typeof alignof  __attribute__  defined\n  no space before the postfix increment & decrement unary operators:\n    ++  --\n  and no space around the '.' and \"->\" structure member operators.\n\n- 0 and NULL; use 0 for integers, 0.0 for doubles, NULL for pointers, and\n  '\\0' for chars.\n- Test pointers against NULL. E.g, use:\n    if (p == NULL)\n\n    not:\n\n    !(p)\n\n- Do not use ! for tests unless it is a boolean. E.g. use:\n    if (*p == '\\0')\n\n    not:\n\n    if (!*p)\n\n- Don't use assignments inside if or while-conditions. E.g, use:\n\n    struct foo *foo;\n    foo = malloc(sizeof(*foo));\n    if (foo == NULL) {\n        return -1\n    }\n\n    not:\n\n    struct foo *foo;\n    if ((foo = malloc(sizeof(*foo))) == NULL) {\n        return -1;\n    }\n\n- Don't ever use typedef for structure types. Typedefs are problematic\n  because they do not properly hide their underlying type; for example you\n  need to know if the typedef is the structure itself or a pointer to the\n  structure. In addition they must be declared exactly once, whereas an\n  incomplete structure type can be mentioned as many times as necessary.\n  Typedefs are difficult to use in stand-alone header files: the header\n  that defines the typedef must be included before the header that uses it,\n  or by the header that uses it (which causes namespace pollution), or\n  there must be a back-door mechanism for obtaining the typedef.\n- The only exception for using a typedef is when you are defining a type\n  for a function pointer or a type for an enum. Eg:\n\n  typedef void (*foo_handler_t)(int, void *);\n\n  or:\n\n  typedef enum types {\n      TYPE_1,\n      TYPE_2\n  } types_t;\n\n- Use just one variable declaration per line when variables are part of a\n  struct. This leaves you room for a small comment on each item, explaining\n  its use. Declarations should also be aligned. Eg, use:\n\n  struct foo {\n    int      *foo_a;   /* comment for foo_a */\n    int      foo_b;    /* comment for foo_b */\n    unsigned foo_c:1;  /* comment for foo_c */\n  };\n\n  and not:\n\n  struct foo {\n    int *foo_a, foo_b;\n    unsigned foo_c:1;\n  };\n\n- For variable declaration outside a struct, either collect all the\n  declarations of the same type on a single line, or use one variable\n  per line if the variables purpose needs to be commented. Eg:\n  char *a, *b, c;\n\n  or:\n\n  char *a, *b;\n  char c;       /* comments for c */\n\n- Avoid magic numbers because no-one has a clue (including the author) of\n  what it means after a month.\n\n- Function definitions should start the name of the function in column\n  one. This is useful because it makes searching for function definitions\n  fairly trivial. Eg:\nstatic char *\nconcat(char *s1, char *s2)\n{\n  body of the function\n}\n\n- Function and variables local to a file should be static.\n- Separate two successive functions with one blank line.\n- Include parameter names with their datypes in function declaration. Eg:\nvoid function(int param);\n\n- Functions should be short and sweet, and do just one thing. They should\n  fit on one or two screenfuls of text (80 x 24 screen size), and do one\n  thing and do that well.\n  The maximum length of a function is inversely proportional to the\n  complexity and indentation level of that function. So, if you have a\n  conceptually simple function that is just one long (but simple)\n  case-statement, where you have to do lots of small things for a lot of\n  different cases, it's OK to have a longer function.\n  Another measure of the function is the number of local variables. They\n  shouldn't exceed 5-10, or you're doing something wrong. Re-think the\n  function, and split it into smaller pieces. A human brain can\n  generally easily keep track of about 7 different things, anything more\n  and it gets confused. You know you're brilliant, but maybe you'd like\n  to understand what you did 2 weeks from now.\n- Use const for function parameters passed by reference, if the passed\n  pointer has no side effect.\n\n- C style comments only. Don't use // for single line comments. Instead\n  use /* ... */ style.\n- For multi-line comments use the following style\n    /*\n     * This is the preferred style for multi-line\n     * comments in the Linux kernel source code.\n     * Please use it consistently.\n     *\n     * Description:  A column of asterisks on the left side,\n     * with beginning and ending almost-blank lines.\n     */\n\n- To comment out block of code spanning several lines use preprocessor\n  directive \"#ifdef 0 ... #endif\"\n\n- Please write a brief comment at the start of each source file, with the\n  file name and a line or two about the overall purpose of the file.\n\n- All major functions should have comments describing what they do at the\n  head of the function. Avoid putting comments in the function body unless\n  absolutely needed. If possible, add a comment on what sorts of arguments\n  the function gets, and what the possible values of arguments mean and\n  what they are used for and the significance of return value if there is\n  one. It is not necessary to duplicate in words the meaning of the C\n  argument declarations, if a C type is being used in its customary fashion.\n  If there is anything nonstandard about its use (such as an argument of\n  type char * which is really the address of the second character of a\n  string, not the first), or any possible values that would not work the\n  way one would expect (such as, that strings containing newlines are not\n  guaranteed to work), be sure to say so. Eg:\n\n/*\n * Try to acquire a physical address lock while a pmap is locked. If we\n * fail to trylock we unlock and lock the pmap directly and cache the\n * locked pa in *locked. The caller should then restart their loop in case\n * the virtual to physical mapping has changed.\n *\n * Returns 0 on success and -1 on failure.\n */\nint\nvm_page_pa_tryrelock(pmap_t pmap, vm_paddr_t pa, vm_paddr_t *locked)\n{\n    ...\n\n- The comment on a function is much clearer if you use the argument names\n  to speak about the argument values. The variable name itself should be\n  lower case, but write it in upper case when you are speaking about the\n  value rather than the variable itself. Thus, “the inode number NODE_NUM”\n  rather than “an inode”.\n\n- Every struct definition should have an accompanying comment that\n  describes what it is for and how it should be used.\n\n- Finally, while comments are absolutely important to keep the code readable,\n  remember that the best code is self-documenting. Giving sensible names to\n  types and variables is much better than using obscure names that you must\n  then explain through comments.\n\n- Recommend using UPPERCASE for macro names. However, sometimes using\n  lowercase for macro names makes sense when macros masquerade as well-known\n  function calls. Eg, it makes sense to write the wrapper for the\n  standard free() function in lowercase to keep the readability\n  consistent:\n\n#define my_free(_p) do {    \\\n    free(_p);               \\\n    (_p) = NULL;            \\\n} while (0)\n\n- Use enums when defining more than one related constants. All enumeration\n  values are in UPPERCASE.\n- Avoid macros as much as possible and use inline functions, enums and const\n  variables wherever you can.\n- For macros encapsulating compound statements, right justify the backslashes\n  and enclose it in do { ... } while (0)\n- For parameterized macros, all the parameters used in the macro body must\n  be surrounded by parentheses. Eg:\n  #define ADD_1(_x) ((_x) + 1)\n\n- Use sizeof(varname) instead of sizeof(type) whenever possible. Eg:\n  char *p;\n  p = malloc(sizeof(*p));   /* good example */\n  p = malloc(sizeof(char)); /* bad example */\n\n- All variables should be declared at the beginning of a scope block {..}.\n  It is even preferred to declare all variables at the beginning of the\n  function so that all the local variable declarations is in one place and\n  we can see the comprehensive list in one glance.\n- Global structs should be declared at the top of the file in which they\n  are used, or in separate header files if they are used in multiple\n  source files.\n- Declarations of external functions and functions to appear later in the\n  source file should all go in one place near the beginning of the file,\n  somewhere before the first function definition in the file or else\n  should go in a header file.\n- Use of extern should be considered as evil, if it is used in header files\n  to reference global variables.\n- Don’t put extern declarations inside functions.\n\n- Usually every *.c file should have an associated *.h file. There are some\n  exceptions to this rule, such as unit tests and small *.c files containing\n  just the main() function.\n- Every header file in the source code must have preprocessor conditional\n  to prevent the header file from being scanned multiple times and avoiding\n  mutual dependency cycles. Alternatively you can use #pragma once directive,\n  as it avoids name clashes and increases the compile speed. Eg, for a\n  header file named foo.h, the entire contents of the header file must be\n  between the guard macros as follows:\n\n#ifndef _FOO_H_\n#define _FOO_H_\n...\n#endif /* _FOO_H_ */\n\nOr,\n\n#pragma once\n#ifndef _FOO_H_\n#define _FOO_H_\n...\n#endif /* _FOO_H_ */\n\n- Don't use #include when a forward declaration would suffice.\n- Functions defined in header files should be static inline.\n\n- Don’t make the program ugly just to placate GCC when extra warnings options\n  such as ‘-Wconversion’ or ‘-Wundef’ are used. These options can help in\n  finding bugs, but they can also generate so many false alarms that that\n  it hurts readability to silence them with unnecessary casts, wrappers, and\n  other complications.\n\n- Conditional compilation: when supporting configuration options already\n  known when building your program we prefer using if (... ) over conditional\n  compilation, as in the former case the compiler is able to perform more\n  extensive checking of all possible code paths. Eg, use:\n\n  if (HAS_FOO)\n    ...\n  else\n    ...\n\ninstead of:\n\n  #ifdef HAS_FOO\n    ...\n  #else\n    ...\n  #endif\n\n  A modern compiler such as GCC will generate exactly the same code in both\n  cases and of course, the former method assumes that HAS_FOO is defined as\n  either 0 or 1.\n\n- Finally, rules are rules. Sometimes they are sensible and sometimes not\n  and regardless of your preference, we would like you to follow them.\n  A project is easier to follow if all project contributors follow the style\n  rules so that they can all read and understand everyone's code easily. But\n  remember, like all good rules, they are exceptions where it makes sense not\n  to be too rigid on the grounds of common sense and consistency!\n"
  },
  {
    "path": "notes/debug.txt",
    "content": "- strace\n  strace -o strace.txt -ttT -s 1024 -p `pgrep nutcracker`\n\n- libyaml (yaml-0.1.4)\n\n  - yaml tokens:\n\n  0  YAML_NO_TOKEN,\n  1  YAML_STREAM_START_TOKEN,\n  2  YAML_STREAM_END_TOKEN,\n  3  YAML_VERSION_DIRECTIVE_TOKEN,\n  4  YAML_TAG_DIRECTIVE_TOKEN,\n  5  YAML_DOCUMENT_START_TOKEN,\n  6  YAML_DOCUMENT_END_TOKEN,\n  7  YAML_BLOCK_SEQUENCE_START_TOKEN,\n  8  YAML_BLOCK_MAPPING_START_TOKEN,\n  9  YAML_BLOCK_END_TOKEN,\n  10 YAML_FLOW_SEQUENCE_START_TOKEN,\n  11 YAML_FLOW_SEQUENCE_END_TOKEN,\n  12 YAML_FLOW_MAPPING_START_TOKEN,\n  13 YAML_FLOW_MAPPING_END_TOKEN,\n  14 YAML_BLOCK_ENTRY_TOKEN,\n  15 YAML_FLOW_ENTRY_TOKEN,\n  16 YAML_KEY_TOKEN,\n  17 YAML_VALUE_TOKEN,\n  18 YAML_ALIAS_TOKEN,\n  19 YAML_ANCHOR_TOKEN,\n  20 YAML_TAG_TOKEN,\n  21 YAML_SCALAR_TOKEN\n\n  - yaml events\n\n  0  YAML_NO_EVENT,\n  1  YAML_STREAM_START_EVENT,\n  2  YAML_STREAM_END_EVENT,\n  3  YAML_DOCUMENT_START_EVENT,\n  4  YAML_DOCUMENT_END_EVENT,\n  5  YAML_ALIAS_EVENT,\n  6  YAML_SCALAR_EVENT,\n  7  YAML_SEQUENCE_START_EVENT,\n  8  YAML_SEQUENCE_END_EVENT,\n  9  YAML_MAPPING_START_EVENT,\n  10 YAML_MAPPING_END_EVENT\n\n- sys/queue.h\n\n  queue.h is a generic linked list library adapted from BSD. It has three\n  macro knobs that are useful for debugging:\n\n  - QUEUE_MACRO_SCRUB nullifies links (next and prev pointers) of deleted\n    elements and catches cases where we are attempting to do operations\n    on an element that has already been unlinked.\n  - QUEUE_MACRO_TRACE keeps track of __FILE__ and __LINE__ of last two\n    updates to the list data structure.\n  - QUEUE_MACRO_ASSERT verifies the sanity of list data structure on every\n    operation.\n\n- valgrind\n  valgrind --tool=memcheck --leak-check=yes <program>\n\n- Core dump\n  ulimit -c unlimited\n\n- Generate ENOMEM to test \"Out of Memory\"\n  ulimit -m <size>    # limit maximum memory size\n  ulimit -v <size>    # limit virtual memory\n\n- get nutcracker stats\n  printf \"\" | socat  - TCP:localhost:22222 | tee stats.txt\n  printf \"\" | nc localhost 22222 | python -mjson.tool\n\n- Signalling and Logging\n  SIGTTIN - To up the log level\n  SIGTTOU - To down the log level\n  SIGHUP  - To reopen log file\n\n- Error codes:\n  http://www.cs.utah.edu/dept/old/texinfo/glibc-manual-0.02/library_2.html\n  /usr/include/asm-generic/errno-base.h\n  /usr/include/asm-generic/errno.h\n\n- epoll (linux)\n\n  union epoll_data {\n      void *ptr;\n      int fd;\n      uint32_t u32;\n      uint64_t u64;\n  };\n\n  struct epoll_event {\n      uint32_t          events;  /* epoll events */\n      struct epoll_data data;    /* user data variable */\n  };\n\n  /* events */\n  EPOLLIN       = 0x001,\n  EPOLLPRI      = 0x002,\n  EPOLLOUT      = 0x004,\n  EPOLLERR      = 0x008,\n  EPOLLHUP      = 0x010,\n  EPOLLRDNORM   = 0x040,\n  EPOLLRDBAND   = 0x080,\n  EPOLLWRNORM   = 0x100,\n  EPOLLWRBAND   = 0x200,\n  EPOLLMSG      = 0x400,\n  EPOLLRDHUP    = 0x2000,\n  EPOLLONESHOT  = (1 << 30),\n  EPOLLET       = (1 << 31)\n\n  /* opcodes */\n  EPOLL_CTL_ADD = 1 /* add a file decriptor to the interface */\n  EPOLL_CTL_DEL = 2 /* remove a file decriptor from the interface */\n  EPOLL_CTL_MOD = 3 /* change file decriptor epoll_event structure */\n\n- kqueue (bsd)\n\n  struct kevent {\n      uintptr_t   ident;    /* identifier for this event */\n      int16_t     filter;   /* filter for event */\n      uint16_t    flags;    /* general flags */\n      uint32_t    fflags;   /* filter-specific flags */\n      intptr_t    data;     /* filter-specific data */\n      void        *udata;   /* opaque user data identifier */\n  };\n\n  /* flags / events */\n  EV_ADD        = 0x0001    /* action - add event to kq (implies enable) */\n  EV_DELETE     = 0x0002    /* action - delete event from kq */\n  EV_ENABLE     = 0x0004    /* action - enable event */\n  EV_DISABLE    = 0x0008    /* action - disable event (not reported) */\n  EV_RECEIPT    = 0x0040    /* action - force EV_ERROR on success, data == 0 */\n\n  EV_ONESHOT    = 0x0010    /* flags - only report one occurrence */\n  EV_CLEAR      = 0x0020    /* flags - clear event state after reporting */\n  EV_DISPATCH   = 0x0080    /* flags - disable event after reporting */\n  EV_SYSFLAGS   = 0xF000    /* flags - reserved by system */\n  EV_FLAG0      = 0x1000    /* flags - filter-specific flag */\n  EV_FLAG1      = 0x2000    /* flags - filter-specific flag */\n\n  EV_EOF        = 0x8000    /* returned values - EOF detected */\n  EV_ERROR      = 0x4000    /* returned values - error, data contains errno */\n\n  /* filters */\n  EVFILT_READ       (-1)    /* readable */\n  EVFILT_WRITE      (-2)    /* writable */\n  EVFILT_AIO        (-3)    /* attached to aio requests */\n  EVFILT_VNODE      (-4)    /* attached to vnodes */\n  EVFILT_PROC       (-5)    /* attached to struct proc */\n  EVFILT_SIGNAL     (-6)    /* attached to struct proc */\n  EVFILT_TIMER      (-7)    /* timers */\n  EVFILT_MACHPORT   (-8)    /* mach portsets */\n  EVFILT_FS         (-9)    /* filesystem events */\n  EVFILT_USER       (-10)   /* user events */\n  EVFILT_VM         (-12)   /* virtual memory events */\n\n  EV_CLEAR behaves like EPOLLET because it resets the event after it is\n  returned; without this flag, the event would be repeatedly returned.\n\n- poll (unix)\n\n  POLLIN       0x001    /* there is data to read */\n  POLLPRI      0x002    /* there is urgent data to read */\n  POLLOUT      0x004    /* writing now will not block */\n\n  POLLRDNORM   0x040    /* normal data may be read */\n  POLLRDBAND   0x080    /* priority data may be read */\n  POLLWRNORM   0x100    /* writing now will not block */\n  POLLWRBAND   0x200    /* priority data may be written */\n\n  POLLMSG      0x400\n  POLLREMOVE  0x1000\n  POLLRDHUP   0x2000\n\n  POLLERR\t   0x008    /* error condition */\n  POLLHUP      0x010    /* hung up */\n  POLLNVAL     0x020    /* invalid polling request */\n\n- event ports (solaris)\n\n  typedef struct port_event {\n      int         portev_events;  /* event data is source specific */\n      ushort_t    portev_source;  /* event source */\n      ushort_t    portev_pad;     /* port internal use */\n      uintptr_t   portev_object;  /* source specific object */\n      void        *portev_user;   /* user cookie */\n  } port_event_t;\n\n  /* port sources */\n  PORT_SOURCE_AIO     1\n  PORT_SOURCE_TIMER   2\n  PORT_SOURCE_USER    3\n  PORT_SOURCE_FD      4\n  PORT_SOURCE_ALERT   5\n  PORT_SOURCE_MQ      6\n  PORT_SOURCE_FILE    7\n"
  },
  {
    "path": "notes/socket.txt",
    "content": "- int listen(int sockfd, int backlog);\n\n  Linux: The  backlog  argument defines the maximum length to which the\n  queue of pending connections for sockfd may grow.  If a connection\n  request arrives when the queue is full, the client may receive an error\n  with an indication of ECONNREFUSED or, if the underlying protocol\n  supports retransmission, the request may be ignored so that a later\n  reattempt at connection succeeds.\n\n  backlog specifies the queue length for completely established sockets\n  waiting to be accepted, instead of the number of incomplete connection\n  requests. The maximum length of the queue for incomplete sockets can\n  be set using /proc/sys/net/ipv4/tcp_max_syn_backlog.\n\n  If the backlog argument is greater than the value in /proc/sys/net/core/somaxconn,\n  then it is silently truncated to that value; the default value in this\n  file is 128.  In kernels before 2.4.25, this limit was a hard coded value,\n  SOMAXCONN, with the value 128.\n\n  BSD: The backlog argument defines the maximum length the queue of pending\n  connections may grow to.  The real maximum queue length will be 1.5 times\n  more than the value specified in the backlog argument.  A subsequent\n  listen() system call on the listening socket allows the caller to change\n  the maximum queue length using a new backlog argument.  If a connection\n  request arrives with the queue full the client may receive an error with\n  an indication of ECONNREFUSED, or, in the case of TCP, the connection\n  will be silently dropped.\n\n  The listen() system call appeared in 4.2BSD.  The ability to configure\n  the maximum backlog at run-time, and to use a negative backlog to request\n  the maximum allowable value, was introduced in FreeBSD 2.2.\n\n- SO_LINGER (linger) socket option\n\n  This option specifies what should happen when the socket of a type that\n  promises reliable delivery still has untransmitted messages when it is\n  closed\n\n  struct linger {\n      int l_onoff;  /* nonzero to linger on close  */\n      int l_linger; /* time to linger (in secs)  */\n  };\n\n  l_onoff = 0 (default), then l_linger value is ignored and close returns\n  immediately. But if there is any data still remaining in the socket send\n  buffer, the system will try to deliver the data to the peer\n\n  l_onoff = nonzero, then close blocks until data is transmitted or the\n  l_linger timeout period expires\n  a) l_linger = 0, TCP aborts connection, discards any data still remaining\n     in the socket send buffer and sends RST to peer. This avoids the\n     TCP's TIME_WAIT state\n  b) l_linger = nonzero, then kernel will linger when socket is closed. If\n     there is any pending data in the socket send buffer, the kernel waits\n     until all the data is sent and acknowledged by peer TCP, or the\n     linger time expires\n\n  If a socket is set as nonblocking, it will not wait for close to complete\n  even if linger time is nonzero\n\n- TIME_WAIT state\n\n  The end that performs active close i.e. the end that sends the first FIN\n  goes into TIME_WAIT state. After a FIN packet is sent to the peer and\n  after that peers FIN/ACK arrvies and is ACKed, we go into a TIME_WAIT\n  state. The duration that the end point remains in this state is 2 x MSL\n  (maximum segment lifetime). The reason that the duration of the TIME_WAIT\n  state is 2 x MSL is because the maximum amount of time a packet can wander\n  around a network is assumed to be MSL seconds. The factor of 2 is for the\n  round-trip. The recommended value for MSL is 120 seconds, but Berkeley\n  derived implementations normally use 30 seconds instead. This means a\n  TIME_WAIT delay is between 1 and 4 minutes.\n\n  For Linux, the TIME_WAIT state duration is 1 minute (net/tcp.h):\n  #define TCP_TIMEWAIT_LEN (60*HZ) /* how long to wait to destroy TIME-WAIT\n                                    * state, about 60 seconds */\n\n  TIME_WAIT state on client, combined with limited number of ephermeral ports\n  available for TCP connections severely limits the rate at which new\n  connections to the server can be created. On Linux, by default ephemeral\n  ports are in the range of 32768 to 61000:\n\n  $ cat /proc/sys/net/ipv4/ip_local_port_range\n  32768 61000\n\n  So with a TIME_WAIT state duration of 1 minute, the maximum sustained rate\n  for any client is ~470 new connections per second\n\n- TCP keepalive\n\n  TCP keepalive packet (TCP packet with no data and the ACK flag turned on)\n  is used to assert that connection is still up and running. This is useful\n  because if the remote peer goes away without closing their connection, the\n  keepalive probe will detect this and notice that the connection is broken\n  even if there is no traffic on it.\n\n  Imagine, the following scenario: You have a valid TCP connection established\n  between two endpoints A and B. B terminates abnormally (think kernel panic\n  or unplugging of network cable) without sending anything over the network\n  to notify A that connection is broken. A, from its side, is ready to\n  receive data, and has no idea that B has gone away. Now B comes back up\n  again, and while A knows about a connection with B and still thinks that it\n  active, B has no such idea. A tries to send data to B over a dead\n  connection, and B replies with an RST packet, causing A to finally close\n  the connection. So, without a keepalive probe A would never close the\n  connection if it never sent data over it.\n\n- There are four socket functions that pass a socket address structure from\n  the process to the kernel - bind, connect, sendmsg and sendto. These\n  function are also responsible for passing the length of the sockaddr that\n  they are passing (socklen_t).\n  There are five socket functions that pass a socket from the kernel to the\n  process - accept, recvfrom, recvmsg, getpeername, getsockname. The kernel\n  is also responsible for returning the length of the sockaddr struct that\n  it returns back to the userspace\n\n  Different sockaddr structs:\n  1. sockaddr_in\n  2. sockaddr_in6\n  3. sockaddr_un\n\n  Special types of in_addr_t\n  /* Address to accept any incoming messages */\n  #define INADDR_ANY ((in_addr_t) 0x00000000)\n\n  /* Address to send to all hosts */\n  #define INADDR_BROADCAST ((in_addr_t) 0xffffffff)\n\n  /* Address indicating an error return */\n  #define INADDR_NONE ((in_addr_t) 0xffffffff)\n\n"
  },
  {
    "path": "scripts/.gitignore",
    "content": "*.pyc\n*.out\n*.log\n"
  },
  {
    "path": "src/Makefile.am",
    "content": "MAINTAINERCLEANFILES = Makefile.in\n\nAM_CPPFLAGS =\nif !OS_SOLARIS\nAM_CPPFLAGS += -D_GNU_SOURCE\nendif\nAM_CPPFLAGS += -I $(top_srcdir)/dep/dhashkit\nAM_CPPFLAGS += -I $(top_srcdir)/dep/ae\nAM_CPPFLAGS += -I $(top_srcdir)/dep/util\nAM_CPPFLAGS += -I $(top_srcdir)/dep/jemalloc-4.2.0/include\nAM_CPPFLAGS += -I $(top_srcdir)/dep/dmalloc\nAM_CPPFLAGS += -I $(top_srcdir)/dep/sds\nAM_CPPFLAGS += -I $(top_srcdir)/dep/darray\nAM_CPPFLAGS += -I $(top_srcdir)/dep/dlist\n\nAM_CFLAGS = \nAM_CFLAGS += -fno-strict-aliasing\nAM_CFLAGS += -Wall -Wshadow\nAM_CFLAGS += -Wpointer-arith\nAM_CFLAGS += -Winline\nAM_CFLAGS += -Wunused-function -Wunused-variable -Wunused-value\nAM_CFLAGS += -Wno-unused-parameter -Wno-unused-value\nAM_CFLAGS += -Wconversion -Wsign-compare\nAM_CFLAGS += -Wstrict-prototypes -Wmissing-prototypes -Wredundant-decls -Wmissing-declarations\n\nAM_LDFLAGS =\nAM_LDFLAGS += -lm -lpthread -rdynamic\nif !OS_DARWIN\nAM_LDFLAGS += -lrt\nendif\nif OS_SOLARIS\nAM_LDFLAGS += -lnsl -lsocket\nendif\nif OS_FREEBSD\nAM_LDFLAGS += -lexecinfo\nendif\n\nsbin_PROGRAMS = vire\n\nvire_SOURCES =                          \\\n    vr_aof.c vr_aof.h                   \\\n    vr_block.c vr_block.h               \\\n    vr_client.c vr_client.h             \\\n    vr_command.c vr_command.h           \\\n    vr_conf.c vr_conf.h                 \\\n    vr_connection.c vr_connection.h     \\\n    vr_core.c vr_core.h                 \\\n    vr_db.c vr_db.h                     \\\n    vr_dict.c vr_dict.h                 \\\n    vr_eventloop.c vr_eventloop.h       \\\n    vr_intset.c vr_intset.h             \\\n    vr_listen.c vr_listen.h             \\\n    vr_lzf.h vr_lzfP.h                  \\\n    vr_lzf_c.c vr_lzf_d.c               \\\n    vr_master.c vr_master.h             \\\n    vr_multi.c vr_multi.h               \\\n    vr_notify.c vr_notify.h             \\\n    vr_object.c vr_object.h             \\\n    vr_pubsub.c vr_pubsub.h             \\\n    vr_quicklist.c vr_quicklist.h       \\\n    vr_rbtree.c vr_rbtree.h             \\\n    vr_rdb.c vr_rdb.h                   \\\n    vr_replication.c vr_replication.h   \\\n    vr_scripting.c vr_scripting.h       \\\n    vr_server.c vr_server.h             \\\n    vr_signal.c vr_signal.h             \\\n    vr_slowlog.c vr_slowlog.h           \\\n    vr_specialconfig.h                  \\\n    vr_stats.c vr_stats.h               \\\n    vr_thread.c vr_thread.h             \\\n    vr_t_hash.c vr_t_hash.h             \\\n    vr_t_list.c vr_t_list.h             \\\n    vr_t_set.c vr_t_set.h               \\\n    vr_t_string.c vr_t_string.h         \\\n    vr_t_zset.c vr_t_zset.h             \\\n    vr_util.c vr_util.h                 \\\n    vr_worker.c vr_worker.h             \\\n    vr_backend.c vr_backend.h           \\\n    vr_ziplist.c vr_ziplist.h           \\\n    vr_zipmap.c vr_zipmap.h             \\\n    vr_bitops.c vr_bitops.h             \\\n    vr_hyperloglog.c vr_hyperloglog.h   \\\n    vr.c\n    \nvire_LDADD = $(top_builddir)/dep/util/libdutil.a\nvire_LDADD += $(top_builddir)/dep/ae/libae.a\nvire_LDADD += $(top_builddir)/dep/sds/libsds.a\nvire_LDADD += $(top_builddir)/dep/darray/libdarray.a\nvire_LDADD += $(top_builddir)/dep/dlist/libdlist.a\nvire_LDADD += $(top_builddir)/dep/dhashkit/libdhashkit.a\nvire_LDADD += $(top_builddir)/dep/jemalloc-4.2.0/lib/libjemalloc.a\nvire_LDADD += $(top_builddir)/dep/dmalloc/libdmalloc.a"
  },
  {
    "path": "src/vr.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <unistd.h>\n#include <signal.h>\n#include <getopt.h>\n#include <fcntl.h>\n#include <sys/stat.h>\n#include <sys/utsname.h>\n\n#include <vr_core.h>\n#include <vr_conf.h>\n#include <vr_signal.h>\n\n#define VR_CONF_PATH        \"conf/vire.conf\"\n\n#define VR_LOG_DEFAULT      LOG_NOTICE\n#define VR_LOG_MIN          LOG_EMERG\n#define VR_LOG_MAX          LOG_PVERB\n#define VR_LOG_PATH         NULL\n\n#define VR_PORT             8889\n#define VR_ADDR             \"0.0.0.0\"\n#define VR_INTERVAL         (30 * 1000) /* in msec */\n\n#define VR_PID_FILE         NULL\n\n#define VR_THREAD_NUM_DEFAULT\t(sysconf(_SC_NPROCESSORS_ONLN)>6?6:sysconf(_SC_NPROCESSORS_ONLN))\n\nstatic int show_help;\nstatic int show_version;\nstatic int test_conf;\nstatic int daemonize;\n\nstatic struct option long_options[] = {\n    { \"help\",           no_argument,        NULL,   'h' },\n    { \"version\",        no_argument,        NULL,   'V' },\n    { \"test-conf\",      no_argument,        NULL,   't' },\n    { \"daemonize\",      no_argument,        NULL,   'd' },\n    { \"verbose\",        required_argument,  NULL,   'v' },\n    { \"output\",         required_argument,  NULL,   'o' },\n    { \"conf-file\",      required_argument,  NULL,   'c' },\n    { \"pid-file\",       required_argument,  NULL,   'p' },\n    { \"thread-num\",     required_argument,  NULL,   'T' },\n    { NULL,             0,                  NULL,    0  }\n};\n\nstatic char short_options[] = \"hVtdv:o:c:p:T:\";\n\nstatic rstatus_t\nvr_daemonize(int dump_core)\n{\n    rstatus_t status;\n    pid_t pid, sid;\n    int fd;\n\n    pid = fork();\n    switch (pid) {\n    case -1:\n        log_error(\"fork() failed: %s\", strerror(errno));\n        return VR_ERROR;\n\n    case 0:\n        break;\n\n    default:\n        /* parent terminates */\n        _exit(0);\n    }\n\n    /* 1st child continues and becomes the session leader */\n\n    sid = setsid();\n    if (sid < 0) {\n        log_error(\"setsid() failed: %s\", strerror(errno));\n        return VR_ERROR;\n    }\n\n    if (signal(SIGHUP, SIG_IGN) == SIG_ERR) {\n        log_error(\"signal(SIGHUP, SIG_IGN) failed: %s\", strerror(errno));\n        return VR_ERROR;\n    }\n\n    pid = fork();\n    switch (pid) {\n    case -1:\n        log_error(\"fork() failed: %s\", strerror(errno));\n        return VR_ERROR;\n\n    case 0:\n        break;\n\n    default:\n        /* 1st child terminates */\n        _exit(0);\n    }\n\n    /* 2nd child continues */\n\n    /* change working directory */\n    if (dump_core == 0) {\n        status = chdir(\"/\");\n        if (status < 0) {\n            log_error(\"chdir(\\\"/\\\") failed: %s\", strerror(errno));\n            return VR_ERROR;\n        }\n    }\n\n    /* clear file mode creation mask */\n    umask(0);\n\n    /* redirect stdin, stdout and stderr to \"/dev/null\" */\n\n    fd = open(\"/dev/null\", O_RDWR);\n    if (fd < 0) {\n        log_error(\"open(\\\"/dev/null\\\") failed: %s\", strerror(errno));\n        return VR_ERROR;\n    }\n\n    status = dup2(fd, STDIN_FILENO);\n    if (status < 0) {\n        log_error(\"dup2(%d, STDIN) failed: %s\", fd, strerror(errno));\n        close(fd);\n        return VR_ERROR;\n    }\n\n    status = dup2(fd, STDOUT_FILENO);\n    if (status < 0) {\n        log_error(\"dup2(%d, STDOUT) failed: %s\", fd, strerror(errno));\n        close(fd);\n        return VR_ERROR;\n    }\n\n    status = dup2(fd, STDERR_FILENO);\n    if (status < 0) {\n        log_error(\"dup2(%d, STDERR) failed: %s\", fd, strerror(errno));\n        close(fd);\n        return VR_ERROR;\n    }\n\n    if (fd > STDERR_FILENO) {\n        status = close(fd);\n        if (status < 0) {\n            log_error(\"close(%d) failed: %s\", fd, strerror(errno));\n            return VR_ERROR;\n        }\n    }\n\n    return VR_OK;\n}\n\nstatic void\nvr_print_run(struct instance *nci)\n{\n    int status;\n    struct utsname name;\n\n    status = uname(&name);\n\n    if (nci->log_filename) {\n        char *ascii_logo =\n\"                _._                                                  \\n\"\n\"           _.-``__ ''-._                                             \\n\"\n\"      _.-``    `.  *_.  ''-._           Vire %s %s bit\\n\"\n\"  .-`` .-```.  ```\\-/    _.,_ ''-._                                   \\n\"\n\" (    |      |       .-`    `,    )     Running in %s mode\\n\"\n\" |`-._`-...-` __...-.``-._;'` _.-'|     Port: %d\\n\"\n\" |    `-._   `._    /     _.-'    |     PID: %ld\\n\"\n\"  `-._    `-._  `-./  _.-'    _.-'      OS: %s %s %s\\n\"\n\" |`-._`-._    `-.__.-'    _.-'_.-'|                                  \\n\"\n\" |    `-._`-._        _.-'_.-'    |     https://github.com/vipshop/vire\\n\"\n\"  `-._    `-._`-.__.-'_.-'    _.-'                                   \\n\"\n\" |`-._`-._    `-.__.-'    _.-'_.-'|                                  \\n\"\n\" |    `-._`-._        _.-'_.-'    |                                  \\n\"\n\"  `-._    `-._`-.__.-'_.-'    _.-'                                   \\n\"\n\"      `-._    `-.__.-'    _.-'                                       \\n\"\n\"          `-._        _.-'                                           \\n\"\n\"              `-.__.-'                                               \\n\\n\";\n        char *buf = dalloc(1024*16);\n        snprintf(buf,1024*16,ascii_logo,\n            VR_VERSION_STRING,\n            (sizeof(long) == 8) ? \"64\" : \"32\",\n            \"standalone\", server.port,\n            (long) nci->pid,\n            status < 0 ? \" \":name.sysname,\n            status < 0 ? \" \":name.release,\n            status < 0 ? \" \":name.machine);\n        log_write_len(buf, strlen(buf));\n        dfree(buf);\n    }else {\n        char buf[256];\n        snprintf(buf,256,\"Vire %s, %s bit, %s mode, port %d, pid %ld, built for %s %s %s ready to run.\\n\",\n            VR_VERSION_STRING, (sizeof(long) == 8) ? \"64\" : \"32\",\n            \"standalone\", server.port, (long) nci->pid,\n            status < 0 ? \" \":name.sysname,\n            status < 0 ? \" \":name.release,\n            status < 0 ? \" \":name.machine);\n        log_write_len(buf, strlen(buf));\n    }\n}\n\nstatic void\nvr_print_done(void)\n{\n    loga(\"done, rabbit done\");\n}\n\nstatic void\nvr_show_usage(void)\n{\n    log_stderr(\n        \"Usage: vire [-?hVdt] [-v verbosity level] [-o output file]\" CRLF\n        \"            [-c conf file] [-p pid file]\" CRLF\n        \"            [-T worker threads number]\" CRLF\n        \"\");\n    log_stderr(\n        \"Options:\" CRLF\n        \"  -h, --help             : this help\" CRLF\n        \"  -V, --version          : show version and exit\" CRLF\n        \"  -t, --test-conf        : test configuration for syntax errors and exit\" CRLF\n        \"  -d, --daemonize        : run as a daemon\");\n    log_stderr(\n        \"  -v, --verbose=N        : set logging level (default: %d, min: %d, max: %d)\" CRLF\n        \"  -o, --output=S         : set logging file (default: %s)\" CRLF\n        \"  -c, --conf-file=S      : set configuration file (default: %s)\" CRLF\n        \"  -p, --pid-file=S       : set pid file (default: %s)\" CRLF\n        \"  -T, --thread_num=N     : set the worker threads number (default: %d)\" CRLF\n        \"\",\n        VR_LOG_DEFAULT, VR_LOG_MIN, VR_LOG_MAX,\n        VR_LOG_PATH != NULL ? VR_LOG_PATH : \"stderr\",\n        VR_CONF_PATH,\n        VR_PID_FILE != NULL ? VR_PID_FILE : \"off\",\n        VR_THREAD_NUM_DEFAULT);\n}\n\nstatic rstatus_t\nvr_create_pidfile(struct instance *nci)\n{\n    char pid[VR_UINTMAX_MAXLEN];\n    int fd, pid_len;\n    ssize_t n;\n\n    fd = open(nci->pid_filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);\n    if (fd < 0) {\n        log_error(\"opening pid file '%s' failed: %s\", nci->pid_filename,\n                  strerror(errno));\n        return VR_ERROR;\n    }\n    nci->pidfile = 1;\n\n    pid_len = dsnprintf(pid, VR_UINTMAX_MAXLEN, \"%d\", nci->pid);\n\n    n = vr_write(fd, pid, pid_len);\n    if (n < 0) {\n        log_error(\"write to pid file '%s' failed: %s\", nci->pid_filename,\n                  strerror(errno));\n        return VR_ERROR;\n    }\n\n    close(fd);\n\n    return VR_OK;\n}\n\nstatic void\nvr_remove_pidfile(struct instance *nci)\n{\n    int status;\n\n    status = unlink(nci->pid_filename);\n    if (status < 0) {\n        log_error(\"unlink of pid file '%s' failed, ignored: %s\",\n                  nci->pid_filename, strerror(errno));\n    }\n}\n\nstatic void\nvr_set_default_options(struct instance *nci)\n{\n    int status;\n\n    nci->log_level = VR_LOG_DEFAULT;\n    nci->log_filename = VR_LOG_PATH;\n\n    nci->conf_filename = VR_CONF_PATH;\n\n    status = vr_gethostname(nci->hostname, VR_MAXHOSTNAMELEN);\n    if (status < 0) {\n        log_warn(\"gethostname failed, ignored: %s\", strerror(errno));\n        dsnprintf(nci->hostname, VR_MAXHOSTNAMELEN, \"unknown\");\n    }\n    nci->hostname[VR_MAXHOSTNAMELEN - 1] = '\\0';\n\n    nci->pid = (pid_t)-1;\n    nci->pid_filename = NULL;\n    nci->pidfile = 0;\n\n    nci->thread_num = (int)VR_THREAD_NUM_DEFAULT;\n}\n\nstatic rstatus_t\nvr_get_options(int argc, char **argv, struct instance *nci)\n{\n    int c, value;\n\n    opterr = 0;\n\n    for (;;) {\n        c = getopt_long(argc, argv, short_options, long_options, NULL);\n        if (c == -1) {\n            /* no more options */\n            break;\n        }\n\n        switch (c) {\n        case 'h':\n            show_version = 1;\n            show_help = 1;\n            break;\n\n        case 'V':\n            show_version = 1;\n            break;\n\n        case 't':\n            test_conf = 1;\n            break;\n\n        case 'd':\n            daemonize = 1;\n            break;\n\n        case 'v':\n            value = vr_atoi(optarg, strlen(optarg));\n            if (value < 0) {\n                log_stderr(\"vire: option -v requires a number\");\n                return VR_ERROR;\n            }\n            nci->log_level = value;\n            break;\n\n        case 'o':\n            nci->log_filename = optarg;\n            break;\n\n        case 'c':\n            nci->conf_filename = optarg;\n            break;\n\n        case 'p':\n            nci->pid_filename = optarg;\n            break;\n            \n        case 'T':\n            value = vr_atoi(optarg, strlen(optarg));\n            if (value < 0) {\n                log_stderr(\"vire: option -T requires a number\");\n                return VR_ERROR;\n            }\n\n            nci->thread_num = value;\n            break;\n\n        case '?':\n            switch (optopt) {\n            case 'o':\n            case 'c':\n            case 'p':\n                log_stderr(\"vire: option -%c requires a file name\",\n                           optopt);\n                break;\n\n            case 'v':\n            case 'T':\n                log_stderr(\"vire: option -%c requires a number\", optopt);\n                break;\n\n            default:\n                log_stderr(\"vire: invalid option -- '%c'\", optopt);\n                break;\n            }\n            return VR_ERROR;\n\n        default:\n            log_stderr(\"vire: invalid option -- '%c'\", optopt);\n            return VR_ERROR;\n\n        }\n    }\n\n    return VR_OK;\n}\n\n/*\n * Returns true if configuration file has a valid syntax, otherwise\n * returns false\n */\nstatic bool\nvr_test_conf(struct instance *nci, int test)\n{\n    vr_conf *cf;\n\n    cf = conf_create(nci->conf_filename);\n    if (cf == NULL) {\n        if (test)\n            log_stderr(\"vire: configuration file '%s' syntax is invalid\",\n                nci->conf_filename);\n        return false;\n    }\n\n    conf_destroy(cf);\n\n    if (test)\n        log_stderr(\"vire: configuration file '%s' syntax is ok\",\n            nci->conf_filename);\n    return true;\n}\n\nstatic int\nvr_pre_run(struct instance *nci)\n{\n    int ret;\n\n    ret = log_init(nci->log_level, nci->log_filename);\n    if (ret != VR_OK) {\n        return ret;\n    }\n\n    log_debug(LOG_VERB, \"Vire used logfile: %s\", nci->conf_filename);\n\n    if (!vr_test_conf(nci, false)) {\n        log_error(\"conf file %s is error\", nci->conf_filename);\n        return VR_ERROR;\n    }\n\n    if (daemonize) {\n        ret = vr_daemonize(1);\n        if (ret != VR_OK) {\n            return ret;\n        }\n    }\n\n    nci->pid = getpid();\n\n    ret = signal_init();\n    if (ret != VR_OK) {\n        return ret;\n    }\n\n    if (nci->pid_filename) {\n        ret = vr_create_pidfile(nci);\n        if (ret != VR_OK) {\n            return VR_ERROR;\n        }\n    }\n\n    ret = init_server(nci);\n    if (ret != VR_OK) {\n        return VR_ERROR;\n    }\n\n    vr_print_run(nci);\n\n    return VR_OK;\n}\n\nstatic void\nvr_post_run(struct instance *nci)\n{\n    /* deinit the threads */\n    workers_deinit();\n    backends_deinit();\n    master_deinit();\n    \n    if (nci->pidfile) {\n        vr_remove_pidfile(nci);\n    }\n\n    signal_deinit();\n\n    vr_print_done();\n\n    log_deinit();\n}\n\nstatic void\nvr_run(struct instance *nci)\n{\n    if (nci->thread_num <= 0) {\n        log_error(\"number of work threads must be greater than 0\");\n        return;\n    } else if (nci->thread_num > 64) {\n        log_warn(\"WARNING: Setting a high number of worker threads is not recommended.\"\n            \" Set this value to the number of cores in your machine or less.\");\n    }\n\n    /* run the threads */\n    master_run();\n    workers_run();\n    backends_run();\n\n    /* wait for the threads finish */\n    workers_wait();\n    backends_wait();\n}\n\nint\nmain(int argc, char **argv)\n{\n    rstatus_t status;\n    struct instance nci;\n\n    vr_set_default_options(&nci);\n\n    status = vr_get_options(argc, argv, &nci);\n    if (status != VR_OK) {\n        vr_show_usage();\n        exit(1);\n    }\n\n    if (show_version) {\n        log_stderr(\"This is vire-%s\" CRLF, VR_VERSION_STRING);\n        if (show_help) {\n            vr_show_usage();\n        }\n        exit(0);\n    }\n\n    if (test_conf) {\n        if (!vr_test_conf(&nci, true)) {\n            exit(1);\n        }\n        exit(0);\n    }\n\n    status = vr_pre_run(&nci);\n    if (status != VR_OK) {\n        vr_post_run(&nci);\n        exit(1);\n    }\n\n    server.executable = getAbsolutePath(argv[0]);\n\n    vr_run(&nci);\n\n    vr_post_run(&nci);\n\n    return VR_OK;\n}\n"
  },
  {
    "path": "src/vr_aof.c",
    "content": "#include <vr_core.h>\n\n/* Return the current size of the AOF rewrite buffer. */\nunsigned long aofRewriteBufferSize(void) {\n    dlistNode *ln;\n    dlistIter li;\n    unsigned long size = 0;\n\n    dlistRewind(server.aof_rewrite_buf_blocks,&li);\n    while((ln = dlistNext(&li))) {\n        aofrwblock *block = dlistNodeValue(ln);\n        size += block->used;\n    }\n    return size;\n}\n\n/* Create the sds representation of an PEXPIREAT command, using\n * 'seconds' as time to live and 'cmd' to understand what command\n * we are translating into a PEXPIREAT.\n *\n * This command is used in order to translate EXPIRE and PEXPIRE commands\n * into PEXPIREAT command so that we retain precision in the append only\n * file, and the time is always absolute and not relative. */\nsds catAppendOnlyExpireAtCommand(sds buf, struct redisCommand *cmd, robj *key, robj *seconds) {\n    long long when;\n    robj *argv[3];\n\n    /* Make sure we can use strtoll */\n    seconds = getDecodedObject(seconds);\n    when = strtoll(seconds->ptr,NULL,10);\n    /* Convert argument into milliseconds for EXPIRE, SETEX, EXPIREAT */\n    if (cmd->proc == expireCommand || cmd->proc == setexCommand ||\n        cmd->proc == expireatCommand)\n    {\n        when *= 1000;\n    }\n    /* Convert into absolute time for EXPIRE, PEXPIRE, SETEX, PSETEX */\n    if (cmd->proc == expireCommand || cmd->proc == pexpireCommand ||\n        cmd->proc == setexCommand || cmd->proc == psetexCommand)\n    {\n        when += vr_msec_now();\n    }\n    decrRefCount(seconds);\n\n    argv[0] = createStringObject(\"PEXPIREAT\",9);\n    argv[1] = key;\n    argv[2] = createStringObjectFromLongLong(when);\n    buf = catAppendOnlyGenericCommand(buf, 3, argv);\n    decrRefCount(argv[0]);\n    decrRefCount(argv[2]);\n    return buf;\n}\n\nsds catAppendOnlyGenericCommand(sds dst, int argc, robj **argv) {\n    char buf[32];\n    int len, j;\n    robj *o;\n\n    buf[0] = '*';\n    len = 1+ll2string(buf+1,sizeof(buf)-1,argc);\n    buf[len++] = '\\r';\n    buf[len++] = '\\n';\n    dst = sdscatlen(dst,buf,len);\n\n    for (j = 0; j < argc; j++) {\n        o = getDecodedObject(argv[j]);\n        buf[0] = '$';\n        len = 1+ll2string(buf+1,sizeof(buf)-1,sdslen(o->ptr));\n        buf[len++] = '\\r';\n        buf[len++] = '\\n';\n        dst = sdscatlen(dst,buf,len);\n        dst = sdscatlen(dst,o->ptr,sdslen(o->ptr));\n        dst = sdscatlen(dst,\"\\r\\n\",2);\n        decrRefCount(o);\n    }\n    return dst;\n}\n\n/* Event handler used to send data to the child process doing the AOF\n * rewrite. We send pieces of our AOF differences buffer so that the final\n * write when the child finishes the rewrite will be small. */\nvoid aofChildWriteDiffData(aeEventLoop *el, int fd, void *privdata, int mask) {\n    dlistNode *ln;\n    aofrwblock *block;\n    ssize_t nwritten;\n    UNUSED(el);\n    UNUSED(fd);\n    UNUSED(privdata);\n    UNUSED(mask);\n\n    while(1) {\n        ln = dlistFirst(server.aof_rewrite_buf_blocks);\n        block = ln ? ln->value : NULL;\n        if (server.aof_stop_sending_diff || !block) {\n            aeDeleteFileEvent(server.el,server.aof_pipe_write_data_to_child,\n                              AE_WRITABLE);\n            return;\n        }\n        if (block->used > 0) {\n            nwritten = vr_write(server.aof_pipe_write_data_to_child,\n                             block->buf,block->used);\n            if (nwritten <= 0) return;\n            memmove(block->buf,block->buf+nwritten,block->used-nwritten);\n            block->used -= nwritten;\n        }\n        if (block->used == 0) dlistDelNode(server.aof_rewrite_buf_blocks,ln);\n    }\n}\n\n/* Append data to the AOF rewrite buffer, allocating new blocks if needed. */\nvoid aofRewriteBufferAppend(unsigned char *s, unsigned long len) {\n    dlistNode *ln = dlistLast(server.aof_rewrite_buf_blocks);\n    aofrwblock *block = ln ? ln->value : NULL;\n\n    while(len) {\n        /* If we already got at least an allocated block, try appending\n         * at least some piece into it. */\n        if (block) {\n            unsigned long thislen = (block->free < len) ? block->free : len;\n            if (thislen) {  /* The current block is not already full. */\n                memcpy(block->buf+block->used, s, thislen);\n                block->used += thislen;\n                block->free -= thislen;\n                s += thislen;\n                len -= thislen;\n            }\n        }\n\n        if (len) { /* First block to allocate, or need another block. */\n            int numblocks;\n\n            block = dalloc(sizeof(*block));\n            block->free = AOF_RW_BUF_BLOCK_SIZE;\n            block->used = 0;\n            dlistAddNodeTail(server.aof_rewrite_buf_blocks,block);\n\n            /* Log every time we cross more 10 or 100 blocks, respectively\n             * as a notice or warning. */\n            numblocks = dlistLength(server.aof_rewrite_buf_blocks);\n            if (((numblocks+1) % 10) == 0) {\n                int level = ((numblocks+1) % 100) == 0 ? LOG_WARN :\n                                                         LOG_NOTICE;\n                log_debug(level, \"Background AOF buffer size: %lu MB\",\n                    aofRewriteBufferSize()/(1024*1024));\n            }\n        }\n    }\n\n    /* Install a file event to send data to the rewrite child if there is\n     * not one already. */\n    if (aeGetFileEvents(server.el,server.aof_pipe_write_data_to_child) == 0) {\n        aeCreateFileEvent(server.el, server.aof_pipe_write_data_to_child,\n            AE_WRITABLE, aofChildWriteDiffData, NULL);\n    }\n}\n\nvoid feedAppendOnlyFile(struct redisCommand *cmd, int dictid, robj **argv, int argc) {\n    sds buf = sdsempty();\n    robj *tmpargv[3];\n\n    /* The DB this command was targeting is not the same as the last command\n     * we appended. To issue a SELECT command is needed. */\n    if (dictid != server.aof_selected_db) {\n        char seldb[64];\n\n        snprintf(seldb,sizeof(seldb),\"%d\",dictid);\n        buf = sdscatprintf(buf,\"*2\\r\\n$6\\r\\nSELECT\\r\\n$%lu\\r\\n%s\\r\\n\",\n            (unsigned long)strlen(seldb),seldb);\n        server.aof_selected_db = dictid;\n    }\n\n    if (cmd->proc == expireCommand || cmd->proc == pexpireCommand ||\n        cmd->proc == expireatCommand) {\n        /* Translate EXPIRE/PEXPIRE/EXPIREAT into PEXPIREAT */\n        buf = catAppendOnlyExpireAtCommand(buf,cmd,argv[1],argv[2]);\n    } else if (cmd->proc == setexCommand || cmd->proc == psetexCommand) {\n        /* Translate SETEX/PSETEX to SET and PEXPIREAT */\n        tmpargv[0] = createStringObject(\"SET\",3);\n        tmpargv[1] = argv[1];\n        tmpargv[2] = argv[3];\n        buf = catAppendOnlyGenericCommand(buf,3,tmpargv);\n        decrRefCount(tmpargv[0]);\n        buf = catAppendOnlyExpireAtCommand(buf,cmd,argv[1],argv[2]);\n    } else {\n        /* All the other commands don't need translation or need the\n         * same translation already operated in the command vector\n         * for the replication itself. */\n        buf = catAppendOnlyGenericCommand(buf,argc,argv);\n    }\n\n    /* Append to the AOF buffer. This will be flushed on disk just before\n     * of re-entering the event loop, so before the client will get a\n     * positive reply about the operation performed. */\n    if (server.aof_state == AOF_ON)\n        server.aof_buf = sdscatlen(server.aof_buf,buf,sdslen(buf));\n\n    /* If a background append only file rewriting is in progress we want to\n     * accumulate the differences between the child DB and the current one\n     * in a buffer, so that when the child process will do its work we\n     * can append the differences to the new append only file. */\n    if (server.aof_child_pid != -1)\n        aofRewriteBufferAppend((unsigned char*)buf,sdslen(buf));\n\n    sdsfree(buf);\n}\n\n"
  },
  {
    "path": "src/vr_aof.h",
    "content": "#ifndef _VR_AOF_H_\n#define _VR_AOF_H_\n\n/* AOF states */\n#define AOF_OFF 0             /* AOF is off */\n#define AOF_ON 1              /* AOF is on */\n#define AOF_WAIT_REWRITE 2    /* AOF waits rewrite to start appending */\n\n#define AOF_AUTOSYNC_BYTES (1024*1024*32) /* fdatasync every 32MB */\n\n/* ----------------------------------------------------------------------------\n * AOF rewrite buffer implementation.\n *\n * The following code implement a simple buffer used in order to accumulate\n * changes while the background process is rewriting the AOF file.\n *\n * We only need to append, but can't just use realloc with a large block\n * because 'huge' reallocs are not always handled as one could expect\n * (via remapping of pages at OS level) but may involve copying data.\n *\n * For this reason we use a list of blocks, every block is\n * AOF_RW_BUF_BLOCK_SIZE bytes.\n * ------------------------------------------------------------------------- */\n\n#define AOF_RW_BUF_BLOCK_SIZE (1024*1024*10)    /* 10 MB per block */\n\ntypedef struct aofrwblock {\n    unsigned long used, free;\n    char buf[AOF_RW_BUF_BLOCK_SIZE];\n} aofrwblock;\n\nunsigned long aofRewriteBufferSize(void);\nvoid aofChildWriteDiffData(aeEventLoop *el, int fd, void *privdata, int mask);\nsds catAppendOnlyExpireAtCommand(sds buf, struct redisCommand *cmd, robj *key, robj *seconds);\nsds catAppendOnlyGenericCommand(sds dst, int argc, robj **argv);\nvoid aofRewriteBufferAppend(unsigned char *s, unsigned long len);\nvoid feedAppendOnlyFile(struct redisCommand *cmd, int dictid, robj **argv, int argc);\n\n#endif\n"
  },
  {
    "path": "src/vr_backend.c",
    "content": "#include <vr_core.h>\n\n/* Which thread we assigned a connection to most recently. */\nstatic int num_backend_threads;\n\nstruct darray backends;\n\nstatic void *backend_thread_run(void *args);\n\nint\nvr_backend_init(vr_backend *backend)\n{\n    rstatus_t status;\n    int threads_num;\n    \n    if (backend == NULL) {\n        return VR_ERROR;\n    }\n\n    backend->id = 0;\n    backend->current_db = 0;\n    backend->timelimit_exit = 0;\n    backend->last_fast_cycle = 0;\n    backend->resize_db = 0;\n    backend->rehash_db = 0;\n\n    vr_eventloop_init(&backend->vel, 10);\n    backend->vel.thread.fun_run = backend_thread_run;\n    backend->vel.thread.data = backend;\n    \n    return VR_OK;\n}\n\nvoid\nvr_backend_deinit(vr_backend *backend)\n{\n    if (backend == NULL) {\n        return;\n    }\n\n    vr_eventloop_deinit(&backend->vel);\n}\n\nstatic int\nbackend_cron(struct aeEventLoop *eventLoop, long long id, void *clientData) {\n    vr_worker *backend = clientData;\n    vr_eventloop *vel = &backend->vel;\n    size_t stat_used_memory, stats_peak_memory;\n\n    UNUSED(eventLoop);\n    UNUSED(id);\n    UNUSED(clientData);\n\n    ASSERT(eventLoop == vel->el);\n\n    vel->unixtime = time(NULL);\n    vel->mstime = vr_msec_now();\n\n    /* Record the max memory used since the server was started. */\n    stat_used_memory = dalloc_used_memory();\n    update_stats_get(vel->stats, peak_memory, &stats_peak_memory);\n    if (stat_used_memory > stats_peak_memory) {\n        update_stats_set(vel->stats, peak_memory, stat_used_memory);\n    }\n\n    databasesCron(backend);\n\n    /* Update the config cache */\n    run_with_period(1000, vel->cronloops) {\n        conf_cache_update(&vel->cc);\n    }\n    \n    vel->cronloops ++;\n    return 1000/vel->hz;\n}\n\nstatic int\nsetup_backend(vr_backend *backend)\n{\n    /* Create the serverCron() time event, that's our main way to process\n     * background operations. */\n    if(aeCreateTimeEvent(backend->vel.el, 1, backend_cron, backend, NULL) == AE_ERR) {\n        serverPanic(\"Can't create the serverCron time event.\");\n        return VR_ERROR;\n    }\n    \n    return VR_OK;\n}\n\nstatic void *\nbackend_thread_run(void *args)\n{\n    vr_worker *backend = args;\n    \n    /* vire worker run */\n    aeMain(backend->vel.el);\n\n    return NULL;\n}\n\nint\nbackends_init(uint32_t backend_count)\n{\n    rstatus_t status;\n    uint32_t idx;\n    vr_backend *backend;\n\n    darray_init(&backends, backend_count, sizeof(vr_backend));\n\n    for (idx = 0; idx < backend_count; idx ++) {\n        backend = darray_push(&backends);\n        vr_backend_init(backend);\n        backend->id = idx;\n        status = setup_backend(backend);\n        if (status != VR_OK) {\n            exit(1);\n        }\n    }\n    \n    num_backend_threads = (int)darray_n(&backends);\n\n    return VR_OK;\n}\n\nint\nbackends_run(void)\n{\n    uint32_t i, thread_count;\n    vr_backend *backend;\n\n    thread_count = (uint32_t)num_backend_threads;\n\n    for (i = 0; i < thread_count; i ++) {\n        backend = darray_get(&backends, i);\n        vr_thread_start(&backend->vel.thread);\n    }\n\n    return VR_OK;\n}\n\nint\nbackends_wait(void)\n{\n    uint32_t i, thread_count;\n    vr_backend *backend;\n\n    thread_count = (uint32_t)num_backend_threads;\n\n    for (i = 0; i < thread_count; i ++) {\n        backend = darray_get(&backends, i);\n        pthread_join(backend->vel.thread.thread_id, NULL);\n    }\n\n    return VR_OK;\n}\n\nvoid\nbackends_deinit(void)\n{\n    vr_backend *backend;\n\n    while(darray_n(&backends)) {\n        backend = darray_pop(&backends);\n\t\tvr_backend_deinit(backend);\n    }\n}\n"
  },
  {
    "path": "src/vr_backend.h",
    "content": "#ifndef _VR_BACKEND_H_\n#define _VR_BACKEND_H_\n\ntypedef struct vr_backend {\n\n    int id;\n    vr_eventloop vel;\n\n    /* Some global state in order to continue the work incrementally \n       * across calls for activeExpireCycle() to expire some keys. */\n    unsigned int current_db;    /* Last DB tested. */\n    int timelimit_exit;         /* Time limit hit in previous call? */\n    long long last_fast_cycle;  /* When last fast cycle ran. */\n\n    /* We use global counters so if we stop the computation at a given\n       * DB we'll be able to start from the successive in the next\n       * cron loop iteration for databasesCron() to resize and reshash db. */\n    unsigned int resize_db;\n    unsigned int rehash_db;\n}vr_backend;\n\nextern struct darray backends;\n\nint backends_init(uint32_t backend_count);\nint backends_run(void);\nint backends_wait(void);\nvoid backends_deinit(void);\n\n#endif\n"
  },
  {
    "path": "src/vr_bitops.c",
    "content": "#include <vr_core.h>\n\n/* -----------------------------------------------------------------------------\n * Helpers and low level bit functions.\n * -------------------------------------------------------------------------- */\n\n/* Count number of bits set in the binary array pointed by 's' and long\n * 'count' bytes. The implementation of this function is required to\n * work with a input string length up to 512 MB. */\nsize_t redisPopcount(void *s, long count) {\n    size_t bits = 0;\n    unsigned char *p = s;\n    uint32_t *p4;\n    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};\n\n    /* Count initial bytes not aligned to 32 bit. */\n    while((unsigned long)p & 3 && count) {\n        bits += bitsinbyte[*p++];\n        count--;\n    }\n\n    /* Count bits 28 bytes at a time */\n    p4 = (uint32_t*)p;\n    while(count>=28) {\n        uint32_t aux1, aux2, aux3, aux4, aux5, aux6, aux7;\n\n        aux1 = *p4++;\n        aux2 = *p4++;\n        aux3 = *p4++;\n        aux4 = *p4++;\n        aux5 = *p4++;\n        aux6 = *p4++;\n        aux7 = *p4++;\n        count -= 28;\n\n        aux1 = aux1 - ((aux1 >> 1) & 0x55555555);\n        aux1 = (aux1 & 0x33333333) + ((aux1 >> 2) & 0x33333333);\n        aux2 = aux2 - ((aux2 >> 1) & 0x55555555);\n        aux2 = (aux2 & 0x33333333) + ((aux2 >> 2) & 0x33333333);\n        aux3 = aux3 - ((aux3 >> 1) & 0x55555555);\n        aux3 = (aux3 & 0x33333333) + ((aux3 >> 2) & 0x33333333);\n        aux4 = aux4 - ((aux4 >> 1) & 0x55555555);\n        aux4 = (aux4 & 0x33333333) + ((aux4 >> 2) & 0x33333333);\n        aux5 = aux5 - ((aux5 >> 1) & 0x55555555);\n        aux5 = (aux5 & 0x33333333) + ((aux5 >> 2) & 0x33333333);\n        aux6 = aux6 - ((aux6 >> 1) & 0x55555555);\n        aux6 = (aux6 & 0x33333333) + ((aux6 >> 2) & 0x33333333);\n        aux7 = aux7 - ((aux7 >> 1) & 0x55555555);\n        aux7 = (aux7 & 0x33333333) + ((aux7 >> 2) & 0x33333333);\n        bits += ((((aux1 + (aux1 >> 4)) & 0x0F0F0F0F) +\n                    ((aux2 + (aux2 >> 4)) & 0x0F0F0F0F) +\n                    ((aux3 + (aux3 >> 4)) & 0x0F0F0F0F) +\n                    ((aux4 + (aux4 >> 4)) & 0x0F0F0F0F) +\n                    ((aux5 + (aux5 >> 4)) & 0x0F0F0F0F) +\n                    ((aux6 + (aux6 >> 4)) & 0x0F0F0F0F) +\n                    ((aux7 + (aux7 >> 4)) & 0x0F0F0F0F))* 0x01010101) >> 24;\n    }\n    /* Count the remaining bytes. */\n    p = (unsigned char*)p4;\n    while(count--) bits += bitsinbyte[*p++];\n    return bits;\n}\n\n/* Return the position of the first bit set to one (if 'bit' is 1) or\n * zero (if 'bit' is 0) in the bitmap starting at 's' and long 'count' bytes.\n *\n * The function is guaranteed to return a value >= 0 if 'bit' is 0 since if\n * no zero bit is found, it returns count*8 assuming the string is zero\n * padded on the right. However if 'bit' is 1 it is possible that there is\n * not a single set bit in the bitmap. In this special case -1 is returned. */\nlong redisBitpos(void *s, unsigned long count, int bit) {\n    unsigned long *l;\n    unsigned char *c;\n    unsigned long skipval, word = 0, one;\n    long pos = 0; /* Position of bit, to return to the caller. */\n    unsigned long j;\n\n    /* Process whole words first, seeking for first word that is not\n     * all ones or all zeros respectively if we are lookig for zeros\n     * or ones. This is much faster with large strings having contiguous\n     * blocks of 1 or 0 bits compared to the vanilla bit per bit processing.\n     *\n     * Note that if we start from an address that is not aligned\n     * to sizeof(unsigned long) we consume it byte by byte until it is\n     * aligned. */\n\n    /* Skip initial bits not aligned to sizeof(unsigned long) byte by byte. */\n    skipval = bit ? 0 : UCHAR_MAX;\n    c = (unsigned char*) s;\n    while((unsigned long)c & (sizeof(*l)-1) && count) {\n        if (*c != skipval) break;\n        c++;\n        count--;\n        pos += 8;\n    }\n\n    /* Skip bits with full word step. */\n    skipval = bit ? 0 : ULONG_MAX;\n    l = (unsigned long*) c;\n    while (count >= sizeof(*l)) {\n        if (*l != skipval) break;\n        l++;\n        count -= sizeof(*l);\n        pos += sizeof(*l)*8;\n    }\n\n    /* Load bytes into \"word\" considering the first byte as the most significant\n     * (we basically consider it as written in big endian, since we consider the\n     * string as a set of bits from left to right, with the first bit at position\n     * zero.\n     *\n     * Note that the loading is designed to work even when the bytes left\n     * (count) are less than a full word. We pad it with zero on the right. */\n    c = (unsigned char*)l;\n    for (j = 0; j < sizeof(*l); j++) {\n        word <<= 8;\n        if (count) {\n            word |= *c;\n            c++;\n            count--;\n        }\n    }\n\n    /* Special case:\n     * If bits in the string are all zero and we are looking for one,\n     * return -1 to signal that there is not a single \"1\" in the whole\n     * string. This can't happen when we are looking for \"0\" as we assume\n     * that the right of the string is zero padded. */\n    if (bit == 1 && word == 0) return -1;\n\n    /* Last word left, scan bit by bit. The first thing we need is to\n     * have a single \"1\" set in the most significant position in an\n     * unsigned long. We don't know the size of the long so we use a\n     * simple trick. */\n    one = ULONG_MAX; /* All bits set to 1.*/\n    one >>= 1;       /* All bits set to 1 but the MSB. */\n    one = ~one;      /* All bits set to 0 but the MSB. */\n\n    while(one) {\n        if (((one & word) != 0) == bit) return pos;\n        pos++;\n        one >>= 1;\n    }\n\n    /* If we reached this point, there is a bug in the algorithm, since\n     * the case of no match is handled as a special case before. */\n    serverPanic(\"End of redisBitpos() reached.\");\n    return 0; /* Just to avoid warnings. */\n}\n\n/* The following set.*Bitfield and get.*Bitfield functions implement setting\n * and getting arbitrary size (up to 64 bits) signed and unsigned integers\n * at arbitrary positions into a bitmap.\n *\n * The representation considers the bitmap as having the bit number 0 to be\n * the most significant bit of the first byte, and so forth, so for example\n * setting a 5 bits unsigned integer to value 23 at offset 7 into a bitmap\n * previously set to all zeroes, will produce the following representation:\n *\n * +--------+--------+\n * |00000001|01110000|\n * +--------+--------+\n *\n * When offsets and integer sizes are aligned to bytes boundaries, this is the\n * same as big endian, however when such alignment does not exist, its important\n * to also understand how the bits inside a byte are ordered.\n *\n * Note that this format follows the same convention as SETBIT and related\n * commands.\n */\n\nvoid setUnsignedBitfield(unsigned char *p, uint64_t offset, uint64_t bits, uint64_t value) {\n    uint64_t byte, bit, byteval, bitval, j;\n\n    for (j = 0; j < bits; j++) {\n        bitval = (value & ((uint64_t)1<<(bits-1-j))) != 0;\n        byte = offset >> 3;\n        bit = 7 - (offset & 0x7);\n        byteval = p[byte];\n        byteval &= ~(1 << bit);\n        byteval |= bitval << bit;\n        p[byte] = byteval & 0xff;\n        offset++;\n    }\n}\n\nvoid setSignedBitfield(unsigned char *p, uint64_t offset, uint64_t bits, int64_t value) {\n    uint64_t uv;\n\n    if (value >= 0)\n        uv = value;\n    else\n        uv = UINT64_MAX + value + 1;\n    setUnsignedBitfield(p,offset,bits,uv);\n}\n\nuint64_t getUnsignedBitfield(unsigned char *p, uint64_t offset, uint64_t bits) {\n    uint64_t byte, bit, byteval, bitval, j, value = 0;\n\n    for (j = 0; j < bits; j++) {\n        byte = offset >> 3;\n        bit = 7 - (offset & 0x7);\n        byteval = p[byte];\n        bitval = (byteval >> bit) & 1;\n        value = (value<<1) | bitval;\n        offset++;\n    }\n    return value;\n}\n\nint64_t getSignedBitfield(unsigned char *p, uint64_t offset, uint64_t bits) {\n    int64_t value = getUnsignedBitfield(p,offset,bits);\n    /* If the top significant bit is 1, propagate it to all the\n     * higher bits for two complement representation of signed\n     * integers. */\n    if (value & ((uint64_t)1 << (bits-1)))\n        value |= ((uint64_t)-1) << bits;\n    return value;\n}\n\n/* The following two functions detect overflow of a value in the context\n * of storing it as an unsigned or signed integer with the specified\n * number of bits. The functions both take the value and a possible increment.\n * If no overflow could happen and the value+increment fit inside the limits,\n * then zero is returned, otherwise in case of overflow, 1 is returned,\n * otherwise in case of underflow, -1 is returned.\n *\n * When non-zero is returned (oferflow or underflow), if not NULL, *limit is\n * set to the value the operation should result when an overflow happens,\n * depending on the specified overflow semantics:\n *\n * For BFOVERFLOW_SAT if 1 is returned, *limit it is set maximum value that\n * you can store in that integer. when -1 is returned, *limit is set to the\n * minimum value that an integer of that size can represent.\n *\n * For BFOVERFLOW_WRAP *limit is set by performing the operation in order to\n * \"wrap\" around towards zero for unsigned integers, or towards the most\n * negative number that is possible to represent for signed integers. */\n\n#define BFOVERFLOW_WRAP 0\n#define BFOVERFLOW_SAT 1\n#define BFOVERFLOW_FAIL 2 /* Used by the BITFIELD command implementation. */\n\nint checkUnsignedBitfieldOverflow(uint64_t value, int64_t incr, uint64_t bits, int owtype, uint64_t *limit) {\n    uint64_t max = (bits == 64) ? UINT64_MAX : (((uint64_t)1<<bits)-1);\n    int64_t maxincr = max-value;\n    int64_t minincr = -value;\n\n    if (value > max || (incr > 0 && incr > maxincr)) {\n        if (limit) {\n            if (owtype == BFOVERFLOW_WRAP) {\n                goto handle_wrap;\n            } else if (owtype == BFOVERFLOW_SAT) {\n                *limit = max;\n            }\n        }\n        return 1;\n    } else if (incr < 0 && incr < minincr) {\n        if (limit) {\n            if (owtype == BFOVERFLOW_WRAP) {\n                goto handle_wrap;\n            } else if (owtype == BFOVERFLOW_SAT) {\n                *limit = 0;\n            }\n        }\n        return -1;\n    }\n    return 0;\n\nhandle_wrap:\n    {\n        uint64_t mask = ((int64_t)-1) << bits;\n        uint64_t res = value+incr;\n\n        res &= ~mask;\n        *limit = res;\n    }\n    return 1;\n}\n\nint checkSignedBitfieldOverflow(int64_t value, int64_t incr, uint64_t bits, int owtype, int64_t *limit) {\n    int64_t max = (bits == 64) ? INT64_MAX : (((int64_t)1<<(bits-1))-1);\n    int64_t min = (-max)-1;\n\n    /* Note that maxincr and minincr could overflow, but we use the values\n     * only after checking 'value' range, so when we use it no overflow\n     * happens. */\n    int64_t maxincr = max-value;\n    int64_t minincr = min-value;\n\n    if (value > max || (bits != 64 && incr > maxincr) || (value >= 0 && incr > 0 && incr > maxincr))\n    {\n        if (limit) {\n            if (owtype == BFOVERFLOW_WRAP) {\n                goto handle_wrap;\n            } else if (owtype == BFOVERFLOW_SAT) {\n                *limit = max;\n            }\n        }\n        return 1;\n    } else if (value < min || (bits != 64 && incr < minincr) || (value < 0 && incr < 0 && incr < minincr)) {\n        if (limit) {\n            if (owtype == BFOVERFLOW_WRAP) {\n                goto handle_wrap;\n            } else if (owtype == BFOVERFLOW_SAT) {\n                *limit = min;\n            }\n        }\n        return -1;\n    }\n    return 0;\n\nhandle_wrap:\n    {\n        uint64_t mask = ((int64_t)-1) << bits;\n        uint64_t msb = (uint64_t)1 << (bits-1);\n        uint64_t a = value, b = incr, c;\n        c = a+b; /* Perform addition as unsigned so that's defined. */\n\n        /* If the sign bit is set, propagate to all the higher order\n         * bits, to cap the negative value. If it's clear, mask to\n         * the positive integer limit. */\n        if (c & msb) {\n            c |= mask;\n        } else {\n            c &= ~mask;\n        }\n        *limit = c;\n    }\n    return 1;\n}\n\n/* Debugging function. Just show bits in the specified bitmap. Not used\n * but here for not having to rewrite it when debugging is needed. */\nvoid printBits(unsigned char *p, unsigned long count) {\n    unsigned long j, i, byte;\n\n    for (j = 0; j < count; j++) {\n        byte = p[j];\n        for (i = 0x80; i > 0; i /= 2)\n            printf(\"%c\", (byte & i) ? '1' : '0');\n        printf(\"|\");\n    }\n    printf(\"\\n\");\n}\n\n/* -----------------------------------------------------------------------------\n * Bits related string commands: GETBIT, SETBIT, BITCOUNT, BITOP.\n * -------------------------------------------------------------------------- */\n\n#define BITOP_AND   0\n#define BITOP_OR    1\n#define BITOP_XOR   2\n#define BITOP_NOT   3\n\n#define BITFIELDOP_GET 0\n#define BITFIELDOP_SET 1\n#define BITFIELDOP_INCRBY 2\n\n/* This helper function used by GETBIT / SETBIT parses the bit offset argument\n * making sure an error is returned if it is negative or if it overflows\n * Redis 512 MB limit for the string value.\n *\n * If the 'hash' argument is true, and 'bits is positive, then the command\n * will also parse bit offsets prefixed by \"#\". In such a case the offset\n * is multiplied by 'bits'. This is useful for the BITFIELD command. */\nint getBitOffsetFromArgument(client *c, robj *o, size_t *offset, int hash, int bits) {\n    long long loffset;\n    char *err = \"bit offset is not an integer or out of range\";\n    char *p = o->ptr;\n    size_t plen = sdslen(p);\n    int usehash = 0;\n\n    /* Handle #<offset> form. */\n    if (p[0] == '#' && hash && bits > 0) usehash = 1;\n\n    if (string2ll(p+usehash,plen-usehash,&loffset) == 0) {\n        addReplyError(c,err);\n        return VR_ERROR;\n    }\n\n    /* Adjust the offset by 'bits' for #<offset> form. */\n    if (usehash) loffset *= bits;\n\n    /* Limit offset to 512MB in bytes */\n    if ((loffset < 0) || ((unsigned long long)loffset >> 3) >= (512*1024*1024))\n    {\n        addReplyError(c,err);\n        return VR_ERROR;\n    }\n\n    *offset = (size_t)loffset;\n    return VR_OK;\n}\n\n/* This helper function for BITFIELD parses a bitfield type in the form\n * <sign><bits> where sign is 'u' or 'i' for unsigned and signed, and\n * the bits is a value between 1 and 64. However 64 bits unsigned integers\n * are reported as an error because of current limitations of Redis protocol\n * to return unsigned integer values greater than INT64_MAX.\n *\n * On error VR_ERROR is returned and an error is sent to the client. */\nint getBitfieldTypeFromArgument(client *c, robj *o, int *sign, int *bits) {\n    char *p = o->ptr;\n    char *err = \"Invalid bitfield type. Use something like i16 u8. Note that u64 is not supported but i64 is.\";\n    long long llbits;\n\n    if (p[0] == 'i') {\n        *sign = 1;\n    } else if (p[0] == 'u') {\n        *sign = 0;\n    } else {\n        addReplyError(c,err);\n        return VR_ERROR;\n    }\n\n    if ((string2ll(p+1,strlen(p+1),&llbits)) == 0 ||\n        llbits < 1 ||\n        (*sign == 1 && llbits > 64) ||\n        (*sign == 0 && llbits > 63))\n    {\n        addReplyError(c,err);\n        return VR_ERROR;\n    }\n    *bits = llbits;\n    return VR_OK;\n}\n\n/* This is an helper function for commands implementations that need to write\n * bits to a string object. The command creates or pad with zeroes the string\n * so that the 'maxbit' bit can be addressed. The object is finally\n * returned. Otherwise if the key holds a wrong type NULL is returned and\n * an error is sent to the client. */\nrobj *lookupStringForBitCommand(client *c, size_t maxbit, int *expired) {\n    size_t byte = maxbit >> 3;\n    robj *o = lookupKeyWrite(c->db,c->argv[1],expired);\n\n    if (o == NULL) {\n        o = createObject(OBJ_STRING,sdsnewlen(NULL, byte+1));\n        dbAdd(c->db,c->argv[1],o);\n    } else {\n        if (checkType(c,o,OBJ_STRING)) return NULL;\n        o = dbUnshareStringValue(c->db,c->argv[1],o);\n        o->ptr = sdsgrowzero(o->ptr,byte+1);\n    }\n    return o;\n}\n\n/* SETBIT key offset bitvalue */\nvoid setbitCommand(client *c) {\n    robj *o;\n    char *err = \"bit is not an integer or out of range\";\n    size_t bitoffset;\n    ssize_t byte, bit;\n    int byteval, bitval;\n    long on;\n    int expired = 0;\n\n    if (getBitOffsetFromArgument(c,c->argv[2],&bitoffset,0,0) != VR_OK)\n        return;\n\n    if (getLongFromObjectOrReply(c,c->argv[3],&on,err) != VR_OK)\n        return;\n\n    /* Bits can only be set or cleared... */\n    if (on & ~1) {\n        addReplyError(c,err);\n        return;\n    }\n\n    fetchInternalDbByKey(c, c->argv[1]);\n    lockDbWrite(c->db);\n    if ((o = lookupStringForBitCommand(c,bitoffset,&expired)) == NULL) { \n        unlockDb(c->db);\n        if (expired) update_stats_add(c->vel->stats,expiredkeys,1);\n        return;\n    }\n\n    /* Get current values */\n    byte = bitoffset >> 3;\n    byteval = ((uint8_t*)o->ptr)[byte];\n    bit = 7 - (bitoffset & 0x7);\n    bitval = byteval & (1 << bit);\n\n    /* Update byte with new bit value and return original value */\n    byteval &= ~(1 << bit);\n    byteval |= ((on & 0x1) << bit);\n    ((uint8_t*)o->ptr)[byte] = byteval;\n    signalModifiedKey(c->db,c->argv[1]);\n    notifyKeyspaceEvent(NOTIFY_STRING,\"setbit\",c->argv[1],c->db->id);\n    c->vel->dirty++;\n    addReply(c, bitval ? shared.cone : shared.czero);\n    unlockDb(c->db);\n    if (expired) update_stats_add(c->vel->stats,expiredkeys,1);\n}\n\n/* GETBIT key offset */\nvoid getbitCommand(client *c) {\n    robj *o;\n    char llbuf[32];\n    size_t bitoffset;\n    size_t byte, bit;\n    size_t bitval = 0;\n\n    if (getBitOffsetFromArgument(c,c->argv[2],&bitoffset,0,0) != VR_OK)\n        return;\n\n    fetchInternalDbByKey(c, c->argv[1]);\n    lockDbRead(c->db);\n    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL) {\n        unlockDb(c->db);\n        update_stats_add(c->vel->stats, keyspace_misses, 1);\n        return;\n    } else if (checkType(c,o,OBJ_STRING)) {\n        unlockDb(c->db);\n        update_stats_add(c->vel->stats, keyspace_hits, 1);\n        return;\n    }\n    byte = bitoffset >> 3;\n    bit = 7 - (bitoffset & 0x7);\n    if (sdsEncodedObject(o)) {\n        if (byte < sdslen(o->ptr))\n            bitval = ((uint8_t*)o->ptr)[byte] & (1 << bit);\n    } else {\n        if (byte < (size_t)ll2string(llbuf,sizeof(llbuf),(long)o->ptr))\n            bitval = llbuf[byte] & (1 << bit);\n    }\n\n    addReply(c, bitval ? shared.cone : shared.czero);\n    unlockDb(c->db);\n    update_stats_add(c->vel->stats, keyspace_hits, 1);\n}\n\n/* BITOP op_name target_key src_key1 src_key2 src_key3 ... src_keyN */\nvoid bitopCommand(client *c) {\n    char *opname = c->argv[1]->ptr;\n    robj *o, *targetkey = c->argv[2];\n    unsigned long op, j, numkeys;\n    robj **objects;      /* Array of source objects. */\n    unsigned char **src; /* Array of source strings pointers. */\n    unsigned long *len, maxlen = 0; /* Array of length of src strings,\n                                       and max len. */\n    unsigned long minlen = 0;    /* Min len among the input keys. */\n    unsigned char *res = NULL; /* Resulting string. */\n\n    /* Parse the operation name. */\n    if ((opname[0] == 'a' || opname[0] == 'A') && !strcasecmp(opname,\"and\"))\n        op = BITOP_AND;\n    else if((opname[0] == 'o' || opname[0] == 'O') && !strcasecmp(opname,\"or\"))\n        op = BITOP_OR;\n    else if((opname[0] == 'x' || opname[0] == 'X') && !strcasecmp(opname,\"xor\"))\n        op = BITOP_XOR;\n    else if((opname[0] == 'n' || opname[0] == 'N') && !strcasecmp(opname,\"not\"))\n        op = BITOP_NOT;\n    else {\n        addReply(c,shared.syntaxerr);\n        return;\n    }\n\n    /* Sanity check: NOT accepts only a single key argument. */\n    if (op == BITOP_NOT && c->argc != 4) {\n        addReplyError(c,\"BITOP NOT must be called with a single source key.\");\n        return;\n    }\n\n    /* Lookup keys, and store pointers to the string objects into an array. */\n    numkeys = c->argc - 3;\n    src = dalloc(sizeof(unsigned char*) * numkeys);\n    len = dalloc(sizeof(long) * numkeys);\n    objects = dalloc(sizeof(robj*) * numkeys);\n    for (j = 0; j < numkeys; j++) {\n        o = lookupKeyRead(c->db,c->argv[j+3]);\n        /* Handle non-existing keys as empty strings. */\n        if (o == NULL) {\n            objects[j] = NULL;\n            src[j] = NULL;\n            len[j] = 0;\n            minlen = 0;\n            continue;\n        }\n        /* Return an error if one of the keys is not a string. */\n        if (checkType(c,o,OBJ_STRING)) {\n            unsigned long i;\n            for (i = 0; i < j; i++) {\n                if (objects[i])\n                    decrRefCount(objects[i]);\n            }\n            dfree(src);\n            dfree(len);\n            dfree(objects);\n            return;\n        }\n        objects[j] = getDecodedObject(o);\n        src[j] = objects[j]->ptr;\n        len[j] = sdslen(objects[j]->ptr);\n        if (len[j] > maxlen) maxlen = len[j];\n        if (j == 0 || len[j] < minlen) minlen = len[j];\n    }\n\n    /* Compute the bit operation, if at least one string is not empty. */\n    if (maxlen) {\n        res = (unsigned char*) sdsnewlen(NULL,maxlen);\n        unsigned char output, byte;\n        unsigned long i;\n\n        /* Fast path: as far as we have data for all the input bitmaps we\n         * can take a fast path that performs much better than the\n         * vanilla algorithm. */\n        j = 0;\n        if (minlen >= sizeof(unsigned long)*4 && numkeys <= 16) {\n            unsigned long *lp[16];\n            unsigned long *lres = (unsigned long*) res;\n\n            /* Note: sds pointer is always aligned to 8 byte boundary. */\n            memcpy(lp,src,sizeof(unsigned long*)*numkeys);\n            memcpy(res,src[0],minlen);\n\n            /* Different branches per different operations for speed (sorry). */\n            if (op == BITOP_AND) {\n                while(minlen >= sizeof(unsigned long)*4) {\n                    for (i = 1; i < numkeys; i++) {\n                        lres[0] &= lp[i][0];\n                        lres[1] &= lp[i][1];\n                        lres[2] &= lp[i][2];\n                        lres[3] &= lp[i][3];\n                        lp[i]+=4;\n                    }\n                    lres+=4;\n                    j += sizeof(unsigned long)*4;\n                    minlen -= sizeof(unsigned long)*4;\n                }\n            } else if (op == BITOP_OR) {\n                while(minlen >= sizeof(unsigned long)*4) {\n                    for (i = 1; i < numkeys; i++) {\n                        lres[0] |= lp[i][0];\n                        lres[1] |= lp[i][1];\n                        lres[2] |= lp[i][2];\n                        lres[3] |= lp[i][3];\n                        lp[i]+=4;\n                    }\n                    lres+=4;\n                    j += sizeof(unsigned long)*4;\n                    minlen -= sizeof(unsigned long)*4;\n                }\n            } else if (op == BITOP_XOR) {\n                while(minlen >= sizeof(unsigned long)*4) {\n                    for (i = 1; i < numkeys; i++) {\n                        lres[0] ^= lp[i][0];\n                        lres[1] ^= lp[i][1];\n                        lres[2] ^= lp[i][2];\n                        lres[3] ^= lp[i][3];\n                        lp[i]+=4;\n                    }\n                    lres+=4;\n                    j += sizeof(unsigned long)*4;\n                    minlen -= sizeof(unsigned long)*4;\n                }\n            } else if (op == BITOP_NOT) {\n                while(minlen >= sizeof(unsigned long)*4) {\n                    lres[0] = ~lres[0];\n                    lres[1] = ~lres[1];\n                    lres[2] = ~lres[2];\n                    lres[3] = ~lres[3];\n                    lres+=4;\n                    j += sizeof(unsigned long)*4;\n                    minlen -= sizeof(unsigned long)*4;\n                }\n            }\n        }\n\n        /* j is set to the next byte to process by the previous loop. */\n        for (; j < maxlen; j++) {\n            output = (len[0] <= j) ? 0 : src[0][j];\n            if (op == BITOP_NOT) output = ~output;\n            for (i = 1; i < numkeys; i++) {\n                byte = (len[i] <= j) ? 0 : src[i][j];\n                switch(op) {\n                case BITOP_AND: output &= byte; break;\n                case BITOP_OR:  output |= byte; break;\n                case BITOP_XOR: output ^= byte; break;\n                }\n            }\n            res[j] = output;\n        }\n    }\n    for (j = 0; j < numkeys; j++) {\n        if (objects[j])\n            decrRefCount(objects[j]);\n    }\n    dfree(src);\n    dfree(len);\n    dfree(objects);\n\n    /* Store the computed value into the target key */\n    if (maxlen) {\n        o = createObject(OBJ_STRING,res);\n        setKey(c->db,targetkey,o,NULL);\n        notifyKeyspaceEvent(NOTIFY_STRING,\"set\",targetkey,c->db->id);\n        decrRefCount(o);\n    } else if (dbDelete(c->db,targetkey)) {\n        signalModifiedKey(c->db,targetkey);\n        notifyKeyspaceEvent(NOTIFY_GENERIC,\"del\",targetkey,c->db->id);\n    }\n    server.dirty++;\n    addReplyLongLong(c,maxlen); /* Return the output string length in bytes. */\n}\n\n/* BITCOUNT key [start end] */\nvoid bitcountCommand(client *c) {\n    robj *o;\n    long start, end, strlen;\n    unsigned char *p;\n    char llbuf[32];\n\n    fetchInternalDbByKey(c, c->argv[1]);\n    lockDbRead(c->db);\n    /* Lookup, check for type, and return 0 for non existing keys. */\n    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL) {\n        unlockDb(c->db);\n        update_stats_add(c->vel->stats, keyspace_misses, 1);\n        return;\n    } else if (checkType(c,o,OBJ_STRING)) {\n        unlockDb(c->db);\n        update_stats_add(c->vel->stats, keyspace_hits, 1);\n        return;\n    }\n\n    /* Set the 'p' pointer to the string, that can be just a stack allocated\n     * array if our string was integer encoded. */\n    if (o->encoding == OBJ_ENCODING_INT) {\n        p = (unsigned char*) llbuf;\n        strlen = ll2string(llbuf,sizeof(llbuf),(long)o->ptr);\n    } else {\n        p = (unsigned char*) o->ptr;\n        strlen = sdslen(o->ptr);\n    }\n\n    /* Parse start/end range if any. */\n    if (c->argc == 4) {\n        if (getLongFromObjectOrReply(c,c->argv[2],&start,NULL) != VR_OK) {\n            unlockDb(c->db);\n            update_stats_add(c->vel->stats, keyspace_hits, 1);\n            return;\n        }\n        if (getLongFromObjectOrReply(c,c->argv[3],&end,NULL) != VR_OK) {\n            unlockDb(c->db);\n            update_stats_add(c->vel->stats, keyspace_hits, 1);\n            return;\n        }\n        /* Convert negative indexes */\n        if (start < 0) start = strlen+start;\n        if (end < 0) end = strlen+end;\n        if (start < 0) start = 0;\n        if (end < 0) end = 0;\n        if (end >= strlen) end = strlen-1;\n    } else if (c->argc == 2) {\n        /* The whole string. */\n        start = 0;\n        end = strlen-1;\n    } else {\n        unlockDb(c->db);\n        update_stats_add(c->vel->stats, keyspace_hits, 1);\n        /* Syntax error. */\n        addReply(c,shared.syntaxerr);\n        return;\n    }\n\n    /* Precondition: end >= 0 && end < strlen, so the only condition where\n     * zero can be returned is: start > end. */\n    if (start > end) {\n        addReply(c,shared.czero);\n    } else {\n        long bytes = end-start+1;\n\n        addReplyLongLong(c,redisPopcount(p+start,bytes));\n    }\n    unlockDb(c->db);\n    update_stats_add(c->vel->stats, keyspace_hits, 1);\n}\n\n/* BITPOS key bit [start [end]] */\nvoid bitposCommand(client *c) {\n    robj *o;\n    long bit, start, end, strlen;\n    unsigned char *p;\n    char llbuf[32];\n    int end_given = 0;\n\n    /* Parse the bit argument to understand what we are looking for, set\n     * or clear bits. */\n    if (getLongFromObjectOrReply(c,c->argv[2],&bit,NULL) != VR_OK)\n        return;\n    if (bit != 0 && bit != 1) {\n        addReplyError(c, \"The bit argument must be 1 or 0.\");\n        return;\n    }\n\n    fetchInternalDbByKey(c, c->argv[1]);\n    lockDbRead(c->db);\n    /* If the key does not exist, from our point of view it is an infinite\n     * array of 0 bits. If the user is looking for the fist clear bit return 0,\n     * If the user is looking for the first set bit, return -1. */\n    if ((o = lookupKeyRead(c->db,c->argv[1])) == NULL) {\n        unlockDb(c->db);\n        update_stats_add(c->vel->stats, keyspace_misses, 1);\n        addReplyLongLong(c, bit ? -1 : 0);\n        return;\n    }\n    if (checkType(c,o,OBJ_STRING)) {\n        unlockDb(c->db);\n        update_stats_add(c->vel->stats, keyspace_hits, 1);\n        return;\n    }\n\n    /* Set the 'p' pointer to the string, that can be just a stack allocated\n     * array if our string was integer encoded. */\n    if (o->encoding == OBJ_ENCODING_INT) {\n        p = (unsigned char*) llbuf;\n        strlen = ll2string(llbuf,sizeof(llbuf),(long)o->ptr);\n    } else {\n        p = (unsigned char*) o->ptr;\n        strlen = sdslen(o->ptr);\n    }\n\n    /* Parse start/end range if any. */\n    if (c->argc == 4 || c->argc == 5) {\n        if (getLongFromObjectOrReply(c,c->argv[3],&start,NULL) != VR_OK) {\n            unlockDb(c->db);\n            update_stats_add(c->vel->stats, keyspace_hits, 1);\n            return;\n        }\n        if (c->argc == 5) {\n            if (getLongFromObjectOrReply(c,c->argv[4],&end,NULL) != VR_OK) {\n                unlockDb(c->db);\n                update_stats_add(c->vel->stats, keyspace_hits, 1);\n                return;\n            }\n            end_given = 1;\n        } else {\n            end = strlen-1;\n        }\n        /* Convert negative indexes */\n        if (start < 0) start = strlen+start;\n        if (end < 0) end = strlen+end;\n        if (start < 0) start = 0;\n        if (end < 0) end = 0;\n        if (end >= strlen) end = strlen-1;\n    } else if (c->argc == 3) {\n        /* The whole string. */\n        start = 0;\n        end = strlen-1;\n    } else {\n        unlockDb(c->db);\n        update_stats_add(c->vel->stats, keyspace_hits, 1);\n        /* Syntax error. */\n        addReply(c,shared.syntaxerr);\n        return;\n    }\n\n    /* For empty ranges (start > end) we return -1 as an empty range does\n     * not contain a 0 nor a 1. */\n    if (start > end) {\n        addReplyLongLong(c, -1);\n    } else {\n        long bytes = end-start+1;\n        long pos = redisBitpos(p+start,bytes,bit);\n\n        /* If we are looking for clear bits, and the user specified an exact\n         * range with start-end, we can't consider the right of the range as\n         * zero padded (as we do when no explicit end is given).\n         *\n         * So if redisBitpos() returns the first bit outside the range,\n         * we return -1 to the caller, to mean, in the specified range there\n         * is not a single \"0\" bit. */\n        if (end_given && bit == 0 && pos == bytes*8) {\n            unlockDb(c->db);\n            update_stats_add(c->vel->stats, keyspace_hits, 1);\n            addReplyLongLong(c,-1);\n            return;\n        }\n        if (pos != -1) pos += start*8; /* Adjust for the bytes we skipped. */\n        addReplyLongLong(c,pos);\n    }\n    unlockDb(c->db);\n    update_stats_add(c->vel->stats, keyspace_hits, 1);\n}\n\n/* BITFIELD key subcommmand-1 arg ... subcommand-2 arg ... subcommand-N ...\n *\n * Supported subcommands:\n *\n * GET <type> <offset>\n * SET <type> <offset> <value>\n * INCRBY <type> <offset> <increment>\n * OVERFLOW [WRAP|SAT|FAIL]\n */\n\nstruct bitfieldOp {\n    uint64_t offset;    /* Bitfield offset. */\n    int64_t i64;        /* Increment amount (INCRBY) or SET value */\n    int opcode;         /* Operation id. */\n    int owtype;         /* Overflow type to use. */\n    int bits;           /* Integer bitfield bits width. */\n    int sign;           /* True if signed, otherwise unsigned op. */\n};\n\nvoid bitfieldCommand(client *c) {\n    robj *o;\n    size_t bitoffset;\n    int j, numops = 0, changes = 0;\n    struct bitfieldOp *ops = NULL; /* Array of ops to execute at end. */\n    int owtype = BFOVERFLOW_WRAP; /* Overflow type. */\n\n    for (j = 2; j < c->argc; j++) {\n        int remargs = c->argc-j-1; /* Remaining args other than current. */\n        char *subcmd = c->argv[j]->ptr; /* Current command name. */\n        int opcode; /* Current operation code. */\n        long long i64 = 0;  /* Signed SET value. */\n        int sign = 0; /* Signed or unsigned type? */\n        int bits = 0; /* Bitfield width in bits. */\n\n        if (!strcasecmp(subcmd,\"get\") && remargs >= 2)\n            opcode = BITFIELDOP_GET;\n        else if (!strcasecmp(subcmd,\"set\") && remargs >= 3)\n            opcode = BITFIELDOP_SET;\n        else if (!strcasecmp(subcmd,\"incrby\") && remargs >= 3)\n            opcode = BITFIELDOP_INCRBY;\n        else if (!strcasecmp(subcmd,\"overflow\") && remargs >= 1) {\n            char *owtypename = c->argv[j+1]->ptr;\n            j++;\n            if (!strcasecmp(owtypename,\"wrap\"))\n                owtype = BFOVERFLOW_WRAP;\n            else if (!strcasecmp(owtypename,\"sat\"))\n                owtype = BFOVERFLOW_SAT;\n            else if (!strcasecmp(owtypename,\"fail\"))\n                owtype = BFOVERFLOW_FAIL;\n            else {\n                addReplyError(c,\"Invalid OVERFLOW type specified\");\n                dfree(ops);\n                return;\n            }\n            continue;\n        } else {\n            addReply(c,shared.syntaxerr);\n            dfree(ops);\n            return;\n        }\n\n        /* Get the type and offset arguments, common to all the ops. */\n        if (getBitfieldTypeFromArgument(c,c->argv[j+1],&sign,&bits) != VR_OK) {\n            dfree(ops);\n            return;\n        }\n\n        if (getBitOffsetFromArgument(c,c->argv[j+2],&bitoffset,1,bits) != VR_OK){\n            dfree(ops);\n            return;\n        }\n\n        /* INCRBY and SET require another argument. */\n        if (opcode != BITFIELDOP_GET) {\n            if (getLongLongFromObjectOrReply(c,c->argv[j+3],&i64,NULL) != VR_OK){\n                dfree(ops);\n                return;\n            }\n        }\n\n        /* Populate the array of operations we'll process. */\n        ops = drealloc(ops,sizeof(*ops)*(numops+1));\n        ops[numops].offset = bitoffset;\n        ops[numops].i64 = i64;\n        ops[numops].opcode = opcode;\n        ops[numops].owtype = owtype;\n        ops[numops].bits = bits;\n        ops[numops].sign = sign;\n        numops++;\n\n        j += 3 - (opcode == BITFIELDOP_GET);\n    }\n\n    addReplyMultiBulkLen(c,numops);\n\n    /* Actually process the operations. */\n    for (j = 0; j < numops; j++) {\n        struct bitfieldOp *thisop = ops+j;\n\n        /* Execute the operation. */\n        if (thisop->opcode == BITFIELDOP_SET ||\n            thisop->opcode == BITFIELDOP_INCRBY)\n        {\n            /* SET and INCRBY: We handle both with the same code path\n             * for simplicity. SET return value is the previous value so\n             * we need fetch & store as well. */\n\n            /* Lookup by making room up to the farest bit reached by\n             * this operation. */\n            if ((o = lookupStringForBitCommand(c,\n                thisop->offset + (thisop->bits-1), NULL)) == NULL) return;\n\n            /* We need two different but very similar code paths for signed\n             * and unsigned operations, since the set of functions to get/set\n             * the integers and the used variables types are different. */\n            if (thisop->sign) {\n                int64_t oldval, newval, wrapped, retval;\n                int overflow;\n\n                oldval = getSignedBitfield(o->ptr,thisop->offset,\n                        thisop->bits);\n\n                if (thisop->opcode == BITFIELDOP_INCRBY) {\n                    newval = oldval + thisop->i64;\n                    overflow = checkSignedBitfieldOverflow(oldval,\n                            thisop->i64,thisop->bits,thisop->owtype,&wrapped);\n                    if (overflow) newval = wrapped;\n                    retval = newval;\n                } else {\n                    newval = thisop->i64;\n                    overflow = checkSignedBitfieldOverflow(newval,\n                            0,thisop->bits,thisop->owtype,&wrapped);\n                    if (overflow) newval = wrapped;\n                    retval = oldval;\n                }\n\n                /* On overflow of type is \"FAIL\", don't write and return\n                 * NULL to signal the condition. */\n                if (!(overflow && thisop->owtype == BFOVERFLOW_FAIL)) {\n                    addReplyLongLong(c,retval);\n                    setSignedBitfield(o->ptr,thisop->offset,\n                                      thisop->bits,newval);\n                } else {\n                    addReply(c,shared.nullbulk);\n                }\n            } else {\n                uint64_t oldval, newval, wrapped, retval;\n                int overflow;\n\n                oldval = getUnsignedBitfield(o->ptr,thisop->offset,\n                        thisop->bits);\n\n                if (thisop->opcode == BITFIELDOP_INCRBY) {\n                    newval = oldval + thisop->i64;\n                    overflow = checkUnsignedBitfieldOverflow(oldval,\n                            thisop->i64,thisop->bits,thisop->owtype,&wrapped);\n                    if (overflow) newval = wrapped;\n                    retval = newval;\n                } else {\n                    newval = thisop->i64;\n                    overflow = checkUnsignedBitfieldOverflow(newval,\n                            0,thisop->bits,thisop->owtype,&wrapped);\n                    if (overflow) newval = wrapped;\n                    retval = oldval;\n                }\n                /* On overflow of type is \"FAIL\", don't write and return\n                 * NULL to signal the condition. */\n                if (!(overflow && thisop->owtype == BFOVERFLOW_FAIL)) {\n                    addReplyLongLong(c,retval);\n                    setUnsignedBitfield(o->ptr,thisop->offset,\n                                        thisop->bits,newval);\n                } else {\n                    addReply(c,shared.nullbulk);\n                }\n            }\n            changes++;\n        } else {\n            /* GET */\n            o = lookupKeyRead(c->db,c->argv[1]);\n            size_t olen = (o == NULL) ? 0 : sdslen(o->ptr);\n            unsigned char buf[9];\n\n            /* For GET we use a trick: before executing the operation\n             * copy up to 9 bytes to a local buffer, so that we can easily\n             * execute up to 64 bit operations that are at actual string\n             * object boundaries. */\n            memset(buf,0,9);\n            unsigned char *src = o ? o->ptr : NULL;\n            int i;\n            size_t byte = thisop->offset >> 3;\n            for (i = 0; i < 9; i++) {\n                if (src == NULL || i+byte >= olen) break;\n                buf[i] = src[i+byte];\n            }\n\n            /* Now operate on the copied buffer which is guaranteed\n             * to be zero-padded. */\n            if (thisop->sign) {\n                int64_t val = getSignedBitfield(buf,thisop->offset-(byte*8),\n                                            thisop->bits);\n                addReplyLongLong(c,val);\n            } else {\n                uint64_t val = getUnsignedBitfield(buf,thisop->offset-(byte*8),\n                                            thisop->bits);\n                addReplyLongLong(c,val);\n            }\n        }\n    }\n\n    if (changes) {\n        signalModifiedKey(c->db,c->argv[1]);\n        notifyKeyspaceEvent(NOTIFY_STRING,\"setbit\",c->argv[1],c->db->id);\n        server.dirty += changes;\n    }\n    dfree(ops);\n}\n"
  },
  {
    "path": "src/vr_bitops.h",
    "content": "#ifndef _VR_BITOPS_H_\n#define _VR_BITOPS_H_\n\nsize_t redisPopcount(void *s, long count);\nlong redisBitpos(void *s, unsigned long count, int bit);\nvoid setUnsignedBitfield(unsigned char *p, uint64_t offset, uint64_t bits, uint64_t value);\nvoid setSignedBitfield(unsigned char *p, uint64_t offset, uint64_t bits, int64_t value);\nuint64_t getUnsignedBitfield(unsigned char *p, uint64_t offset, uint64_t bits);\nint64_t getSignedBitfield(unsigned char *p, uint64_t offset, uint64_t bits);\nint checkUnsignedBitfieldOverflow(uint64_t value, int64_t incr, uint64_t bits, int owtype, uint64_t *limit);\nint checkSignedBitfieldOverflow(int64_t value, int64_t incr, uint64_t bits, int owtype, int64_t *limit);\nvoid printBits(unsigned char *p, unsigned long count);\nint getBitOffsetFromArgument(struct client *c, robj *o, size_t *offset, int hash, int bits);\nint getBitfieldTypeFromArgument(struct client *c, robj *o, int *sign, int *bits);\nrobj *lookupStringForBitCommand(struct client *c, size_t maxbit, int *expired);\nvoid setbitCommand(struct client *c);\nvoid getbitCommand(struct client *c);\nvoid bitopCommand(struct client *c);\nvoid bitcountCommand(struct client *c);\nvoid bitposCommand(struct client *c);\nvoid bitfieldCommand(client *c);\n#endif\n"
  },
  {
    "path": "src/vr_block.c",
    "content": "#include <vr_core.h>\n\n\n/* Unblock a client calling the right function depending on the kind\n * of operation the client is blocking for. */\nvoid unblockClient(client *c) {\n    if (c->btype == BLOCKED_LIST) {\n        unblockClientWaitingData(c);\n    } else if (c->btype == BLOCKED_WAIT) {\n        unblockClientWaitingReplicas(c);\n    } else {\n        serverPanic(\"Unknown btype in unblockClient().\");\n    }\n    /* Clear the flags, and put the client in the unblocked list so that\n     * we'll process new commands in its query buffer ASAP. */\n    c->flags &= ~CLIENT_BLOCKED;\n    c->btype = BLOCKED_NONE;\n    c->vel->bpop_blocked_clients--;\n    /* The client may already be into the unblocked list because of a previous\n     * blocking operation, don't add back it into the list multiple times. */\n    if (!(c->flags & CLIENT_UNBLOCKED)) {\n        c->flags |= CLIENT_UNBLOCKED;\n        dlistAddNodeTail(c->vel->unblocked_clients,c);\n    }\n}\n\n/* Get a timeout value from an object and store it into 'timeout'.\n * The final timeout is always stored as milliseconds as a time where the\n * timeout will expire, however the parsing is performed according to\n * the 'unit' that can be seconds or milliseconds.\n *\n * Note that if the timeout is zero (usually from the point of view of\n * commands API this means no timeout) the value stored into 'timeout'\n * is zero. */\nint getTimeoutFromObjectOrReply(client *c, robj *object, long long *timeout, int unit) {\n    long long tval;\n\n    if (getLongLongFromObjectOrReply(c,object,&tval,\n        \"timeout is not an integer or out of range\") != VR_OK)\n        return VR_ERROR;\n\n    if (tval < 0) {\n        addReplyError(c,\"timeout is negative\");\n        return VR_ERROR;\n    }\n\n    if (tval > 0) {\n        if (unit == UNIT_SECONDS) tval *= 1000;\n        tval += vr_msec_now();\n    }\n    *timeout = tval;\n\n    return VR_OK;\n}\n\n/* Block a client for the specific operation type. Once the CLIENT_BLOCKED\n * flag is set client query buffer is not longer processed, but accumulated,\n * and will be processed when the client is unblocked. */\nvoid blockClient(client *c, int btype) {\n    c->flags |= CLIENT_BLOCKED;\n    c->btype = btype;\n    c->vel->bpop_blocked_clients++;\n}\n"
  },
  {
    "path": "src/vr_block.h",
    "content": "#ifndef _VR_BLOCK_H_\n#define _VR_BLOCK_H_\n\n/* This structure holds the blocking operation state for a client.\n * The fields used depend on client->btype. */\ntypedef struct blockingState {\n    /* Generic fields. */\n    long long timeout;       /* Blocking operation timeout. If UNIX current time\n                             * is > timeout then the operation timed out. */\n\n    /* BLOCKED_LIST */\n    dict *keys;             /* The keys we are waiting to terminate a blocking\n                             * operation such as BLPOP. Otherwise NULL. */\n    robj *target;           /* The key that should receive the element,\n                             * for BRPOPLPUSH. */\n\n    /* BLOCKED_WAIT */\n    int numreplicas;        /* Number of replicas we are waiting for ACK. */\n    long long reploffset;   /* Replication offset to reach. */\n} blockingState;\n\nvoid blockClient(struct client *c, int btype);\nvoid unblockClient(struct client *c);\nint getTimeoutFromObjectOrReply(struct client *c, robj *object, long long *timeout, int unit);\n\n#endif\n"
  },
  {
    "path": "src/vr_client.c",
    "content": "#include <vr_core.h>\n\nint ncurr_cconn = 0;       /* current # client connections */\n\nstatic void setProtocolError(client *c, int pos);\n\n/* Return the size consumed from the allocator, for the specified SDS string,\n * including internal fragmentation. This function is used in order to compute\n * the client output buffer size. */\nsize_t sdsZmallocSize(sds s) {\n    void *sh = sdsAllocPtr(s);\n    return dmalloc_size(sh);\n}\n\nvoid *dupClientReplyValue(void *o) {\n    return o;\n}\n\nvoid freeClientReplyValue(void *o) {\n    freeObject(o);\n}\n\nint listMatchObjects(void *a, void *b) {\n    return equalStringObjects(a,b);\n}\n\nclient *createClient(vr_eventloop *vel, struct conn *conn) {\n    client *c = dalloc(sizeof(client));\n\n    /* passing -1 as fd it is possible to create a non connected client.\n     * This is useful since all the commands needs to be executed\n     * in the context of a client. When commands are executed in other\n     * contexts (for instance a Lua script) we need a non connected client. */\n    if (conn->sd != -1) {\n        vr_set_nonblocking(conn->sd);\n        vr_set_tcpnodelay(conn->sd);\n        if (server.tcpkeepalive)\n            vr_set_tcpkeepalive(conn->sd,server.tcpkeepalive,0,0);\n        if (aeCreateFileEvent(vel->el,conn->sd,AE_READABLE,\n            readQueryFromClient, c) == AE_ERR)\n        {\n            log_error(\"Unrecoverable error creating client ipfd file event.\");\n            dfree(c);\n            return NULL;\n        }\n    }\n\n    selectDb(c,0);\n    c->id = vel->next_client_id++;\n    c->conn = conn;\n    c->vel = vel;\n    c->scanid = -1;\n    c->name = NULL;\n    c->bufpos = 0;\n    c->querybuf = sdsempty();\n    c->querybuf_peak = 0;\n    c->reqtype = 0;\n    c->argc = 0;\n    c->argv = NULL;\n    c->cmd = c->lastcmd = NULL;\n    c->multibulklen = 0;\n    c->bulklen = -1;\n    c->sentlen = 0;\n    c->flags = 0;\n    c->ctime = c->lastinteraction = vel->unixtime;\n    c->authenticated = 0;\n    c->replstate = REPL_STATE_NONE;\n    c->repl_put_online_on_ack = 0;\n    c->reploff = 0;\n    c->repl_ack_off = 0;\n    c->repl_ack_time = 0;\n    c->slave_listening_port = 0;\n    c->slave_capa = SLAVE_CAPA_NONE;\n    c->reply = dlistCreate();\n    c->reply_bytes = 0;\n    c->obuf_soft_limit_reached_time = 0;\n    dlistSetFreeMethod(c->reply,freeClientReplyValue);\n    dlistSetDupMethod(c->reply,dupClientReplyValue);\n    c->btype = BLOCKED_NONE;\n    c->bpop.timeout = 0;\n    c->bpop.keys = dictCreate(&setDictType,NULL);\n    c->bpop.target = NULL;\n    c->bpop.numreplicas = 0;\n    c->bpop.reploffset = 0;\n    c->woff = 0;\n    c->watched_keys = dlistCreate();\n    c->pubsub_channels = dictCreate(&setDictType,NULL);\n    c->pubsub_patterns = dlistCreate();\n    c->peerid = NULL;\n    c->curidx = -1;\n    c->taridx = -1;\n    c->steps = 0;\n    c->cache = NULL;\n    dlistSetFreeMethod(c->pubsub_patterns,decrRefCountVoid);\n    dlistSetMatchMethod(c->pubsub_patterns,listMatchObjects);\n    if (conn->sd != -1) dlistAddNodeTail(vel->clients,c);\n    initClientMultiState(c);\n    return c;\n}\n\n/* This function is called every time we are going to transmit new data\n * to the client. The behavior is the following:\n *\n * If the client should receive new data (normal clients will) the function\n * returns VR_OK, and make sure to install the write handler in our event\n * loop so that when the socket is writable new data gets written.\n *\n * If the client should not receive new data, because it is a fake client\n * (used to load AOF in memory), a master or because the setup of the write\n * handler failed, the function returns VR_ERROR.\n *\n * The function may return VR_OK without actually installing the write\n * event handler in the following cases:\n *\n * 1) The event handler should already be installed since the output buffer\n *    already contained something.\n * 2) The client is a slave but not yet online, so we want to just accumulate\n *    writes in the buffer but not actually sending them yet.\n *\n * Typically gets called every time a reply is built, before adding more\n * data to the clients output buffers. If the function returns VR_ERROR no\n * data should be appended to the output buffers. */\nint prepareClientToWrite(client *c) {\n    /* If it's the Lua client we always return ok without installing any\n     * handler since there is no socket at all. */\n    if (c->flags & CLIENT_LUA) return VR_OK;\n\n    /* CLIENT REPLY OFF / SKIP handling: don't send replies. */\n    if (c->flags & (CLIENT_REPLY_OFF|CLIENT_REPLY_SKIP)) return VR_ERROR;\n\n    /* Masters don't receive replies, unless CLIENT_MASTER_FORCE_REPLY flag\n     * is set. */\n    if ((c->flags & CLIENT_MASTER) &&\n        !(c->flags & CLIENT_MASTER_FORCE_REPLY)) return VR_ERROR;\n\n    if (c->conn->sd <= 0) return VR_ERROR; /* Fake client for AOF loading. */\n\n    /* Schedule the client to write the output buffers to the socket only\n     * if not already done (there were no pending writes already and the client\n     * was yet not flagged), and, for slaves, if the slave can actually\n     * receive writes at this stage. */\n    if (!clientHasPendingReplies(c) &&\n        !(c->flags & CLIENT_PENDING_WRITE) &&\n        (c->replstate == REPL_STATE_NONE ||\n         (c->replstate == SLAVE_STATE_ONLINE && !c->repl_put_online_on_ack)))\n    {\n        /* Here instead of installing the write handler, we just flag the\n         * client and put it into a list of clients that have something\n         * to write to the socket. This way before re-entering the event\n         * loop, we can try to directly write to the client sockets avoiding\n         * a system call. We'll only really install the write handler if\n         * we'll not be able to write the whole reply at once. */\n        c->flags |= CLIENT_PENDING_WRITE;\n        dlistAddNodeHead(c->vel->clients_pending_write,c);\n    }\n\n    /* Authorize the caller to queue in the output buffer of this client. */\n    return VR_OK;\n}\n\n/* Create a duplicate of the last object in the reply list when\n * it is not exclusively owned by the reply list. */\nrobj *dupLastObjectIfNeeded(dlist *reply) {\n    robj *new, *cur;\n    dlistNode *ln;\n    ASSERT(dlistLength(reply) > 0);\n    ln = dlistLast(reply);\n    cur = dlistNodeValue(ln);\n    if (cur->constant) {\n        new = dupStringObject(cur);\n        dlistNodeValue(ln) = new;\n    }\n    return dlistNodeValue(ln);\n}\n\n/* -----------------------------------------------------------------------------\n * Low level functions to add more data to output buffers.\n * -------------------------------------------------------------------------- */\n\nint _addReplyToBuffer(client *c, const char *s, size_t len) {\n    size_t available = sizeof(c->buf)-c->bufpos;\n\n    if (c->flags & CLIENT_CLOSE_AFTER_REPLY) return VR_OK;\n\n    /* If there already are entries in the reply list, we cannot\n     * add anything more to the static buffer. */\n    if (dlistLength(c->reply) > 0) return VR_ERROR;\n\n    /* Check that the buffer has enough space available for this string. */\n    if (len > available) return VR_ERROR;\n\n    memcpy(c->buf+c->bufpos,s,len);\n    c->bufpos+=len;\n    return VR_OK;\n}\n\nvoid _addReplyObjectToList(client *c, robj *o) {\n    robj *tail, *obj;\n\n    if (c->flags & CLIENT_CLOSE_AFTER_REPLY) return;\n\n    if (dlistLength(c->reply) == 0) {\n        if (o->constant)\n            obj = o;\n        else\n            obj = dupStringObject(o);\n        dlistAddNodeTail(c->reply,obj);\n        c->reply_bytes += getStringObjectSdsUsedMemory(obj);\n    } else {\n        tail = dlistNodeValue(dlistLast(c->reply));\n\n        /* Append to this object when possible. */\n        if (tail->ptr != NULL &&\n            tail->encoding == OBJ_ENCODING_RAW &&\n            sdslen(tail->ptr)+sdslen(o->ptr) <= PROTO_REPLY_CHUNK_BYTES)\n        {\n            c->reply_bytes -= sdsZmallocSize(tail->ptr);\n            tail = dupLastObjectIfNeeded(c->reply);\n            tail->ptr = sdscatlen(tail->ptr,o->ptr,sdslen(o->ptr));\n            c->reply_bytes += sdsZmallocSize(tail->ptr);\n        } else {\n            if (o->constant)\n                obj = o;\n            else\n                obj = dupStringObject(o);\n            dlistAddNodeTail(c->reply,obj);\n            c->reply_bytes += getStringObjectSdsUsedMemory(obj);\n        }\n    }\n    asyncCloseClientOnOutputBufferLimitReached(c);\n}\n\n/* This method takes responsibility over the sds. When it is no longer\n * needed it will be free'd, otherwise it ends up in a robj. */\nvoid _addReplySdsToList(client *c, sds s) {\n    robj *tail;\n\n    if (c->flags & CLIENT_CLOSE_AFTER_REPLY) {\n        sdsfree(s);\n        return;\n    }\n\n    if (dlistLength(c->reply) == 0) {\n        dlistAddNodeTail(c->reply,createObject(OBJ_STRING,s));\n        c->reply_bytes += sdsZmallocSize(s);\n    } else {\n        tail = dlistNodeValue(dlistLast(c->reply));\n\n        /* Append to this object when possible. */\n        if (tail->ptr != NULL && tail->encoding == OBJ_ENCODING_RAW &&\n            sdslen(tail->ptr)+sdslen(s) <= PROTO_REPLY_CHUNK_BYTES)\n        {\n            c->reply_bytes -= sdsZmallocSize(tail->ptr);\n            tail = dupLastObjectIfNeeded(c->reply);\n            tail->ptr = sdscatlen(tail->ptr,s,sdslen(s));\n            c->reply_bytes += sdsZmallocSize(tail->ptr);\n            sdsfree(s);\n        } else {\n            dlistAddNodeTail(c->reply,createObject(OBJ_STRING,s));\n            c->reply_bytes += sdsZmallocSize(s);\n        }\n    }\n    asyncCloseClientOnOutputBufferLimitReached(c);\n}\n\nvoid _addReplyStringToList(client *c, const char *s, size_t len) {\n    robj *tail;\n\n    if (c->flags & CLIENT_CLOSE_AFTER_REPLY) return;\n\n    if (dlistLength(c->reply) == 0) {\n        robj *o = createStringObject(s,len);\n\n        dlistAddNodeTail(c->reply,o);\n        c->reply_bytes += getStringObjectSdsUsedMemory(o);\n    } else {\n        tail = dlistNodeValue(dlistLast(c->reply));\n\n        /* Append to this object when possible. */\n        if (tail->ptr != NULL && tail->encoding == OBJ_ENCODING_RAW &&\n            sdslen(tail->ptr)+len <= PROTO_REPLY_CHUNK_BYTES)\n        {\n            c->reply_bytes -= sdsZmallocSize(tail->ptr);\n            tail = dupLastObjectIfNeeded(c->reply);\n            tail->ptr = sdscatlen(tail->ptr,s,len);\n            c->reply_bytes += sdsZmallocSize(tail->ptr);\n        } else {\n            robj *o = createStringObject(s,len);\n\n            dlistAddNodeTail(c->reply,o);\n            c->reply_bytes += getStringObjectSdsUsedMemory(o);\n        }\n    }\n    asyncCloseClientOnOutputBufferLimitReached(c);\n}\n\n/* -----------------------------------------------------------------------------\n * Higher level functions to queue data on the client output buffer.\n * The following functions are the ones that commands implementations will call.\n * -------------------------------------------------------------------------- */\n\nvoid addReply(client *c, robj *obj) {\n    if (prepareClientToWrite(c) != VR_OK) return;\n\n    /* This is an important place where we can avoid copy-on-write\n     * when there is a saving child running, avoiding touching the\n     * refcount field of the object if it's not needed.\n     *\n     * If the encoding is RAW and there is room in the static buffer\n     * we'll be able to send the object to the client without\n     * messing with its page. */\n    if (sdsEncodedObject(obj)) {\n        if (_addReplyToBuffer(c,obj->ptr,sdslen(obj->ptr)) != VR_OK)\n            _addReplyObjectToList(c,obj);\n    } else if (obj->encoding == OBJ_ENCODING_INT) {\n        robj *obj_new;\n        /* Optimization: if there is room in the static buffer for 32 bytes\n         * (more than the max chars a 64 bit integer can take as string) we\n         * avoid decoding the object and go for the lower level approach. */\n        if (dlistLength(c->reply) == 0 && (sizeof(c->buf) - c->bufpos) >= 32) {\n            char buf[32];\n            int len;\n\n            len = ll2string(buf,sizeof(buf),(long)obj->ptr);\n            if (_addReplyToBuffer(c,buf,len) == VR_OK)\n                return;\n            /* else... continue with the normal code path, but should never\n             * happen actually since we verified there is room. */\n        }\n        obj_new = getDecodedObject(obj);\n        if (_addReplyToBuffer(c,obj_new->ptr,sdslen(obj_new->ptr)) != VR_OK)\n            _addReplyObjectToList(c,obj_new);\n        if (obj_new != obj) freeObject(obj_new);\n    } else {\n        serverPanic(\"Wrong obj->encoding in addReply()\");\n    }\n}\n\nvoid addReplySds(client *c, sds s) {\n    if (prepareClientToWrite(c) != VR_OK) {\n        /* The caller expects the sds to be free'd. */\n        sdsfree(s);\n        return;\n    }\n    if (_addReplyToBuffer(c,s,sdslen(s)) == VR_OK) {\n        sdsfree(s);\n    } else {\n        /* This method free's the sds when it is no longer needed. */\n        _addReplySdsToList(c,s);\n    }\n}\n\nvoid addReplyString(client *c, const char *s, size_t len) {\n    if (prepareClientToWrite(c) != VR_OK) return;\n    if (_addReplyToBuffer(c,s,len) != VR_OK)\n        _addReplyStringToList(c,s,len);\n}\n\nvoid addReplyErrorLength(client *c, const char *s, size_t len) {\n    addReplyString(c,\"-ERR \",5);\n    addReplyString(c,s,len);\n    addReplyString(c,\"\\r\\n\",2);\n}\n\nvoid addReplyError(client *c, const char *err) {\n    addReplyErrorLength(c,err,strlen(err));\n}\n\nvoid addReplyErrorFormat(client *c, const char *fmt, ...) {\n    size_t l, j;\n    va_list ap;\n    va_start(ap,fmt);\n    sds s = sdscatvprintf(sdsempty(),fmt,ap);\n    va_end(ap);\n    /* Make sure there are no newlines in the string, otherwise invalid protocol\n     * is emitted. */\n    l = sdslen(s);\n    for (j = 0; j < l; j++) {\n        if (s[j] == '\\r' || s[j] == '\\n') s[j] = ' ';\n    }\n    addReplyErrorLength(c,s,sdslen(s));\n    sdsfree(s);\n}\n\nvoid addReplyStatusLength(client *c, const char *s, size_t len) {\n    addReplyString(c,\"+\",1);\n    addReplyString(c,s,len);\n    addReplyString(c,\"\\r\\n\",2);\n}\n\nvoid addReplyStatus(client *c, const char *status) {\n    addReplyStatusLength(c,status,strlen(status));\n}\n\nvoid addReplyStatusFormat(client *c, const char *fmt, ...) {\n    va_list ap;\n    va_start(ap,fmt);\n    sds s = sdscatvprintf(sdsempty(),fmt,ap);\n    va_end(ap);\n    addReplyStatusLength(c,s,sdslen(s));\n    sdsfree(s);\n}\n\n/* Adds an empty object to the reply list that will contain the multi bulk\n * length, which is not known when this function is called. */\nvoid *addDeferredMultiBulkLength(client *c) {\n    /* Note that we install the write event here even if the object is not\n     * ready to be sent, since we are sure that before returning to the\n     * event loop setDeferredMultiBulkLength() will be called. */\n    if (prepareClientToWrite(c) != VR_OK) return NULL;\n    dlistAddNodeTail(c->reply,createObject(OBJ_STRING,NULL));\n    return dlistLast(c->reply);\n}\n\n/* Populate the length object and try gluing it to the next chunk. */\nvoid setDeferredMultiBulkLength(client *c, void *node, long length) {\n    dlistNode *ln = (dlistNode*)node;\n    robj *len, *next;\n\n    /* Abort when *node is NULL (see addDeferredMultiBulkLength). */\n    if (node == NULL) return;\n\n    len = dlistNodeValue(ln);\n    len->ptr = sdscatprintf(sdsempty(),\"*%ld\\r\\n\",length);\n    len->encoding = OBJ_ENCODING_RAW; /* in case it was an EMBSTR. */\n    c->reply_bytes += sdsZmallocSize(len->ptr);\n    if (ln->next != NULL) {\n        next = dlistNodeValue(ln->next);\n\n        /* Only glue when the next node is non-NULL (an sds in this case) */\n        if (next->ptr != NULL) {\n            c->reply_bytes -= sdsZmallocSize(len->ptr);\n            c->reply_bytes -= getStringObjectSdsUsedMemory(next);\n            len->ptr = sdscatlen(len->ptr,next->ptr,sdslen(next->ptr));\n            c->reply_bytes += sdsZmallocSize(len->ptr);\n            dlistDelNode(c->reply,ln->next);\n        }\n    }\n    asyncCloseClientOnOutputBufferLimitReached(c);\n}\n\n/* Add a double as a bulk reply */\nvoid addReplyDouble(client *c, double d) {\n    char dbuf[128], sbuf[128];\n    int dlen, slen;\n    if (isinf(d)) {\n        /* Libc in odd systems (Hi Solaris!) will format infinite in a\n         * different way, so better to handle it in an explicit way. */\n        addReplyBulkCString(c, d > 0 ? \"inf\" : \"-inf\");\n    } else {\n        dlen = snprintf(dbuf,sizeof(dbuf),\"%.17g\",d);\n        slen = snprintf(sbuf,sizeof(sbuf),\"$%d\\r\\n%s\\r\\n\",dlen,dbuf);\n        addReplyString(c,sbuf,slen);\n    }\n}\n\n/* Add a long double as a bulk reply, but uses a human readable formatting\n * of the double instead of exposing the crude behavior of doubles to the\n * dear user. */\nvoid addReplyHumanLongDouble(client *c, long double d) {\n    robj *o = createStringObjectFromLongDouble(d,1);\n    addReplyBulk(c,o);\n    decrRefCount(o);\n}\n\n/* Add a long long as integer reply or bulk len / multi bulk count.\n * Basically this is used to output <prefix><long long><crlf>. */\nvoid addReplyLongLongWithPrefix(client *c, long long ll, char prefix) {\n    char buf[128];\n    int len;\n\n    /* Things like $3\\r\\n or *2\\r\\n are emitted very often by the protocol\n     * so we have a few shared objects to use if the integer is small\n     * like it is most of the times. */\n    if (prefix == '*' && ll < OBJ_SHARED_BULKHDR_LEN && ll >= 0) {\n        addReply(c,shared.mbulkhdr[ll]);\n        return;\n    } else if (prefix == '$' && ll < OBJ_SHARED_BULKHDR_LEN && ll >= 0) {\n        addReply(c,shared.bulkhdr[ll]);\n        return;\n    }\n\n    buf[0] = prefix;\n    len = ll2string(buf+1,sizeof(buf)-1,ll);\n    buf[len+1] = '\\r';\n    buf[len+2] = '\\n';\n    addReplyString(c,buf,len+3);\n}\n\nvoid addReplyLongLong(client *c, long long ll) {\n    if (ll == 0)\n        addReply(c,shared.czero);\n    else if (ll == 1)\n        addReply(c,shared.cone);\n    else\n        addReplyLongLongWithPrefix(c,ll,':');\n}\n\nvoid addReplyMultiBulkLen(client *c, long length) {\n    if (length < OBJ_SHARED_BULKHDR_LEN)\n        addReply(c,shared.mbulkhdr[length]);\n    else\n        addReplyLongLongWithPrefix(c,length,'*');\n}\n\n/* Create the length prefix of a bulk reply, example: $2234 */\nvoid addReplyBulkLen(client *c, robj *obj) {\n    size_t len;\n\n    if (sdsEncodedObject(obj)) {\n        len = sdslen(obj->ptr);\n    } else {\n        long n = (long)obj->ptr;\n\n        /* Compute how many bytes will take this integer as a radix 10 string */\n        len = 1;\n        if (n < 0) {\n            len++;\n            n = -n;\n        }\n        while((n = n/10) != 0) {\n            len++;\n        }\n    }\n\n    if (len < OBJ_SHARED_BULKHDR_LEN)\n        addReply(c,shared.bulkhdr[len]);\n    else\n        addReplyLongLongWithPrefix(c,len,'$');\n}\n\n/* Add a Redis Object as a bulk reply */\nvoid addReplyBulk(client *c, robj *obj) {\n    addReplyBulkLen(c,obj);\n    addReply(c,obj);\n    addReply(c,shared.crlf);\n}\n\n/* Add a C buffer as bulk reply */\nvoid addReplyBulkCBuffer(client *c, const void *p, size_t len) {\n    addReplyLongLongWithPrefix(c,len,'$');\n    addReplyString(c,p,len);\n    addReply(c,shared.crlf);\n}\n\n/* Add sds to reply (takes ownership of this sds and frees it) */\nvoid addReplyBulkSds(client *c, sds s)  {\n    addReplySds(c,sdscatfmt(sdsempty(),\"$%u\\r\\n\",\n        (unsigned long)sdslen(s)));\n    addReplySds(c,s);\n    addReply(c,shared.crlf);\n}\n\n/* Add a C nul term string as bulk reply */\nvoid addReplyBulkCString(client *c, const char *s) {\n    if (s == NULL) {\n        addReply(c,shared.nullbulk);\n    } else {\n        addReplyBulkCBuffer(c,s,strlen(s));\n    }\n}\n\n/* Add a long long as a bulk reply */\nvoid addReplyBulkLongLong(client *c, long long ll) {\n    char buf[64];\n    int len;\n\n    len = ll2string(buf,64,ll);\n    addReplyBulkCBuffer(c,buf,len);\n}\n\n/* Copy 'src' client output buffers into 'dst' client output buffers.\n * The function takes care of freeing the old output buffers of the\n * destination client. */\nvoid copyClientOutputBuffer(client *dst, client *src) {\n    dlistRelease(dst->reply);\n    dst->reply = dlistDup(src->reply);\n    memcpy(dst->buf,src->buf,src->bufpos);\n    dst->bufpos = src->bufpos;\n    dst->reply_bytes = src->reply_bytes;\n}\n\n/* Return true if the specified client has pending reply buffers to write to\n * the socket. */\nint clientHasPendingReplies(client *c) {\n    return c->bufpos || dlistLength(c->reply);\n}\n\nstatic void freeClientArgv(client *c) {\n    int j;\n    for (j = 0; j < c->argc; j++)\n        freeObject(c->argv[j]);\n    c->argc = 0;\n    c->cmd = NULL;\n}\n\n/* Close all the slaves connections. This is useful in chained replication\n * when we resync with our own master and want to force all our slaves to\n * resync with us as well. */\nvoid disconnectSlaves(void) {\n    while (dlistLength(repl.slaves)) {\n        dlistNode *ln = dlistFirst(repl.slaves);\n        freeClient((client*)ln->value);\n    }\n}\n\n/* Remove the specified client from eventloop lists where the client could\n * be referenced from this eventloop, not including the Pub/Sub channels.\n * This is used by clients jump between workers. */\nvoid unlinkClientFromEventloop(client *c) {\n    dlistNode *ln;\n    vr_eventloop *vel = c->vel;\n\n    c->vel = NULL;\n\n    if (c->steps >= 1) return;\n    \n    /* If this is marked as current client unset it. */\n    if (vel->current_client == c) vel->current_client = NULL;\n\n    /* Certain operations must be done only if the client has an active socket.\n     * If the client was already unlinked or if it's a \"fake client\" the\n     * fd is already set to -1. */\n    if (c->conn->sd != -1) {\n        /* Remove from the list of active clients. */\n        ln = dlistSearchKey(vel->clients,c);\n        ASSERT(ln != NULL);\n        dlistDelNode(vel->clients,ln);\n\n        /* Unregister async I/O handlers and close the socket. */\n        aeDeleteFileEvent(vel->el,c->conn->sd,AE_READABLE);\n        aeDeleteFileEvent(vel->el,c->conn->sd,AE_WRITABLE);\n    }\n\n    /* Remove from the list of pending writes if needed. */\n    if (c->flags & CLIENT_PENDING_WRITE) {\n        ln = dlistSearchKey(vel->clients_pending_write,c);\n        ASSERT(ln != NULL);\n        dlistDelNode(vel->clients_pending_write,ln);\n        c->flags &= ~CLIENT_PENDING_WRITE;\n    }\n\n    /* When client was just unblocked because of a blocking operation,\n     * remove it from the list of unblocked clients. */\n    if (c->flags & CLIENT_UNBLOCKED) {\n        ln = dlistSearchKey(vel->unblocked_clients,c);\n        ASSERT(ln != NULL);\n        dlistDelNode(vel->unblocked_clients,ln);\n        c->flags &= ~CLIENT_UNBLOCKED;\n    }\n}\n\nvoid linkClientToEventloop(client *c,vr_eventloop *vel) {\n    dlistPush(vel->clients,c);\n    c->vel = vel;\n    if (aeCreateFileEvent(vel->el,c->conn->sd,AE_READABLE,\n        readQueryFromClient,c) == AE_ERR)\n    {\n        freeClient(c);\n        return;\n    }\n\n    /* Handle the remain query buffer */\n    processInputBuffer(c);\n    if (c->flags&CLIENT_JUMP) {\n        dispatch_conn_exist(c,c->taridx);\n    } else {\n        if (clientHasPendingReplies(c) && \n            !(c->flags&CLIENT_PENDING_WRITE)) {\n            if (aeCreateFileEvent(vel->el, c->conn->sd, AE_WRITABLE,\n                sendReplyToClient, c) == AE_ERR)\n            {\n                freeClientAsync(c);\n            }\n        }\n    }\n}\n\n/* Remove the specified client from global lists where the client could\n * be referenced, not including the Pub/Sub channels.\n * This is used by freeClient() and replicationCacheMaster(). */\nvoid unlinkClient(client *c) {\n    dlistNode *ln;\n\n    /* If this is marked as current client unset it. */\n    if (c->vel->current_client == c) c->vel->current_client = NULL;\n\n    /* Certain operations must be done only if the client has an active socket.\n     * If the client was already unlinked or if it's a \"fake client\" the\n     * fd is already set to -1. */\n    if (c->conn->sd != -1) {\n        /* Remove from the list of active clients. */\n        ln = dlistSearchKey(c->vel->clients,c);\n        ASSERT(ln != NULL);\n        dlistDelNode(c->vel->clients,ln);\n\n        /* Unregister async I/O handlers and close the socket. */\n        aeDeleteFileEvent(c->vel->el,c->conn->sd,AE_READABLE);\n        aeDeleteFileEvent(c->vel->el,c->conn->sd,AE_WRITABLE);\n        conn_put(c->conn);\n        c->conn = NULL;\n    }\n\n    /* Remove from the list of pending writes if needed. */\n    if (c->flags & CLIENT_PENDING_WRITE) {\n        ln = dlistSearchKey(c->vel->clients_pending_write,c);\n        ASSERT(ln != NULL);\n        dlistDelNode(c->vel->clients_pending_write,ln);\n        c->flags &= ~CLIENT_PENDING_WRITE;\n    }\n\n    /* When client was just unblocked because of a blocking operation,\n     * remove it from the list of unblocked clients. */\n    if (c->flags & CLIENT_UNBLOCKED) {\n        ln = dlistSearchKey(c->vel->unblocked_clients,c);\n        ASSERT(ln != NULL);\n        dlistDelNode(c->vel->unblocked_clients,ln);\n        c->flags &= ~CLIENT_UNBLOCKED;\n    }\n}\n\nvoid freeClient(client *c) {\n    dlistNode *ln;\n\n    /* If it is our master that's beging disconnected we should make sure\n     * to cache the state to try a partial resynchronization later.\n     *\n     * Note that before doing this we make sure that the client is not in\n     * some unexpected state, by checking its flags. */\n    if (repl.role == REPLICATION_ROLE_MASTER && c->flags & CLIENT_MASTER) {\n        log_warn(\"connection with master lost.\");\n        if (!(c->flags & (CLIENT_CLOSE_AFTER_REPLY|\n                          CLIENT_CLOSE_ASAP|\n                          CLIENT_BLOCKED|\n                          CLIENT_UNBLOCKED)))\n        {\n            replicationCacheMaster(c);\n            return;\n        }\n    }\n\n    /* Log link disconnection with slave */\n    if ((c->flags & CLIENT_SLAVE) && !(c->flags & CLIENT_MONITOR)) {\n        log_warn(\"connection with slave %s lost.\",\n            replicationGetSlaveName(c));\n    }\n\n    /* Free the query buffer */\n    sdsfree(c->querybuf);\n    c->querybuf = NULL;\n\n    /* Deallocate structures used to block on blocking ops. */\n    if (c->flags & CLIENT_BLOCKED) unblockClient(c);\n    dictRelease(c->bpop.keys);\n\n    /* UNWATCH all the keys */\n    unwatchAllKeys(c);\n    dlistRelease(c->watched_keys);\n\n    /* Unsubscribe from all the pubsub channels */\n    pubsubUnsubscribeAllChannels(c,0);\n    pubsubUnsubscribeAllPatterns(c,0);\n    dictRelease(c->pubsub_channels);\n    dlistRelease(c->pubsub_patterns);\n\n    /* Free data structures. */\n    dlistRelease(c->reply);\n    freeClientArgv(c);\n\n    /* Unlink the client: this will close the socket, remove the I/O\n     * handlers, and remove references of the client from different\n     * places where active clients may be referenced. */\n    unlinkClient(c);\n\n    /* Master/slave cleanup Case 1:\n     * we lost the connection with a slave. */\n    if (c->flags & CLIENT_SLAVE) {\n        if (c->replstate == SLAVE_STATE_SEND_BULK) {\n            if (c->repldbfd != -1) close(c->repldbfd);\n            if (c->replpreamble) sdsfree(c->replpreamble);\n        }\n        dlist *l = (c->flags & CLIENT_MONITOR) ? server.monitors : repl.slaves;\n        ln = dlistSearchKey(l,c);\n        ASSERT(ln != NULL);\n        dlistDelNode(l,ln);\n        /* We need to remember the time when we started to have zero\n         * attached slaves, as after some time we'll free the replication\n         * backlog. */\n        if (c->flags & CLIENT_SLAVE && dlistLength(repl.slaves) == 0)\n            repl.repl_no_slaves_since = c->vel->unixtime;\n        refreshGoodSlavesCount();\n    }\n\n    /* Master/slave cleanup Case 2:\n     * we lost the connection with the master. */\n    if (c->flags & CLIENT_MASTER) replicationHandleMasterDisconnection();\n\n    /* If this client was scheduled for async freeing we need to remove it\n     * from the queue. */\n    if (c->flags & CLIENT_CLOSE_ASAP) {\n        ln = dlistSearchKey(c->vel->clients_to_close,c);\n        ASSERT(ln != NULL);\n        dlistDelNode(c->vel->clients_to_close,ln);\n    }\n\n    /* Release other dynamically allocated client structure fields,\n     * and finally release the client structure itself. */\n    if (c->name) freeObject(c->name);\n    if (c->argv) dfree(c->argv);\n    freeClientMultiState(c);\n    sdsfree(c->peerid);\n    dfree(c);\n}\n\n/* Schedule a client to free it at a safe time in the serverCron() function.\n * This function is useful when we need to terminate a client but we are in\n * a context where calling freeClient() is not possible, because the client\n * should be valid for the continuation of the flow of the program. */\nvoid freeClientAsync(client *c) {\n    if (c->flags & CLIENT_CLOSE_ASAP || c->flags & CLIENT_LUA) return;\n    c->flags |= CLIENT_CLOSE_ASAP;\n    dlistAddNodeTail(c->vel->clients_to_close,c);\n}\n\nvoid freeClientsInAsyncFreeQueue(vr_eventloop *vel) {\n    while (dlistLength(vel->clients_to_close)) {\n        dlistNode *ln = dlistFirst(vel->clients_to_close);\n        client *c = dlistNodeValue(ln);\n\n        c->flags &= ~CLIENT_CLOSE_ASAP;\n        freeClient(c);\n        dlistDelNode(vel->clients_to_close,ln);\n    }\n}\n\n/* Write data in output buffers to client. Return VR_OK if the client\n * is still valid after the call, VR_ERROR if it was freed. */\nint writeToClient(int fd, client *c, int handler_installed) {\n    ssize_t nwritten = 0, totwritten = 0;\n    size_t objlen;\n    size_t objmem;\n    robj *o;\n    long long maxmemory;\n\n    maxmemory = c->vel->cc.maxmemory;\n    while(clientHasPendingReplies(c)) {\n        if (c->bufpos > 0) {\n            nwritten = vr_write(fd,c->buf+c->sentlen,c->bufpos-c->sentlen);\n            if (nwritten <= 0) break;\n            c->sentlen += nwritten;\n            totwritten += nwritten;\n\n            /* If the buffer was sent, set bufpos to zero to continue with\n             * the remainder of the reply. */\n            if ((int)c->sentlen == c->bufpos) {\n                c->bufpos = 0;\n                c->sentlen = 0;\n            }\n        } else {\n            o = dlistNodeValue(dlistFirst(c->reply));\n            objlen = sdslen(o->ptr);\n            objmem = getStringObjectSdsUsedMemory(o);\n\n            if (objlen == 0) {\n                dlistDelNode(c->reply,dlistFirst(c->reply));\n                c->reply_bytes -= objmem;\n                continue;\n            }\n\n            nwritten = vr_write(fd, ((char*)o->ptr)+c->sentlen,objlen-c->sentlen);\n            if (nwritten <= 0) break;\n            c->sentlen += nwritten;\n            totwritten += nwritten;\n\n            /* If we fully sent the object on head go to the next one */\n            if (c->sentlen == objlen) {\n                dlistDelNode(c->reply,dlistFirst(c->reply));\n                c->sentlen = 0;\n                c->reply_bytes -= objmem;\n            }\n        }\n        /* Note that we avoid to send more than NET_MAX_WRITES_PER_EVENT\n         * bytes, in a single threaded server it's a good idea to serve\n         * other clients as well, even if a very large request comes from\n         * super fast link that is always able to accept data (in real world\n         * scenario think about 'KEYS *' against the loopback interface).\n         *\n         * However if we are over the maxmemory limit we ignore that and\n         * just deliver as much data as it is possible to deliver. */\n        if (totwritten > NET_MAX_WRITES_PER_EVENT &&\n            (maxmemory == 0 || dalloc_used_memory() < maxmemory)) \n            break;\n    }\n    if (nwritten == -1) {\n        if (errno == EAGAIN) {\n            nwritten = 0;\n        } else {\n            log_debug(LOG_VERB,\n                \"error writing to client: %s\", strerror(errno));\n            freeClient(c);\n            return VR_ERROR;\n        }\n    }\n    if (totwritten > 0) {\n        update_stats_add(c->vel->stats, net_output_bytes, (long long)totwritten);\n        /* For clients representing masters we don't count sending data\n         * as an interaction, since we always send REPLCONF ACK commands\n         * that take some time to just fill the socket output buffer.\n         * We just rely on data / pings received for timeout detection. */\n        if (!(c->flags & CLIENT_MASTER)) c->lastinteraction = c->vel->unixtime;\n    }\n    if (!clientHasPendingReplies(c)) {\n        c->sentlen = 0;\n        if (handler_installed) aeDeleteFileEvent(c->vel->el,c->conn->sd,AE_WRITABLE);\n\n        /* Close connection after entire reply has been sent. */\n        if (c->flags & CLIENT_CLOSE_AFTER_REPLY) {\n            freeClient(c);\n            return VR_ERROR;\n        }\n    }\n    return VR_OK;\n}\n\n/* Write event handler. Just send data to the client. */\nvoid sendReplyToClient(aeEventLoop *el, int fd, void *privdata, int mask) {\n    UNUSED(el);\n    UNUSED(mask);\n    writeToClient(fd,privdata,1);\n}\n\n/* This function is called just before entering the event loop, in the hope\n * we can just write the replies to the client output buffer without any\n * need to use a syscall in order to install the writable event handler,\n * get it called, and so forth. */\nint handleClientsWithPendingWrites(vr_eventloop *vel) {\n    dlistIter li;\n    dlistNode *ln;\n    int processed = dlistLength(vel->clients_pending_write);\n\n    dlistRewind(vel->clients_pending_write,&li);\n    while((ln = dlistNext(&li))) {\n        client *c = dlistNodeValue(ln);\n        c->flags &= ~CLIENT_PENDING_WRITE;\n        dlistDelNode(vel->clients_pending_write,ln);\n\n        /* Try to write buffers to the client socket. */\n        if (writeToClient(c->conn->sd,c,0) == VR_ERROR) continue;\n\n        /* If there is nothing left, do nothing. Otherwise install\n         * the write handler. */\n        if (clientHasPendingReplies(c) &&\n            aeCreateFileEvent(vel->el, c->conn->sd, AE_WRITABLE,\n                sendReplyToClient, c) == AE_ERR)\n        {\n            freeClientAsync(c);\n        }\n    }\n    return processed;\n}\n\n/* resetClient prepare the client to process the next command */\nvoid resetClient(client *c) {\n    redisCommandProc *prevcmd = c->cmd ? c->cmd->proc : NULL;\n\n    if (c->flags&CLIENT_JUMP)\n        return;\n\n    freeClientArgv(c);\n    c->reqtype = 0;\n    c->multibulklen = 0;\n    c->bulklen = -1;\n\n    /* Remove the CLIENT_REPLY_SKIP flag if any so that the reply\n     * to the next command will be sent, but set the flag if the command\n     * we just processed was \"CLIENT REPLY SKIP\". */\n    c->flags &= ~CLIENT_REPLY_SKIP;\n    if (c->flags & CLIENT_REPLY_SKIP_NEXT) {\n        c->flags |= CLIENT_REPLY_SKIP;\n        c->flags &= ~CLIENT_REPLY_SKIP_NEXT;\n    }\n}\n\nint processInlineBuffer(client *c) {\n    char *newline;\n    int argc, j;\n    sds *argv, aux;\n    size_t querylen;\n\n    /* Search for end of line */\n    newline = strchr(c->querybuf,'\\n');\n\n    /* Nothing to do without a \\r\\n */\n    if (newline == NULL) {\n        if (sdslen(c->querybuf) > PROTO_INLINE_MAX_SIZE) {\n            addReplyError(c,\"Protocol error: too big inline request\");\n            setProtocolError(c,0);\n        }\n        return VR_ERROR;\n    }\n\n    /* Handle the \\r\\n case. */\n    if (newline && newline != c->querybuf && *(newline-1) == '\\r')\n        newline--;\n\n    /* Split the input buffer up to the \\r\\n */\n    querylen = newline-(c->querybuf);\n    aux = sdsnewlen(c->querybuf,querylen);\n    argv = sdssplitargs(aux,&argc);\n    sdsfree(aux);\n    if (argv == NULL) {\n        addReplyError(c,\"Protocol error: unbalanced quotes in request\");\n        setProtocolError(c,0);\n        return VR_ERROR;\n    }\n\n    /* Newline from slaves can be used to refresh the last ACK time.\n     * This is useful for a slave to ping back while loading a big\n     * RDB file. */\n    if (querylen == 0 && c->flags & CLIENT_SLAVE)\n        c->repl_ack_time = c->vel->unixtime;\n\n    /* Leave data after the first line of the query in the buffer */\n    sdsrange(c->querybuf,querylen+2,-1);\n\n    /* Setup argv array on client structure */\n    if (argc) {\n        if (c->argv) dfree(c->argv);\n        c->argv = dalloc(sizeof(robj*)*argc);\n    }\n\n    /* Create redis objects for all arguments. */\n    for (c->argc = 0, j = 0; j < argc; j++) {\n        if (sdslen(argv[j])) {\n            c->argv[c->argc] = createObject(OBJ_STRING,argv[j]);\n            c->argc++;\n        } else {\n            sdsfree(argv[j]);\n        }\n    }\n    dfree(argv);\n    return VR_OK;\n}\n\n/* Helper function. Trims query buffer to make the function that processes\n * multi bulk requests idempotent. */\nstatic void setProtocolError(client *c, int pos) {\n    if (log_loggable(LOG_VERB)) {\n        sds client = catClientInfoString(sdsempty(),c);\n        log_debug(LOG_VERB,\n            \"Protocol error from client: %s\", client);\n        sdsfree(client);\n    }\n    c->flags |= CLIENT_CLOSE_AFTER_REPLY;\n    sdsrange(c->querybuf,pos,-1);\n}\n\nint processMultibulkBuffer(client *c) {\n    char *newline = NULL;\n    int pos = 0, ok;\n    long long ll;\n\n    if (c->multibulklen == 0) {\n        /* The client should have been reset */\n        serverAssertWithInfo(c,NULL,c->argc == 0);\n\n        /* Multi bulk length cannot be read without a \\r\\n */\n        newline = strchr(c->querybuf,'\\r');\n        if (newline == NULL) {\n            if (sdslen(c->querybuf) > PROTO_INLINE_MAX_SIZE) {\n                addReplyError(c,\"Protocol error: too big mbulk count string\");\n                setProtocolError(c,0);\n            }\n            return VR_ERROR;\n        }\n\n        /* Buffer should also contain \\n */\n        if (newline-(c->querybuf) > ((signed)sdslen(c->querybuf)-2))\n            return VR_ERROR;\n\n        /* We know for sure there is a whole line since newline != NULL,\n         * so go ahead and find out the multi bulk length. */\n        serverAssertWithInfo(c,NULL,c->querybuf[0] == '*');\n        ok = string2ll(c->querybuf+1,newline-(c->querybuf+1),&ll);\n        if (!ok || ll > 1024*1024) {\n            addReplyError(c,\"Protocol error: invalid multibulk length\");\n            setProtocolError(c,pos);\n            return VR_ERROR;\n        }\n\n        pos = (newline-c->querybuf)+2;\n        if (ll <= 0) {\n            sdsrange(c->querybuf,pos,-1);\n            return VR_OK;\n        }\n\n        c->multibulklen = ll;\n\n        /* Setup argv array on client structure */\n        if (c->argv) dfree(c->argv);\n        c->argv = dalloc(sizeof(robj*)*c->multibulklen);\n    }\n\n    serverAssertWithInfo(c,NULL,c->multibulklen > 0);\n    while(c->multibulklen) {\n        /* Read bulk length if unknown */\n        if (c->bulklen == -1) {\n            newline = strchr(c->querybuf+pos,'\\r');\n            if (newline == NULL) {\n                if (sdslen(c->querybuf) > PROTO_INLINE_MAX_SIZE) {\n                    addReplyError(c,\n                        \"Protocol error: too big bulk count string\");\n                    setProtocolError(c,0);\n                    return VR_ERROR;\n                }\n                break;\n            }\n\n            /* Buffer should also contain \\n */\n            if (newline-(c->querybuf) > ((signed)sdslen(c->querybuf)-2))\n                break;\n\n            if (c->querybuf[pos] != '$') {\n                addReplyErrorFormat(c,\n                    \"Protocol error: expected '$', got '%c'\",\n                    c->querybuf[pos]);\n                setProtocolError(c,pos);\n                return VR_ERROR;\n            }\n\n            ok = string2ll(c->querybuf+pos+1,newline-(c->querybuf+pos+1),&ll);\n            if (!ok || ll < 0 || ll > 512*1024*1024) {\n                addReplyError(c,\"Protocol error: invalid bulk length\");\n                setProtocolError(c,pos);\n                return VR_ERROR;\n            }\n\n            pos += newline-(c->querybuf+pos)+2;\n            if (ll >= PROTO_MBULK_BIG_ARG) {\n                size_t qblen;\n\n                /* If we are going to read a large object from network\n                 * try to make it likely that it will start at c->querybuf\n                 * boundary so that we can optimize object creation\n                 * avoiding a large copy of data. */\n                sdsrange(c->querybuf,pos,-1);\n                pos = 0;\n                qblen = sdslen(c->querybuf);\n                /* Hint the sds library about the amount of bytes this string is\n                 * going to contain. */\n                if (qblen < (size_t)ll+2)\n                    c->querybuf = sdsMakeRoomFor(c->querybuf,ll+2-qblen);\n            }\n            c->bulklen = ll;\n        }\n\n        /* Read bulk argument */\n        if (sdslen(c->querybuf)-pos < (unsigned)(c->bulklen+2)) {\n            /* Not enough data (+2 == trailing \\r\\n) */\n            break;\n        } else {\n            /* Optimization: if the buffer contains JUST our bulk element\n             * instead of creating a new object by *copying* the sds we\n             * just use the current sds string. */\n            if (pos == 0 &&\n                c->bulklen >= PROTO_MBULK_BIG_ARG &&\n                (signed) sdslen(c->querybuf) == c->bulklen+2)\n            {\n                c->argv[c->argc++] = createObject(OBJ_STRING,c->querybuf);\n                sdsIncrLen(c->querybuf,-2); /* remove CRLF */\n                /* Assume that if we saw a fat argument we'll see another one\n                 * likely... */\n                c->querybuf = sdsnewlen(NULL,c->bulklen+2);\n                sdsclear(c->querybuf);\n                pos = 0;\n            } else {\n                c->argv[c->argc++] =\n                    createStringObject(c->querybuf+pos,c->bulklen);\n                pos += c->bulklen+2;\n            }\n            c->bulklen = -1;\n            c->multibulklen--;\n        }\n    }\n\n    /* Trim to pos */\n    if (pos) sdsrange(c->querybuf,pos,-1);\n\n    /* We're done when c->multibulk == 0 */\n    if (c->multibulklen == 0) return VR_OK;\n\n    /* Still not read to process the command */\n    return VR_ERROR;\n}\n\nvoid processInputBuffer(client *c) {\n    c->vel->current_client = c;\n    /* Keep processing while there is something in the input buffer */\n    while(sdslen(c->querybuf)) {\n        /* Return if clients are paused. */\n        if (!(c->flags & CLIENT_SLAVE) && clientsArePaused(c->vel)) break;\n\n        /* Immediately abort if the client is in the middle of something. */\n        if (c->flags & CLIENT_BLOCKED) break;\n\n        /* CLIENT_CLOSE_AFTER_REPLY closes the connection once the reply is\n         * written to the client. Make sure to not let the reply grow after\n         * this flag has been set (i.e. don't process more commands). */\n        if (c->flags & CLIENT_CLOSE_AFTER_REPLY) break;\n\n        /* Determine request type when unknown. */\n        if (!c->reqtype) {\n            if (c->querybuf[0] == '*') {\n                c->reqtype = PROTO_REQ_MULTIBULK;\n            } else {\n                c->reqtype = PROTO_REQ_INLINE;\n            }\n        }\n\n        if (c->reqtype == PROTO_REQ_INLINE) {\n            if (processInlineBuffer(c) != VR_OK) break;\n        } else if (c->reqtype == PROTO_REQ_MULTIBULK) {\n            if (processMultibulkBuffer(c) != VR_OK) break;\n        } else {\n            serverPanic(\"Unknown request type\");\n        }\n\n        /* Multibulk processing could see a <= 0 length. */\n        if (c->argc == 0) {\n            resetClient(c);\n        } else {\n            /* Only reset the client when the command was executed. */\n            if (processCommand(c) == VR_OK)\n                resetClient(c);\n            /* freeMemoryIfNeeded may flush slave output buffers. This may result\n             * into a slave, that may be the active client, to be freed. */\n            if (c->vel->current_client == NULL) break;\n\n            /* If this client need to jump to another worker,\n             * break this while loop. When this client jumped finished, \n             * continue handle the remain query buffer. */\n            if (c->flags&CLIENT_JUMP) break;\n        }\n    }\n    c->vel->current_client = NULL;\n}\n\nvoid readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask) {\n    client *c = (client*) privdata;\n    int nread, readlen;\n    size_t qblen;\n    UNUSED(el);\n    UNUSED(mask);\n\n    readlen = PROTO_IOBUF_LEN;\n    /* If this is a multi bulk request, and we are processing a bulk reply\n     * that is large enough, try to maximize the probability that the query\n     * buffer contains exactly the SDS string representing the object, even\n     * at the risk of requiring more read(2) calls. This way the function\n     * processMultiBulkBuffer() can avoid copying buffers to create the\n     * Redis Object representing the argument. */\n    if (c->reqtype == PROTO_REQ_MULTIBULK && c->multibulklen && c->bulklen != -1\n        && c->bulklen >= PROTO_MBULK_BIG_ARG)\n    {\n        int remaining = (unsigned)(c->bulklen+2)-sdslen(c->querybuf);\n\n        if (remaining < readlen) readlen = remaining;\n    }\n\n    qblen = sdslen(c->querybuf);\n    if (c->querybuf_peak < qblen) c->querybuf_peak = qblen;\n    c->querybuf = sdsMakeRoomFor(c->querybuf, readlen);\n    nread = vr_read(fd, c->querybuf+qblen, readlen);\n    if (nread == -1) {\n        if (errno == EAGAIN) {\n            return;\n        } else {\n            log_debug(LOG_VERB, \"reading from client: %s\",strerror(errno));\n            freeClient(c);\n            return;\n        }\n    } else if (nread == 0) {\n        log_debug(LOG_VERB, \"client closed connection\");\n        freeClient(c);\n        return;\n    }\n\n    sdsIncrLen(c->querybuf,nread);\n    c->lastinteraction = c->vel->unixtime;\n    if (c->flags & CLIENT_MASTER) c->reploff += nread;\n    update_stats_add(c->vel->stats, net_input_bytes, nread);\n    if (sdslen(c->querybuf) > server.client_max_querybuf_len) {\n        sds ci = catClientInfoString(sdsempty(),c), bytes = sdsempty();\n\n        bytes = sdscatrepr(bytes,c->querybuf,64);\n        log_warn(\"closing client that reached max query buffer length: %s (qbuf initial bytes: %s)\", ci, bytes);\n        sdsfree(ci);\n        sdsfree(bytes);\n        freeClient(c);\n        return;\n    }\n    processInputBuffer(c);\n\n    if (c->flags&CLIENT_JUMP) {\n        dispatch_conn_exist(c,c->taridx);\n    }\n}\n\nvoid getClientsMaxBuffers(vr_eventloop *vel, unsigned long *longest_output_list,\n                          unsigned long *biggest_input_buffer) {\n    client *c;\n    dlistNode *ln;\n    dlistIter li;\n    unsigned long lol = 0, bib = 0;\n\n    dlistRewind(vel->clients,&li);\n    while ((ln = dlistNext(&li)) != NULL) {\n        c = dlistNodeValue(ln);\n\n        if (dlistLength(c->reply) > lol) lol = dlistLength(c->reply);\n        if (sdslen(c->querybuf) > bib) bib = sdslen(c->querybuf);\n    }\n    *longest_output_list = lol;\n    *biggest_input_buffer = bib;\n}\n\n/* A Redis \"Peer ID\" is a colon separated ip:port pair.\n * For IPv4 it's in the form x.y.z.k:port, example: \"127.0.0.1:1234\".\n * For IPv6 addresses we use [] around the IP part, like in \"[::1]:1234\".\n * For Unix sockets we use path:0, like in \"/tmp/redis:0\".\n *\n * A Peer ID always fits inside a buffer of NET_PEER_ID_LEN bytes, including\n * the null term.\n *\n * On failure the function still populates 'peerid' with the \"?:0\" string\n * in case you want to relax error checking or need to display something\n * anyway (see anetPeerToString implementation for more info). */\nvoid genClientPeerId(client *client, char *peerid,\n                            size_t peerid_len) {\n    if (client->flags & CLIENT_UNIX_SOCKET) {\n        /* Unix socket client. */\n        snprintf(peerid,peerid_len,\"%s:0\",server.unixsocket);\n    } else {\n        /* TCP client. */\n        vr_net_format_peer(client->conn->sd,peerid,peerid_len);\n    }\n}\n\n/* This function returns the client peer id, by creating and caching it\n * if client->peerid is NULL, otherwise returning the cached value.\n * The Peer ID never changes during the life of the client, however it\n * is expensive to compute. */\nchar *getClientPeerId(client *c) {\n    char peerid[VR_INET_PEER_ID_LEN];\n\n    if (c->peerid == NULL) {\n        genClientPeerId(c,peerid,sizeof(peerid));\n        c->peerid = sdsnew(peerid);\n    }\n    return c->peerid;\n}\n\n/* Concatenate a string representing the state of a client in an human\n * readable format, into the sds string 's'. */\nsds catClientInfoString(sds s, client *client) {\n    char flags[16], events[3], *p;\n    int emask;\n\n    p = flags;\n    if (client->flags & CLIENT_SLAVE) {\n        if (client->flags & CLIENT_MONITOR)\n            *p++ = 'O';\n        else\n            *p++ = 'S';\n    }\n    if (client->flags & CLIENT_MASTER) *p++ = 'M';\n    if (client->flags & CLIENT_MULTI) *p++ = 'x';\n    if (client->flags & CLIENT_BLOCKED) *p++ = 'b';\n    if (client->flags & CLIENT_DIRTY_CAS) *p++ = 'd';\n    if (client->flags & CLIENT_CLOSE_AFTER_REPLY) *p++ = 'c';\n    if (client->flags & CLIENT_UNBLOCKED) *p++ = 'u';\n    if (client->flags & CLIENT_CLOSE_ASAP) *p++ = 'A';\n    if (client->flags & CLIENT_UNIX_SOCKET) *p++ = 'U';\n    if (client->flags & CLIENT_READONLY) *p++ = 'r';\n    if (p == flags) *p++ = 'N';\n    *p++ = '\\0';\n\n    emask = client->conn->sd == -1 ? 0 : aeGetFileEvents(client->vel->el,client->conn->sd);\n    p = events;\n    if (emask & AE_READABLE) *p++ = 'r';\n    if (emask & AE_WRITABLE) *p++ = 'w';\n    *p = '\\0';\n    \n    return sdscatfmt(s,\n        \"oid=%i id=%U addr=%s fd=%i name=%s age=%I idle=%I flags=%s db=%i sub=%i psub=%i multi=%i qbuf=%U qbuf-free=%U obl=%U oll=%U omem=%U events=%s cmd=%s\",\n        client->curidx,\n        (unsigned long long) client->id,\n        getClientPeerId(client),\n        client->conn->sd,\n        client->name ? (char*)client->name->ptr : \"\",\n        (long long)(client->vel->unixtime - client->ctime),\n        (long long)(client->vel->unixtime - client->lastinteraction),\n        flags,\n        client->dictid,\n        (int) dictSize(client->pubsub_channels),\n        (int) dlistLength(client->pubsub_patterns),\n        (client->flags & CLIENT_MULTI) ? client->mstate.count : -1,\n        (unsigned long long) sdslen(client->querybuf),\n        (unsigned long long) sdsavail(client->querybuf),\n        (unsigned long long) client->bufpos,\n        (unsigned long long) dlistLength(client->reply),\n        (unsigned long long) getClientOutputBufferMemoryUsage(client),\n        events,\n        client->lastcmd ? client->lastcmd->name : \"NULL\");\n}\n\nsds getAllClientsInfoString(vr_eventloop *vel) {\n    dlistNode *ln;\n    dlistIter li;\n    client *client;\n    sds o = sdsnewlen(NULL,200*dlistLength(vel->clients));\n    sdsclear(o);\n    dlistRewind(vel->clients,&li);\n    while ((ln = dlistNext(&li)) != NULL) {\n        client = dlistNodeValue(ln);\n        o = catClientInfoString(o,client);\n        o = sdscatlen(o,\"\\n\",1);\n    }\n    return o;\n}\n\nstruct clientkilldata {\n    sds addr;\n    int type;\n    uint64_t id;\n    int skipme;\n    int killed;\n    int close_this_client;\n};\n\nvoid clientCommand(client *c) {\n    dlistNode *ln;\n    dlistIter li;\n    client *client;\n\n    if (!strcasecmp(c->argv[1]->ptr,\"list\") && c->argc == 2) {\n        /* CLIENT LIST */\n        sds str = c->cache;\n        sds o = getAllClientsInfoString(c->vel);\n\n        str = sdscatsds(str?str:sdsempty(),o);\n\n        if (c->steps >= (darray_n(&workers) - 1)) {\n            addReplyBulkCBuffer(c,str,sdslen(str));\n            c->steps = 0;\n            c->taridx = -1;\n            sdsfree(str);\n            c->cache = NULL;\n            c->flags &= ~CLIENT_JUMP;\n        } else {\n            if (!(c->flags&CLIENT_JUMP))\n                c->flags |= CLIENT_JUMP;\n            c->taridx = worker_get_next_idx(c->curidx);\n            c->cache = str;\n        }\n        sdsfree(o);\n        return;\n    } else if (!strcasecmp(c->argv[1]->ptr,\"kill\")) {\n        /* CLIENT KILL <ip:port>\n         * CLIENT KILL <option> [value] ... <option> [value] */\n        struct clientkilldata *ckd;\n\n        if (c->steps == 0) {\n            ckd = dalloc(sizeof(struct clientkilldata));\n            ckd->addr = NULL;\n            ckd->type = -1;\n            ckd->id = 0;\n            ckd->skipme = 1;\n            ckd->killed = 0;\n            ckd->close_this_client = 0;\n\n            if (c->argc == 3) {\n                /* Old style syntax: CLIENT KILL <addr> */\n                ckd->addr = sdsnew(c->argv[2]->ptr);\n                ckd->skipme = 0; /* With the old form, you can kill yourself. */\n            } else if (c->argc > 3) {\n                int i = 2; /* Next option index. */\n    \n                /* New style syntax: parse options. */\n                while(i < c->argc) {\n                    int moreargs = c->argc > i+1;\n    \n                    if (!strcasecmp(c->argv[i]->ptr,\"id\") && moreargs) {\n                        long long tmp;\n    \n                        if (getLongLongFromObjectOrReply(c,c->argv[i+1],&tmp,NULL)\n                            != VR_OK) {\n                            if (ckd->addr) sdsfree(ckd->addr);\n                            dfree(ckd);\n                            return;\n                        }\n                        ckd->id = tmp;\n                    } else if (!strcasecmp(c->argv[i]->ptr,\"type\") && moreargs) {\n                        ckd->type = getClientTypeByName(c->argv[i+1]->ptr);\n                        if (ckd->type == -1) {\n                            if (ckd->addr) sdsfree(ckd->addr);\n                            dfree(ckd);\n                            addReplyErrorFormat(c,\"Unknown client type '%s'\",\n                                (char*) c->argv[i+1]->ptr);\n                            return;\n                        }\n                    } else if (!strcasecmp(c->argv[i]->ptr,\"addr\") && moreargs) {\n                        ckd->addr = sdsnew(c->argv[i+1]->ptr);\n                    } else if (!strcasecmp(c->argv[i]->ptr,\"skipme\") && moreargs) {\n                        if (!strcasecmp(c->argv[i+1]->ptr,\"yes\")) {\n                            ckd->skipme = 1;\n                        } else if (!strcasecmp(c->argv[i+1]->ptr,\"no\")) {\n                            ckd->skipme = 0;\n                        } else {\n                            if (ckd->addr) sdsfree(ckd->addr);\n                            dfree(ckd);\n                            addReply(c,shared.syntaxerr);\n                            return;\n                        }\n                    } else {\n                        if (ckd->addr) sdsfree(ckd->addr);\n                        dfree(ckd);\n                        addReply(c,shared.syntaxerr);\n                        return;\n                    }\n                    i += 2;\n                }\n            } else {\n                if (ckd->addr) sdsfree(ckd->addr);\n                dfree(ckd);\n                addReply(c,shared.syntaxerr);\n                return;\n            }\n\n            if (!(c->flags&CLIENT_JUMP))\n                c->flags |= CLIENT_JUMP;\n            c->taridx = worker_get_next_idx(c->curidx);\n            c->cache = ckd;\n        } else {\n            ckd = c->cache;\n            c->taridx = worker_get_next_idx(c->curidx);\n        }\n\n        /* Iterate clients killing all the matching clients. */\n        dlistRewind(c->vel->clients,&li);\n        while ((ln = dlistNext(&li)) != NULL) {\n            client = dlistNodeValue(ln);\n            if (ckd->addr && strcmp(getClientPeerId(client),ckd->addr) != 0) continue;\n            if (ckd->type != -1 && getClientType(client) != ckd->type) continue;\n            if (ckd->id != 0 && client->id != ckd->id) continue;\n            if (c == client && ckd->skipme) continue;\n\n            /* Kill it. */\n            if (c == client) {\n                ckd->close_this_client = 1;\n            } else {\n                freeClient(client);\n            }\n            ckd->killed++;\n        }\n\n        if (c->steps >= (darray_n(&workers) - 1)) {\n            /* Reply according to old/new format. */\n            if (c->argc == 3) {\n                if (ckd->killed == 0)\n                    addReplyError(c,\"No such client\");\n                else\n                    addReply(c,shared.ok);\n            } else {\n                addReplyLongLong(c,ckd->killed);\n            }\n            \n            c->steps = 0;\n            c->taridx = -1;\n            c->cache = NULL;\n            c->flags &= ~CLIENT_JUMP;\n            \n            /* If this client has to be closed, flag it as CLOSE_AFTER_REPLY\n             * only after we queued the reply to its output buffers. */\n            if (ckd->close_this_client) c->flags |= CLIENT_CLOSE_AFTER_REPLY;\n\n            if (ckd->addr) sdsfree(ckd->addr);\n            dfree(ckd);\n        }\n\n        return;\n    } else if (!strcasecmp(c->argv[1]->ptr,\"setname\") && c->argc == 3) {\n        int j, len = sdslen(c->argv[2]->ptr);\n        char *p = c->argv[2]->ptr;\n\n        /* Setting the client name to an empty string actually removes\n         * the current name. */\n        if (len == 0) {\n            if (c->name) freeObject(c->name);\n            c->name = NULL;\n            addReply(c,shared.ok);\n            return;\n        }\n\n        /* Otherwise check if the charset is ok. We need to do this otherwise\n         * CLIENT LIST format will break. You should always be able to\n         * split by space to get the different fields. */\n        for (j = 0; j < len; j++) {\n            if (p[j] < '!' || p[j] > '~') { /* ASCII is assumed. */\n                addReplyError(c,\n                    \"Client names cannot contain spaces, \"\n                    \"newlines or special characters.\");\n                return;\n            }\n        }\n        if (c->name) freeObject(c->name);\n        c->name = dupStringObjectUnconstant(c->argv[2]);\n        addReply(c,shared.ok);\n        return;\n    } else if (!strcasecmp(c->argv[1]->ptr,\"getname\") && c->argc == 2) {\n        if (c->name)\n            addReplyBulk(c,c->name);\n        else\n            addReply(c,shared.nullbulk);\n        return;\n    } else {\n        addReplyError(c, \"Syntax error, try CLIENT (LIST | KILL ip:port | SETNAME connection-name)\");\n        return;\n    }\n\n    if (!strcasecmp(c->argv[1]->ptr,\"reply\") && c->argc == 3) {\n        /* CLIENT REPLY ON|OFF|SKIP */\n        if (!strcasecmp(c->argv[2]->ptr,\"on\")) {\n            c->flags &= ~(CLIENT_REPLY_SKIP|CLIENT_REPLY_OFF);\n            addReply(c,shared.ok);\n        } else if (!strcasecmp(c->argv[2]->ptr,\"off\")) {\n            c->flags |= CLIENT_REPLY_OFF;\n        } else if (!strcasecmp(c->argv[2]->ptr,\"skip\")) {\n            if (!(c->flags & CLIENT_REPLY_OFF))\n                c->flags |= CLIENT_REPLY_SKIP_NEXT;\n        } else {\n            addReply(c,shared.syntaxerr);\n            return;\n        }\n    } else if (!strcasecmp(c->argv[1]->ptr,\"pause\") && c->argc == 3) {\n        long long duration;\n\n        if (getTimeoutFromObjectOrReply(c,c->argv[2],&duration,UNIT_MILLISECONDS)\n                                        != VR_OK) return;\n        pauseClients(NULL, duration);\n        addReply(c,shared.ok);\n    } else {\n        addReplyError(c, \"Syntax error, try CLIENT (LIST | KILL ip:port | GETNAME | SETNAME connection-name)\");\n    }\n}\n\n/* Rewrite the command vector of the client. All the new objects should \n * be independent. The old command vector is freed. */\nvoid rewriteClientCommandVector(client *c, int argc, ...) {\n    va_list ap;\n    int j;\n    robj **argv; /* The new argument vector */\n\n    argv = dalloc(sizeof(robj*)*argc);\n    va_start(ap,argc);\n    for (j = 0; j < argc; j++) {\n        robj *a;\n        a = va_arg(ap, robj*);\n        argv[j] = a;\n    }\n    /* We free the objects in the original vector at the end. */\n    for (j = 0; j < c->argc; j++) freeObject(c->argv[j]);\n    dfree(c->argv);\n    /* Replace argv and argc with our new versions. */\n    c->argv = argv;\n    c->argc = argc;\n    c->cmd = lookupCommandOrOriginal(c->argv[0]->ptr);\n    serverAssertWithInfo(c,NULL,c->cmd != NULL);\n    va_end(ap);\n}\n\n/* Completely replace the client command vector with the provided one. */\nvoid replaceClientCommandVector(client *c, int argc, robj **argv) {\n    freeClientArgv(c);\n    dfree(c->argv);\n    c->argv = argv;\n    c->argc = argc;\n    c->cmd = lookupCommandOrOriginal(c->argv[0]->ptr);\n    serverAssertWithInfo(c,NULL,c->cmd != NULL);\n}\n\n/* Rewrite a single item in the command vector.\n * The new val should be independent, and the old freed.\n *\n * It is possible to specify an argument over the current size of the\n * argument vector: in this case the array of objects gets reallocated\n * and c->argc set to the max value. However it's up to the caller to\n *\n * 1. Make sure there are no \"holes\" and all the arguments are set.\n * 2. If the original argument vector was longer than the one we\n *    want to end with, it's up to the caller to set c->argc and\n *    free the no longer used objects on c->argv. */\nvoid rewriteClientCommandArgument(client *c, int i, robj *newval) {\n    robj *oldval;\n\n    if (i >= c->argc) {\n        c->argv = drealloc(c->argv,sizeof(robj*)*(i+1));\n        c->argc = i+1;\n        c->argv[i] = NULL;\n    }\n    oldval = c->argv[i];\n    c->argv[i] = newval;\n    if (oldval) freeObject(oldval);\n\n    /* If this is the command name make sure to fix c->cmd. */\n    if (i == 0) {\n        c->cmd = lookupCommandOrOriginal(c->argv[0]->ptr);\n        serverAssertWithInfo(c,NULL,c->cmd != NULL);\n    }\n}\n\n/* This function returns the number of bytes that Redis is virtually\n * using to store the reply still not read by the client.\n * It is \"virtual\" since the reply output list may contain objects that\n * are shared and are not really using additional memory.\n *\n * The function returns the total sum of the length of all the objects\n * stored in the output list, plus the memory used to allocate every\n * list node. The static reply buffer is not taken into account since it\n * is allocated anyway.\n *\n * Note: this function is very fast so can be called as many time as\n * the caller wishes. The main usage of this function currently is\n * enforcing the client output length limits. */\nunsigned long getClientOutputBufferMemoryUsage(client *c) {\n    unsigned long list_item_size = sizeof(dlistNode)+sizeof(robj);\n\n    return c->reply_bytes + (list_item_size*dlistLength(c->reply));\n}\n\n/* Get the class of a client, used in order to enforce limits to different\n * classes of clients.\n *\n * The function will return one of the following:\n * CLIENT_TYPE_NORMAL -> Normal client\n * CLIENT_TYPE_SLAVE  -> Slave or client executing MONITOR command\n * CLIENT_TYPE_PUBSUB -> Client subscribed to Pub/Sub channels\n * CLIENT_TYPE_MASTER -> The client representing our replication master.\n */\nint getClientType(client *c) {\n    if (c->flags & CLIENT_MASTER) return CLIENT_TYPE_MASTER;\n    if ((c->flags & CLIENT_SLAVE) && !(c->flags & CLIENT_MONITOR))\n        return CLIENT_TYPE_SLAVE;\n    if (c->flags & CLIENT_PUBSUB) return CLIENT_TYPE_PUBSUB;\n    return CLIENT_TYPE_NORMAL;\n}\n\nint getClientTypeByName(char *name) {\n    if (!strcasecmp(name,\"normal\")) return CLIENT_TYPE_NORMAL;\n    else if (!strcasecmp(name,\"slave\")) return CLIENT_TYPE_SLAVE;\n    else if (!strcasecmp(name,\"pubsub\")) return CLIENT_TYPE_PUBSUB;\n    else if (!strcasecmp(name,\"master\")) return CLIENT_TYPE_MASTER;\n    else return -1;\n}\n\nchar *getClientTypeName(int class) {\n    switch(class) {\n    case CLIENT_TYPE_NORMAL: return \"normal\";\n    case CLIENT_TYPE_SLAVE:  return \"slave\";\n    case CLIENT_TYPE_PUBSUB: return \"pubsub\";\n    case CLIENT_TYPE_MASTER: return \"master\";\n    default:                       return NULL;\n    }\n}\n\n/* The function checks if the client reached output buffer soft or hard\n * limit, and also update the state needed to check the soft limit as\n * a side effect.\n *\n * Return value: non-zero if the client reached the soft or the hard limit.\n *               Otherwise zero is returned. */\nint checkClientOutputBufferLimits(client *c) {\n    int soft = 0, hard = 0, class;\n    unsigned long used_mem = getClientOutputBufferMemoryUsage(c);\n\n    class = getClientType(c);\n    /* For the purpose of output buffer limiting, masters are handled\n     * like normal clients. */\n    if (class == CLIENT_TYPE_MASTER) class = CLIENT_TYPE_NORMAL;\n\n    if (server.client_obuf_limits[class].hard_limit_bytes &&\n        used_mem >= server.client_obuf_limits[class].hard_limit_bytes)\n        hard = 1;\n    if (server.client_obuf_limits[class].soft_limit_bytes &&\n        used_mem >= server.client_obuf_limits[class].soft_limit_bytes)\n        soft = 1;\n\n    /* We need to check if the soft limit is reached continuously for the\n     * specified amount of seconds. */\n    if (soft) {\n        if (c->obuf_soft_limit_reached_time == 0) {\n            c->obuf_soft_limit_reached_time = c->vel->unixtime;\n            soft = 0; /* First time we see the soft limit reached */\n        } else {\n            time_t elapsed = c->vel->unixtime - c->obuf_soft_limit_reached_time;\n\n            if (elapsed <=\n                server.client_obuf_limits[class].soft_limit_seconds) {\n                soft = 0; /* The client still did not reached the max number of\n                             seconds for the soft limit to be considered\n                             reached. */\n            }\n        }\n    } else {\n        c->obuf_soft_limit_reached_time = 0;\n    }\n    return soft || hard;\n}\n\n/* Asynchronously close a client if soft or hard limit is reached on the\n * output buffer size. The caller can check if the client will be closed\n * checking if the client CLIENT_CLOSE_ASAP flag is set.\n *\n * Note: we need to close the client asynchronously because this function is\n * called from contexts where the client can't be freed safely, i.e. from the\n * lower level functions pushing data inside the client output buffers. */\nvoid asyncCloseClientOnOutputBufferLimitReached(client *c) {\n    ASSERT(c->reply_bytes < SIZE_MAX-(1024*64));\n    if (c->reply_bytes == 0 || c->flags & CLIENT_CLOSE_ASAP) return;\n    if (checkClientOutputBufferLimits(c)) {\n        sds client = catClientInfoString(sdsempty(),c);\n\n        freeClientAsync(c);\n        log_warn(\"Client %s scheduled to be closed ASAP for overcoming of output buffer limits.\", client);\n        sdsfree(client);\n    }\n}\n\n/* Helper function used by freeMemoryIfNeeded() in order to flush slaves\n * output buffers without returning control to the event loop.\n * This is also called by SHUTDOWN for a best-effort attempt to send\n * slaves the latest writes. */\nvoid flushSlavesOutputBuffers(void) {\n    dlistIter li;\n    dlistNode *ln;\n\n    dlistRewind(repl.slaves,&li);\n    while((ln = dlistNext(&li))) {\n        client *slave = dlistNodeValue(ln);\n        int events;\n\n        /* Note that the following will not flush output buffers of slaves\n         * in STATE_ONLINE but having put_online_on_ack set to true: in this\n         * case the writable event is never installed, since the purpose\n         * of put_online_on_ack is to postpone the moment it is installed.\n         * This is what we want since slaves in this state should not receive\n         * writes before the first ACK. */\n        events = aeGetFileEvents(repl.vel.el,slave->conn->sd);\n        if (events & AE_WRITABLE &&\n            slave->replstate == SLAVE_STATE_ONLINE &&\n            clientHasPendingReplies(slave))\n        {\n            writeToClient(slave->conn->sd,slave,0);\n        }\n    }\n}\n\n/* Pause clients up to the specified unixtime (in ms). While clients\n * are paused no command is processed from clients, so the data set can't\n * change during that time.\n *\n * However while this function pauses normal and Pub/Sub clients, slaves are\n * still served, so this function can be used on server upgrades where it is\n * required that slaves process the latest bytes from the replication stream\n * before being turned to masters.\n *\n * This function is also internally used by Redis Cluster for the manual\n * failover procedure implemented by CLUSTER FAILOVER.\n *\n * The function always succeed, even if there is already a pause in progress.\n * In such a case, the pause is extended if the duration is more than the\n * time left for the previous duration. However if the duration is smaller\n * than the time left for the previous pause, no change is made to the\n * left duration. */\nvoid pauseClients(vr_eventloop *vel, long long end) {\n    if (vel == NULL) return;\n\n    if (!vel->clients_paused || end > vel->clients_pause_end_time)\n        vel->clients_pause_end_time = end;\n    vel->clients_paused = 1;\n}\n\n/* Return non-zero if clients are currently paused. As a side effect the\n * function checks if the pause time was reached and clear it. */\nint clientsArePaused(vr_eventloop *vel) {\n    if (vel->clients_paused &&\n        vel->clients_pause_end_time < vel->mstime)\n    {\n        dlistNode *ln;\n        dlistIter li;\n        client *c;\n\n        vel->clients_paused = 0;\n\n        /* Put all the clients in the unblocked clients queue in order to\n         * force the re-processing of the input buffer if any. */\n        dlistRewind(vel->clients,&li);\n        while ((ln = dlistNext(&li)) != NULL) {\n            c = dlistNodeValue(ln);\n\n            /* Don't touch slaves and blocked clients. The latter pending\n             * requests be processed when unblocked. */\n            if (c->flags & (CLIENT_SLAVE|CLIENT_BLOCKED)) continue;\n            c->flags |= CLIENT_UNBLOCKED;\n            dlistAddNodeTail(vel->unblocked_clients,c);\n        }\n    }\n    return vel->clients_paused;\n}\n\n/* This function is called by Redis in order to process a few events from\n * time to time while blocked into some not interruptible operation.\n * This allows to reply to clients with the -LOADING error while loading the\n * data set at startup or after a full resynchronization with the master\n * and so forth.\n *\n * It calls the event loop in order to process a few events. Specifically we\n * try to call the event loop 4 times as long as we receive acknowledge that\n * some event was processed, in order to go forward with the accept, read,\n * write, close sequence needed to serve a client.\n *\n * The function returns the total number of events processed. */\nint processEventsWhileBlocked(vr_eventloop *vel) {\n    int iterations = 4; /* See the function top-comment. */\n    int count = 0;\n    while (iterations--) {\n        int events = 0;\n        events += aeProcessEvents(vel->el, AE_FILE_EVENTS|AE_DONT_WAIT);\n        events += handleClientsWithPendingWrites(vel);\n        if (!events) break;\n        count += events;\n    }\n    return count;\n}\n\nint\ncurrent_clients(void)\n{\n    int ccs;\n\n#if defined(__ATOMIC_RELAXED) || defined(HAVE_ATOMIC)\n    ccs = update_curr_clients_add(0);\n#else\n    pthread_mutex_lock(&curr_clients_mutex);\n    ccs = ncurr_cconn;\n    pthread_mutex_unlock(&curr_clients_mutex);\n#endif\n\n    return ccs;\n}\n"
  },
  {
    "path": "src/vr_client.h",
    "content": "#ifndef _VR_CLIENT_H_\n#define _VR_CLIENT_H_\n\n#define NET_MAX_WRITES_PER_EVENT (1024*64)\n\n#define PROTO_MAX_QUERYBUF_LEN  (1024*1024*1024) /* 1GB max query buffer. */\n#define PROTO_IOBUF_LEN         (1024*16)  /* Generic I/O buffer size */\n#define PROTO_REPLY_CHUNK_BYTES (16*1024) /* 16k output buffer */\n#define PROTO_INLINE_MAX_SIZE   (1024*64) /* Max size of inline reads */\n#define PROTO_MBULK_BIG_ARG     (1024*32)\n\n/* Client flags */\n#define CLIENT_SLAVE (1<<0)   /* This client is a slave server */\n#define CLIENT_MASTER (1<<1)  /* This client is a master server */\n#define CLIENT_MONITOR (1<<2) /* This client is a slave monitor, see MONITOR */\n#define CLIENT_MULTI (1<<3)   /* This client is in a MULTI context */\n#define CLIENT_BLOCKED (1<<4) /* The client is waiting in a blocking operation */\n#define CLIENT_DIRTY_CAS (1<<5) /* Watched keys modified. EXEC will fail. */\n#define CLIENT_CLOSE_AFTER_REPLY (1<<6) /* Close after writing entire reply. */\n#define CLIENT_UNBLOCKED (1<<7) /* This client was unblocked and is stored in\n                                  server.unblocked_clients */\n#define CLIENT_LUA (1<<8) /* This is a non connected client used by Lua */\n#define CLIENT_ASKING (1<<9)     /* Client issued the ASKING command */\n#define CLIENT_CLOSE_ASAP (1<<10)/* Close this client ASAP */\n#define CLIENT_UNIX_SOCKET (1<<11) /* Client connected via Unix domain socket */\n#define CLIENT_DIRTY_EXEC (1<<12)  /* EXEC will fail for errors while queueing */\n#define CLIENT_MASTER_FORCE_REPLY (1<<13)  /* Queue replies even if is master */\n#define CLIENT_FORCE_AOF (1<<14)   /* Force AOF propagation of current cmd. */\n#define CLIENT_FORCE_REPL (1<<15)  /* Force replication of current cmd. */\n#define CLIENT_PRE_PSYNC (1<<16)   /* Instance don't understand PSYNC. */\n#define CLIENT_READONLY (1<<17)    /* Cluster client is in read-only state. */\n#define CLIENT_PUBSUB (1<<18)      /* Client is in Pub/Sub mode. */\n#define CLIENT_PREVENT_AOF_PROP (1<<19)  /* Don't propagate to AOF. */\n#define CLIENT_PREVENT_REPL_PROP (1<<20)  /* Don't propagate to slaves. */\n#define CLIENT_PREVENT_PROP (CLIENT_PREVENT_AOF_PROP|CLIENT_PREVENT_REPL_PROP)\n#define CLIENT_PENDING_WRITE (1<<21) /* Client has output to send but a write\n                                        handler is yet not installed. */\n#define CLIENT_REPLY_OFF (1<<22)   /* Don't send replies to client. */\n#define CLIENT_REPLY_SKIP_NEXT (1<<23)  /* Set CLIENT_REPLY_SKIP for next cmd */\n#define CLIENT_REPLY_SKIP (1<<24)  /* Don't send just this reply. */\n#define CLIENT_LUA_DEBUG (1<<25)  /* Run EVAL in debug mode. */\n#define CLIENT_LUA_DEBUG_SYNC (1<<26)  /* EVAL debugging without fork() */\n#define CLIENT_JUMP (1<<27)\n\n#define REDIS_REPLY_CHUNK_BYTES (16*1024) /* 16k output buffer */\n\n/* Client request types */\n#define PROTO_REQ_INLINE 1\n#define PROTO_REQ_MULTIBULK 2\n\n/* Client classes for client limits, currently used only for\n * the max-client-output-buffer limit implementation. */\n#define CLIENT_TYPE_NORMAL 0 /* Normal req-reply clients + MONITORs */\n#define CLIENT_TYPE_SLAVE 1  /* Slaves. */\n#define CLIENT_TYPE_PUBSUB 2 /* Clients subscribed to PubSub channels. */\n#define CLIENT_TYPE_MASTER 3 /* Master. */\n#define CLIENT_TYPE_OBUF_COUNT 3 /* Number of clients to expose to output\n                                    buffer configuration. Just the first\n                                    three: normal, slave, pubsub. */\n\n/* Client block type (btype field in client structure)\n * if CLIENT_BLOCKED flag is set. */\n#define BLOCKED_NONE 0    /* Not blocked, no CLIENT_BLOCKED flag set. */\n#define BLOCKED_LIST 1    /* BLPOP & co. */\n#define BLOCKED_WAIT 2    /* WAIT for synchronous replication. */\n\n/* With multiplexing we need to take per-client state.\n * Clients are taken in a linked list. */\ntypedef struct client {\n    uint64_t id;            /* Client incremental unique ID. */\n    \n    struct conn *conn;\n    vr_eventloop *vel;\n    \n    redisDb *db;            /* Pointer to currently dispatch DB. */\n    int dictid;             /* ID of the currently SELECTed DB. */\n    int scanid;             /* Internal ID of the currently DB for scan */\n    robj *name;             /* As set by CLIENT SETNAME. */\n    sds querybuf;           /* Buffer we use to accumulate client queries. */\n    size_t querybuf_peak;   /* Recent (100ms or more) peak of querybuf size. */\n    int argc;               /* Num of arguments of current command. */\n    robj **argv;            /* Arguments of current command. */\n    struct redisCommand *cmd, *lastcmd;  /* Last command executed. */\n    int reqtype;            /* Request protocol type: PROTO_REQ_* */\n    int multibulklen;       /* Number of multi bulk arguments left to read. */\n    long bulklen;           /* Length of bulk argument in multi bulk request. */\n    dlist *reply;            /* List of reply objects to send to the client. */\n    unsigned long long reply_bytes; /* Tot bytes of objects in reply list. */\n    size_t sentlen;         /* Amount of bytes already sent in the current\n                               buffer or object being sent. */\n    time_t ctime;           /* Client creation time. */\n    time_t lastinteraction; /* Time of the last interaction, used for timeout */\n    time_t obuf_soft_limit_reached_time;\n    int flags;              /* Client flags: CLIENT_* macros. */\n    int authenticated;      /* When requirepass(1) or adminpass(2) is non-NULL. */\n    int replstate;          /* Replication state if this is a slave. */\n    int repl_put_online_on_ack; /* Install slave write handler on ACK. */\n    int repldbfd;           /* Replication DB file descriptor. */\n    off_t repldboff;        /* Replication DB file offset. */\n    off_t repldbsize;       /* Replication DB file size. */\n    sds replpreamble;       /* Replication DB preamble. */\n    long long reploff;      /* Replication offset if this is our master. */\n    long long repl_ack_off; /* Replication ack offset, if this is a slave. */\n    long long repl_ack_time;/* Replication ack time, if this is a slave. */\n    long long psync_initial_offset; /* FULLRESYNC reply offset other slaves\n                                       copying this slave output buffer\n                                       should use. */\n    char replrunid[CONFIG_RUN_ID_SIZE+1]; /* Master run id if is a master. */\n    int slave_listening_port; /* As configured with: SLAVECONF listening-port */\n    int slave_capa;         /* Slave capabilities: SLAVE_CAPA_* bitwise OR. */\n    multiState mstate;      /* MULTI/EXEC state */\n    int btype;              /* Type of blocking op if CLIENT_BLOCKED. */\n    blockingState bpop;     /* blocking state */\n    long long woff;         /* Last write global replication offset. */\n    dlist *watched_keys;     /* Keys WATCHED for MULTI/EXEC CAS */\n    dict *pubsub_channels;  /* channels a client is interested in (SUBSCRIBE) */\n    dlist *pubsub_patterns;  /* patterns a client is interested in (SUBSCRIBE) */\n    sds peerid;             /* Cached peer ID. */\n\n    int curidx;             /* The worker idx that this client current belong to. */\n    int taridx;             /* The target worker idx that this client will jump to */\n    int steps;              /* The steps that this client jumps between workers. */\n    void *cache;            /* Cache data for client to jump between workers. */\n\n    /* Response buffer */\n    int bufpos;\n    char buf[PROTO_REPLY_CHUNK_BYTES];\n} client;\n\ntypedef struct clientBufferLimitsConfig {\n    unsigned long long hard_limit_bytes;\n    unsigned long long soft_limit_bytes;\n    time_t soft_limit_seconds;\n} clientBufferLimitsConfig;\n\n/* networking.c -- Networking and Client related operations */\nclient *createClient(vr_eventloop *vel, struct conn *conn);\nvoid closeTimedoutClients(void);\nvoid freeClient(client *c);\nvoid freeClientAsync(client *c);\nvoid resetClient(client *c);\nvoid sendReplyToClient(aeEventLoop *el, int fd, void *privdata, int mask);\nvoid *addDeferredMultiBulkLength(client *c);\nvoid setDeferredMultiBulkLength(client *c, void *node, long length);\nvoid processInputBuffer(client *c);\nvoid readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask);\nvoid addReplyBulk(client *c, robj *obj);\nvoid addReplyBulkCString(client *c, const char *s);\nvoid addReplyBulkCBuffer(client *c, const void *p, size_t len);\nvoid addReplyBulkLongLong(client *c, long long ll);\nvoid addReply(client *c, robj *obj);\nvoid addReplySds(client *c, sds s);\nvoid addReplyString(client *c, const char *s, size_t len);\nvoid addReplyErrorLength(client *c, const char *s, size_t len);\nvoid addReplyBulkSds(client *c, sds s);\nvoid addReplyError(client *c, const char *err);\nvoid addReplyStatusLength(client *c, const char *s, size_t len);\nvoid addReplyStatus(client *c, const char *status);\nvoid addReplyDouble(client *c, double d);\nvoid addReplyHumanLongDouble(client *c, long double d);\nvoid addReplyLongLong(client *c, long long ll);\nvoid addReplyMultiBulkLen(client *c, long length);\nvoid copyClientOutputBuffer(client *dst, client *src);\nvoid *dupClientReplyValue(void *o);\nvoid freeClientReplyValue(void *o);\nvoid getClientsMaxBuffers(vr_eventloop *vel, unsigned long *longest_output_list,\n                          unsigned long *biggest_input_buffer);\nchar *getClientPeerId(client *client);\nsds catClientInfoString(sds s, client *client);\nsds getAllClientsInfoString(vr_eventloop *vel);\nvoid clientCommand(client *c);\nvoid rewriteClientCommandVector(client *c, int argc, ...);\nvoid rewriteClientCommandArgument(client *c, int i, robj *newval);\nvoid replaceClientCommandVector(client *c, int argc, robj **argv);\nunsigned long getClientOutputBufferMemoryUsage(client *c);\nvoid freeClientsInAsyncFreeQueue(vr_eventloop *vel);\nvoid asyncCloseClientOnOutputBufferLimitReached(client *c);\nint getClientType(client *c);\nint getClientTypeByName(char *name);\nchar *getClientTypeName(int class);\nvoid flushSlavesOutputBuffers(void);\nvoid disconnectSlaves(void);\nint listenToPort(int port, int *fds, int *count);\nvoid pauseClients(vr_eventloop *vel, long long duration);\nint clientsArePaused(vr_eventloop *vel);\nint processEventsWhileBlocked(vr_eventloop *vel);\nint handleClientsWithPendingWrites(vr_eventloop *vel);\nint clientHasPendingReplies(client *c);\nvoid unlinkClientFromEventloop(client *c);\nvoid linkClientToEventloop(client *c,vr_eventloop *vel);\nvoid unlinkClient(client *c);\nint writeToClient(int fd, client *c, int handler_installed);\n\n#ifdef __GNUC__\nvoid addReplyErrorFormat(client *c, const char *fmt, ...)\n    __attribute__((format(printf, 2, 3)));\nvoid addReplyStatusFormat(client *c, const char *fmt, ...)\n    __attribute__((format(printf, 2, 3)));\n#else\nvoid addReplyErrorFormat(client *c, const char *fmt, ...);\nvoid addReplyStatusFormat(client *c, const char *fmt, ...);\n#endif\n\nextern int ncurr_cconn;\n\n#if defined(__ATOMIC_RELAXED)\n#define update_curr_clients_add(__n) __atomic_add_fetch(&ncurr_cconn, (__n), __ATOMIC_RELAXED)\n#define update_curr_clients_sub(__n) __atomic_sub_fetch(&ncurr_cconn, (__n), __ATOMIC_RELAXED)\n#elif defined(HAVE_ATOMIC)\n#define update_curr_clients_add(__n) __sync_add_and_fetch(&ncurr_cconn, (__n))\n#define update_curr_clients_sub(__n) __sync_sub_and_fetch(&ncurr_cconn, (__n))\n#else\npthread_mutex_t curr_clients_mutex = PTHREAD_MUTEX_INITIALIZER;\n\n#define update_curr_clients_add(__n) do {       \\\n    pthread_mutex_lock(&curr_clients_mutex);    \\\n    ncurr_cconn += (__n);                       \\\n    pthread_mutex_unlock(&curr_clients_mutex);  \\\n} while(0)\n\n#define update_curr_clients_sub(__n) do {       \\\n    pthread_mutex_lock(&curr_clients_mutex);    \\\n    ncurr_cconn -= (__n);                       \\\n    pthread_mutex_unlock(&curr_clients_mutex);  \\\n} while(0)\n#endif\n\nint current_clients(void);\n\n#endif\n"
  },
  {
    "path": "src/vr_command.c",
    "content": "#include <vr_core.h>\n\n/* Command table. sds string -> command struct pointer. */\ndictType commandTableDictType = {\n    dictSdsCaseHash,           /* hash function */\n    NULL,                      /* key dup */\n    NULL,                      /* val dup */\n    dictSdsKeyCaseCompare,     /* key compare */\n    dictSdsDestructor,         /* key destructor */\n    NULL                       /* val destructor */\n};\n\n/* Our command table.\n *\n * Every entry is composed of the following fields:\n *\n * name: a string representing the command name.\n * function: pointer to the C function implementing the command.\n * arity: number of arguments, it is possible to use -N to say >= N\n * sflags: command flags as string. See below for a table of flags.\n * flags: flags as bitmask. Computed by Redis using the 'sflags' field.\n * get_keys_proc: an optional function to get key arguments from a command.\n *                This is only used when the following three fields are not\n *                enough to specify what arguments are keys.\n * first_key_index: first argument that is a key\n * last_key_index: last argument that is a key\n * key_step: step to get all the keys from first to last argument. For instance\n *           in MSET the step is two since arguments are key,val,key,val,...\n * microseconds: microseconds of total execution time for this command.\n * calls: total number of calls of this command.\n *\n * The flags, microseconds and calls fields are computed by Redis and should\n * always be set to zero.\n *\n * Command flags are expressed using strings where every character represents\n * a flag. Later the populateCommandTable() function will take care of\n * populating the real 'flags' field using this characters.\n *\n * This is the meaning of the flags:\n *\n * w: write command (may modify the key space).\n * r: read command  (will never modify the key space).\n * m: may increase memory usage once called. Don't allow if out of memory.\n * a: admin command, like SAVE or SHUTDOWN.\n * p: Pub/Sub related command.\n * f: force replication of this command, regardless of server.dirty.\n * s: command not allowed in scripts.\n * R: random command. Command is not deterministic, that is, the same command\n *    with the same arguments, with the same key space, may have different\n *    results. For instance SPOP and RANDOMKEY are two random commands.\n * S: Sort command output array if called from script, so that the output\n *    is deterministic.\n * l: Allow command while loading the database.\n * t: Allow command while a slave has stale data but is not allowed to\n *    server this data. Normally no command is accepted in this condition\n *    but just a few.\n * M: Do not automatically propagate the command on MONITOR.\n * k: Perform an implicit ASKING for this command, so the command will be\n *    accepted in cluster mode if the slot is marked as 'importing'.\n * F: Fast command: O(1) or O(log(N)) command that should never delay\n *    its execution as long as the kernel scheduler is giving us time.\n *    Note that commands that may trigger a DEL as a side effect (like SET)\n *    are not fast commands.\n */\nstruct redisCommand redisCommandTable[] = {\n    /*Connectong*/\n    {\"ping\",pingCommand,-1,\"tF\",0,NULL,0,0,0,0,0},\n    {\"echo\",echoCommand,2,\"F\",0,NULL,0,0,0,0,0},\n    {\"select\",selectCommand,2,\"lF\",0,NULL,0,0,0,0,0},\n    {\"auth\",authCommand,2,\"sltF\",0,NULL,0,0,0,0,0},\n    {\"admin\",adminCommand,2,\"sltF\",0,NULL,0,0,0,0,0},\n    /* Server */\n    {\"info\",infoCommand,-1,\"lt\",0,NULL,0,0,0,0,0},\n    {\"flushdb\",flushdbCommand,1,\"w\",0,NULL,0,0,0,0,0},\n    {\"flushall\",flushallCommand,1,\"w\",0,NULL,0,0,0,0,0},\n    {\"time\",timeCommand,1,\"RF\",0,NULL,0,0,0,0,0},\n    {\"dbsize\",dbsizeCommand,1,\"rF\",0,NULL,0,0,0,0,0},\n    {\"command\",commandCommand,0,\"lt\",0,NULL,0,0,0,0,0},\n    {\"config\",configCommand,-2,\"lat\",0,NULL,0,0,0,0,0},\n    {\"client\",clientCommand,-2,\"as\",0,NULL,0,0,0,0,0},\n    {\"slowlog\",slowlogCommand,-2,\"a\",0,NULL,0,0,0,0,0},\n    /* Key */\n    {\"del\",delCommand,-2,\"w\",0,NULL,1,-1,1,0,0},\n    {\"exists\",existsCommand,-2,\"rF\",0,NULL,1,-1,1,0,0},\n    {\"ttl\",ttlCommand,2,\"rF\",0,NULL,1,1,1,0,0},\n    {\"pttl\",pttlCommand,2,\"rF\",0,NULL,1,1,1,0,0},\n    {\"expire\",expireCommand,3,\"wF\",0,NULL,1,1,1,0,0},\n    {\"expireat\",expireatCommand,3,\"wF\",0,NULL,1,1,1,0,0},\n    {\"pexpire\",pexpireCommand,3,\"wF\",0,NULL,1,1,1,0,0},\n    {\"pexpireat\",pexpireatCommand,3,\"wF\",0,NULL,1,1,1,0,0},\n    {\"persist\",persistCommand,2,\"wF\",0,NULL,1,1,1,0,0},\n    {\"randomkey\",randomkeyCommand,1,\"rR\",0,NULL,0,0,0,0,0},\n    {\"type\",typeCommand,2,\"rF\",0,NULL,1,1,1,0,0},\n    {\"keys\",keysCommand,2,\"rS\",0,NULL,0,0,0,0,0},\n    {\"scan\",scanCommand,-2,\"rR\",0,NULL,0,0,0,0,0},\n    {\"object\",objectCommand,3,\"r\",0,NULL,2,2,2,0,0},\n    /* String */\n    {\"get\",getCommand,2,\"rF\",0,NULL,1,1,1,0,0},\n    {\"set\",setCommand,-3,\"wm\",0,NULL,1,1,1,0,0},\n    {\"setnx\",setnxCommand,3,\"wmF\",0,NULL,1,1,1,0,0},\n    {\"setex\",setexCommand,4,\"wm\",0,NULL,1,1,1,0,0},\n    {\"psetex\",psetexCommand,4,\"wm\",0,NULL,1,1,1,0,0},\n    {\"incr\",incrCommand,2,\"wmF\",0,NULL,1,1,1,0,0},\n    {\"decr\",decrCommand,2,\"wmF\",0,NULL,1,1,1,0,0},\n    {\"incrby\",incrbyCommand,3,\"wmF\",0,NULL,1,1,1,0,0},\n    {\"decrby\",decrbyCommand,3,\"wmF\",0,NULL,1,1,1,0,0},\n    {\"append\",appendCommand,3,\"wm\",0,NULL,1,1,1,0,0},\n    {\"strlen\",strlenCommand,2,\"rF\",0,NULL,1,1,1,0,0},\n    {\"getset\",getsetCommand,3,\"wm\",0,NULL,1,1,1,0,0},\n    {\"incrbyfloat\",incrbyfloatCommand,3,\"wmF\",0,NULL,1,1,1,0,0},\n    {\"setbit\",setbitCommand,4,\"wm\",0,NULL,1,1,1,0,0},\n    {\"getbit\",getbitCommand,3,\"rF\",0,NULL,1,1,1,0,0},\n    {\"setrange\",setrangeCommand,4,\"wm\",0,NULL,1,1,1,0,0},\n    {\"getrange\",getrangeCommand,4,\"r\",0,NULL,1,1,1,0,0},\n    {\"bitcount\",bitcountCommand,-2,\"r\",0,NULL,1,1,1,0,0},\n    {\"bitpos\",bitposCommand,-3,\"r\",0,NULL,1,1,1,0,0},\n    {\"mget\",mgetCommand,-2,\"r\",0,NULL,1,-1,1,0,0},\n    {\"mset\",msetCommand,-3,\"wm\",0,NULL,1,-1,2,0,0},\n    /* Hash */\n    {\"hset\",hsetCommand,4,\"wmF\",0,NULL,1,1,1,0,0},\n    {\"hget\",hgetCommand,3,\"rF\",0,NULL,1,1,1,0,0},\n    {\"hlen\",hlenCommand,2,\"rF\",0,NULL,1,1,1,0,0},\n    {\"hdel\",hdelCommand,-3,\"wF\",0,NULL,1,1,1,0,0},\n    {\"hexists\",hexistsCommand,3,\"rF\",0,NULL,1,1,1,0,0},\n    {\"hkeys\",hkeysCommand,2,\"rS\",0,NULL,1,1,1,0,0},\n    {\"hvals\",hvalsCommand,2,\"rS\",0,NULL,1,1,1,0,0},\n    {\"hgetall\",hgetallCommand,2,\"r\",0,NULL,1,1,1,0,0},\n    {\"hincrby\",hincrbyCommand,4,\"wmF\",0,NULL,1,1,1,0,0},\n    {\"hincrbyfloat\",hincrbyfloatCommand,4,\"wmF\",0,NULL,1,1,1,0,0},\n    {\"hmget\",hmgetCommand,-3,\"r\",0,NULL,1,1,1,0,0},\n    {\"hmset\",hmsetCommand,-4,\"wm\",0,NULL,1,1,1,0,0},\n    {\"hsetnx\",hsetnxCommand,4,\"wmF\",0,NULL,1,1,1,0,0},\n    {\"hstrlen\",hstrlenCommand,3,\"rF\",0,NULL,1,1,1,0,0},\n    {\"hscan\",hscanCommand,-3,\"rR\",0,NULL,1,1,1,0,0},\n    /* List */\n    {\"rpush\",rpushCommand,-3,\"wmF\",0,NULL,1,1,1,0,0},\n    {\"lpush\",lpushCommand,-3,\"wmF\",0,NULL,1,1,1,0,0},\n    {\"lrange\",lrangeCommand,4,\"r\",0,NULL,1,1,1,0,0},\n    {\"rpop\",rpopCommand,2,\"wF\",0,NULL,1,1,1,0,0},\n    {\"lpop\",lpopCommand,2,\"wF\",0,NULL,1,1,1,0,0},\n    {\"llen\",llenCommand,2,\"rF\",0,NULL,1,1,1,0,0},\n    {\"lrem\",lremCommand,4,\"w\",0,NULL,1,1,1,0,0},\n    {\"ltrim\",ltrimCommand,4,\"w\",0,NULL,1,1,1,0,0},\n    {\"lindex\",lindexCommand,3,\"r\",0,NULL,1,1,1,0,0},\n    {\"lset\",lsetCommand,4,\"wm\",0,NULL,1,1,1,0,0},\n    /* Set */\n    {\"sadd\",saddCommand,-3,\"wmF\",0,NULL,1,1,1,0,0},\n    {\"smembers\",smembersCommand,2,\"rS\",0,NULL,1,1,1,0,0},\n    {\"scard\",scardCommand,2,\"rF\",0,NULL,1,1,1,0,0},\n    {\"srem\",sremCommand,-3,\"wF\",0,NULL,1,1,1,0,0},\n    {\"spop\",spopCommand,-2,\"wRsF\",0,NULL,1,1,1,0,0},\n    {\"sismember\",sismemberCommand,3,\"rF\",0,NULL,1,1,1,0,0},\n    {\"sscan\",sscanCommand,-3,\"rR\",0,NULL,1,1,1,0,0},\n    {\"sunion\",sunionCommand,-2,\"rS\",0,NULL,1,-1,1,0,0},\n    {\"sunionstore\",sunionstoreCommand,-3,\"wm\",0,NULL,1,-1,1,0,0},\n    {\"sdiff\",sdiffCommand,-2,\"rS\",0,NULL,1,-1,1,0,0},\n    {\"sdiffstore\",sdiffstoreCommand,-3,\"wm\",0,NULL,1,-1,1,0,0},\n    {\"sinter\",sinterCommand,-2,\"rS\",0,NULL,1,-1,1,0,0},\n    {\"sinterstore\",sinterstoreCommand,-3,\"wm\",0,NULL,1,-1,1,0,0},\n    /* SortedSet */\n    {\"zadd\",zaddCommand,-4,\"wmF\",0,NULL,1,1,1,0,0},\n    {\"zincrby\",zincrbyCommand,4,\"wmF\",0,NULL,1,1,1,0,0},\n    {\"zrange\",zrangeCommand,-4,\"r\",0,NULL,1,1,1,0,0},\n    {\"zrevrange\",zrevrangeCommand,-4,\"r\",0,NULL,1,1,1,0,0},\n    {\"zrem\",zremCommand,-3,\"wF\",0,NULL,1,1,1,0,0},\n    {\"zcard\",zcardCommand,2,\"rF\",0,NULL,1,1,1,0,0},\n    {\"zcount\",zcountCommand,4,\"rF\",0,NULL,1,1,1,0,0},\n    {\"zrangebyscore\",zrangebyscoreCommand,-4,\"r\",0,NULL,1,1,1,0,0},\n    {\"zrevrangebyscore\",zrevrangebyscoreCommand,-4,\"r\",0,NULL,1,1,1,0,0},\n    {\"zrank\",zrankCommand,3,\"rF\",0,NULL,1,1,1,0,0},\n    {\"zrevrank\",zrevrankCommand,3,\"rF\",0,NULL,1,1,1,0,0},\n    {\"zscore\",zscoreCommand,3,\"rF\",0,NULL,1,1,1,0,0},\n    {\"zremrangebyscore\",zremrangebyscoreCommand,4,\"w\",0,NULL,1,1,1,0,0},\n    {\"zremrangebyrank\",zremrangebyrankCommand,4,\"w\",0,NULL,1,1,1,0,0},\n    {\"zremrangebylex\",zremrangebylexCommand,4,\"w\",0,NULL,1,1,1,0,0},\n    {\"zscan\",zscanCommand,-3,\"rR\",0,NULL,1,1,1,0,0},\n    /* HyperLogLog */\n    {\"pfadd\",pfaddCommand,-2,\"wmF\",0,NULL,1,1,1,0,0},\n    {\"pfcount\",pfcountCommand,-2,\"r\",0,NULL,1,-1,1,0,0}\n};\n\n/* Populates the Redis Command Table starting from the hard coded list\n * we have on top of redis.c file. */\nvoid populateCommandTable(void) {\n    int j;\n    int numcommands = sizeof(redisCommandTable)/sizeof(struct redisCommand);\n\n    for (j = 0; j < numcommands; j++) {\n        struct redisCommand *c = redisCommandTable+j;\n        char *f = c->sflags;\n        int retval1;\n\n        while(*f != '\\0') {\n            switch(*f) {\n            case 'w': c->flags |= CMD_WRITE; break;\n            case 'r': c->flags |= CMD_READONLY; break;\n            case 'm': c->flags |= CMD_DENYOOM; break;\n            case 'a': c->flags |= CMD_ADMIN; break;\n            case 'p': c->flags |= CMD_PUBSUB; break;\n            case 's': c->flags |= CMD_NOSCRIPT; break;\n            case 'R': c->flags |= CMD_RANDOM; break;\n            case 'S': c->flags |= CMD_SORT_FOR_SCRIPT; break;\n            case 'l': c->flags |= CMD_LOADING; break;\n            case 't': c->flags |= CMD_STALE; break;\n            case 'M': c->flags |= CMD_SKIP_MONITOR; break;\n            case 'k': c->flags |= CMD_ASKING; break;\n            case 'F': c->flags |= CMD_FAST; break;\n            default: serverPanic(\"Unsupported command flag\"); break;\n            }\n            f++;\n        }\n\n        retval1 = dictAdd(server.commands, sdsnew(c->name), c);\n        ASSERT(retval1 == DICT_OK);\n\n        c->idx = j;\n    }\n}\n\nint populateCommandsNeedAdminpass(void) {\n    struct darray commands_need_adminpass;\n    sds *command_name;\n    struct redisCommand *command;\n\n    darray_init(&commands_need_adminpass,1,sizeof(sds));\n    conf_server_get(CONFIG_SOPN_COMMANDSNAP,&commands_need_adminpass);\n    while (darray_n(&commands_need_adminpass)) {\n        command_name = darray_pop(&commands_need_adminpass);\n        command = lookupCommand(*command_name);\n        if (command == NULL) {\n            log_error(\"Unknow command %s for commands-need-amdminpass\",\n                command_name);\n            return VR_ERROR;\n        }\n        command->needadmin = 1;\n        sdsfree(*command_name);\n    }\n    darray_deinit(&commands_need_adminpass);\n\n    return VR_OK;\n}\n\nstruct redisCommand *lookupCommand(sds name) {\n    return dictFetchValue(server.commands, name);\n}\n\n/* Lookup the command in the current table, if not found also check in\n * the original table containing the original command names unaffected by\n * redis.conf rename-command statement.\n *\n * This is used by functions rewriting the argument vector such as\n * rewriteClientCommandVector() in order to set client->cmd pointer\n * correctly even if the command was renamed. */\nstruct redisCommand *lookupCommandOrOriginal(sds name) {\n    struct redisCommand *cmd = dictFetchValue(server.commands, name);\n\n    if (!cmd) cmd = dictFetchValue(server.orig_commands,name);\n    return cmd;\n}\n\nstruct redisCommand *lookupCommandByCString(char *s) {\n    struct redisCommand *cmd;\n    sds name = sdsnew(s);\n\n    cmd = dictFetchValue(server.commands, name);\n    sdsfree(name);\n    return cmd;\n}\n\n\n/* Call() is the core of Redis execution of a command.\n *\n * The following flags can be passed:\n * CMD_CALL_NONE        No flags.\n * CMD_CALL_SLOWLOG     Check command speed and log in the slow log if needed.\n * CMD_CALL_STATS       Populate command stats.\n * CMD_CALL_PROPAGATE_AOF   Append command to AOF if it modified the dataset\n *                          or if the client flags are forcing propagation.\n * CMD_CALL_PROPAGATE_REPL  Send command to salves if it modified the dataset\n *                          or if the client flags are forcing propagation.\n * CMD_CALL_PROPAGATE   Alias for PROPAGATE_AOF|PROPAGATE_REPL.\n * CMD_CALL_FULL        Alias for SLOWLOG|STATS|PROPAGATE.\n *\n * The exact propagation behavior depends on the client flags.\n * Specifically:\n *\n * 1. If the client flags CLIENT_FORCE_AOF or CLIENT_FORCE_REPL are set\n *    and assuming the corresponding CMD_CALL_PROPAGATE_AOF/REPL is set\n *    in the call flags, then the command is propagated even if the\n *    dataset was not affected by the command.\n * 2. If the client flags CLIENT_PREVENT_REPL_PROP or CLIENT_PREVENT_AOF_PROP\n *    are set, the propagation into AOF or to slaves is not performed even\n *    if the command modified the dataset.\n *\n * Note that regardless of the client flags, if CMD_CALL_PROPAGATE_AOF\n * or CMD_CALL_PROPAGATE_REPL are not set, then respectively AOF or\n * slaves propagation will never occur.\n *\n * Client flags are modified by the implementation of a given command\n * using the following API:\n *\n * forceCommandPropagation(client *c, int flags);\n * preventCommandPropagation(client *c);\n * preventCommandAOF(client *c);\n * preventCommandReplication(client *c);\n *\n */\nvoid call(client *c, int flags) {\n    long long dirty, start, duration;\n    int client_old_flags = c->flags;\n\n    /* Sent the command to clients in MONITOR mode, only if the commands are\n     * not generated from reading an AOF. */\n    if (dlistLength(server.monitors) &&\n        !server.loading &&\n        !(c->cmd->flags & (CMD_SKIP_MONITOR|CMD_ADMIN)))\n    {\n        replicationFeedMonitors(c,server.monitors,c->db->id,c->argv,c->argc);\n    }\n\n    /* Initialization: clear the flags that must be set by the command on\n     * demand, and initialize the array for additional commands propagation. */\n    c->flags &= ~(CLIENT_FORCE_AOF|CLIENT_FORCE_REPL|CLIENT_PREVENT_PROP);\n    redisOpArrayInit(&server.also_propagate);\n\n    /* Call the command. */\n    dirty = c->vel->dirty;\n    start = vr_usec_now();\n    c->cmd->proc(c);\n    duration = vr_usec_now()-start;\n    dirty = c->vel->dirty-dirty;\n    if (dirty < 0) dirty = 0;\n\n    /* When EVAL is called loading the AOF we don't want commands called\n     * from Lua to go into the slowlog or to populate statistics. */\n    if (server.loading && c->flags & CLIENT_LUA)\n        flags &= ~(CMD_CALL_SLOWLOG | CMD_CALL_STATS);\n\n    /* If the caller is Lua, we want to force the EVAL caller to propagate\n     * the script if the command flag or client flag are forcing the\n     * propagation. */\n    if (c->flags & CLIENT_LUA && server.lua_caller) {\n        if (c->flags & CLIENT_FORCE_REPL)\n            server.lua_caller->flags |= CLIENT_FORCE_REPL;\n        if (c->flags & CLIENT_FORCE_AOF)\n            server.lua_caller->flags |= CLIENT_FORCE_AOF;\n    }\n\n    /* Log the command into the Slow log if needed, and populate the\n     * per-command statistics that we show in INFO commandstats. */\n    if (flags & CMD_CALL_SLOWLOG && c->cmd->proc != execCommand) {\n        //char *latency_event = (c->cmd->flags & CMD_FAST) ?\n        //                      \"fast-command\" : \"command\";\n        //latencyAddSampleIfNeeded(latency_event,duration/1000);\n        slowlogPushEntryIfNeeded(c->vel,c->argv,c->argc,duration);\n    }\n    if (flags & CMD_CALL_STATS) {\n        commandStats *cstats = darray_get(c->vel->cstable,c->lastcmd->idx);\n        cstats->microseconds += duration;\n        cstats->calls++;\n    }\n\n    /* Propagate the command into the AOF and replication link */\n    if (flags & CMD_CALL_PROPAGATE &&\n        (c->flags & CLIENT_PREVENT_PROP) != CLIENT_PREVENT_PROP)\n    {\n        int propagate_flags = PROPAGATE_NONE;\n\n        /* Check if the command operated changes in the data set. If so\n         * set for replication / AOF propagation. */\n        if (dirty) propagate_flags |= (PROPAGATE_AOF|PROPAGATE_REPL);\n\n        /* If the client forced AOF / replication of the command, set\n         * the flags regardless of the command effects on the data set. */\n        if (c->flags & CLIENT_FORCE_REPL) propagate_flags |= PROPAGATE_REPL;\n        if (c->flags & CLIENT_FORCE_AOF) propagate_flags |= PROPAGATE_AOF;\n\n        /* However prevent AOF / replication propagation if the command\n         * implementatino called preventCommandPropagation() or similar,\n         * or if we don't have the call() flags to do so. */\n        if (c->flags & CLIENT_PREVENT_REPL_PROP ||\n            !(flags & CMD_CALL_PROPAGATE_REPL))\n                propagate_flags &= ~PROPAGATE_REPL;\n        if (c->flags & CLIENT_PREVENT_AOF_PROP ||\n            !(flags & CMD_CALL_PROPAGATE_AOF))\n                propagate_flags &= ~PROPAGATE_AOF;\n\n        /* Call propagate() only if at least one of AOF / replication\n         * propagation is needed. */\n        if (propagate_flags != PROPAGATE_NONE)\n            propagate(c->cmd,c->db->id,c->argv,c->argc,propagate_flags);\n    }\n\n    /* Restore the old replication flags, since call() can be executed\n     * recursively. */\n    c->flags &= ~(CLIENT_FORCE_AOF|CLIENT_FORCE_REPL|CLIENT_PREVENT_PROP);\n    c->flags |= client_old_flags &\n        (CLIENT_FORCE_AOF|CLIENT_FORCE_REPL|CLIENT_PREVENT_PROP);\n\n    /* Handle the alsoPropagate() API to handle commands that want to propagate\n     * multiple separated commands. Note that alsoPropagate() is not affected\n     * by CLIENT_PREVENT_PROP flag. */\n    if (server.also_propagate.numops) {\n        int j;\n        redisOp *rop;\n\n        if (flags & CMD_CALL_PROPAGATE) {\n            for (j = 0; j < server.also_propagate.numops; j++) {\n                rop = &server.also_propagate.ops[j];\n                int target = rop->target;\n                /* Whatever the command wish is, we honor the call() flags. */\n                if (!(flags&CMD_CALL_PROPAGATE_AOF)) target &= ~PROPAGATE_AOF;\n                if (!(flags&CMD_CALL_PROPAGATE_REPL)) target &= ~PROPAGATE_REPL;\n                if (target)\n                    propagate(rop->cmd,rop->dbid,rop->argv,rop->argc,target);\n            }\n        }\n        redisOpArrayFree(&server.also_propagate);\n    }\n    update_stats_add(c->vel->stats, numcommands, 1);\n}\n\n/* If this function gets called we already read a whole\n * command, arguments are in the client argv/argc fields.\n * processCommand() execute the command or prepare the\n * server for a bulk read from the client.\n *\n * If VR_OK is returned the client is still alive and valid and\n * other operations can be performed by the caller. Otherwise\n * if VR_ERROR is returned the client was destroyed (i.e. after QUIT). */\nint processCommand(client *c) {\n    long long maxmemory;\n\n    /* The QUIT command is handled separately. Normal command procs will\n     * go through checking for replication and QUIT will cause trouble\n     * when FORCE_REPLICATION is enabled and would be implemented in\n     * a regular command proc. */\n    if (!strcasecmp(c->argv[0]->ptr,\"quit\")) {\n        addReply(c,shared.ok);\n        c->flags |= CLIENT_CLOSE_AFTER_REPLY;\n        return VR_ERROR;\n    }\n\n    /* Now lookup the command and check ASAP about trivial error conditions\n     * such as wrong arity, bad command name and so forth. */\n    c->cmd = c->lastcmd = lookupCommand(c->argv[0]->ptr);\n    if (!c->cmd) {\n        flagTransaction(c);\n        addReplyErrorFormat(c,\"unknown command '%s'\",\n            (char*)c->argv[0]->ptr);\n        return VR_OK;\n    } else if ((c->cmd->arity > 0 && c->cmd->arity != c->argc) ||\n               (c->argc < -c->cmd->arity)) {\n        flagTransaction(c);\n        addReplyErrorFormat(c,\"wrong number of arguments for '%s' command\",\n            c->cmd->name);\n        return VR_OK;\n    }\n\n    /* Check if the user is authenticated */\n    if (c->vel->cc.requirepass && !c->authenticated && \n        c->cmd->proc != authCommand && c->cmd->proc != adminCommand)\n    {\n        flagTransaction(c);\n        addReply(c,shared.noautherr);\n        return VR_OK;\n    }\n\n    if (c->cmd->needadmin && c->vel->cc.adminpass && \n        c->authenticated < 2 && c->cmd->proc != adminCommand)\n    {\n        flagTransaction(c);\n        addReply(c,shared.noadminerr);\n        return VR_OK;\n    }\n\n    /* Handle the maxmemory directive.\n     *\n     * First we try to free some memory if possible (if there are volatile\n     * keys in the dataset). If there are not the only thing we can do\n     * is returning an error. */\n    maxmemory = c->vel->cc.maxmemory;\n    if (maxmemory) {\n        int retval = freeMemoryIfNeeded(c->vel);\n        /* freeMemoryIfNeeded may flush slave output buffers. This may result\n         * into a slave, that may be the active client, to be freed. */\n        if (c->vel->current_client == NULL) return VR_ERROR;\n\n        /* It was impossible to free enough memory, and the command the client\n         * is trying to execute is denied during OOM conditions? Error. */\n        if ((c->cmd->flags & CMD_DENYOOM) && retval == VR_ERROR) {\n            flagTransaction(c);\n            addReply(c, shared.oomerr);\n            return VR_OK;\n        }\n    }\n\n    /* Don't accept write commands if there are problems persisting on disk\n     * and if this is a master instance. */\n    if (((server.stop_writes_on_bgsave_err &&\n          server.saveparamslen > 0 &&\n          server.lastbgsave_status == VR_ERROR) ||\n          server.aof_last_write_status == VR_ERROR) &&\n        repl.masterhost == NULL &&\n        (c->cmd->flags & CMD_WRITE ||\n         c->cmd->proc == pingCommand))\n    {\n        flagTransaction(c);\n        if (server.aof_last_write_status == VR_OK)\n            addReply(c, shared.bgsaveerr);\n        else\n            addReplySds(c,\n                sdscatprintf(sdsempty(),\n                \"-MISCONF Errors writing to the AOF file: %s\\r\\n\",\n                strerror(server.aof_last_write_errno)));\n        return VR_OK;\n    }\n\n    /* Don't accept write commands if there are not enough good slaves and\n     * user configured the min-slaves-to-write option. */\n    if (repl.masterhost == NULL &&\n        repl.repl_min_slaves_to_write &&\n        repl.repl_min_slaves_max_lag &&\n        c->cmd->flags & CMD_WRITE &&\n        repl.repl_good_slaves_count < repl.repl_min_slaves_to_write)\n    {\n        flagTransaction(c);\n        addReply(c, shared.noreplicaserr);\n        return VR_OK;\n    }\n\n    /* Don't accept write commands if this is a read only slave. But\n     * accept write commands if this is our master. */\n    if (repl.masterhost && repl.repl_slave_ro &&\n        !(c->flags & CLIENT_MASTER) &&\n        c->cmd->flags & CMD_WRITE)\n    {\n        addReply(c, shared.roslaveerr);\n        return VR_OK;\n    }\n\n    /* Only allow SUBSCRIBE and UNSUBSCRIBE in the context of Pub/Sub */\n    if (c->flags & CLIENT_PUBSUB &&\n        c->cmd->proc != pingCommand &&\n        c->cmd->proc != subscribeCommand &&\n        c->cmd->proc != unsubscribeCommand &&\n        c->cmd->proc != psubscribeCommand &&\n        c->cmd->proc != punsubscribeCommand) {\n        addReplyError(c,\"only (P)SUBSCRIBE / (P)UNSUBSCRIBE / PING / QUIT allowed in this context\");\n        return VR_OK;\n    }\n\n    /* Only allow INFO and SLAVEOF when slave-serve-stale-data is no and\n     * we are a slave with a broken link with master. */\n    if (repl.masterhost && repl.repl_state != REPL_STATE_CONNECTED &&\n        repl.repl_serve_stale_data == 0 &&\n        !(c->cmd->flags & CMD_STALE))\n    {\n        flagTransaction(c);\n        addReply(c, shared.masterdownerr);\n        return VR_OK;\n    }\n\n    /* Loading DB? Return an error if the command has not the\n     * CMD_LOADING flag. */\n    if (server.loading && !(c->cmd->flags & CMD_LOADING)) {\n        addReply(c, shared.loadingerr);\n        return VR_OK;\n    }\n\n    /* Lua script too slow? Only allow a limited number of commands. */\n    if (server.lua_timedout &&\n          c->cmd->proc != authCommand &&\n          c->cmd->proc != replconfCommand &&\n        !(c->cmd->proc == shutdownCommand &&\n          c->argc == 2 &&\n          tolower(((char*)c->argv[1]->ptr)[0]) == 'n') &&\n        !(c->cmd->proc == scriptCommand &&\n          c->argc == 2 &&\n          tolower(((char*)c->argv[1]->ptr)[0]) == 'k'))\n    {\n        flagTransaction(c);\n        addReply(c, shared.slowscripterr);\n        return VR_OK;\n    }\n\n    /* Exec the command */\n    if (c->flags & CLIENT_MULTI &&\n        c->cmd->proc != execCommand && c->cmd->proc != discardCommand &&\n        c->cmd->proc != multiCommand && c->cmd->proc != watchCommand)\n    {\n        queueMultiCommand(c);\n        addReply(c,shared.queued);\n    } else {\n        call(c,CMD_CALL_FULL);\n        c->woff = repl.master_repl_offset;\n        if (dlistLength(server.ready_keys))\n            handleClientsBlockedOnLists();\n    }\n\n    return VR_OK;\n}\n\n/* ========================== Redis OP Array API ============================ */\n\nvoid redisOpArrayInit(redisOpArray *oa) {\n    oa->ops = NULL;\n    oa->numops = 0;\n}\n\nint redisOpArrayAppend(redisOpArray *oa, struct redisCommand *cmd, int dbid,\n                       robj **argv, int argc, int target)\n{\n    redisOp *op;\n\n    oa->ops = drealloc(oa->ops,sizeof(redisOp)*((size_t)oa->numops+1));\n    op = oa->ops+oa->numops;\n    op->cmd = cmd;\n    op->dbid = dbid;\n    op->argv = argv;\n    op->argc = argc;\n    op->target = target;\n    oa->numops++;\n    return oa->numops;\n}\n\nvoid redisOpArrayFree(redisOpArray *oa) {\n    while(oa->numops) {\n        int j;\n        redisOp *op;\n\n        oa->numops--;\n        op = oa->ops+oa->numops;\n        for (j = 0; j < op->argc; j++)\n            decrRefCount(op->argv[j]);\n        dfree(op->argv);\n    }\n    dfree(oa->ops);\n}\n\n/* Propagate the specified command (in the context of the specified database id)\n * to AOF and Slaves.\n *\n * flags are an xor between:\n * + PROPAGATE_NONE (no propagation of command at all)\n * + PROPAGATE_AOF (propagate into the AOF file if is enabled)\n * + PROPAGATE_REPL (propagate into the replication link)\n *\n * This should not be used inside commands implementation. Use instead\n * alsoPropagate(), preventCommandPropagation(), forceCommandPropagation().\n */\nvoid propagate(struct redisCommand *cmd, int dbid, robj **argv, int argc,\n               int flags)\n{\n    if (server.aof_state != AOF_OFF && flags & PROPAGATE_AOF)\n        feedAppendOnlyFile(cmd,dbid,argv,argc);\n    if (flags & PROPAGATE_REPL)\n        replicationFeedSlaves(repl.slaves,dbid,argv,argc);\n}\n\n/* Used inside commands to schedule the propagation of additional commands\n * after the current command is propagated to AOF / Replication.\n *\n * 'cmd' must be a pointer to the Redis command to replicate, dbid is the\n * database ID the command should be propagated into.\n * Arguments of the command to propagte are passed as an array of redis\n * objects pointers of len 'argc', using the 'argv' vector.\n *\n * The function does not take a reference to the passed 'argv' vector,\n * so it is up to the caller to release the passed argv (but it is usually\n * stack allocated).  The function autoamtically increments ref count of\n * passed objects, so the caller does not need to. */\nvoid alsoPropagate(struct redisCommand *cmd, int dbid, robj **argv, int argc,\n                   int target)\n{\n    robj **argvcopy;\n    int j;\n\n    if (server.loading) return; /* No propagation during loading. */\n\n    argvcopy = dalloc(sizeof(robj*)*(size_t)argc);\n    for (j = 0; j < argc; j++) {\n        argvcopy[j] = dupStringObjectUnconstant(argv[j]);\n    }\n    redisOpArrayAppend(&server.also_propagate,cmd,dbid,argvcopy,argc,target);\n}\n\n/* It is possible to call the function forceCommandPropagation() inside a\n * Redis command implementation in order to to force the propagation of a\n * specific command execution into AOF / Replication. */\nvoid forceCommandPropagation(client *c, int flags) {\n    if (flags & PROPAGATE_REPL) c->flags |= CLIENT_FORCE_REPL;\n    if (flags & PROPAGATE_AOF) c->flags |= CLIENT_FORCE_AOF;\n}\n\n/* Avoid that the executed command is propagated at all. This way we\n * are free to just propagate what we want using the alsoPropagate()\n * API. */\nvoid preventCommandPropagation(client *c) {\n    c->flags |= CLIENT_PREVENT_PROP;\n}\n\n/* AOF specific version of preventCommandPropagation(). */\nvoid preventCommandAOF(client *c) {\n    c->flags |= CLIENT_PREVENT_AOF_PROP;\n}\n\n/* Replication specific version of preventCommandPropagation(). */\nvoid preventCommandReplication(client *c) {\n    c->flags |= CLIENT_PREVENT_REPL_PROP;\n}\n\n/* Helper function for addReplyCommand() to output flags. */\nstatic int addReplyCommandFlag(client *c, struct redisCommand *cmd, int f, char *reply) {\n    if (cmd->flags & f) {\n        addReplyStatus(c, reply);\n        return 1;\n    }\n    return 0;\n}\n\n/* Output the representation of a Redis command. Used by the COMMAND command. */\nstatic void addReplyCommand(client *c, struct redisCommand *cmd) {\n    if (!cmd) {\n        addReply(c, shared.nullbulk);\n    } else {\n        /* We are adding: command name, arg count, flags, first, last, offset */\n        addReplyMultiBulkLen(c, 6);\n        addReplyBulkCString(c, cmd->name);\n        addReplyLongLong(c, cmd->arity);\n\n        int flagcount = 0;\n        void *flaglen = addDeferredMultiBulkLength(c);\n        flagcount += addReplyCommandFlag(c,cmd,CMD_WRITE, \"write\");\n        flagcount += addReplyCommandFlag(c,cmd,CMD_READONLY, \"readonly\");\n        flagcount += addReplyCommandFlag(c,cmd,CMD_DENYOOM, \"denyoom\");\n        flagcount += addReplyCommandFlag(c,cmd,CMD_ADMIN, \"admin\");\n        flagcount += addReplyCommandFlag(c,cmd,CMD_PUBSUB, \"pubsub\");\n        flagcount += addReplyCommandFlag(c,cmd,CMD_NOSCRIPT, \"noscript\");\n        flagcount += addReplyCommandFlag(c,cmd,CMD_RANDOM, \"random\");\n        flagcount += addReplyCommandFlag(c,cmd,CMD_SORT_FOR_SCRIPT,\"sort_for_script\");\n        flagcount += addReplyCommandFlag(c,cmd,CMD_LOADING, \"loading\");\n        flagcount += addReplyCommandFlag(c,cmd,CMD_STALE, \"stale\");\n        flagcount += addReplyCommandFlag(c,cmd,CMD_SKIP_MONITOR, \"skip_monitor\");\n        flagcount += addReplyCommandFlag(c,cmd,CMD_ASKING, \"asking\");\n        flagcount += addReplyCommandFlag(c,cmd,CMD_FAST, \"fast\");\n        if (cmd->getkeys_proc) {\n            addReplyStatus(c, \"movablekeys\");\n            flagcount += 1;\n        }\n        setDeferredMultiBulkLength(c, flaglen, flagcount);\n\n        addReplyLongLong(c, cmd->firstkey);\n        addReplyLongLong(c, cmd->lastkey);\n        addReplyLongLong(c, cmd->keystep);\n    }\n}\n\n/* COMMAND <subcommand> <args> */\nvoid commandCommand(client *c) {\n    if (c->argc == 1) {\n        dictIterator *di;\n        dictEntry *de;\n        \n        addReplyMultiBulkLen(c, dictSize(server.commands));\n        di = dictGetIterator(server.commands);\n        while ((de = dictNext(di)) != NULL) {\n            addReplyCommand(c, dictGetVal(de));\n        }\n        dictReleaseIterator(di);\n    } else if (!strcasecmp(c->argv[1]->ptr, \"info\")) {\n        int i;\n        addReplyMultiBulkLen(c, c->argc-2);\n        for (i = 2; i < c->argc; i++) {\n            addReplyCommand(c, dictFetchValue(server.commands, c->argv[i]->ptr));\n        }\n    } else if (!strcasecmp(c->argv[1]->ptr, \"count\") && c->argc == 2) {\n        addReplyLongLong(c, dictSize(server.commands));\n    } else if (!strcasecmp(c->argv[1]->ptr,\"getkeys\") && c->argc >= 3) {\n        struct redisCommand *cmd = lookupCommand(c->argv[2]->ptr);\n        int *keys, numkeys, j;\n\n        if (!cmd) {\n            addReplyErrorFormat(c,\"Invalid command specified\");\n            return;\n        } else if ((cmd->arity > 0 && cmd->arity != c->argc-2) ||\n                   ((c->argc-2) < -cmd->arity))\n        {\n            addReplyError(c,\"Invalid number of arguments specified for command\");\n            return;\n        }\n\n        keys = getKeysFromCommand(cmd,c->argv+2,c->argc-2,&numkeys);\n        addReplyMultiBulkLen(c,numkeys);\n        for (j = 0; j < numkeys; j++) addReplyBulk(c,c->argv[keys[j]+2]);\n        getKeysFreeResult(keys);\n    } else if (!strcasecmp(c->argv[1]->ptr, \"stats\") && c->argc == 2) {\n        int j;\n        struct darray *cstableall;\n        struct array *cstable = c->vel->cstable;\n        commandStats *cstats, *cstatsall;\n\n        if (c->steps == 0) {\n            cstableall = commandStatsTableCreate();\n            if (!(c->flags&CLIENT_JUMP))\n                c->flags |= CLIENT_JUMP;\n            c->taridx = worker_get_next_idx(c->curidx);\n            c->cache = cstableall;\n        } else {\n            cstableall = c->cache;\n            c->taridx = worker_get_next_idx(c->curidx);\n        }\n\n        for (j = 0; j < darray_n(cstable); j ++) {\n            cstats = darray_get(cstable, j);\n            if (!cstats->calls) continue;\n            \n            cstatsall = darray_get(cstableall, j);\n            cstatsall->microseconds += cstats->microseconds;\n            cstatsall->calls += cstats->calls;\n        }\n        \n        if (c->steps >= (darray_n(&workers) - 1)) {\n            sds command_stats_info;\n            void *replylen_node;\n            long replylen = 0;\n            \n            c->steps = 0;\n            c->taridx = -1;\n            c->cache = NULL;\n            c->flags &= ~CLIENT_JUMP;\n            \n            replylen_node = addDeferredMultiBulkLength(c);\n            for (j = 0; j < darray_n(cstableall); j ++) {\n                cstatsall = darray_get(cstableall, j);\n                if (!cstatsall->calls) continue;\n                \n                command_stats_info = sdscatprintf(sdsempty(),\n                    \"%s:calls=%lld,usec=%lld,usec_per_call=%.2f\",\n                    cstatsall->name, cstatsall->calls, cstatsall->microseconds,\n                    (float)cstatsall->microseconds/cstatsall->calls);\n                addReplyBulkSds(c,command_stats_info);\n                replylen ++;\n            }\n\n            setDeferredMultiBulkLength(c,replylen_node,replylen);\n            \n            commandStatsTableDestroy(cstableall);\n        }\n    } else {\n        addReplyError(c, \"Unknown subcommand or wrong number of arguments.\");\n        return;\n    }\n}\n\nstruct darray *\ncommandStatsTableCreate(void)\n{\n    int j;\n    commandStats *cstats;\n    struct darray *cstatstable;\n    int numcommands = sizeof(redisCommandTable)/sizeof(struct redisCommand);\n    \n\n    cstatstable = darray_create(numcommands,sizeof(commandStats));\n    if (cstatstable == NULL) return NULL;\n    for (j = 0; j < numcommands; j ++) {\n        struct redisCommand *c = redisCommandTable+j;\n        cstats = darray_push(cstatstable);\n        cstats->name = c->name;\n        cstats->microseconds = 0;\n        cstats->calls = 0;\n        ASSERT(j == c->idx);\n    }\n\n    return cstatstable;\n}\n\nvoid\ncommandStatsTableDestroy(struct darray *cstatstable)\n{\n    cstatstable->nelem = 0;\n    darray_destroy(cstatstable);\n}\n"
  },
  {
    "path": "src/vr_command.h",
    "content": "#ifndef _VR_COMMAND_H_\n#define _VR_COMMAND_H_\n\n/* Command flags. Please check the command table defined in the redis.c file\n * for more information about the meaning of every flag. */\n#define CMD_WRITE 1                   /* \"w\" flag */\n#define CMD_READONLY 2                /* \"r\" flag */\n#define CMD_DENYOOM 4                 /* \"m\" flag */\n#define CMD_NOT_USED_1 8              /* no longer used flag */\n#define CMD_ADMIN 16                  /* \"a\" flag */\n#define CMD_PUBSUB 32                 /* \"p\" flag */\n#define CMD_NOSCRIPT  64              /* \"s\" flag */\n#define CMD_RANDOM 128                /* \"R\" flag */\n#define CMD_SORT_FOR_SCRIPT 256       /* \"S\" flag */\n#define CMD_LOADING 512               /* \"l\" flag */\n#define CMD_STALE 1024                /* \"t\" flag */\n#define CMD_SKIP_MONITOR 2048         /* \"M\" flag */\n#define CMD_ASKING 4096               /* \"k\" flag */\n#define CMD_FAST 8192                 /* \"F\" flag */\n\n/* Command call flags, see call() function */\n#define CMD_CALL_NONE 0\n#define CMD_CALL_SLOWLOG (1<<0)\n#define CMD_CALL_STATS (1<<1)\n#define CMD_CALL_PROPAGATE_AOF (1<<2)\n#define CMD_CALL_PROPAGATE_REPL (1<<3)\n#define CMD_CALL_PROPAGATE (CMD_CALL_PROPAGATE_AOF|CMD_CALL_PROPAGATE_REPL)\n#define CMD_CALL_FULL (CMD_CALL_SLOWLOG | CMD_CALL_STATS | CMD_CALL_PROPAGATE)\n\n/* Command propagation flags, see propagate() function */\n#define PROPAGATE_NONE 0\n#define PROPAGATE_AOF 1\n#define PROPAGATE_REPL 2\n\n/* SHUTDOWN flags */\n#define SHUTDOWN_NOFLAGS 0      /* No flags. */\n#define SHUTDOWN_SAVE 1         /* Force SAVE on SHUTDOWN even if no save\n                                   points are configured. */\n#define SHUTDOWN_NOSAVE 2       /* Don't SAVE on SHUTDOWN. */\n\ntypedef void redisCommandProc(struct client *c);\ntypedef int *redisGetKeysProc(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);\nstruct redisCommand {\n    char *name;\n    redisCommandProc *proc;\n    int arity;\n    char *sflags; /* Flags as string representation, one char per flag. */\n    int flags;    /* The actual flags, obtained from the 'sflags' field. */\n    /* Use a function to determine keys arguments in a command line.\n     * Used for Redis Cluster redirect. */\n    redisGetKeysProc *getkeys_proc;\n    /* What keys should be loaded in background when calling this command? */\n    int firstkey; /* The first argument that's a key (0 = no keys) */\n    int lastkey;  /* The last argument that's a key */\n    int keystep;  /* The step between first and last key */\n    int idx;\n    int needadmin;\n};\n\ntypedef struct commandStats {\n    char *name;\n    long long microseconds;\n    long long calls;\n}commandStats;\n\n/* The redisOp structure defines a Redis Operation, that is an instance of\n * a command with an argument vector, database ID, propagation target\n * (PROPAGATE_*), and command pointer.\n *\n * Currently only used to additionally propagate more commands to AOF/Replication\n * after the propagation of the executed command. */\ntypedef struct redisOp {\n    robj **argv;\n    int argc, dbid, target;\n    struct redisCommand *cmd;\n} redisOp;\n\n/* Defines an array of Redis operations. There is an API to add to this\n * structure in a easy way.\n *\n * redisOpArrayInit();\n * redisOpArrayAppend();\n * redisOpArrayFree();\n */\ntypedef struct redisOpArray {\n    redisOp *ops;\n    int numops;\n} redisOpArray;\n\nextern dictType commandTableDictType;\n\nvoid populateCommandTable(void);\nint populateCommandsNeedAdminpass(void);\n\nstruct redisCommand *lookupCommand(sds name);\nstruct redisCommand *lookupCommandOrOriginal(sds name);\nstruct redisCommand *lookupCommandByCString(char *s);\n\nvoid call(struct client *c, int flags);\nint processCommand(struct client *c);\n\nvoid redisOpArrayInit(redisOpArray *oa);\nint redisOpArrayAppend(redisOpArray *oa, struct redisCommand *cmd, int dbid, robj **argv, int argc, int target);\nvoid redisOpArrayFree(redisOpArray *oa);\n\nvoid propagate(struct redisCommand *cmd, int dbid, robj **argv, int argc, int flags);\nvoid alsoPropagate(struct redisCommand *cmd, int dbid, robj **argv, int argc, int target);\nvoid forceCommandPropagation(struct client *c, int flags);\nvoid preventCommandPropagation(struct client *c);\nvoid preventCommandAOF(struct client *c);\nvoid preventCommandReplication(struct client *c);\n\nvoid commandCommand(struct client *c);\n\nstruct darray *commandStatsTableCreate(void);\nvoid commandStatsTableDestroy(struct darray *cstatstable);\n\n#endif\n"
  },
  {
    "path": "src/vr_conf.c",
    "content": "#include <fcntl.h>\n\n#include <vr_core.h>\n\ntypedef const char *(*configEnumGetStrFun)(int type);\n\n#define CONF_TOKEN_ORGANIZATION_START   \"[\"\n#define CONF_TOKEN_ORGANIZATION_END     \"]\"\n#define CONF_TOKEN_KEY_VALUE_BETWEEN    \":\"\n#define CONF_TOKEN_ARRAY_START          \"-\"\n\n#define CONF_ORGANIZATION_NAME_COMMAN   \"common\"\n#define CONF_ORGANIZATION_NAME_SERVER   \"server\"\n\n#define CONF_VALUE_YES                  \"yes\"\n#define CONF_VALUE_NO                   \"no\"\n\n#define CONF_MAX_LINE                   1024\n\n#define CONF_TAG_DEFAULT_TYPE           GROUP_TYPE_SINGLE\n#define CONF_TAG_DEFAULT_HASH           HASH_FNV1_64\n#define CONF_TAG_DEFAULT_HASH_TAG       NULL\n#define CONF_TAG_DEFAULT_DISTRIBUTION   \"ketama\"\n#define CONF_TAG_DEFAULT_REDIS_AUTH     NULL\n#define CONF_TAG_DEFAULT_REDIS_DB       0\n#define CONF_TAG_DEFAULT_TIMEOUT        300\n#define CONF_TAG_DEFAULT_SERVERS        \"127.0.0.1:6379\"\n#define CONF_TAG_DEFAULT_LISTEN         \"127.0.0.1:6380\"\n#define CONF_TAG_DEFAULT_MAXMEMORY      1073741824   // 1Gb\n#define CONF_TAG_DEFAULT_THREADS        sysconf(_SC_NPROCESSORS_ONLN)\n#define CONF_TAG_DEFAULT_NOREPLY        \"false\"\n#define CONF_TAG_DEFAULT_RDB_DISKLESS   \"false\"\n\n#define DEFINE_ACTION(_hash, _name) (char*)(#_name),\nstatic char* hash_strings[] = {\n    HASH_CODEC( DEFINE_ACTION )\n    NULL\n};\n#undef DEFINE_ACTION\n\n#define DEFINE_ACTION(_dist, _name) (char*)(#_name),\nstatic char* dist_strings[] = {\n    DIST_CODEC( DEFINE_ACTION )\n    NULL\n};\n#undef DEFINE_ACTION\n\n#define DEFINE_ACTION(_policy, _name) (char*)(#_name),\nstatic char* evictpolicy_strings[] = {\n    EVICTPOLICY_CODEC( DEFINE_ACTION )\n    NULL\n};\n#undef DEFINE_ACTION\n\nstatic conf_option conf_server_options[] = {\n    { (char *)CONFIG_SOPN_DATABASES,\n      CONF_FIELD_TYPE_INT, 1,\n      conf_set_int_non_zero, conf_get_int,\n      offsetof(conf_server, databases) },\n    { (char *)CONFIG_SOPN_IDPDATABASE,\n      CONF_FIELD_TYPE_INT, 1,\n      conf_set_int_non_zero, conf_get_int,\n      offsetof(conf_server, internal_dbs_per_databases) },\n    { (char *)CONFIG_SOPN_MAXMEMORY,\n      CONF_FIELD_TYPE_LONGLONG, 0,\n      conf_set_maxmemory, conf_get_longlong,\n      offsetof(conf_server, maxmemory) },\n    { (char *)CONFIG_SOPN_MAXMEMORYP,\n      CONF_FIELD_TYPE_INT, 0,\n      conf_set_maxmemory_policy, conf_get_int,\n      offsetof(conf_server, maxmemory_policy) },\n    { (char *)CONFIG_SOPN_MAXMEMORYS,\n      CONF_FIELD_TYPE_INT, 0,\n      conf_set_int_non_zero, conf_get_int,\n      offsetof(conf_server, maxmemory_samples) },\n    { (char *)CONFIG_SOPN_MTCLIMIT,\n      CONF_FIELD_TYPE_LONGLONG, 0,\n      conf_set_longlong, conf_get_longlong,\n      offsetof(conf_server, max_time_complexity_limit) },\n    { (char *)CONFIG_SOPN_BIND,\n      CONF_FIELD_TYPE_ARRAYSDS, 1,\n      conf_set_array_sds, conf_get_array_sds,\n      offsetof(conf_server, binds) },\n    { (char *)CONFIG_SOPN_PORT,\n      CONF_FIELD_TYPE_INT, 1,\n      conf_set_int, conf_get_int,\n      offsetof(conf_server, port) },\n    { (char *)CONFIG_SOPN_THREADS,\n      CONF_FIELD_TYPE_INT, 1,\n      conf_set_int, conf_get_int,\n      offsetof(conf_server, threads) },\n    { (char *)CONFIG_SOPN_MAXCLIENTS,\n      CONF_FIELD_TYPE_INT, 0,\n      conf_set_int_non_zero, conf_get_int,\n      offsetof(conf_server, maxclients) },\n    { (char *)CONFIG_SOPN_SLOWLOGLST,\n      CONF_FIELD_TYPE_LONGLONG, 0,\n      conf_set_longlong, conf_get_longlong,\n      offsetof(conf_server, slowlog_log_slower_than) },\n    { (char *)CONFIG_SOPN_SLOWLOGML,\n      CONF_FIELD_TYPE_INT, 0,\n      conf_set_int, conf_get_int,\n      offsetof(conf_server, slowlog_max_len) },\n    { (char *)CONFIG_SOPN_REQUIREPASS,\n      CONF_FIELD_TYPE_SDS, 0,\n      conf_set_password, conf_get_sds,\n      offsetof(conf_server, requirepass) },\n    { (char *)CONFIG_SOPN_ADMINPASS,\n      CONF_FIELD_TYPE_SDS, 0,\n      conf_set_password, conf_get_sds,\n      offsetof(conf_server, adminpass) },\n    { (char *)CONFIG_SOPN_COMMANDSNAP,\n      CONF_FIELD_TYPE_ARRAYSDS, 1,\n      conf_set_commands_need_adminpass, conf_get_array_sds,\n      offsetof(conf_server, commands_need_adminpass) },\n    { NULL, NULL, 0 }\n};\n\nvr_conf *conf = NULL;\nconf_server *cserver = NULL;\n\nstatic void\nconf_value_dump(conf_value *cv, int log_level)\n{\n    uint32_t i;\n    conf_value **cv_sub;\n    \n    if(cv == NULL){\n        return;\n    }\n\n    if(cv->type == CONF_VALUE_TYPE_STRING){\n        log_debug(log_level, \"%.*s\", sdslen(cv->value), cv->value);\n    }else if(cv->type == CONF_VALUE_TYPE_ARRAY){\n        for(i = 0; i < darray_n(cv->value); i++){\n            cv_sub = darray_get(cv->value, i);\n            conf_value_dump(*cv_sub, log_level);\n        }\n    }else{\n        NOT_REACHED();\n    }\n}\n\nstatic void\nconf_organization_dump(sds name, dict *org, int log_level)\n{\n    dictIterator *di;\n    dictEntry *de;\n    sds key;\n    conf_value *cv;\n\n    if(name == NULL || org == NULL){\n        return;\n    }\n\n    log_debug(log_level, \"[%.*s]\", sdslen(name), name);\n    \n    di = dictGetIterator(org);\n\n    while((de = dictNext(di)) != NULL){\n        key = dictGetKey(de);\n        cv = dictGetVal(de);\n\n        if(cv->type == CONF_VALUE_TYPE_STRING){\n            log_debug(log_level, \"%.*s: %.*s\", \n                sdslen(key), key,\n                sdslen(cv->value), cv->value);\n        }else if(cv->type == CONF_VALUE_TYPE_ARRAY){\n            log_debug(log_level, \"%.*s:\",sdslen(key), key);\n            conf_value_dump(cv, log_level);\n        }else{\n            NOT_REACHED();\n        }\n    }\n\n    dictReleaseIterator(di);\n}\n\nstatic void\nconf_organizations_dump(vr_conf *cf)\n{\n    dict *orgs, *org;\n    dictIterator *di;\n    dictEntry *de;\n    sds name;\n    int log_level = LOG_VERB;\n    \n    if(cf == NULL){\n        return;\n    }\n\n    orgs = cf->organizations;\n    if(orgs == NULL){\n        log_debug(log_level, \"organization is NULL\");\n        return;\n    }\n    \n    di = dictGetIterator(orgs);\n\n    while((de = dictNext(di)) != NULL){\n        name = dictGetKey(de);\n        org = dictGetVal(de);\n\n        conf_organization_dump(name, org, log_level);\n        log_debug(log_level, \"\");\n    }\n\n    dictReleaseIterator(di);\n}\n\nint\nconf_set_maxmemory(void *obj, conf_option *opt, void *data)\n{\n    uint8_t *p;\n    conf_value *cv = data;\n    long long value;\n    long long *gt;\n    int err;\n\n    if(cv->type != CONF_VALUE_TYPE_STRING){\n        log_error(\"conf pool %s in the conf file error\", \n            opt->name);\n        return VR_ERROR;\n    }\n\n    CONF_WLOCK();\n    \n    p = obj;\n    gt = (long long *)(p + opt->offset);\n\n    value = memtoll(cv->value, &err);\n    if(err != 0 || value < 0){\n        CONF_UNLOCK();\n        log_error(\"value for the key %s in conf file is invalid\", \n             opt->name);\n        return VR_ERROR;\n    }\n\n    *gt = value;\n    conf->version ++;\n    CONF_UNLOCK();\n    return VR_OK;\n}\n\nint\nconf_set_maxmemory_policy(void *obj, conf_option *opt, void *data)\n{\n    uint8_t *p;\n    conf_value *cv = data;\n    int *gt;\n    char **policy;\n\n    if(cv->type != CONF_VALUE_TYPE_STRING){\n        log_error(\"conf server in the conf file is not a string\");\n        return VR_ERROR;\n    }\n\n    CONF_WLOCK();\n\n    p = obj;\n    gt = (int*)(p + opt->offset);\n\n    for (policy = evictpolicy_strings; *policy; policy ++) {\n        if (strcmp(cv->value, *policy) == 0) {\n            *gt = policy - evictpolicy_strings;\n            break;\n        }\n    }\n\n    if (*policy == NULL) {\n        CONF_UNLOCK();\n        log_error(\"ERROR: Conf maxmemory policy '%s' is invalid\", \n            cv->value);\n        return VR_ERROR;\n    }\n\n    if (*gt == MAXMEMORY_VOLATILE_LRU || *gt == MAXMEMORY_ALLKEYS_LRU) {\n        CONF_UNLOCK();\n        log_error(\"ERROR: Conf maxmemory policy now is not support %s and %s\", \n            evictpolicy_strings[MAXMEMORY_VOLATILE_LRU], \n            evictpolicy_strings[MAXMEMORY_ALLKEYS_LRU]);\n        return VR_ERROR;\n    }\n\n    CONF_UNLOCK();\n    return VR_OK;\n}\n\nint\nconf_set_int_non_zero(void *obj, conf_option *opt, void *data)\n{\n    uint8_t *p;\n    conf_value *cv = data;\n    int *gt;\n\n    if(cv->type != CONF_VALUE_TYPE_STRING){\n        log_error(\"conf pool %s in the conf file error\", \n            opt->name);\n        return VR_ERROR;\n    }\n\n    CONF_WLOCK();\n\n    p = obj;\n    gt = (int*)(p + opt->offset);\n\n    if(!sdsIsNum(cv->value)){\n        CONF_UNLOCK();\n        log_error(\"value of the key %s in conf file is not a number\", \n            opt->name);\n        return VR_ERROR;\n    }\n\n    *gt = vr_atoi(cv->value, sdslen(cv->value));\n\n    if (*gt < 0) {\n        CONF_UNLOCK();\n        log_error(\"value of the key %s in conf file is invalid\", \n            opt->name);\n        return VR_ERROR;\n    } else if (*gt < 1) {\n        CONF_UNLOCK();\n        log_error(\"value of the key %s in conf file must be 1 or greater\", \n            opt->name);\n        return VR_ERROR;\n    }\n    conf->version ++;\n    CONF_UNLOCK();\n    return VR_OK;\n}\n\n/* The return data need to free by users. */\nint\nconf_get_sds(void *obj, conf_option *opt, void *data)\n{\n    uint8_t *p;\n    sds *str = data;\n    sds *gt;\n\n    if (data == NULL)\n        return VR_ERROR;\n\n    CONF_RLOCK();\n    p = obj;\n    gt = (sds*)(p + opt->offset);\n    if (*gt == NULL) *str = NULL;\n    else *str = sdsdup(*gt);\n    CONF_UNLOCK();\n    return VR_OK;\n}\n\nint\nconf_set_sds(void *obj, conf_option *opt, void *data)\n{\n    uint8_t *p;\n    conf_value *cv = data;\n    sds *gt;\n\n    if(cv->type != CONF_VALUE_TYPE_STRING){\n        log_error(\"conf pool %s in the conf file is not a string\", \n            opt->name);\n        return VR_ERROR;\n    }\n\n    CONF_WLOCK();\n    p = obj;\n    gt = (sds*)(p + opt->offset);\n\n    *gt = sdsnewlen(cv->value, sdslen(cv->value));\n    conf->version ++;\n    CONF_UNLOCK();\n    return VR_OK;\n}\n\nint\nconf_set_password(void *obj, conf_option *opt, void *data)\n{\n    uint8_t *p;\n    conf_value *cv = data;\n    sds *gt;\n\n    if(cv->type != CONF_VALUE_TYPE_STRING){\n        log_error(\"Conf pool %s in the conf file is not a string\", \n            opt->name);\n        return VR_ERROR;\n    } else if (sdslen(cv->value) > CONFIG_AUTHPASS_MAX_LEN) {\n        log_error(\"Password is longer than CONFIG_AUTHPASS_MAX_LEN\");\n        return VR_ERROR;\n    }\n\n    CONF_WLOCK();\n    p = obj;\n    gt = (sds*)(p + opt->offset);\n\n    if (*gt != NULL) sdsfree(*gt);\n    if (sdslen(cv->value) == 0) *gt = NULL;\n    else *gt = sdsnewlen(cv->value, sdslen(cv->value));\n    conf->version ++;\n    CONF_UNLOCK();\n    return VR_OK;\n}\n\nint\nconf_get_int(void *obj, conf_option *opt, void *data)\n{\n    uint8_t *p;\n    int *integer = data;\n    int *gt;\n\n    if (data == NULL) \n        return VR_ERROR;\n\n    CONF_RLOCK();\n    p = obj;\n    gt = (int*)(p + opt->offset);\n    *integer = *gt;\n    CONF_UNLOCK();\n    return VR_OK;\n}\n\nint\nconf_set_int(void *obj, conf_option *opt, void *data)\n{\n    uint8_t *p;\n    conf_value *cv = data;\n    int *gt;\n\n    if(cv->type != CONF_VALUE_TYPE_STRING){\n        log_error(\"conf pool %s in the conf file error\", \n            opt->name);\n        return VR_ERROR;\n    }\n\n    CONF_WLOCK();\n    \n    p = obj;\n    gt = (int*)(p + opt->offset);\n\n    if(!sdsIsNum(cv->value)){\n        CONF_UNLOCK();\n        log_error(\"value of the key %s in conf file is not a number\", \n            opt->name);\n        return VR_ERROR;\n    }\n\n    *gt = vr_atoi(cv->value, sdslen(cv->value));\n\n    if (*gt < 0) {\n        CONF_UNLOCK();\n        log_error(\"value of the key %s in conf file is invalid\", \n            opt->name);\n        return VR_ERROR;\n    }\n    conf->version ++;\n    CONF_UNLOCK();\n    return VR_OK;\n}\n\nint\nconf_get_longlong(void *obj, conf_option *opt, void *data)\n{\n    uint8_t *p;\n    long long *integer = data;\n    long long *gt;\n\n    if (data == NULL)\n        return VR_ERROR;\n\n    CONF_RLOCK();\n    p = obj;\n    gt = (long long*)(p + opt->offset);\n    *integer = *gt;\n    CONF_UNLOCK();\n    return VR_OK;\n}\n\nint\nconf_set_longlong(void *obj, conf_option *opt, void *data)\n{\n    uint8_t *p;\n    conf_value *cv = data;\n    long long *gt;\n\n    if(cv->type != CONF_VALUE_TYPE_STRING){\n        log_error(\"conf pool %s in the conf file error\", \n            opt->name);\n        return VR_ERROR;\n    }\n\n    CONF_WLOCK();\n    \n    p = obj;\n    gt = (long long*)(p + opt->offset);\n\n    if (!string2ll(cv->value, sdslen(cv->value), gt)) {\n        CONF_UNLOCK();\n        log_error(\"value of the key %s in conf file is invalid\", \n            opt->name);\n        return VR_ERROR;\n    }\n    conf->version ++;\n    CONF_UNLOCK();\n    return VR_OK;\n}\n\nint\nconf_set_yesorno(void *obj, conf_option *opt, void *data)\n{\n    uint8_t *p;\n    conf_value *cv = data;\n    int *gt;\n\n    if(cv->type != CONF_VALUE_TYPE_STRING){\n        log_error(\"conf pool %s in the conf file error\", \n            opt->name);\n        return VR_ERROR;\n    }\n\n    CONF_WLOCK();\n    \n    p = obj;\n    gt = (int*)(p + opt->offset);\n\n    if(!strcasecmp(cv->value, CONF_VALUE_YES)){\n        *gt = 1;\n    }else if(!strcasecmp(cv->value, CONF_VALUE_NO)){\n        *gt = 0;\n    }else{\n        CONF_UNLOCK();\n        log_error(\"key %s in conf file must be %s or %s\",\n            opt->name, CONF_VALUE_YES, CONF_VALUE_NO);\n        return VR_ERROR;\n    }\n    conf->version ++;\n    CONF_UNLOCK();\n    return VR_OK;\n}\n\nint\nconf_set_array_sds(void *obj, conf_option *opt, void *data)\n{\n    uint8_t *p;\n    uint32_t j;\n    conf_value **cv_sub, *cv = data;\n    struct darray *gt;\n    sds *str;\n\n    if(cv->type != CONF_VALUE_TYPE_STRING && \n        cv->type != CONF_VALUE_TYPE_ARRAY){\n        log_error(\"conf pool %s in the conf file is not a string or array\", \n            opt->name);\n        return VR_ERROR;\n    } else if (cv->type == CONF_VALUE_TYPE_ARRAY) {\n        cv_sub = darray_get(cv->value, j);\n        if ((*cv_sub)->type != CONF_VALUE_TYPE_STRING) {\n            log_error(\"conf pool %s in the conf file is not a string array\", \n                opt->name);\n            return VR_ERROR;            \n        }\n    }\n\n    CONF_WLOCK();\n    p = obj;\n    gt = (struct darray*)(p + opt->offset);\n\n    while (darray_n(gt) > 0) {\n        str = darray_pop(gt);\n        sdsfree(*str);\n    }\n\n    if (cv->type == CONF_VALUE_TYPE_STRING) {\n        str = darray_push(gt);\n        *str = sdsdup(cv->value);\n    } else if (cv->type == CONF_VALUE_TYPE_ARRAY) {\n        for (j = 0; j < darray_n(cv->value); j ++) {\n            cv_sub = darray_get(cv->value, j);\n            str = darray_push(gt);\n            *str = sdsdup((*cv_sub)->value);\n        }\n    }\n    conf->version ++;\n    CONF_UNLOCK();\n    return VR_OK;\n}\n\nint\nconf_set_commands_need_adminpass(void *obj, conf_option *opt, void *data)\n{\n    uint8_t *p;\n    uint32_t j;\n    conf_value **cv_sub, *cv = data;\n    struct darray *gt;\n    sds *str;\n\n    if(cv->type != CONF_VALUE_TYPE_STRING && \n        cv->type != CONF_VALUE_TYPE_ARRAY){\n        log_error(\"conf pool %s in the conf file is not a string or array\", \n            opt->name);\n        return VR_ERROR;\n    } else if (cv->type == CONF_VALUE_TYPE_ARRAY) {\n        cv_sub = darray_get(cv->value, j);\n        if ((*cv_sub)->type != CONF_VALUE_TYPE_STRING) {\n            log_error(\"conf pool %s in the conf file is not a string array\", \n                opt->name);\n            return VR_ERROR;            \n        }\n    }\n\n    CONF_WLOCK();\n    p = obj;\n    gt = (struct darray*)(p + opt->offset);\n\n    while (darray_n(gt) > 0) {\n        str = darray_pop(gt);\n        sdsfree(*str);\n    }\n\n    if (cv->type == CONF_VALUE_TYPE_STRING) {\n        str = darray_push(gt);\n        *str = sdsdup(cv->value);\n    } else if (cv->type == CONF_VALUE_TYPE_ARRAY) {\n        for (j = 0; j < darray_n(cv->value); j ++) {\n            cv_sub = darray_get(cv->value, j);\n            str = darray_push(gt);\n            *str = sdsdup((*cv_sub)->value);\n        }\n    }\n    conf->version ++;\n    CONF_UNLOCK();\n    return VR_OK;\n}\n\nint\nconf_get_array_sds(void *obj, conf_option *opt, void *data)\n{\n    uint8_t *p;\n    uint32_t j;\n    struct darray *strs = data;\n    struct array *gt;\n    sds *str1, *str2;\n\n    if (data == NULL) {\n        return VR_ERROR;\n    }\n\n    CONF_RLOCK();\n    p = obj;\n    gt = (struct darray*)(p + opt->offset);\n\n    ASSERT(darray_n(strs) == 0);\n\n    for (j = 0; j < darray_n(gt); j ++) {\n        str1 = darray_get(gt, j);\n        str2 = darray_push(strs);\n        *str2 = sdsdup(*str1);\n    }\n    \n    CONF_UNLOCK();\n    return VR_OK;\n}\n\nstatic void dictConfValueDestructor(void *privdata, void *val)\n{\n    DICT_NOTUSED(privdata);\n\n    conf_value_destroy(val);\n}\n\nstatic void dictDestructor(void *privdata, void *val)\n{\n    DICT_NOTUSED(privdata);\n\n    dictRelease(val);\n}\n\nstatic dictType OrganizationDictType = {\n    dictSdsHash,                /* hash function */\n    NULL,                       /* key dup */\n    NULL,                       /* val dup */\n    dictSdsKeyCompare,          /* key compare */\n    dictSdsDestructor,          /* key destructor */\n    dictDestructor              /* val destructor */\n};\n\nstatic dictType KeyValueDictType = {\n    dictSdsHash,                /* hash function */\n    NULL,                       /* key dup */\n    NULL,                       /* val dup */\n    dictSdsKeyCompare,          /* key compare */\n    dictSdsDestructor,          /* key destructor */\n    dictConfValueDestructor     /* val destructor */\n};\n\nstatic dictType ConfTableDictType = {\n    dictStrCaseHash,            /* hash function */\n    NULL,                       /* key dup */\n    NULL,                       /* val dup */\n    dictStrKeyCaseCompare,      /* key compare */\n    NULL,                       /* key destructor */\n    NULL                        /* val destructor */\n};\n\nconf_value *conf_value_create(int type)\n{\n    conf_value *cv;\n\n    cv = dalloc(sizeof(*cv));\n    if(cv == NULL){\n        return NULL;\n    }\n\n    cv->type = type;\n    cv->value = NULL;\n\n    if(cv->type == CONF_VALUE_TYPE_ARRAY){\n        cv->value = darray_create(3, sizeof(conf_value*));\n        if(cv->value == NULL){\n            dfree(cv);\n            return NULL;\n        }\n    }\n\n    return cv;\n}\n\nvoid conf_value_destroy(conf_value *cv)\n{\n    conf_value **cv_sub;\n    \n    if(cv == NULL){\n        return;\n    }\n    \n    if(cv->type == CONF_VALUE_TYPE_UNKNOW){\n        dfree(cv);\n        return;\n    }else if(cv->type == CONF_VALUE_TYPE_STRING){\n        if(cv->value != NULL){\n            sdsfree(cv->value);\n        }\n    }else if(cv->type == CONF_VALUE_TYPE_ARRAY){\n        if(cv->value != NULL){\n            while(darray_n(cv->value) > 0){\n                cv_sub = darray_pop(cv->value);\n                conf_value_destroy(*cv_sub);\n            }\n\n            darray_destroy(cv->value);\n        }\n    }else{\n        NOT_REACHED();\n    }\n\n    dfree(cv);\n}\n\nstatic int conf_server_init(conf_server *cs)\n{\n    if(cs == NULL){\n        return VR_ERROR;\n    }\n\n    cs->ctable = dictCreate(&ConfTableDictType,NULL);\n\n    cs->databases = CONF_UNSET_NUM;\n    cs->internal_dbs_per_databases = CONF_UNSET_NUM;\n    cs->max_time_complexity_limit = CONF_UNSET_NUM;\n    cs->maxmemory = CONF_UNSET_NUM;\n    cs->maxmemory_policy = CONF_UNSET_NUM;\n    cs->maxmemory_samples = CONF_UNSET_NUM;\n    cs->maxclients = CONF_UNSET_NUM;\n    cs->threads = CONF_UNSET_NUM;\n    darray_init(&cs->binds,1,sizeof(sds));\n    cs->port = CONF_UNSET_NUM;\n    cs->requirepass = CONF_UNSET_PTR;\n    cs->adminpass = CONF_UNSET_PTR;\n    cs->dir = CONF_UNSET_PTR;\n    darray_init(&cs->commands_need_adminpass,1,sizeof(sds));\n\n    return VR_OK;\n}\n\nstatic int conf_server_set_default(conf_server *cs)\n{\n    sds *str;\n    conf_option *opt;\n\n    if(cs == NULL){\n        return VR_ERROR;\n    }\n\n    for (opt = conf_server_options; opt&&opt->name; opt++) {\n        dictAdd(cs->ctable,opt->name,opt);\n    }\n\n    cs->databases = CONFIG_DEFAULT_LOGICAL_DBNUM;\n    cs->internal_dbs_per_databases = CONFIG_DEFAULT_INTERNAL_DBNUM;\n    cs->max_time_complexity_limit = CONFIG_DEFAULT_MAX_TIME_COMPLEXITY_LIMIT;\n    cs->maxmemory = CONFIG_DEFAULT_MAXMEMORY;\n    cs->maxmemory_policy = CONFIG_DEFAULT_MAXMEMORY_POLICY;\n    cs->maxmemory_samples = CONFIG_DEFAULT_MAXMEMORY_SAMPLES;\n    cs->maxclients = CONFIG_DEFAULT_MAX_CLIENTS;\n    cs->threads = CONFIG_DEFAULT_THREADS_NUM;\n    cs->slowlog_log_slower_than = CONFIG_DEFAULT_SLOWLOG_LOG_SLOWER_THAN;\n    cs->slowlog_max_len = CONFIG_DEFAULT_SLOWLOG_MAX_LEN;\n    cs->requirepass = CONF_UNSET_PTR;\n    cs->adminpass = CONF_UNSET_PTR;\n\n    while (darray_n(&cs->binds) > 0) {\n        str = darray_pop(&cs->binds);\n        sdsfree(*str);\n    }\n    str = darray_push(&cs->binds);\n    *str = sdsnew(CONFIG_DEFAULT_HOST);\n    \n    cs->port = CONFIG_DEFAULT_SERVER_PORT;\n\n    if (cs->dir != CONF_UNSET_PTR) {\n        sdsfree(cs->dir);\n    }\n    cs->dir = sdsnew(CONFIG_DEFAULT_DATA_DIR);\n\n    while (darray_n(&cs->commands_need_adminpass) > 0) {\n        str = darray_pop(&cs->commands_need_adminpass);\n        sdsfree(*str);\n    }\n\n    return VR_OK;\n}\n\nstatic void conf_server_deinit(conf_server *cs)\n{\n    sds *str;\n    \n    if(cs == NULL){\n        return;\n    }\n\n    cs->databases = CONF_UNSET_NUM;\n    cs->internal_dbs_per_databases = CONF_UNSET_NUM;\n    cs->maxmemory = CONF_UNSET_NUM;\n    cs->maxmemory_policy = CONF_UNSET_NUM;\n    cs->maxmemory_samples = CONF_UNSET_NUM;\n    cs->max_time_complexity_limit = CONF_UNSET_NUM;\n    cs->maxclients = CONF_UNSET_NUM;\n    cs->threads = CONF_UNSET_NUM;\n\n    while (darray_n(&cs->binds) > 0) {\n        str = darray_pop(&cs->binds);\n        sdsfree(*str);\n    }\n    darray_deinit(&cs->binds);\n\n    cs->port = CONF_UNSET_NUM;\n    \n    if (cs->dir != CONF_UNSET_PTR) {\n        sdsfree(cs->dir);\n        cs->dir = CONF_UNSET_PTR;    \n    }\n\n    if (cs->requirepass != CONF_UNSET_PTR) {\n        sdsfree(cs->requirepass);\n        cs->requirepass = CONF_UNSET_PTR;    \n    }\n    if (cs->adminpass != CONF_UNSET_PTR) {\n        sdsfree(cs->adminpass);\n        cs->adminpass = CONF_UNSET_PTR;    \n    }\n\n    while (darray_n(&cs->commands_need_adminpass) > 0) {\n        str = darray_pop(&cs->commands_need_adminpass);\n        sdsfree(*str);\n    }\n    darray_deinit(&cs->commands_need_adminpass);\n}\n\nint\nconf_server_get(const char *option_name, void *value)\n{\n    conf_option *opt;\n\n    opt = dictFetchValue(cserver->ctable, option_name);\n    if (opt == NULL)\n        return VR_ERROR;\n\n    return opt->get(cserver, opt, value);\n}\n\nint\nconf_server_set(const char *option_name, conf_value *value)\n{\n    conf_option *opt;\n\n    opt = dictFetchValue(cserver->ctable, option_name);\n    if (opt == NULL || opt->flags&CONF_FIELD_FLAGS_NO_MODIFY)\n        return VR_ERROR;\n\n    return opt->set(cserver, opt, value);\n}\n\nstatic int conf_init(vr_conf *cf)\n{\n    int ret;\n\n    if(cf == NULL){\n        return VR_ERROR;\n    }\n\n    cf->fname = NULL;\n    cf->organizations = NULL;\n    cf->version = 0;\n    pthread_rwlock_init(&cf->rwl, NULL);\n    pthread_mutex_init(&cf->flock, NULL);\n\n    cf->organizations = dictCreate(&OrganizationDictType, NULL);\n    if (cf->organizations == NULL) {\n        return VR_ERROR;\n    }\n\n    conf_server_init(&cf->cserver);\n\n    conf = cf;\n    \n    return VR_OK;\n}\n\nstatic int conf_set_default(vr_conf *cf)\n{\n    CONF_WLOCK();\n    conf_server_set_default(&cf->cserver);\n    CONF_UNLOCK();\n    return VR_OK;\n}\n\nstatic void conf_deinit(vr_conf *cf)\n{\n    if(cf == NULL){\n        return;\n    }\n\n    if (cf->fname != NULL) {\n        sdsfree(cf->fname);\n        cf->fname = NULL;\n    }\n\n    if(cf->organizations != NULL){\n        dictRelease(cf->organizations);\n        cf->organizations = NULL;\n    }\n\n    conf_server_deinit(&cf->cserver);\n\n    cf->version = 0;\n    pthread_rwlock_destroy(&cf->rwl);\n    pthread_mutex_destroy(&cf->flock);\n}\n\nstatic void\nconf_server_dump(conf_server *cs, int log_level)\n{\n    if(cs == NULL){\n        return;\n    }\n\n    log_debug(log_level, \"  databases : %d\", cs->databases);\n    log_debug(log_level, \"  internal_dbs_per_databases : %d\", cs->internal_dbs_per_databases);\n    log_debug(log_level, \"  maxmemory : %lld\", cs->maxmemory);\n    log_debug(log_level, \"  maxmemory_policy : %d\", cs->maxmemory_policy);    \n    log_debug(log_level, \"  maxmemory_samples : %d\", cs->maxmemory_samples);\n    log_debug(log_level, \"  max_time_complexity_limit : %lld\", cs->max_time_complexity_limit);\n}\n\nstatic void\nconf_dump(vr_conf *cf)\n{\n    int log_level = LOG_VERB;\n    conf_server *cs;\n    \n    if(cf == NULL){\n        return;\n    }\n\n    cs = &cf->cserver;\n    log_debug(log_level, \"server in conf file\");\n    conf_server_dump(cs, log_level);\n    log_debug(log_level, \"\");\n}\n\n/* return -1: error\n  * return 0: conf value is append\n  * return 1: conf value is insert*/\nstatic int\nconf_key_value_insert(dict *org, sds key, conf_value *cv)\n{\n    if (key == NULL) {\n        log_error(\"value in conf file has no key\");\n        return -1;\n    }\n\n    if (cv == NULL) {\n        log_error(\"key %s in conf file has no value\", key);\n        return -1;\n    }\n\n    if (org == NULL) {\n        log_error(\"key %s in conf file has no organization\", \n            key);\n        return -1;\n    }\n    \n    if (dictAdd(org,key,cv) != DICT_OK) {\n        dictEntry *de;\n        conf_value *cv_old, *cv_new, **cv_sub;\n        de = dictFind(org,key);\n        cv_old = dictGetVal(de);\n        if (cv_old->type != CONF_VALUE_TYPE_ARRAY) {\n            cv_new = conf_value_create(CONF_VALUE_TYPE_ARRAY);\n            cv_sub = darray_push(cv_new->value);\n            *cv_sub = cv_old;\n            cv_sub = darray_push(cv_new->value);\n            *cv_sub = cv;\n            dictSetVal(org,de,cv_new);\n        } else {\n            cv_sub = darray_push(cv_old->value);\n            *cv_sub = cv;\n        }\n        return 0;\n    }\n\n    return 1;\n}\n\nstatic int\nconf_pre_load_from_string(vr_conf *cf, char *config)\n{\n    int ret;\n    int linenum = 0, totlines, i, j;\n    int slaveof_linenum = 0;\n    sds *lines = NULL;\n    dict *org = NULL;\n    sds org_name = NULL;\n    dictEntry *de;\n    sds key = NULL;\n    conf_value *cv = NULL;\n\n    lines = sdssplitlen(config,strlen(config),\"\\n\",1,&totlines);\n\n    for (i = 0; i < totlines; i++) {\n        sds *argv;\n        int argc;\n\n        linenum = i+1;\n        lines[i] = sdstrim(lines[i],\" \\t\\r\\n\");\n\n        /* Skip comments and blank lines */\n        if (lines[i][0] == '#' || lines[i][0] == '\\0') continue;\n\n        if (lines[i][0] == '[') {\n            if (sdslen(lines[i]) <= 2 || lines[i][sdslen(lines[i])-1] == ']') {\n                log_error(\"Organization name %s in conf file %s error\",\n                    lines[i], cf->fname);\n                goto loaderr;\n            }\n            org_name = sdsnewlen(lines[i]+1,sdslen(lines[i])-2);\n            de = dictFind(cf->organizations,org_name);\n            if (de == NULL) {\n                org = dictCreate(&KeyValueDictType, NULL);\n                dictAdd(cf->organizations,org_name,org);\n            } else {\n                org = dictGetVal(de);\n                sdsfree(org_name);\n            }\n            \n            continue;\n        }\n\n        /* Split into arguments */\n        argv = sdssplitargs(lines[i],&argc);\n        if (argv == NULL) {\n            log_error(\"Unbalanced quotes in configuration line\");\n            goto loaderr;\n        }\n\n        /* Skip this line if the resulting command vector is empty. */\n        if (argc == 0) {\n            sdsfreesplitres(argv,argc);\n            continue;\n        }\n        sdstolower(argv[0]);\n\n        if (org == NULL) {\n            org_name = sdsnew(\"server\");\n            org = dictCreate(&KeyValueDictType, NULL);\n            dictAdd(cf->organizations,org_name,org);\n        }\n\n        key = argv[0];\n        argv[0] = NULL;\n        for (j = 1; j < argc; j ++) {\n            cv = conf_value_create(CONF_VALUE_TYPE_STRING);\n            cv->value = argv[j];\n            argv[j] = NULL;\n            ret = conf_key_value_insert(org, key, cv);\n            if(ret == -1){\n                sdsfreesplitres(argv,argc);\n                sdsfree(key);\n                conf_value_destroy(cv);\n                log_error(\"key value insert into organization failed\");\n                goto loaderr;\n            } else if (j == 1 && ret == 0) {\n                sdsfree(key);\n            }\n        }\n\n        sdsfreesplitres(argv,argc);\n    }\n\n    if (lines) {\n        sdsfreesplitres(lines,linenum);\n    }\n    return VR_OK;\n    \nloaderr:\n    if (lines) {\n        sdsfreesplitres(lines,linenum);\n    }\n    return VR_ERROR;\n}\n\nstatic int\nconf_pre_validate(vr_conf *cf)\n{\n    int ret;\n    sds config = sdsempty();\n    char buf[CONF_MAX_LINE+1];\n\n    /* Load the file content */\n    if (cf->fname) {\n        FILE *fp;\n\n        if (cf->fname[0] == '-' && cf->fname[1] == '\\0') {\n            fp = stdin;\n        } else {\n            if ((fp = fopen(cf->fname,\"r\")) == NULL) {\n                log_error(\"Open config file '%s' failed: %s\", cf->fname, strerror(errno));\n                sdsfree(config);\n                return VR_ERROR;\n            }\n        }\n        while(fgets(buf,CONF_MAX_LINE+1,fp) != NULL)\n            config = sdscat(config,buf);\n        if (fp != stdin) fclose(fp);\n    }\n\n    ret = conf_pre_load_from_string(cf,config);\n    if (ret != VR_OK) {\n        sdsfree(config);\n        return VR_ERROR;\n    }\n    \n    sdsfree(config);\n    return VR_OK;\n}\n\nstatic int\nconf_parse_conf_server(conf_server *cs, dict *org)\n{\n    int ret;\n    conf_option *opt;\n    dictEntry *de;\n    sds key;\n    \n    if(cs == NULL || org == NULL){\n        return VR_ERROR;\n    }\n    \n    key = sdsempty();\n    for (opt = conf_server_options; opt&&opt->name; opt++) {\n        key = sdscpy(key,opt->name);\n        de = dictFind(org,key);\n        if (de != NULL) {\n            ret = opt->set(cs, opt, dictGetVal(de));\n            if(ret != VR_OK){\n                log_error(\"parse key %s in conf file error\", key);\n                sdsfree(key);\n                return VR_ERROR;\n            }\n        }\n    }\n\n    sdsfree(key);\n    return VR_OK;\n}\n\nstatic int\nconf_parse(vr_conf *cf)\n{\n    int ret;\n    dict *orgs, *org;\n    dictEntry *de;\n    sds key;\n    \n    if (cf == NULL) {\n        return VR_ERROR;\n    }\n\n    orgs = cf->organizations;\n    if (orgs == NULL) {\n        return VR_ERROR;\n    }\n\n    /* server */\n    key = sdsnew(CONF_ORGANIZATION_NAME_SERVER);\n    de = dictFind(orgs, key);\n    if (de == NULL) {\n        log_error(\"can not find %s organization in conf file %s\", \n            CONF_ORGANIZATION_NAME_SERVER, cf->fname);\n        sdsfree(key);\n        return VR_ERROR;\n    }\n\n    org = dictGetVal(de);\n    if (org == NULL) {\n        log_error(\"dict %s entry value is NULL\", dictGetKey(de));\n        sdsfree(key);\n        return VR_ERROR;\n    }\n\n    ret = conf_parse_conf_server(&cf->cserver, org);\n    if( ret != VR_OK) {\n        log_error(\"common conf parse error\");\n        sdsfree(key);\n        return VR_ERROR;\n    }\n\n    sdsfree(key);\n    \n    return VR_OK;\n}\n\nstatic int\nconf_post_validate(vr_conf *cf)\n{\n    if(cf == NULL){\n        return VR_ERROR;\n    }\n\n    if(cf->organizations != NULL){\n        dictRelease(cf->organizations);\n        cf->organizations = NULL;\n    }\n    \n    return VR_OK;\n}\n\nstatic vr_conf *\nconf_open(char *filename)\n{\n    int ret;\n    vr_conf *cf = NULL;\n    sds path = NULL;\n\n    if (filename == NULL) {\n        log_error(\"configuration file name is NULL.\");\n        return NULL;\n    }\n\n    path = getAbsolutePath(filename);\n    if (path == NULL) {\n        log_error(\"configuration file name '%s' is error.\", filename);\n        goto error;\n    }\n\n    cf = dalloc(sizeof(*cf));\n    if (cf == NULL) {\n        goto error;\n    }\n\n    ret = conf_init(cf);\n    if(ret != VR_OK){\n        goto error;\n    }\n\n    ret = conf_set_default(cf);\n    if (ret != VR_OK) {\n        goto error;\n    }\n\n    cf->fname = path;\n\n    return cf;\n\nerror:\n\n    if (cf != NULL) {\n        conf_destroy(cf);\n    }\n\n    if (path != NULL) {\n        sdsfree(path);\n    }\n    \n    return NULL;\n}\n\nvr_conf *\nconf_create(char *filename)\n{\n    int ret;\n    vr_conf *cf;\n\n    cf = conf_open(filename);\n    if (cf == NULL) {\n        return NULL;\n    }\n\n    /* validate configuration file before parsing */\n    ret = conf_pre_validate(cf);\n    if (ret != VR_OK) {\n        goto error;\n    }\n\n    conf_organizations_dump(cf);\n\n    /* parse the configuration file */\n    ret = conf_parse(cf);\n    if (ret != VR_OK) {\n        goto error;\n    }\n\n    /* validate parsed configuration */\n    ret = conf_post_validate(cf);\n    if (ret != VR_OK) {\n        goto error;\n    }\n\n    conf_dump(cf);\n\n    cserver = &cf->cserver;\n\n    return cf;\n\nerror:\n    conf_destroy(cf);\n    return NULL;\n}\n\nvoid \nconf_destroy(vr_conf *cf)\n{\n    if (cf == NULL) {\n        return;\n    }\n    \n    conf_deinit(cf);\n    \n    dfree(cf);\n}\n\nunsigned long long\nconf_version_get(void)\n{\n    unsigned long long version;\n    \n    CONF_RLOCK();\n    version = conf->version;\n    CONF_UNLOCK();\n\n    return version;\n}\n\nint\nCONF_RLOCK(void)\n{\n    return pthread_rwlock_rdlock(&conf->rwl);\n}\n\nint\nCONF_WLOCK(void)\n{\n    return pthread_rwlock_wrlock(&conf->rwl);\n}\n\nint\nCONF_UNLOCK(void)\n{\n    return pthread_rwlock_unlock(&conf->rwl);\n}\n\nint\nCONFF_LOCK(void)\n{\n    return pthread_mutex_lock(&conf->flock);\n}\n\nint\nCONFF_UNLOCK(void)\n{\n    return pthread_mutex_unlock(&conf->flock);\n}\n\nconst char *\nget_evictpolicy_strings(int evictpolicy_type)\n{\n    return evictpolicy_strings[evictpolicy_type];\n}\n\n/*-----------------------------------------------------------------------------\n * CONFIG SET implementation\n *----------------------------------------------------------------------------*/\n\nstatic void configSetCommand(client *c) {\n    int ret;\n    sds value;\n    sds *fields;\n    int fields_count = 0;\n    conf_option *opt;\n    conf_value *cv;\n    \n    serverAssertWithInfo(c,c->argv[2],sdsEncodedObject(c->argv[2]));\n    serverAssertWithInfo(c,c->argv[3],sdsEncodedObject(c->argv[3]));\n\n    opt = dictFetchValue(cserver->ctable, c->argv[2]->ptr);\n\n    if (opt == NULL) {\n        addReplyErrorFormat(c,\"Unsupported CONFIG parameter: %s\",\n            (char*)c->argv[2]->ptr);\n        return;\n    } else if (opt->flags&CONF_FIELD_FLAGS_NO_MODIFY) {\n        addReplyErrorFormat(c,\"Unsupported modify this CONFIG parameter: %s\",\n            (char*)c->argv[2]->ptr);\n        return;\n    }\n\n    value = c->argv[3]->ptr;\n\n    /* Handle some special action before setting the config value if needed */\n    if (!strcasecmp(c->argv[2]->ptr,CONFIG_SOPN_MAXCLIENTS)) {\n        long maxclients;\n        int filelimit, threads;\n        if (string2l(value,sdslen(value),&maxclients) == 0 || maxclients < 1) goto badfmt;\n        conf_server_get(CONFIG_SOPN_THREADS,&threads);\n        \n        filelimit = adjustOpenFilesLimit((int)maxclients);\n        if ((filelimit-threads*2-CONFIG_MIN_RESERVED_FDS) != maxclients) {\n            addReplyErrorFormat(c,\"The operating system is not able to handle the specified number of clients\");\n            return;\n        }\n    } else if (!strcasecmp(c->argv[2]->ptr,CONFIG_SOPN_ADMINPASS)) {\n        if (c->vel->cc.adminpass && c->authenticated < 2) {\n            addReplyErrorFormat(c,\"You need adminpass to set this CONFIG parameter: %s\",\n                (char*)c->argv[2]->ptr);\n            return;\n        }\n    }\n\n    fields = sdssplitlen(value,sdslen(value),\" \",1,&fields_count);\n    if (fields == NULL) {\n        goto badfmt;\n    } else if (fields_count == 0) {\n        cv = conf_value_create(CONF_VALUE_TYPE_STRING);\n        cv->value = sdsempty();\n    } else if (fields_count == 1) {\n        cv = conf_value_create(CONF_VALUE_TYPE_STRING);\n        cv->value = fields[0];\n        fields[0] = NULL;\n    } else if (fields_count > 1) {\n        conf_value **cv_sub;\n        uint32_t i;\n    \n        cv = conf_value_create(CONF_VALUE_TYPE_ARRAY);\n        for (i = 0; i < fields_count; i ++) {\n            cv_sub = darray_push(cv->value);\n            *cv_sub = conf_value_create(CONF_VALUE_TYPE_STRING);\n            (*cv_sub)->value = fields[i];\n            fields[i] = NULL;\n        }\n    } else {\n        log_debug(LOG_NOTICE, \"fields_count: %d\", fields_count);\n        serverPanic(\"Error config set value\");\n    }\n    sdsfreesplitres(fields,fields_count);\n\n    ret = opt->set(cserver, opt, cv);\n    conf_value_destroy(cv);\n    if (ret != VR_OK) {\n        goto badfmt;\n    }\n\n    /* Handle some special action after setting the config value if needed */\n    if (!strcmp(opt->name,CONFIG_SOPN_MAXMEMORY)) {\n        long long maxmemory;\n        conf_server_get(CONFIG_SOPN_MAXMEMORY,&maxmemory);\n        if (maxmemory) {\n            if (maxmemory < dalloc_used_memory()) {\n                log_warn(\"WARNING: the new maxmemory value set via CONFIG SET is smaller than the current memory usage. This will result in keys eviction and/or inability to accept new write commands depending on the maxmemory-policy.\");\n                freeMemoryIfNeeded(c->vel);\n            }\n        }\n    }\n\n    /* On success we just return a generic OK for all the options. */\n    addReply(c,shared.ok);\n    return;\n\nbadfmt: /* Bad format errors */\n    addReplyErrorFormat(c,\"Invalid argument '%s' for CONFIG SET '%s'\",\n        (char*)value,\n        (char*)c->argv[2]->ptr);\n}\n\n/*-----------------------------------------------------------------------------\n * CONFIG GET implementation\n *----------------------------------------------------------------------------*/\n\nstatic void addReplyConfOption(client *c,conf_option *cop)\n{\n    addReplyBulkCString(c,cop->name);\n    if (cop->type == CONF_FIELD_TYPE_INT) {\n        int value;\n        conf_server_get(cop->name,&value);\n        \n        if (!strcmp(cop->name,CONFIG_SOPN_MAXMEMORYP)) {\n            addReplyBulkCString(c,get_evictpolicy_strings(value));\n        } else {\n            addReplyBulkLongLong(c,value);\n        }\n    } else if (cop->type == CONF_FIELD_TYPE_LONGLONG) {\n        long long value;\n        conf_server_get(cop->name,&value);\n        addReplyBulkLongLong(c,value);\n    } else if (cop->type == CONF_FIELD_TYPE_SDS) {\n        sds value;\n        conf_server_get(cop->name,&value);\n        if (value == NULL) {\n            addReplyBulkCString(c,\"\");\n        } else {\n            addReplyBulkSds(c,value);\n        }\n    } else if (cop->type == CONF_FIELD_TYPE_ARRAYSDS) {\n        struct darray values;\n        sds value = sdsempty();\n        sds *elem;\n\n        darray_init(&values,1,sizeof(sds));\n        conf_server_get(cop->name,&values);\n        while(darray_n(&values) > 0) {\n            elem = darray_pop(&values);\n            value = sdscatsds(value,*elem);\n            value = sdscat(value,\" \");\n            sdsfree(*elem);\n        }\n        darray_deinit(&values);\n        if (sdslen(value) > 0) sdsrange(value,0,-2);\n        addReplyBulkSds(c,value);\n    } else {\n        serverPanic(\"Error conf field type\");\n    }\n}\n\nstatic void configGetCommand(client *c) {\n    robj *o = c->argv[2];\n    char *pattern = o->ptr;\n    conf_option *cop;\n    serverAssertWithInfo(c,o,sdsEncodedObject(o));\n\n    cop = dictFetchValue(cserver->ctable, pattern);\n    if (cop != NULL) {\n        /* Don't show adminpass if user has no right. */\n        if (!strcmp(cop->name,CONFIG_SOPN_ADMINPASS) && \n            c->vel->cc.adminpass && c->authenticated < 2) {\n            addReply(c,shared.noadminerr);\n        } else {\n            addReplyMultiBulkLen(c,2);\n            addReplyConfOption(c,cop);\n        }\n    } else {\n        int matches = 0;\n        void * replylen = addDeferredMultiBulkLength(c);\n        for (cop = conf_server_options; cop&&cop->name; cop++) {\n            if (stringmatch(pattern,cop->name,1)) {\n                /* Don't show adminpass if user has no right. */\n                if (!strcmp(cop->name,CONFIG_SOPN_ADMINPASS) && \n                    c->vel->cc.adminpass && c->authenticated < 2)\n                    continue;\n                \n                addReplyConfOption(c,cop);\n                matches ++;\n            }\n        }\n        setDeferredMultiBulkLength(c,replylen,matches*2);\n    }\n}\n\n/*-----------------------------------------------------------------------------\n * CONFIG REWRITE implementation\n *----------------------------------------------------------------------------*/\n\n/* The config rewrite state. */\nstruct rewriteConfigState {\n    dict *option_to_line; /* Option -> list of config file lines map */\n    dict *rewritten;      /* Dictionary of already processed options */\n    int numlines;         /* Number of lines in current config */\n    sds *lines;           /* Current lines as an array of sds strings */\n    int has_tail;         /* True if we already added directives that were\n                             not present in the original config file. */\n};\n\n/* Append the new line to the current configuration state. */\nstatic void rewriteConfigAppendLine(struct rewriteConfigState *state, sds line) {\n    state->lines = drealloc(state->lines, sizeof(char*) * (state->numlines+1));\n    state->lines[state->numlines++] = line;\n}\n\n/* Populate the option -> list of line numbers map. */\nstatic void rewriteConfigAddLineNumberToOption(struct rewriteConfigState *state, sds option, int linenum) {\n    dlist *l = dictFetchValue(state->option_to_line,option);\n\n    if (l == NULL) {\n        l = dlistCreate();\n        dictAdd(state->option_to_line,sdsdup(option),l);\n    }\n    dlistAddNodeTail(l,(void*)(long)linenum);\n}\n\ndictType optionToLineDictType = {\n    dictSdsCaseHash,            /* hash function */\n    NULL,                       /* key dup */\n    NULL,                       /* val dup */\n    dictSdsKeyCaseCompare,      /* key compare */\n    dictSdsDestructor,          /* key destructor */\n    dictListDestructor          /* val destructor */\n};\n\ndictType optionSetDictType = {\n    dictSdsCaseHash,            /* hash function */\n    NULL,                       /* key dup */\n    NULL,                       /* val dup */\n    dictSdsKeyCaseCompare,      /* key compare */\n    dictSdsDestructor,          /* key destructor */\n    NULL                        /* val destructor */\n};\n\n#define CONFIG_MAX_LINE    1024\n#define REDIS_CONFIG_REWRITE_SIGNATURE \"# Generated by CONFIG REWRITE\"\n/* Read the old file, split it into lines to populate a newly created\n * config rewrite state, and return it to the caller.\n *\n * If it is impossible to read the old file, NULL is returned.\n * If the old file does not exist at all, an empty state is returned. */\nstatic struct rewriteConfigState *rewriteConfigReadOldFile(char *path) {\n    FILE *fp = fopen(path,\"r\");\n    struct rewriteConfigState *state = dalloc(sizeof(*state));\n    char buf[CONFIG_MAX_LINE+1];\n    int linenum = -1;\n\n    if (fp == NULL && errno != ENOENT) return NULL;\n\n    state->option_to_line = dictCreate(&optionToLineDictType,NULL);\n    state->rewritten = dictCreate(&optionSetDictType,NULL);\n    state->numlines = 0;\n    state->lines = NULL;\n    state->has_tail = 0;\n    if (fp == NULL) return state;\n\n    /* Read the old file line by line, populate the state. */\n    while(fgets(buf,CONFIG_MAX_LINE+1,fp) != NULL) {\n        int argc;\n        sds *argv;\n        sds line = sdstrim(sdsnew(buf),\"\\r\\n\\t \");\n\n        linenum++; /* Zero based, so we init at -1 */\n\n        /* Handle comments and empty lines. */\n        if (line[0] == '#' || line[0] == '\\0') {\n            if (!state->has_tail && !strcmp(line,REDIS_CONFIG_REWRITE_SIGNATURE))\n                state->has_tail = 1;\n            rewriteConfigAppendLine(state,line);\n            continue;\n        }\n\n        /* Not a comment, split into arguments. */\n        argv = sdssplitargs(line,&argc);\n        if (argv == NULL) {\n            /* Apparently the line is unparsable for some reason, for\n             * instance it may have unbalanced quotes. Load it as a\n             * comment. */\n            sds aux = sdsnew(\"# ??? \");\n            aux = sdscatsds(aux,line);\n            sdsfree(line);\n            rewriteConfigAppendLine(state,aux);\n            continue;\n        }\n\n        sdstolower(argv[0]); /* We only want lowercase config directives. */\n\n        /* Now we populate the state according to the content of this line.\n         * Append the line and populate the option -> line numbers map. */\n        rewriteConfigAppendLine(state,line);\n        rewriteConfigAddLineNumberToOption(state,argv[0],linenum);\n\n        sdsfreesplitres(argv,argc);\n    }\n    fclose(fp);\n    return state;\n}\n\n/* Add the specified option to the set of processed options.\n * This is useful as only unused lines of processed options will be blanked\n * in the config file, while options the rewrite process does not understand\n * remain untouched. */\nstatic void rewriteConfigMarkAsProcessed(struct rewriteConfigState *state, const char *option) {\n    sds opt = sdsnew(option);\n\n    if (dictAdd(state->rewritten,opt,NULL) != DICT_OK) sdsfree(opt);\n}\n\n/* Rewrite the specified configuration option with the new \"line\".\n * It progressively uses lines of the file that were already used for the same\n * configuration option in the old version of the file, removing that line from\n * the map of options -> line numbers.\n *\n * If there are lines associated with a given configuration option and\n * \"force\" is non-zero, the line is appended to the configuration file.\n * Usually \"force\" is true when an option has not its default value, so it\n * must be rewritten even if not present previously.\n *\n * The first time a line is appended into a configuration file, a comment\n * is added to show that starting from that point the config file was generated\n * by CONFIG REWRITE.\n *\n * \"line\" is either used, or freed, so the caller does not need to free it\n * in any way. */\nstatic void rewriteConfigRewriteLine(struct rewriteConfigState *state, const char *option, sds line, int force) {\n    sds o = sdsnew(option);\n    dlist *l = dictFetchValue(state->option_to_line,o);\n\n    rewriteConfigMarkAsProcessed(state,option);\n\n    if (!l && !force) {\n        /* Option not used previously, and we are not forced to use it. */\n        sdsfree(line);\n        sdsfree(o);\n        return;\n    }\n\n    if (l) {\n        dlistNode *ln = dlistFirst(l);\n        int linenum = (long) ln->value;\n\n        /* There are still lines in the old configuration file we can reuse\n         * for this option. Replace the line with the new one. */\n        dlistDelNode(l,ln);\n        if (dlistLength(l) == 0) dictDelete(state->option_to_line,o);\n        sdsfree(state->lines[linenum]);\n        state->lines[linenum] = line;\n    } else {\n        /* Append a new line. */\n        if (!state->has_tail) {\n            rewriteConfigAppendLine(state,\n                sdsnew(REDIS_CONFIG_REWRITE_SIGNATURE));\n            state->has_tail = 1;\n        }\n        rewriteConfigAppendLine(state,line);\n    }\n    sdsfree(o);\n}\n\n/* Free the configuration rewrite state. */\nstatic void rewriteConfigReleaseState(struct rewriteConfigState *state) {\n    sdsfreesplitres(state->lines,state->numlines);\n    dictRelease(state->option_to_line);\n    dictRelease(state->rewritten);\n    dfree(state);\n}\n\n/* At the end of the rewrite process the state contains the remaining\n * map between \"option name\" => \"lines in the original config file\".\n * Lines used by the rewrite process were removed by the function\n * rewriteConfigRewriteLine(), all the other lines are \"orphaned\" and\n * should be replaced by empty lines.\n *\n * This function does just this, iterating all the option names and\n * blanking all the lines still associated. */\nstatic void rewriteConfigRemoveOrphaned(struct rewriteConfigState *state) {\n    dictIterator *di = dictGetIterator(state->option_to_line);\n    dictEntry *de;\n\n    while((de = dictNext(di)) != NULL) {\n        dlist *l = dictGetVal(de);\n        sds option = dictGetKey(de);\n\n        /* Don't blank lines about options the rewrite process\n         * don't understand. */\n        if (dictFind(state->rewritten,option) == NULL) {\n            log_debug(LOG_DEBUG,\"Not rewritten option: %s\", option);\n            continue;\n        }\n\n        while(dlistLength(l)) {\n            dlistNode *ln = dlistFirst(l);\n            int linenum = (long) ln->value;\n\n            sdsfree(state->lines[linenum]);\n            state->lines[linenum] = sdsempty();\n            dlistDelNode(l,ln);\n        }\n    }\n    dictReleaseIterator(di);\n}\n\n/* Glue together the configuration lines in the current configuration\n * rewrite state into a single string, stripping multiple empty lines. */\nstatic sds rewriteConfigGetContentFromState(struct rewriteConfigState *state) {\n    sds content = sdsempty();\n    int j, was_empty = 0;\n\n    for (j = 0; j < state->numlines; j++) {\n        /* Every cluster of empty lines is turned into a single empty line. */\n        if (sdslen(state->lines[j]) == 0) {\n            if (was_empty) continue;\n            was_empty = 1;\n        } else {\n            was_empty = 0;\n        }\n        content = sdscatsds(content,state->lines[j]);\n        content = sdscatlen(content,\"\\n\",1);\n    }\n    return content;\n}\n\n/* This function overwrites the old configuration file with the new content.\n *\n * 1) The old file length is obtained.\n * 2) If the new content is smaller, padding is added.\n * 3) A single write(2) call is used to replace the content of the file.\n * 4) Later the file is truncated to the length of the new content.\n *\n * This way we are sure the file is left in a consistent state even if the\n * process is stopped between any of the four operations.\n *\n * The function returns 0 on success, otherwise -1 is returned and errno\n * set accordingly. */\nstatic int rewriteConfigOverwriteFile(char *configfile, sds content) {\n    int retval = 0;\n    int fd = open(configfile,O_RDWR|O_CREAT,0644);\n    int content_size = sdslen(content), padding = 0;\n    struct stat sb;\n    sds content_padded;\n\n    /* 1) Open the old file (or create a new one if it does not\n     *    exist), get the size. */\n    if (fd == -1) return -1; /* errno set by open(). */\n    if (fstat(fd,&sb) == -1) {\n        close(fd);\n        return -1; /* errno set by fstat(). */\n    }\n\n    /* 2) Pad the content at least match the old file size. */\n    content_padded = sdsdup(content);\n    if (content_size < sb.st_size) {\n        /* If the old file was bigger, pad the content with\n         * a newline plus as many \"#\" chars as required. */\n        padding = sb.st_size - content_size;\n        content_padded = sdsgrowzero(content_padded,sb.st_size);\n        content_padded[content_size] = '\\n';\n        memset(content_padded+content_size+1,'#',padding-1);\n    }\n\n    /* 3) Write the new content using a single write(2). */\n    if (write(fd,content_padded,strlen(content_padded)) == -1) {\n        retval = -1;\n        goto cleanup;\n    }\n\n    /* 4) Truncate the file to the right length if we used padding. */\n    if (padding) {\n        if (ftruncate(fd,content_size) == -1) {\n            /* Non critical error... */\n        }\n    }\n\ncleanup:\n    sdsfree(content_padded);\n    close(fd);\n    return retval;\n}\n\n/* Rewrite a numerical (int range) option. */\nstatic void rewriteConfigIntOption(struct rewriteConfigState *state, char *option, int defvalue) {\n    int value;\n    int force;\n    sds line;\n\n    conf_server_get(option,&value);\n    line = sdscatprintf(sdsempty(),\"%s %d\",option,value);\n    force = value != defvalue;\n\n    rewriteConfigRewriteLine(state,option,line,force);\n}\n\n/* Rewrite a numerical (int range) option. */\nstatic void rewriteConfigSdsOption(struct rewriteConfigState *state, char *option, sds defvalue) {\n    sds value;\n    int force;\n    sds line;\n\n    conf_server_get(option,&value);\n    if (defvalue == NULL && value == NULL) {\n        force = 0;\n    } else if (defvalue != NULL && value != NULL && !sdscmp(value,defvalue)) {\n        force = 0;\n    } else {\n        force = 1;\n    }\n\n    if (value == NULL) {\n        line = sdscatprintf(sdsempty(),\"%s \\\"\\\"\",option);\n    } else {\n        line = sdscatprintf(sdsempty(),\"%s %s\",option,value);\n        sdsfree(value);\n    }\n    \n    rewriteConfigRewriteLine(state,option,line,force);\n}\n\n/* Rewrite a numerical (long long range) option. */\nstatic void rewriteConfigLongLongOption(struct rewriteConfigState *state, char *option, long long defvalue) {\n    long long value;\n    int force;\n    sds line;\n\n    conf_server_get(option,&value);\n    line = sdscatprintf(sdsempty(),\"%s %lld\",option,value);\n    force = value != defvalue;\n\n    rewriteConfigRewriteLine(state,option,line,force);\n}\n\n/* Write the long long 'bytes' value as a string in a way that is parsable\n * inside redis.conf. If possible uses the GB, MB, KB notation. */\nstatic int rewriteConfigFormatMemory(char *buf, size_t len, long long bytes) {\n    int gb = 1024*1024*1024;\n    int mb = 1024*1024;\n    int kb = 1024;\n\n    if (bytes && (bytes % gb) == 0) {\n        return snprintf(buf,len,\"%lldgb\",bytes/gb);\n    } else if (bytes && (bytes % mb) == 0) {\n        return snprintf(buf,len,\"%lldmb\",bytes/mb);\n    } else if (bytes && (bytes % kb) == 0) {\n        return snprintf(buf,len,\"%lldkb\",bytes/kb);\n    } else {\n        return snprintf(buf,len,\"%lld\",bytes);\n    }\n}\n\n/* Rewrite a simple \"option-name <bytes>\" configuration option. */\nstatic void rewriteConfigBytesOption(struct rewriteConfigState *state, char *option, long long defvalue) {\n     long long value;\n    char buf[64];\n    int force;\n    sds line;\n\n    conf_server_get(option,&value);\n    force = value != defvalue;\n\n    rewriteConfigFormatMemory(buf,sizeof(buf),value);\n    line = sdscatprintf(sdsempty(),\"%s %s\",option,buf);\n    rewriteConfigRewriteLine(state,option,line,force);\n}\n\n/* Rewrite an enumeration option. It takes as usually state and option name,\n * and in addition the enumeration array and the default value for the\n * option. */\nstatic void rewriteConfigEnumOption(struct rewriteConfigState *state, char *option, configEnumGetStrFun fun, int defval) {\n    int value;\n    sds line;\n    const char *name;\n    int force;\n\n    conf_server_get(option,&value);\n    force = value != defval;\n    name = fun(value);\n    line = sdscatprintf(sdsempty(),\"%s %s\",option,name);\n    rewriteConfigRewriteLine(state,option,line,force);\n}\n\n/* Rewrite the bind option. */\nstatic void rewriteConfigBindOption(struct rewriteConfigState *state) {\n    struct darray values;\n    sds *value, line;\n    int force = 1;\n    char *option = CONFIG_SOPN_BIND;\n\n    darray_init(&values,1,sizeof(sds));\n    conf_server_get(option,&values);\n    /* Nothing to rewrite if we don't have bind addresses. */\n    if (darray_n(&values) == 0) {\n        darray_deinit(&values);\n        rewriteConfigMarkAsProcessed(state,option);\n        return;\n    }\n\n    /* Rewrite as bind <addr1> <addr2> ... <addrN> */\n    line = sdsnew(option);\n    while(darray_n(&values) > 0) {\n        line = sdscat(line,\" \");\n        value = darray_pop(&values);\n        line = sdscatsds(line,*value);\n        sdsfree(*value);\n    }\n    darray_deinit(&values);\n\n    rewriteConfigRewriteLine(state,option,line,force);\n}\n\n/* Rewrite the save option. */\nvoid rewriteConfigCommandsNAPOption(struct rewriteConfigState *state) {\n    struct darray values;\n    sds *value, line;\n    int force = 1;\n    char *option = CONFIG_SOPN_COMMANDSNAP;\n\n    darray_init(&values,1,sizeof(sds));\n    conf_server_get(option,&values);\n    /* Nothing to rewrite if we don't have commands that need adminpass. */\n    if (darray_n(&values) == 0) {\n        darray_deinit(&values);\n        rewriteConfigMarkAsProcessed(state,option);\n        return;\n    }\n\n    while(darray_n(&values) > 0) {\n        value = darray_pop(&values);\n        line = sdscatprintf(sdsempty(),\"%s %s\",option,*value);\n        rewriteConfigRewriteLine(state,option,line,force);\n        sdsfree(*value);\n    }\n    darray_deinit(&values);\n    rewriteConfigMarkAsProcessed(state,option);\n}\n\n/* Rewrite the configuration file at \"path\".\n * If the configuration file already exists, we try at best to retain comments\n * and overall structure.\n *\n * Configuration parameters that are at their default value, unless already\n * explicitly included in the old configuration file, are not rewritten.\n *\n * On error -1 is returned and errno is set accordingly, otherwise 0. */\nstatic int rewriteConfig(char *path) {\n    struct rewriteConfigState *state;\n    sds newcontent;\n    int retval;\n    conf_option *cop;\n\n    CONFF_LOCK();\n    /* Step 1: read the old config into our rewrite state. */\n    if ((state = rewriteConfigReadOldFile(path)) == NULL) {\n        CONFF_UNLOCK();\n        return -1;\n    }\n\n    /* Step 2: rewrite every single option, replacing or appending it inside\n     * the rewrite state. */\n    rewriteConfigIntOption(state,CONFIG_SOPN_DATABASES,CONFIG_DEFAULT_LOGICAL_DBNUM);\n    rewriteConfigIntOption(state,CONFIG_SOPN_IDPDATABASE,CONFIG_DEFAULT_INTERNAL_DBNUM);\n    rewriteConfigBytesOption(state,CONFIG_SOPN_MAXMEMORY,CONFIG_DEFAULT_MAXMEMORY);\n    rewriteConfigEnumOption(state,CONFIG_SOPN_MAXMEMORYP,get_evictpolicy_strings,CONFIG_DEFAULT_MAXMEMORY_POLICY);\n    rewriteConfigIntOption(state,CONFIG_SOPN_MAXMEMORYS,CONFIG_DEFAULT_MAXMEMORY_SAMPLES);\n    rewriteConfigLongLongOption(state,CONFIG_SOPN_MTCLIMIT,CONFIG_DEFAULT_MAX_TIME_COMPLEXITY_LIMIT);\n    rewriteConfigBindOption(state);\n    rewriteConfigIntOption(state,CONFIG_SOPN_PORT,CONFIG_DEFAULT_SERVER_PORT);\n    rewriteConfigIntOption(state,CONFIG_SOPN_THREADS,CONFIG_DEFAULT_THREADS_NUM);\n    rewriteConfigLongLongOption(state,CONFIG_SOPN_SLOWLOGLST,CONFIG_DEFAULT_SLOWLOG_LOG_SLOWER_THAN);\n    rewriteConfigIntOption(state,CONFIG_SOPN_SLOWLOGML,CONFIG_DEFAULT_SLOWLOG_MAX_LEN);\n    rewriteConfigIntOption(state,CONFIG_SOPN_MAXCLIENTS,CONFIG_DEFAULT_MAX_CLIENTS);\n    rewriteConfigSdsOption(state,CONFIG_SOPN_REQUIREPASS,NULL);\n    rewriteConfigSdsOption(state,CONFIG_SOPN_ADMINPASS,NULL);\n    rewriteConfigCommandsNAPOption(state);\n    \n    /* Step 3: remove all the orphaned lines in the old file, that is, lines\n     * that were used by a config option and are no longer used, like in case\n     * of multiple \"save\" options or duplicated options. */\n    rewriteConfigRemoveOrphaned(state);\n\n    /* Step 4: generate a new configuration file from the modified state\n     * and write it into the original file. */\n    newcontent = rewriteConfigGetContentFromState(state);\n    retval = rewriteConfigOverwriteFile(server.configfile,newcontent);\n    CONFF_UNLOCK();\n\n    sdsfree(newcontent);\n    rewriteConfigReleaseState(state);\n    return retval;\n}\n\n/*-----------------------------------------------------------------------------\n * CONFIG command entry point\n *----------------------------------------------------------------------------*/\n\nvoid configCommand(client *c) {\n    /* Only allow CONFIG GET while loading. */\n    if (server.loading && strcasecmp(c->argv[1]->ptr,\"get\")) {\n        addReplyError(c,\"Only CONFIG GET is allowed during loading\");\n        return;\n    }\n\n    if (!strcasecmp(c->argv[1]->ptr,\"set\")) {\n        if (c->argc != 4) goto badarity;\n        configSetCommand(c);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"get\")) {\n        if (c->argc != 3) goto badarity;\n        configGetCommand(c);\n    } /*else if (!strcasecmp(c->argv[1]->ptr,\"resetstat\")) {\n        if (c->argc != 2) goto badarity;\n        resetServerStats();\n        resetCommandTableStats();\n        addReply(c,shared.ok);\n    }*/ else if (!strcasecmp(c->argv[1]->ptr,\"rewrite\")) {\n        if (c->argc != 2) goto badarity;\n        if (server.configfile == NULL) {\n            addReplyError(c,\"The server is running without a config file\");\n            return;\n        }\n        if (rewriteConfig(server.configfile) == -1) {\n            log_warn(\"CONFIG REWRITE failed: %s\", strerror(errno));\n            addReplyErrorFormat(c,\"Rewriting config file: %s\", strerror(errno));\n        } else {\n            log_warn(\"CONFIG REWRITE executed with success.\");\n            addReply(c,shared.ok);\n        }\n    } else {\n        addReplyError(c,\n            //\"CONFIG subcommand must be one of GET, SET, RESETSTAT, REWRITE\");\n            \"CONFIG subcommand must be GET, SET, REWRITE\");\n    }\n    return;\n\nbadarity:\n    addReplyErrorFormat(c,\"Wrong number of arguments for CONFIG %s\",\n        (char*) c->argv[1]->ptr);\n}\n\nint\nconf_cache_init(conf_cache *cc)\n{\n    cc->cache_version = 0;\n    conf_server_get(CONFIG_SOPN_MAXCLIENTS,&cc->maxclients);\n    conf_server_get(CONFIG_SOPN_REQUIREPASS,&cc->requirepass);\n    conf_server_get(CONFIG_SOPN_ADMINPASS,&cc->adminpass);\n    conf_server_get(CONFIG_SOPN_MAXMEMORY,&cc->maxmemory);\n    conf_server_get(CONFIG_SOPN_MTCLIMIT,&cc->max_time_complexity_limit);\n    conf_server_get(CONFIG_SOPN_SLOWLOGLST,&cc->slowlog_log_slower_than);\n\n    return VR_OK;\n}\n\nint\nconf_cache_deinit(conf_cache *cc)\n{\n    cc->cache_version = 0;\n    if (cc->requirepass != NULL) {\n        sdsfree(cc->requirepass);\n        cc->requirepass = NULL;\n    }\n    if (cc->adminpass != NULL) {\n        sdsfree(cc->adminpass);\n        cc->adminpass = NULL;\n    }\n\n    return VR_OK;\n}\n\nint\nconf_cache_update(conf_cache *cc)\n{\n    unsigned long long cversion = conf_version_get();\n\n    /* Not need update conf cache. */\n    if (cversion <= cc->cache_version) {\n        return;\n    }\n\n    if (cc->requirepass != NULL) {\n        sdsfree(cc->requirepass);\n        cc->requirepass = NULL;\n    }\n    if (cc->adminpass != NULL) {\n        sdsfree(cc->adminpass);\n        cc->adminpass = NULL;\n    }\n\n    conf_server_get(CONFIG_SOPN_MAXCLIENTS,&cc->maxclients);\n    conf_server_get(CONFIG_SOPN_REQUIREPASS,&cc->requirepass);\n    conf_server_get(CONFIG_SOPN_ADMINPASS,&cc->adminpass);\n    conf_server_get(CONFIG_SOPN_MAXMEMORY,&cc->maxmemory);\n    conf_server_get(CONFIG_SOPN_MTCLIMIT,&cc->max_time_complexity_limit);\n    conf_server_get(CONFIG_SOPN_SLOWLOGLST,&cc->slowlog_log_slower_than);\n\n    cc->cache_version = cversion;\n\n    return VR_OK;\n}\n"
  },
  {
    "path": "src/vr_conf.h",
    "content": "#ifndef _VR_CONF_H_\n#define _VR_CONF_H_\n\n/* Config server option name */\n#define CONFIG_SOPN_DATABASES    \"databases\"\n#define CONFIG_SOPN_IDPDATABASE  \"internal-dbs-per-databases\"\n#define CONFIG_SOPN_MAXMEMORY    \"maxmemory\"\n#define CONFIG_SOPN_MAXMEMORYP   \"maxmemory-policy\"\n#define CONFIG_SOPN_MAXMEMORYS   \"maxmemory-samples\"\n#define CONFIG_SOPN_MTCLIMIT     \"max-time-complexity-limit\"\n#define CONFIG_SOPN_BIND         \"bind\"\n#define CONFIG_SOPN_PORT         \"port\"\n#define CONFIG_SOPN_THREADS      \"threads\"\n#define CONFIG_SOPN_DIR          \"dir\"\n#define CONFIG_SOPN_MAXCLIENTS   \"maxclients\"\n#define CONFIG_SOPN_SLOWLOGLST   \"slowlog-log-slower-than\"\n#define CONFIG_SOPN_SLOWLOGML    \"slowlog-max-len\"\n#define CONFIG_SOPN_REQUIREPASS  \"requirepass\"\n#define CONFIG_SOPN_ADMINPASS    \"adminpass\"\n#define CONFIG_SOPN_COMMANDSNAP  \"commands-need-adminpass\"\n\n#define CONFIG_RUN_ID_SIZE 40\n#define CONFIG_DEFAULT_ACTIVE_REHASHING 1\n\n#define CONFIG_DEFAULT_LOGICAL_DBNUM    6\n#define CONFIG_DEFAULT_INTERNAL_DBNUM   6\n\n#define CONFIG_DEFAULT_MAXMEMORY 0\n#define CONFIG_DEFAULT_MAXMEMORY_SAMPLES 5\n#define CONFIG_DEFAULT_MAX_CLIENTS 10000\n\n#define CONFIG_DEFAULT_MAX_CLIENTS 10000\n\n#define CONFIG_DEFAULT_THREADS_NUM (sysconf(_SC_NPROCESSORS_ONLN)>6?6:sysconf(_SC_NPROCESSORS_ONLN))\n\n#define CONFIG_DEFAULT_HOST \"0.0.0.0\"\n\n#define CONFIG_DEFAULT_SERVER_PORT 55555\n\n#define CONFIG_DEFAULT_DATA_DIR \"viredata\"\n\n#define CONFIG_DEFAULT_MAX_TIME_COMPLEXITY_LIMIT 0 /* Not limited */\n\n#define CONFIG_DEFAULT_SLOWLOG_LOG_SLOWER_THAN 10000\n#define CONFIG_DEFAULT_SLOWLOG_MAX_LEN 128\n\n#define CONFIG_AUTHPASS_MAX_LEN 512\n\n#define CONFIG_BINDADDR_MAX 16\n\n#define CONF_UNSET_NUM      -1\n#define CONF_UNSET_PTR      NULL\n#define CONF_UNSET_GROUP    (group_type_t) -1\n#define CONF_UNSET_HASH     (hash_type_t) -1\n#define CONF_UNSET_DIST     (dist_type_t) -1\n\n/* Config field data type for conf_option struct */\n#define CONF_FIELD_TYPE_INT         0\n#define CONF_FIELD_TYPE_LONGLONG    1\n#define CONF_FIELD_TYPE_SDS         2\n#define CONF_FIELD_TYPE_ARRAYSDS    3\n\n/* Config field flags for conf_option struct */\n#define CONF_FIELD_FLAGS_NO_MODIFY  (1<<0)\n\ntypedef struct conf_option {\n    char    *name;      /* option name */\n    int     type;       /* value type */\n    int     flags;      /* option flags */\n    int     (*set)(void *cf, struct conf_option *opt, void *data);\n    int     (*get)(void *cf, struct conf_option *opt, void *data);\n    int     offset;     /* offset of this option field in the struct  */\n}conf_option;\n\n#define EVICTPOLICY_CODEC(ACTION)                           \\\n    ACTION( MAXMEMORY_VOLATILE_LRU,     volatile-lru)       \\\n    ACTION( MAXMEMORY_VOLATILE_RANDOM,  volatile-random)    \\\n    ACTION( MAXMEMORY_VOLATILE_TTL,     volatile-ttl)       \\\n    ACTION( MAXMEMORY_ALLKEYS_LRU,      allkeys-lru)        \\\n    ACTION( MAXMEMORY_ALLKEYS_RANDOM,   allkeys-random)     \\\n    ACTION( MAXMEMORY_NO_EVICTION,      noeviction)         \\\n\n#define DEFINE_ACTION(_policy, _name) _policy,\ntypedef enum evictpolicy_type {\n    EVICTPOLICY_CODEC( DEFINE_ACTION )\n    EVICTPOLICY_SENTINEL\n} evictpolicy_type_t;\n#undef DEFINE_ACTION\n\ntypedef struct conf_server {\n    dict          *ctable;\n\n    int           databases;\n    int           internal_dbs_per_databases;\n\n    /* Limits */\n    long long     max_time_complexity_limit;\n    long long     maxmemory;            /* Max number of memory bytes to use */\n    int           maxmemory_policy;     /* Policy for key eviction */\n    int           maxmemory_samples;    /* Pricision of random sampling */\n    int           maxclients;           /* Max number of simultaneous clients */\n\n    int           threads;\n\n    struct darray  binds;                /* Type: sds */\n    int           port;\n\n    sds           dir;\n\n    long long     slowlog_log_slower_than;  /* SLOWLOG time limit (to get logged) */\n    int           slowlog_max_len;      /* SLOWLOG max number of items logged */\n\n    sds           requirepass;          /* Pass for AUTH command, or NULL */\n    sds           adminpass;            /* Pass for ADMIN command, or NULL */\n    struct darray  commands_need_adminpass;\n} conf_server;\n\ntypedef struct vr_conf {\n    sds           fname;             /* file name , absolute path */\n\n    dict          *organizations;    /* organizations */\n\n    conf_server   cserver;\n\n    unsigned long long version;      /* config version */\n    pthread_rwlock_t rwl;            /* config read write lock */\n    pthread_mutex_t flock;           /* config file lock */\n}vr_conf;\n\n#define CONF_VALUE_TYPE_UNKNOW   0\n#define CONF_VALUE_TYPE_STRING   1\n#define CONF_VALUE_TYPE_ARRAY    2\n\ntypedef struct conf_value{\n    int     type;\n    void    *value;\n}conf_value;\n\n/* Config option used multi times for every loop, \n * so we cache them here in the cron function. */\ntypedef struct conf_cache {\n    unsigned long long cache_version;\n\n    int maxclients;\n    sds requirepass;\n    sds adminpass;\n    long long maxmemory;\n    long long max_time_complexity_limit;\n    long long slowlog_log_slower_than;\n}conf_cache;\n\nextern vr_conf *conf;\nextern conf_server *cserver;\n\nconf_value *conf_value_create(int type);\nvoid conf_value_destroy(conf_value *cv);\n\nvr_conf *conf_create(char *filename);\nvoid conf_destroy(vr_conf *cf);\n\nunsigned long long conf_version_get(void);\n\nint conf_server_get(const char *option_name, void *value);\nint conf_server_set(const char *option_name, conf_value *value);\n\nint conf_set_maxmemory(void *obj, conf_option *opt, void *data);\nint conf_set_maxmemory_policy(void *obj, conf_option *opt, void *data);\nint conf_set_int_non_zero(void *obj, conf_option *opt, void *data);\n\nint conf_get_sds(void *obj, conf_option *opt, void *data);\nint conf_get_int(void *obj, conf_option *opt, void *data);\nint conf_get_longlong(void *obj, conf_option *opt, void *data);\nint conf_get_array_sds(void *obj, conf_option *opt, void *data);\n\nint conf_set_sds(void *obj, conf_option *opt, void *data);\nint conf_set_password(void *obj, conf_option *opt, void *data);\nint conf_set_int(void *obj, conf_option *opt, void *data);\nint conf_set_longlong(void *obj, conf_option *opt, void *data);\nint conf_set_yesorno(void *obj, conf_option *opt, void *data);\nint conf_set_array_sds(void *obj, conf_option *opt, void *data);\nint conf_set_commands_need_adminpass(void *obj, conf_option *opt, void *data);\n\nint CONF_RLOCK(void);\nint CONF_WLOCK(void);\nint CONF_UNLOCK(void);\n\nint CONFF_LOCK(void);\nint CONFF_UNLOCK(void);\n\nconst char *get_evictpolicy_strings(int evictpolicy_type);\n\nvoid configCommand(struct client *c);\n\nint conf_cache_init(conf_cache *cc);\nint conf_cache_deinit(conf_cache *cc);\nint conf_cache_update(conf_cache *cc);\n\n#endif\n"
  },
  {
    "path": "src/vr_connection.c",
    "content": "#include <sys/uio.h>\n\n#include <vr_core.h>\n\nstatic void conn_free(struct conn *conn);\n\nstatic struct conn *\n_conn_get(conn_base *cb)\n{\n    struct conn *conn;\n\n    if (cb != NULL && dlistLength(cb->free_connq) > 0) {\n        conn = dlistPop(cb->free_connq);\n    } else {\n        conn = dalloc(sizeof(*conn));\n        if (conn == NULL) {\n            return NULL;\n        }\n        conn->cb = cb;\n\n        conn->inqueue = NULL;\n        conn->outqueue = NULL;\n    }\n\n    conn->owner = NULL;\n\n    conn->sd = -1;\n\n    conn->send_bytes = 0;\n    conn->recv_bytes = 0;\n\n    conn->err = 0;\n    conn->recv_active = 0;\n    conn->recv_ready = 0;\n    conn->send_active = 0;\n    conn->send_ready = 0;\n\n    conn->connecting = 0;\n    conn->connected = 0;\n    conn->eof = 0;\n    conn->done = 0;\n\n    if (conn->inqueue == NULL) {\n        conn->inqueue = dlistCreate();\n        if (conn->inqueue == NULL) {\n            conn_free(conn);\n            return NULL;\n        }\n    }\n\n    if (conn->outqueue == NULL) {\n        conn->outqueue = dlistCreate();\n        if (conn->outqueue == NULL) {\n            conn_free(conn);\n            return NULL;\n        }\n    }\n    \n    if (cb != NULL) {\n        cb->ntotal_conn++;\n        cb->ncurr_conn++;\n    }\n    \n    return conn;\n}\n\nstruct conn *\nconn_get(conn_base *cb)\n{\n    struct conn *conn;\n\n    conn = _conn_get(cb);\n    if (conn == NULL) {\n        return NULL;\n    }\n\n    log_debug(LOG_VVERB, \"get conn %p client %d\", conn, conn->sd);\n\n    return conn;\n}\n\nstatic void\nconn_free(struct conn *conn)\n{\n    log_debug(LOG_VVERB, \"free conn %p\", conn);\n\n    if (conn == NULL) {\n        return;\n    }\n\n    if (conn->sd > 0) {\n        close(conn->sd);\n        conn->sd = -1;\n        update_curr_clients_sub(1);\n    }\n\n    if (conn->inqueue) {\n        sds buf;\n        while (buf = dlistPop(conn->inqueue)) {\n            sdsfree(buf);\n        }\n        dlistRelease(conn->inqueue);\n        conn->inqueue = NULL;\n    }\n\n    if (conn->outqueue) {\n        sds buf;\n        while (buf = dlistPop(conn->outqueue)) {\n            sdsfree(buf);\n        }\n        dlistRelease(conn->outqueue);\n        conn->outqueue = NULL;\n    }\n    \n    dfree(conn);\n}\n\nvoid\nconn_put(struct conn *conn)\n{\n    conn_base *cb = conn->cb;\n    \n    ASSERT(conn->owner == NULL);\n\n    log_debug(LOG_VVERB, \"put conn %p\", conn);\n\n    if (conn->sd > 0) {\n        close(conn->sd);\n        conn->sd = -1;\n        update_curr_clients_sub(1);\n    }\n\n    if (cb == NULL) {\n        conn_free(conn);\n        return;\n    }\n\n    if (conn->inqueue) {\n        sds buf;\n        while (buf = dlistPop(conn->inqueue)) {\n            sdsfree(buf);\n        }\n    }\n\n    if (conn->outqueue) {\n        sds buf;\n        while (buf = dlistPop(conn->outqueue)) {\n            sdsfree(buf);\n        }\n    }\n\n    dlistPush(cb->free_connq, conn);\n    cb->ncurr_cconn--;\n    cb->ncurr_conn--;\n}\n\nint\nconn_init(conn_base *cb)\n{\n    log_debug(LOG_DEBUG, \"conn size %d\", sizeof(struct conn));\n\n    cb->free_connq = NULL;\n    cb->ntotal_conn = 0;\n    cb->ncurr_cconn = 0;\n    cb->ncurr_cconn = 0;\n\n    cb->free_connq = dlistCreate();\n    if (cb->free_connq == NULL) {\n        return VR_ENOMEM;\n    }\n\n    return VR_OK;\n}\n\nvoid\nconn_deinit(conn_base *cb)\n{\n    struct conn *conn;\n\n    if (cb->free_connq) {\n        while (conn = dlistPop(cb->free_connq)) {\n            conn_free(conn);\n        }\n        ASSERT(dlistLength(cb->free_connq) == 0);\n        dlistRelease(cb->free_connq);\n    }\n}\n\nssize_t\nconn_recv(struct conn *conn, void *buf, size_t size)\n{\n    ssize_t n;\n\n    ASSERT(buf != NULL);\n    ASSERT(size > 0);\n    ASSERT(conn->recv_ready);\n\n    for (;;) {\n        n = vr_read(conn->sd, buf, size);\n\n        log_debug(LOG_VERB, \"recv on sd %d %zd of %zu\", conn->sd, n, size);\n\n        if (n > 0) {\n            if (n < (ssize_t) size) {\n                conn->recv_ready = 0;\n            }\n            conn->recv_bytes += (size_t)n;\n            return n;\n        }\n\n        if (n == 0) {\n            conn->recv_ready = 0;\n            conn->eof = 1;\n            log_debug(LOG_INFO, \"recv on sd %d eof rb %zu sb %zu\", conn->sd,\n                      conn->recv_bytes, conn->send_bytes);\n            return n;\n        }\n\n        if (errno == EINTR) {\n            log_debug(LOG_VERB, \"recv on sd %d not ready - eintr\", conn->sd);\n            continue;\n        } else if (errno == EAGAIN || errno == EWOULDBLOCK) {\n            conn->recv_ready = 0;\n            log_debug(LOG_VERB, \"recv on sd %d not ready - eagain\", conn->sd);\n            return VR_EAGAIN;\n        } else {\n            conn->recv_ready = 0;\n            conn->err = errno;\n            log_error(\"recv on sd %d failed: %s\", conn->sd, strerror(errno));\n            return VR_ERROR;\n        }\n    }\n\n    NOT_REACHED();\n\n    return VR_ERROR;\n}\n\nssize_t\nconn_send(struct conn *conn, void *buf, size_t nsend)\n{\n    ssize_t n;\n\n    ASSERT(nsend != 0);\n    ASSERT(conn->send_ready);\n\n    for (;;) {\n        n = vr_write(conn->sd, buf, nsend);\n\n        log_debug(LOG_VERB, \"send on sd %d %zd of %zu\",\n                  conn->sd, n, nsend);\n\n        if (n > 0) {\n            if (n < (ssize_t) nsend) {\n                conn->send_ready = 0;\n            }\n            conn->send_bytes += (size_t)n;\n            return n;\n        }\n\n        if (n == 0) {\n            log_warn(\"send on sd %d returned zero\", conn->sd);\n            conn->send_ready = 0;\n            return 0;\n        }\n\n        if (errno == EINTR) {\n            log_debug(LOG_VERB, \"send on sd %d not ready - eintr\", conn->sd);\n            continue;\n        } else if (errno == EAGAIN || errno == EWOULDBLOCK) {\n            conn->send_ready = 0;\n            log_debug(LOG_VERB, \"send on sd %d not ready - eagain\", conn->sd);\n            return VR_EAGAIN;\n        } else {\n            conn->send_ready = 0;\n            conn->err = errno;\n            log_error(\"send on sd %d failed: %s\", conn->sd, strerror(errno));\n            return VR_ERROR;\n        }\n    }\n\n    NOT_REACHED();\n\n    return VR_ERROR;\n}\n\nssize_t\nconn_sendv(struct conn *conn, struct darray *sendv, size_t nsend)\n{\n    ssize_t n;\n\n    ASSERT(darray_n(sendv) > 0);\n    ASSERT(nsend != 0);\n    ASSERT(conn->send_ready);\n\n    for (;;) {\n        n = vr_writev(conn->sd, sendv->elem, sendv->nelem);\n\n        log_debug(LOG_VERB, \"sendv on sd %d %zd of %zu in %\"PRIu32\" buffers\",\n                  conn->sd, n, nsend, sendv->nelem);\n\n        if (n > 0) {\n            if (n < (ssize_t) nsend) {\n                conn->send_ready = 0;\n            }\n            conn->send_bytes += (size_t)n;\n            return n;\n        }\n\n        if (n == 0) {\n            log_warn(\"sendv on sd %d returned zero\", conn->sd);\n            conn->send_ready = 0;\n            return 0;\n        }\n\n        if (errno == EINTR) {\n            log_debug(LOG_VERB, \"sendv on sd %d not ready - eintr\", conn->sd);\n            continue;\n        } else if (errno == EAGAIN || errno == EWOULDBLOCK) {\n            conn->send_ready = 0;\n            log_debug(LOG_VERB, \"sendv on sd %d not ready - eagain\", conn->sd);\n            return VR_EAGAIN;\n        } else {\n            conn->send_ready = 0;\n            conn->err = errno;\n            log_error(\"sendv on sd %d failed: %s\", conn->sd, strerror(errno));\n            return VR_ERROR;\n        }\n    }\n\n    NOT_REACHED();\n\n    return VR_ERROR;\n}\n"
  },
  {
    "path": "src/vr_connection.h",
    "content": "#ifndef _VR_CONNECTION_H_\n#define _VR_CONNECTION_H_\n\ntypedef struct conn_base {\n    dlist *free_connq;           /* free conn q */\n    uint64_t ntotal_conn;       /* total # connections counter from start */\n    uint32_t ncurr_conn;        /* current # connections */\n    uint32_t ncurr_cconn;       /* current # client connections */\n}conn_base;\n\nstruct conn {\n    void                *owner;          /* connection owner */\n    \n    conn_base           *cb;             /* connect base */\n\n    int                 sd;              /* socket descriptor */\n\n    size_t              recv_bytes;      /* received (read) bytes */\n    size_t              send_bytes;      /* sent (written) bytes */\n\n    err_t               err;             /* connection errno */\n    unsigned            recv_active:1;   /* recv active? */\n    unsigned            recv_ready:1;    /* recv ready? */\n    unsigned            send_active:1;   /* send active? */\n    unsigned            send_ready:1;    /* send ready? */\n\n    unsigned            connecting:1;    /* connecting? */\n    unsigned            connected:1;     /* connected? */\n    unsigned            eof:1;           /* eof? aka passive close? */\n    unsigned            done:1;          /* done? aka close? */\n\n    dlist                *inqueue;        /* incoming request queue */\n    dlist                *outqueue;       /* outputing response queue */\n};\n\nstruct conn *conn_get(conn_base *cb);\nvoid conn_put(struct conn *conn);\n\nint conn_init(conn_base *cb);\nvoid conn_deinit(conn_base *cb);\n\nssize_t conn_recv(struct conn *conn, void *buf, size_t size);\nssize_t conn_send(struct conn *conn, void *buf, size_t nsend);\nssize_t conn_sendv(struct conn *conn, struct darray *sendv, size_t nsend);\n\n#endif\n"
  },
  {
    "path": "src/vr_core.c",
    "content": "#include <stdlib.h>\n#include <unistd.h>\n\n#include <vr_core.h>\n\nstatic uint32_t reserved_fds = 0;\n\n"
  },
  {
    "path": "src/vr_core.h",
    "content": "#ifndef _VR_CORE_H_\n#define _VR_CORE_H_\n\n#ifdef HAVE_CONFIG_H\n# include <config.h>\n#endif\n\n#include <dspecialconfig.h>\n\n#ifdef HAVE_STATS\n# define VR_STATS 1\n#else\n# define VR_STATS 0\n#endif\n\n#ifdef HAVE_LITTLE_ENDIAN\n# define VR_LITTLE_ENDIAN 1\n#endif\n\n#ifdef HAVE_BACKTRACE\n# define VR_HAVE_BACKTRACE 1\n#endif\n\n#ifdef HAVE_SPINLOCK\n# define VR_USE_SPINLOCK 1\n#endif\n\n#define VR_OK        0\n#define VR_ERROR    -1\n#define VR_EAGAIN   -2\n#define VR_ENOMEM   -3\n\n/* reserved fds for std streams, log, stats fd, epoll etc. */\n#define RESERVED_FDS 32\n\ntypedef int rstatus_t; /* return type */\ntypedef int err_t;      /* error type */\n\ntypedef long long mstime_t; /* millisecond time type. */\n\nstruct instance;\nstruct darray;\nstruct conn;\nstruct client;\nstruct clientBufferLimitsConfig;\nstruct redisCommand;\nstruct vr_worker;\n\n#include <stddef.h>\n#include <stdint.h>\n#include <inttypes.h>\n#include <string.h>\n#include <stdio.h>\n#include <ctype.h>\n#include <errno.h>\n#include <limits.h>\n#include <time.h>\n#include <unistd.h>\n#include <pthread.h>\n\n#include <sys/types.h>\n#include <sys/socket.h>\n#include <sys/un.h>\n#include <sys/time.h>\n#include <sys/resource.h>\n#include <netinet/in.h>\n\n#include <ae.h>\n#include <sds.h>\n#include <dutil.h>\n#include <dlog.h>\n#include <dhashkit.h>\n#include <dmalloc.h>\n#include <darray.h>\n#include <dlist.h>\n\n#include <vr_util.h>\n#include <vr_signal.h>\n\n#include <vr_ziplist.h>\n#include <vr_zipmap.h>\n#include <vr_dict.h>\n#include <vr_rbtree.h>\n#include <vr_intset.h>\n#include <vr_quicklist.h>\n\n#include <vr_lzf.h>\n#include <vr_lzfP.h>\n\n#include <vr_object.h>\n\n#include <vr_listen.h>\n#include <vr_connection.h>\n\n#include <vr_stats.h>\n#include <vr_conf.h>\n\n#include <vr_thread.h>\n#include <vr_eventloop.h>\n#include <vr_master.h>\n#include <vr_worker.h>\n#include <vr_backend.h>\n\n#include <vr_db.h>\n#include <vr_multi.h>\n\n#include <vr_command.h>\n#include <vr_block.h>\n#include <vr_client.h>\n#include <vr_server.h>\n\n#include <vr_notify.h>\n#include <vr_pubsub.h>\n\n#include <vr_rdb.h>\n#include <vr_aof.h>\n#include <vr_replication.h>\n#include <vr_scripting.h>\n\n#include <vr_t_hash.h>\n#include <vr_t_list.h>\n#include <vr_t_set.h>\n#include <vr_t_string.h>\n#include <vr_t_zset.h>\n\n#include <vr_bitops.h>\n\n#include <vr_hyperloglog.h>\n\n#include <vr_slowlog.h>\n\nstruct instance {\n    int             log_level;                   /* log level */\n    char            *log_filename;               /* log filename */\n    char            *conf_filename;              /* configuration filename */\n    char            hostname[VR_MAXHOSTNAMELEN]; /* hostname */\n    size_t          mbuf_chunk_size;             /* mbuf chunk size */\n    pid_t           pid;                         /* process id */\n    char            *pid_filename;               /* pid filename */\n    unsigned        pidfile:1;                   /* pid file created? */\n    int             thread_num;                  /* the thread number */\n};\n\n#endif\n"
  },
  {
    "path": "src/vr_db.c",
    "content": "#include <signal.h>\n#include <ctype.h>\n\n#include <vr_core.h>\n\n/* Db->dict, keys are sds strings, vals are Redis objects. */\ndictType dbDictType = {\n    dictSdsHash,                /* hash function */\n    NULL,                       /* key dup */\n    NULL,                       /* val dup */\n    dictSdsKeyCompare,          /* key compare */\n    dictSdsDestructor,          /* key destructor */\n    dictObjectDestructor   /* val destructor */\n};\n\n/* Db->expires */\ndictType keyptrDictType = {\n    dictSdsHash,               /* hash function */\n    NULL,                      /* key dup */\n    NULL,                      /* val dup */\n    dictSdsKeyCompare,         /* key compare */\n    NULL,                      /* key destructor */\n    NULL                       /* val destructor */\n};\n\n/* Keylist hash table type has unencoded redis objects as keys and\n * lists as values. It's used for blocking operations (BLPOP) and to\n * map swapped keys to a list of clients waiting for this keys to be loaded. */\ndictType keylistDictType = {\n    dictObjHash,                /* hash function */\n    NULL,                       /* key dup */\n    NULL,                       /* val dup */\n    dictObjKeyCompare,          /* key compare */\n    dictObjectDestructor,       /* key destructor */\n    dictListDestructor          /* val destructor */\n};\n\n/* Create a new eviction pool. */\nstatic struct evictionPoolEntry *evictionPoolAlloc(void) {\n    struct evictionPoolEntry *ep;\n    int j;\n\n    ep = dalloc(sizeof(*ep)*MAXMEMORY_EVICTION_POOL_SIZE);\n    for (j = 0; j < MAXMEMORY_EVICTION_POOL_SIZE; j++) {\n        ep[j].idle = 0;\n        ep[j].key = NULL;\n    }\n    return ep;\n}\n\n/*-----------------------------------------------------------------------------\n * C-level DB API\n *----------------------------------------------------------------------------*/\n\nint redisDbInit(redisDb *db)\n{\n    db->dict = dictCreate(&dbDictType,NULL);\n    db->expires = dictCreate(&keyptrDictType,NULL);\n    db->blocking_keys = dictCreate(&keylistDictType,NULL);\n    db->ready_keys = dictCreate(&setDictType,NULL);\n    db->watched_keys = dictCreate(&keylistDictType,NULL);\n    db->eviction_pool = evictionPoolAlloc();\n    db->avg_ttl = 0;\n\n    pthread_rwlock_init(&db->rwl, NULL);\n\n    return VR_OK;\n}\n\nint \nredisDbDeinit(redisDb *db)\n{\n    pthread_rwlock_destroy(&db->rwl);\n    return VR_OK;\n}\n\nint\nlockDbRead(redisDb *db)\n{\n    pthread_rwlock_rdlock(&db->rwl);\n    return VR_OK;\n}\n\nint\nlockDbWrite(redisDb *db)\n{\n    pthread_rwlock_wrlock(&db->rwl);\n    return VR_OK;\n}\n\nint\nunlockDb(redisDb *db)\n{\n    pthread_rwlock_unlock(&db->rwl);\n    return VR_OK;\n}\n\nrobj *lookupKey(redisDb *db, robj *key) {\n    dictEntry *de = dictFind(db->dict,key->ptr);\n    if (de) {\n        robj *val = dictGetVal(de);\n\n        /* Update the access time for the ageing algorithm.\n         * Don't do it if we have a saving child, as this will trigger\n         * a copy on write madness. */\n        if (server.rdb_child_pid == -1 && server.aof_child_pid == -1)\n            //val->lru = LRU_CLOCK();\n            val->lru = 0;\n        return val;\n    } else {\n        return NULL;\n    }\n}\n\nrobj *lookupKeyRead(redisDb *db, robj *key) {\n    if (checkIfExpired(db, key)) return NULL;\n    return lookupKey(db,key);\n}\n\nrobj *lookupKeyWrite(redisDb *db, robj *key, int *expired) {\n    if (expired) *expired = expireIfNeeded(db,key);\n    return lookupKey(db,key);\n}\n\nrobj *lookupKeyReadOrReply(client *c, robj *key, robj *reply) {\n    robj *o = lookupKeyRead(c->db, key);\n    if (!o) addReply(c,reply);\n    return o;\n}\n\nrobj *lookupKeyWriteOrReply(client *c, robj *key, robj *reply, int *expired) {\n    robj *o = lookupKeyWrite(c->db, key, expired);\n    if (!o) addReply(c,reply);\n    return o;\n}\n\n/* Add the key to the DB. It's up to the caller to increment the reference\n * counter of the value if needed.\n *\n * The program is aborted if the key already exists. \n * Val object must be independent. */\nvoid dbAdd(redisDb *db, robj *key, robj *val) {\n    sds copy = sdsdup(key->ptr);\n    int retval = dictAdd(db->dict, copy, val);\n    serverAssertWithInfo(NULL,key,retval == DICT_OK);\n    if (val->type == OBJ_LIST) signalListAsReady(db, key);\n }\n\n/* Overwrite an existing key with a new value. Incrementing the reference\n * count of the new value is up to the caller.\n * This function does not modify the expire time of the existing key.\n *\n * The program is aborted if the key was not already present. \n * Val object must be independent. */\nvoid dbOverwrite(redisDb *db, robj *key, robj *val) {\n    dictEntry *de = dictFind(db->dict,key->ptr);\n\n    serverAssertWithInfo(NULL,key,de != NULL);\n    dictReplace(db->dict, key->ptr, val);\n}\n\n/* High level Set operation. This function can be used in order to set\n * a key, whatever it was existing or not, to a new object.\n *\n * 1) Val object must be independent.\n * 2) Clients WATCHing for the destination key notified.\n * 3) The expire time of the key is reset (the key is made persistent). */\nvoid setKey(redisDb *db, robj *key, robj *val, int *expired) {\n    if (lookupKeyWrite(db,key,expired) == NULL) {\n        dbAdd(db,key,val);\n    } else {\n        dbOverwrite(db,key,val);\n    }\n    \n    removeExpire(db,key);\n}\n\nint dbExists(redisDb *db, robj *key) {\n    return dictFind(db->dict,key->ptr) != NULL;\n}\n\n/* Return a random key, in form of a Redis object.\n * If there are no keys, NULL is returned.\n *\n * The function makes sure to return keys not already expired. */\nrobj *dbRandomKey(redisDb *db) {\n    dictEntry *de;\n\n    while(1) {\n        sds key;\n        robj *keyobj;\n\n        lockDbRead(db);\n        de = dictGetRandomKey(db->dict);\n        if (de == NULL) {\n            unlockDb(db);\n            return NULL;\n        }\n\n        key = dictGetKey(de);\n        keyobj = createStringObject(key,sdslen(key));\n        if (dictFind(db->expires,key)) {\n            if (checkIfExpired(db,keyobj)) {\n                unlockDb(db);\n                freeObject(keyobj);\n                continue; /* search for another key. This expired. */\n            }\n        }\n        unlockDb(db);\n        return keyobj;\n    }\n}\n\n/* Delete a key, value, and associated expiration entry if any, from the DB */\nint dbDelete(redisDb *db, robj *key) {\n    /* Deleting an entry from the expires dict will not free the sds of\n     * the key, because it is shared with the main dictionary. */\n    if (dictSize(db->expires) > 0) dictDelete(db->expires,key->ptr);\n    if (dictDelete(db->dict,key->ptr) == DICT_OK) {\n        return 1;\n    } else {\n        return 0;\n    }\n}\n\nrobj *dbUnshareStringValue(redisDb *db, robj *key, robj *o) {    \n    ASSERT(o->type == OBJ_STRING);\n    if (o->constant || o->encoding != OBJ_ENCODING_RAW) {\n        robj *decoded, *new;\n        decoded = getDecodedObject(o);\n        new = createRawStringObject(decoded->ptr, sdslen(decoded->ptr));\n        if (decoded != o) freeObject(decoded);\n        dbOverwrite(db,key,new);\n        return new;\n    }\n    return o;\n}\n\nlong long emptyDb(void(callback)(void*)) {\n    int j;\n    long long removed = 0;\n    redisDb *db;\n\n    for (j = 0; j < server.dbnum; j++) {\n        db = darray_get(&server.dbs, (uint32_t)j);\n        removed += dictSize(db->dict);\n        dictEmpty(db->dict,callback);\n        dictEmpty(db->expires,callback);\n    }\n    \n    return removed;\n}\n\nint selectDb(client *c, int id) {\n    redisDb *db;\n    \n    if (id < 0 || id >= server.dblnum)\n        return VR_ERROR;\n\n    c->dictid = id;\n    return VR_OK;\n}\n\n/*-----------------------------------------------------------------------------\n * Hooks for key space changes.\n *\n * Every time a key in the database is modified the function\n * signalModifiedKey() is called.\n *\n * Every time a DB is flushed the function signalFlushDb() is called.\n *----------------------------------------------------------------------------*/\n\nvoid signalModifiedKey(redisDb *db, robj *key) {\n    touchWatchedKey(db,key);\n}\n\nvoid signalFlushedDb(int dbid) {\n    touchWatchedKeysOnFlush(dbid);\n}\n\n/*-----------------------------------------------------------------------------\n * Type agnostic commands operating on the key space\n *----------------------------------------------------------------------------*/\n\nvoid flushdbCommand(client *c) {\n    int idx;\n\n    for (idx = 0; idx < server.dbinum; idx ++) {\n        fetchInternalDbById(c, idx);\n        lockDbWrite(c->db);\n        c->vel->dirty += dictSize(c->db->dict);\n        signalFlushedDb(c->db->id);\n        dictEmpty(c->db->dict,NULL);\n        dictEmpty(c->db->expires,NULL);\n        unlockDb(c->db);\n    }\n\n    addReply(c,shared.ok);\n}\n\nvoid flushallCommand(client *c) {\n    int idx;\n    redisDb *db;\n\n    for (idx = 0; idx < server.dbnum; idx ++) {\n        db = darray_get(&server.dbs, (uint32_t)idx);\n        lockDbWrite(db);\n        dictEmpty(db->dict,NULL);\n        dictEmpty(db->expires,NULL);\n        unlockDb(db);\n    }\n\n    addReply(c,shared.ok);\n}\n\nvoid delCommand(client *c) {\n    int deleted = 0, j;\n    int expired = 0;\n\n    for (j = 1; j < c->argc; j++) {\n        fetchInternalDbByKey(c, c->argv[j]);\n        lockDbWrite(c->db);\n        expired += expireIfNeeded(c->db,c->argv[j]);\n        if (dbDelete(c->db,c->argv[j])) {\n            signalModifiedKey(c->db,c->argv[j]);\n            notifyKeyspaceEvent(NOTIFY_GENERIC,\n                \"del\",c->argv[j],c->db->id);\n            c->vel->dirty++;\n            deleted++;\n        }\n        unlockDb(c->db);\n    }\n    addReplyLongLong(c,deleted);\n\n    if (expired > 0) {\n        update_stats_add(c->vel->stats, expiredkeys, expired);\n    }\n}\n\n/* EXISTS key1 key2 ... key_N.\n * Return value is the number of keys existing. */\nvoid existsCommand(client *c) {\n    long long count = 0;\n    int j;\n\n    for (j = 1; j < c->argc; j++) {\n        fetchInternalDbByKey(c,c->argv[j]);\n        lockDbRead(c->db);\n        if (checkIfExpired(c->db,c->argv[j])) {\n            unlockDb(c->db);\n            continue;\n        }\n        if (dbExists(c->db,c->argv[j])) count++;\n        unlockDb(c->db);\n    }\n    addReplyLongLong(c,count);\n\n    update_stats_add(c->vel->stats, keyspace_hits, count);\n    update_stats_add(c->vel->stats, keyspace_misses, c->argc-1-count);\n}\n\nvoid selectCommand(client *c) {\n    long id;\n\n    if (getLongFromObjectOrReply(c, c->argv[1], &id,\n        \"invalid DB index\") != VR_OK)\n        return;\n\n    if (selectDb(c,id) == VR_ERROR) {\n        addReplyError(c,\"invalid DB index\");\n    } else {\n        addReply(c,shared.ok);\n    }\n}\n\nvoid randomkeyCommand(client *c) {\n    robj *key;\n    int idx, retry_count = 0;\n\n    idx = random()%server.dbinum;\n\nretry:\n    fetchInternalDbById(c, idx);\n    if ((key = dbRandomKey(c->db)) == NULL) {\n        if (retry_count++ < server.dbinum) {\n            if (++idx >= server.dbinum) {\n                idx = 0;\n            }\n            goto retry;\n        }\n\n        addReply(c,shared.nullbulk);\n        return;\n    }\n\n    addReplyBulk(c,key);\n    freeObject(key);\n}\n\nvoid keysCommand(client *c) {\n    dictIterator *di;\n    dictEntry *de;\n    sds pattern = c->argv[1]->ptr;\n    int plen = sdslen(pattern), allkeys;\n    unsigned long numkeys = 0;\n    void *replylen;\n    int idx;\n    long long keys_count = 0;\n    unsigned long expired = 0;\n    long long max_time_complexity_limit;\n\n    /* Check if it is reach the max-time-complexity-limit */\n    for (idx = 0; idx < server.dbinum; idx ++) {\n        fetchInternalDbById(c, idx);\n        lockDbWrite(c->db);\n        keys_count += dictSize(c->db->dict);\n        unlockDb(c->db);\n    }\n\n    max_time_complexity_limit = c->vel->cc.max_time_complexity_limit;\n    if (max_time_complexity_limit && \n        keys_count > max_time_complexity_limit) {\n        addReply(c,shared.outofcomplexitylimit);\n        return;\n    }\n\n    replylen = addDeferredMultiBulkLength(c);\n    for (idx = 0; idx < server.dbinum; idx ++) {\n        fetchInternalDbById(c,idx);\n        lockDbWrite(c->db);\n        di = dictGetSafeIterator(c->db->dict);\n        allkeys = (pattern[0] == '*' && pattern[1] == '\\0');\n        while((de = dictNext(di)) != NULL) {\n            sds key = dictGetKey(de);\n            robj *keyobj;\n\n            if (allkeys || stringmatchlen(pattern,plen,key,sdslen(key),0)) {\n                keyobj = createStringObject(key,sdslen(key));\n                if (expireIfNeeded(c->db,keyobj) == 0) {\n                    addReplyBulk(c,keyobj);\n                    numkeys++;\n                } else {\n                    expired ++;\n                }\n                freeObject(keyobj);\n            }\n        }\n        dictReleaseIterator(di);\n        unlockDb(c->db);\n    }\n    setDeferredMultiBulkLength(c,replylen,numkeys);\n}\n\n/* This callback is used by scanGenericCommand in order to collect elements\n * returned by the dictionary iterator into a list. */\nvoid scanCallback(void *privdata, const dictEntry *de) {\n    void **pd = (void**) privdata;\n    dlist *keys = pd[0];\n    robj *o = pd[1];\n    robj *key, *val = NULL;\n\n    if (o == NULL) {\n        sds sdskey = dictGetKey(de);\n        key = createStringObject(sdskey, sdslen(sdskey));\n    } else if (o->type == OBJ_SET) {\n        key = dictGetKey(de);\n        key = dupStringObjectUnconstant(key);\n    } else if (o->type == OBJ_HASH) {\n        key = dictGetKey(de);\n        key = dupStringObjectUnconstant(key);\n        val = dictGetVal(de);\n        val = dupStringObjectUnconstant(val);\n    } else if (o->type == OBJ_ZSET) {\n        key = dictGetKey(de);\n        key = dupStringObjectUnconstant(key);\n        val = createStringObjectFromLongDouble(*(double*)dictGetVal(de),0);\n    } else {\n        serverPanic(\"Type not handled in SCAN callback.\");\n    }\n\n    dlistAddNodeTail(keys, key);\n    if (val) dlistAddNodeTail(keys, val);\n}\n\n/* Try to parse a SCAN cursor stored at object 'o':\n * if the cursor is valid, store it as unsigned integer into *cursor and\n * returns VR_OK. Otherwise return VR_ERROR and send an error to the\n * client. */\nint parseScanCursorOrReply(client *c, robj *o, unsigned long *cursor) {\n    char *eptr;\n\n    /* Use strtoul() because we need an *unsigned* long, so\n     * getLongLongFromObject() does not cover the whole cursor space. */\n    errno = 0;\n    *cursor = strtoul(o->ptr, &eptr, 10);\n    if (isspace(((char*)o->ptr)[0]) || eptr[0] != '\\0' || errno == ERANGE)\n    {\n        addReplyError(c, \"invalid cursor\");\n        return VR_ERROR;\n    }\n    return VR_OK;\n}\n\n/* This command implements SCAN, HSCAN and SSCAN commands.\n * If object 'o' is passed, then it must be a Hash or Set object, otherwise\n * if 'o' is NULL the command will operate on the dictionary associated with\n * the current database.\n *\n * When 'o' is not NULL the function assumes that the first argument in\n * the client arguments vector is a key so it skips it before iterating\n * in order to parse options.\n *\n * In the case of a Hash object the function returns both the field and value\n * of every element on the Hash. */\nvoid scanGenericCommand(client *c, int scantype) {\n    int i, j;\n    dlist *keys = dlistCreate();\n    dlistNode *node, *nextnode;\n    long count = 10;\n    sds pat = NULL;\n    int patlen = 0, use_pattern = 0;\n    unsigned long cursor;\n    robj *o;\n    dict *ht;\n\n    /* Set i to the first option argument. The previous one is the cursor. */\n    i = (scantype == SCAN_TYPE_KEY) ? 2 : 3; /* Skip the key argument if needed. */\n    if (parseScanCursorOrReply(c,c->argv[i-1],&cursor) == VR_ERROR) return;\n\n    /* Step 1: Parse options. */\n    while (i < c->argc) {\n        j = c->argc - i;\n        if (!strcasecmp(c->argv[i]->ptr, \"count\") && j >= 2) {\n            if (getLongFromObjectOrReply(c, c->argv[i+1], &count, NULL)\n                != VR_OK)\n            {\n                goto cleanup;\n            }\n\n            if (count < 1) {\n                addReply(c,shared.syntaxerr);\n                goto cleanup;\n            }\n\n            i += 2;\n        } else if (!strcasecmp(c->argv[i]->ptr, \"match\") && j >= 2) {\n            pat = c->argv[i+1]->ptr;\n            patlen = sdslen(pat);\n\n            /* The pattern always matches if it is exactly \"*\", so it is\n             * equivalent to disabling it. */\n            use_pattern = !(pat[0] == '*' && patlen == 1);\n\n            i += 2;\n        } else {\n            addReply(c,shared.syntaxerr);\n            goto cleanup;\n        }\n    }\n\n    if (scantype == SCAN_TYPE_KEY) {\n        o = NULL;\n        if (c->scanid == -1 || cursor == 0) c->scanid = 0;\n        fetchInternalDbById(c, c->scanid);\n        lockDbRead(c->db);\n    } else if (scantype == SCAN_TYPE_HASH || \n        scantype == SCAN_TYPE_SET ||\n        scantype == SCAN_TYPE_ZSET) {\n        fetchInternalDbByKey(c, c->argv[1]);\n        lockDbRead(c->db);\n        if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptyscan)) == NULL) {\n            unlockDb(c->db);\n            update_stats_add(c->vel->stats, keyspace_misses, 1);\n            return;\n        }\n    }\n\n    switch (scantype) {\n    case SCAN_TYPE_KEY:\n        ASSERT(o == NULL);\n        break;\n    case SCAN_TYPE_HASH:\n        if (checkType(c,o,OBJ_HASH)) {\n            unlockDb(c->db);\n            update_stats_add(c->vel->stats, keyspace_hits, 1);\n            return;\n        }\n        break;\n    case SCAN_TYPE_SET:\n        if (checkType(c,o,OBJ_SET)) {\n            unlockDb(c->db);\n            update_stats_add(c->vel->stats, keyspace_hits, 1);\n            return;\n        }\n        break;\n    case SCAN_TYPE_ZSET:\n        if (checkType(c,o,OBJ_ZSET)) {\n            unlockDb(c->db);\n            update_stats_add(c->vel->stats, keyspace_hits, 1);\n            return;\n        }\n        break;\n    }\n\n    /* Object must be NULL (to iterate keys names), or the type of the object\n     * must be Set, Sorted Set, or Hash. */\n    ASSERT(o == NULL || o->type == OBJ_SET || o->type == OBJ_HASH ||\n                o->type == OBJ_ZSET);\n\nscan_retry:\n    \n    /* Step 2: Iterate the collection.\n     *\n     * Note that if the object is encoded with a ziplist, intset, or any other\n     * representation that is not a hash table, we are sure that it is also\n     * composed of a small number of elements. So to avoid taking state we\n     * just return everything inside the object in a single call, setting the\n     * cursor to zero to signal the end of the iteration. */\n\n    /* Handle the case of a hash table. */\n    ht = NULL;\n    if (scantype == SCAN_TYPE_KEY) {\n        ht = c->db->dict;\n    } else if (o->type == OBJ_SET && o->encoding == OBJ_ENCODING_HT) {\n        ht = o->ptr;\n    } else if (o->type == OBJ_HASH && o->encoding == OBJ_ENCODING_HT) {\n        ht = o->ptr;\n        count *= 2; /* We return key / value for this type. */\n    } else if (o->type == OBJ_ZSET && o->encoding == OBJ_ENCODING_SKIPLIST) {\n        zset *zs = o->ptr;\n        ht = zs->dict;\n        count *= 2; /* We return key / value for this type. */\n    }\n\n    if (ht) {\n        void *privdata[2];\n        /* We set the max number of iterations to ten times the specified\n         * COUNT, so if the hash table is in a pathological state (very\n         * sparsely populated) we avoid to block too much time at the cost\n         * of returning no or very few elements. */\n        long maxiterations = count*10;\n\n        /* We pass two pointers to the callback: the list to which it will\n         * add new elements, and the object containing the dictionary so that\n         * it is possible to fetch more data in a type-dependent way. */\n        privdata[0] = keys;\n        privdata[1] = o;\n        do {\n            cursor = dictScan(ht, cursor, scanCallback, privdata);\n        } while (cursor &&\n              maxiterations-- &&\n              dlistLength(keys) < (unsigned long)count);\n    } else if (o->type == OBJ_SET) {\n        int pos = 0;\n        int64_t ll;\n\n        while(intsetGet(o->ptr,pos++,&ll))\n            dlistAddNodeTail(keys,createStringObjectFromLongLong(ll));\n        cursor = 0;\n    } else if (o->type == OBJ_HASH || o->type == OBJ_ZSET) {\n        unsigned char *p = ziplistIndex(o->ptr,0);\n        unsigned char *vstr;\n        unsigned int vlen;\n        long long vll;\n\n        while(p) {\n            ziplistGet(p,&vstr,&vlen,&vll);\n            dlistAddNodeTail(keys,\n                (vstr != NULL) ? createStringObject((char*)vstr,vlen) :\n                                 createStringObjectFromLongLong(vll));\n            p = ziplistNext(o->ptr,p);\n        }\n        cursor = 0;\n    } else {\n        serverPanic(\"Not handled encoding in SCAN.\");\n    }\n\n    unlockDb(c->db);\n    if (scantype == SCAN_TYPE_KEY) {\n        if (cursor == 0) {\n            if (c->scanid < (server.dbinum - 1)) {\n                c->scanid ++;\n                fetchInternalDbById(c, c->scanid);\n                lockDbRead(c->db);\n                goto scan_retry;\n            } else {\n                c->scanid = -1;\n            }\n        }\n    } else if (scantype == SCAN_TYPE_HASH || \n        scantype == SCAN_TYPE_SET ||\n        scantype == SCAN_TYPE_ZSET) {\n        update_stats_add(c->vel->stats, keyspace_hits, 1);\n    }\n\n    /* Step 3: Filter elements. */\n    node = dlistFirst(keys);\n    while (node) {\n        robj *kobj = dlistNodeValue(node);\n        nextnode = dlistNextNode(node);\n        int filter = 0;\n\n        /* Filter element if it does not match the pattern. */\n        if (!filter && use_pattern) {\n            if (sdsEncodedObject(kobj)) {\n                if (!stringmatchlen(pat, patlen, kobj->ptr, sdslen(kobj->ptr), 0))\n                    filter = 1;\n            } else {\n                char buf[LONG_STR_SIZE];\n                int len;\n\n                ASSERT(kobj->encoding == OBJ_ENCODING_INT);\n                len = ll2string(buf,sizeof(buf),(long)kobj->ptr);\n                if (!stringmatchlen(pat, patlen, buf, len, 0)) filter = 1;\n            }\n        }\n\n        /* Filter element if it is an expired key. */\n        if (!filter && o == NULL && checkIfExpired(c->db,kobj)) filter = 1;\n\n        /* Remove the element and its associted value if needed. */\n        if (filter) {\n            freeObject(kobj);\n            dlistDelNode(keys, node);\n        }\n\n        /* If this is a hash or a sorted set, we have a flat list of\n         * key-value elements, so if this element was filtered, remove the\n         * value, or skip it if it was not filtered: we only match keys. */\n        if (o && (o->type == OBJ_ZSET || o->type == OBJ_HASH)) {\n            node = nextnode;\n            nextnode = dlistNextNode(node);\n            if (filter) {\n                kobj = dlistNodeValue(node);\n                freeObject(kobj);\n                dlistDelNode(keys, node);\n            }\n        }\n        node = nextnode;\n    }\n\n    /* Step 4: Reply to the client. */\n    addReplyMultiBulkLen(c, 2);\n    addReplyBulkLongLong(c,cursor);\n\n    addReplyMultiBulkLen(c, dlistLength(keys));\n    while ((node = dlistFirst(keys)) != NULL) {\n        robj *kobj = dlistNodeValue(node);\n        addReplyBulk(c, kobj);\n        freeObject(kobj);\n        dlistDelNode(keys, node);\n    }\n\ncleanup:\n    dlistSetFreeMethod(keys,freeObjectVoid);\n    dlistRelease(keys);\n}\n\n/* The SCAN command completely relies on scanGenericCommand. */\nvoid scanCommand(client *c) {\n    scanGenericCommand(c,SCAN_TYPE_KEY);\n}\n\nvoid dbsizeCommand(client *c) {\n    int idx;\n    unsigned long count = 0;\n\n    for (idx = 0; idx < server.dbinum; idx ++) {\n        fetchInternalDbById(c, idx);\n        lockDbRead(c->db);\n        count += dictSize(c->db->dict);\n        unlockDb(c->db);\n    }\n    \n    addReplyLongLong(c,count);\n}\n\nvoid lastsaveCommand(client *c) {\n    addReplyLongLong(c,server.lastsave);\n}\n\nvoid typeCommand(client *c) {\n    robj *o;\n    char *type;\n\n    fetchInternalDbByKey(c, c->argv[1]);\n    lockDbRead(c->db);\n    o = lookupKeyRead(c->db,c->argv[1]);\n    if (o == NULL) {\n        type = \"none\";\n        unlockDb(c->db);\n        addReplyStatus(c,type);\n        update_stats_add(c->vel->stats, keyspace_misses, 1);\n        return;\n    } else {\n        switch(o->type) {\n        case OBJ_STRING: type = \"string\"; break;\n        case OBJ_LIST: type = \"list\"; break;\n        case OBJ_SET: type = \"set\"; break;\n        case OBJ_ZSET: type = \"zset\"; break;\n        case OBJ_HASH: type = \"hash\"; break;\n        default: type = \"unknown\"; break;\n        }\n    }\n\n    unlockDb(c->db);\n    addReplyStatus(c,type);\n    update_stats_add(c->vel->stats, keyspace_hits, 1);\n}\n\nvoid shutdownCommand(client *c) {\n    int flags = 0;\n\n    if (c->argc > 2) {\n        addReply(c,shared.syntaxerr);\n        return;\n    } else if (c->argc == 2) {\n        if (!strcasecmp(c->argv[1]->ptr,\"nosave\")) {\n            flags |= SHUTDOWN_NOSAVE;\n        } else if (!strcasecmp(c->argv[1]->ptr,\"save\")) {\n            flags |= SHUTDOWN_SAVE;\n        } else {\n            addReply(c,shared.syntaxerr);\n            return;\n        }\n    }\n    /* When SHUTDOWN is called while the server is loading a dataset in\n     * memory we need to make sure no attempt is performed to save\n     * the dataset on shutdown (otherwise it could overwrite the current DB\n     * with half-read data).\n     *\n     * Also when in Sentinel mode clear the SAVE flag and force NOSAVE. */\n    if (server.loading)\n        flags = (flags & ~SHUTDOWN_SAVE) | SHUTDOWN_NOSAVE;\n    //if (prepareForShutdown(flags) == VR_OK) exit(0);\n    addReplyError(c,\"Errors trying to SHUTDOWN. Check logs.\");\n}\n\nvoid renameGenericCommand(client *c, int nx) {\n    robj *o;\n    long long expire;\n    int samekey = 0;\n\n    /* When source and dest key is the same, no operation is performed,\n     * if the key exists, however we still return an error on unexisting key. */\n    if (sdscmp(c->argv[1]->ptr,c->argv[2]->ptr) == 0) samekey = 1;\n\n    if ((o = lookupKeyWriteOrReply(c,c->argv[1],shared.nokeyerr,NULL)) == NULL)\n        return;\n\n    if (samekey) {\n        addReply(c,nx ? shared.czero : shared.ok);\n        return;\n    }\n\n    incrRefCount(o);\n    expire = getExpire(c->db,c->argv[1]);\n    if (lookupKeyWrite(c->db,c->argv[2],NULL) != NULL) {\n        if (nx) {\n            decrRefCount(o);\n            addReply(c,shared.czero);\n            return;\n        }\n        /* Overwrite: delete the old key before creating the new one\n         * with the same name. */\n        dbDelete(c->db,c->argv[2]);\n    }\n    dbAdd(c->db,c->argv[2],o);\n    if (expire != -1) setExpire(c->db,c->argv[2],expire);\n    dbDelete(c->db,c->argv[1]);\n    signalModifiedKey(c->db,c->argv[1]);\n    signalModifiedKey(c->db,c->argv[2]);\n    notifyKeyspaceEvent(NOTIFY_GENERIC,\"rename_from\",\n        c->argv[1],c->db->id);\n    notifyKeyspaceEvent(NOTIFY_GENERIC,\"rename_to\",\n        c->argv[2],c->db->id);\n    server.dirty++;\n    addReply(c,nx ? shared.cone : shared.ok);\n}\n\nvoid renameCommand(client *c) {\n    renameGenericCommand(c,0);\n}\n\nvoid renamenxCommand(client *c) {\n    renameGenericCommand(c,1);\n}\n\nvoid moveCommand(client *c) {\n    robj *o;\n    redisDb *src, *dst;\n    int srcid;\n    long long dbid, expire;\n\n    /* Obtain source and target DB pointers */\n    src = c->db;\n    srcid = c->db->id;\n\n    if (getLongLongFromObject(c->argv[2],&dbid) == VR_ERROR ||\n        dbid < INT_MIN || dbid > INT_MAX ||\n        selectDb(c,dbid) == VR_ERROR)\n    {\n        addReply(c,shared.outofrangeerr);\n        return;\n    }\n    dst = c->db;\n    selectDb(c,srcid); /* Back to the source DB */\n\n    /* If the user is moving using as target the same\n     * DB as the source DB it is probably an error. */\n    if (src == dst) {\n        addReply(c,shared.sameobjecterr);\n        return;\n    }\n\n    /* Check if the element exists and get a reference */\n    o = lookupKeyWrite(c->db,c->argv[1],NULL);\n    if (!o) {\n        addReply(c,shared.czero);\n        return;\n    }\n    expire = getExpire(c->db,c->argv[1]);\n\n    /* Return zero if the key already exists in the target DB */\n    if (lookupKeyWrite(dst,c->argv[1],NULL) != NULL) {\n        addReply(c,shared.czero);\n        return;\n    }\n    dbAdd(dst,c->argv[1],o);\n    if (expire != -1) setExpire(dst,c->argv[1],expire);\n    incrRefCount(o);\n\n    /* OK! key moved, free the entry in the source DB */\n    dbDelete(src,c->argv[1]);\n    server.dirty++;\n    addReply(c,shared.cone);\n}\n\n/*-----------------------------------------------------------------------------\n * Expires API\n *----------------------------------------------------------------------------*/\n\nint removeExpire(redisDb *db, robj *key) {\n    /* An expire may only be removed if there is a corresponding entry in the\n     * main dict. Otherwise, the key will never be freed. */\n    serverAssertWithInfo(NULL,key,dictFind(db->dict,key->ptr) != NULL);\n    return dictDelete(db->expires,key->ptr) == DICT_OK;\n}\n\nvoid setExpire(redisDb *db, robj *key, long long when) {\n    dictEntry *kde, *de;\n\n    /* Reuse the sds from the main dict in the expire dict */\n    kde = dictFind(db->dict,key->ptr);\n    serverAssertWithInfo(NULL,key,kde != NULL);\n    de = dictReplaceRaw(db->expires,dictGetKey(kde));\n    dictSetSignedIntegerVal(de,when);\n}\n\n/* Return the expire time of the specified key, or -1 if no expire\n * is associated with this key (i.e. the key is non volatile) */\nlong long getExpire(redisDb *db, robj *key) {\n    dictEntry *de;\n\n    /* No expire? return ASAP */\n    if (dictSize(db->expires) == 0 ||\n       (de = dictFind(db->expires,key->ptr)) == NULL) return -1;\n\n    /* The entry was found in the expire dict, this means it should also\n     * be present in the main dict (safety check). */\n    serverAssertWithInfo(NULL,key,dictFind(db->dict,key->ptr) != NULL);\n    return dictGetSignedIntegerVal(de);\n}\n\n/* Propagate expires into slaves and the AOF file.\n * When a key expires in the master, a DEL operation for this key is sent\n * to all the slaves and the AOF file if enabled.\n *\n * This way the key expiry is centralized in one place, and since both\n * AOF and the master->slave link guarantee operation ordering, everything\n * will be consistent even if we allow write operations against expiring\n * keys. */\nvoid propagateExpire(redisDb *db, robj *key) {\n    robj *argv[2];\n\n    argv[0] = shared.del;\n    argv[1] = key;\n    incrRefCount(argv[0]);\n    incrRefCount(argv[1]);\n\n    if (server.aof_state != AOF_OFF)\n        feedAppendOnlyFile(server.delCommand,db->id,argv,2);\n    replicationFeedSlaves(repl.slaves,db->id,argv,2);\n\n    decrRefCount(argv[0]);\n    decrRefCount(argv[1]);\n}\n\n/* Check if the key exists in the db and had expired */\nint checkIfExpired(redisDb *db, robj *key) {\n    long long when;\n\n    when = getExpire(db,key);\n    if (when > 0 && vr_msec_now() > when) {\n        return 1;\n    }\n\n    return 0;\n}\n\nint expireIfNeeded(redisDb *db, robj *key) {\n    long long when = getExpire(db,key);\n    long long now;\n\n    if (when < 0) return 0; /* No expire for this key */\n\n    /* Don't expire anything while loading. It will be done later. */\n    if (server.loading) return 0;\n\n    /* If we are in the context of a Lua script, we claim that time is\n     * blocked to when the Lua script started. This way a key can expire\n     * only the first time it is accessed and not in the middle of the\n     * script execution, making propagation to slaves / AOF consistent.\n     * See issue #1525 on Github for more information. */\n    now = server.lua_caller ? server.lua_time_start : vr_msec_now();\n\n    /* If we are running in the context of a slave, return ASAP:\n     * the slave key expiration is controlled by the master that will\n     * send us synthesized DEL operations for expired keys.\n     *\n     * Still we try to return the right information to the caller,\n     * that is, 0 if we think the key should be still valid, 1 if\n     * we think the key is expired at this time. */\n    if (repl.masterhost != NULL) return now > when;\n\n    /* Return when this key has not expired */\n    if (now <= when) return 0;\n\n    /* Delete the key */\n    //propagateExpire(db,key);\n    notifyKeyspaceEvent(NOTIFY_EXPIRED,\n        \"expired\",key,db->id);\n    return dbDelete(db,key);\n}\n\n/*-----------------------------------------------------------------------------\n * Expires Commands\n *----------------------------------------------------------------------------*/\n\n/* This is the generic command implementation for EXPIRE, PEXPIRE, EXPIREAT\n * and PEXPIREAT. Because the commad second argument may be relative or absolute\n * the \"basetime\" argument is used to signal what the base time is (either 0\n * for *AT variants of the command, or the current time for relative expires).\n *\n * unit is either UNIT_SECONDS or UNIT_MILLISECONDS, and is only used for\n * the argv[2] parameter. The basetime is always specified in milliseconds. */\nvoid expireGenericCommand(client *c, long long basetime, int unit) {\n    robj *key = c->argv[1], *param = c->argv[2];\n    long long when; /* unix time in milliseconds when the key will expire. */\n    int expired = 0;\n\n    if (getLongLongFromObjectOrReply(c, param, &when, NULL) != VR_OK)\n        return;\n\n    if (unit == UNIT_SECONDS) when *= 1000;\n    when += basetime;\n\n    fetchInternalDbByKey(c, key);\n    lockDbWrite(c->db);\n    /* No key, return zero. */\n    if (lookupKeyWrite(c->db,key,&expired) == NULL) {\n        unlockDb(c->db);\n        if (expired) update_stats_add(c->vel->stats,expiredkeys,1);\n        addReply(c,shared.czero);\n        return;\n    }\n\n    /* EXPIRE with negative TTL, or EXPIREAT with a timestamp into the past\n     * should never be executed as a DEL when load the AOF or in the context\n     * of a slave instance.\n     *\n     * Instead we take the other branch of the IF statement setting an expire\n     * (possibly in the past) and wait for an explicit DEL from the master. */\n    if (when <= vr_msec_now() && !server.loading && !repl.masterhost) {\n        robj *aux;\n\n        serverAssertWithInfo(c,key,dbDelete(c->db,key));\n        c->vel->dirty++;\n\n        /* Replicate/AOF this as an explicit DEL. */\n        aux = dupStringObjectUnconstant(key);\n        rewriteClientCommandVector(c,2,shared.del,aux);\n        signalModifiedKey(c->db,key);\n        notifyKeyspaceEvent(NOTIFY_GENERIC,\"del\",key,c->db->id);\n        unlockDb(c->db);\n        if (expired) update_stats_add(c->vel->stats,expiredkeys,1);\n        addReply(c, shared.cone);\n        return;\n    } else {\n        setExpire(c->db,key,when);\n        addReply(c,shared.cone);\n        signalModifiedKey(c->db,key);\n        notifyKeyspaceEvent(NOTIFY_GENERIC,\"expire\",key,c->db->id);\n        unlockDb(c->db);\n        if (expired) update_stats_add(c->vel->stats,expiredkeys,1);\n        c->vel->dirty++;\n        return;\n    }\n}\n\nvoid expireCommand(client *c) {\n    expireGenericCommand(c,vr_msec_now(),UNIT_SECONDS);\n}\n\nvoid expireatCommand(client *c) {\n    expireGenericCommand(c,0,UNIT_SECONDS);\n}\n\nvoid pexpireCommand(client *c) {\n    expireGenericCommand(c,vr_msec_now(),UNIT_MILLISECONDS);\n}\n\nvoid pexpireatCommand(client *c) {\n    expireGenericCommand(c,0,UNIT_MILLISECONDS);\n}\n\nvoid ttlGenericCommand(client *c, int output_ms) {\n    long long expire, ttl = -1;\n\n    fetchInternalDbByKey(c, c->argv[1]);\n    lockDbRead(c->db);\n    /* If the key does not exist at all, return -2 */\n    if (lookupKeyRead(c->db,c->argv[1]) == NULL) {\n        unlockDb(c->db);\n        update_stats_add(c->vel->stats, keyspace_misses, 1);\n        addReplyLongLong(c,-2);\n        return;\n    }\n    /* The key exists. Return -1 if it has no expire, or the actual\n     * TTL value otherwise. */\n    expire = getExpire(c->db,c->argv[1]);\n    unlockDb(c->db);\n    update_stats_add(c->vel->stats, keyspace_hits, 1);\n    \n    if (expire != -1) {\n        ttl = expire-vr_msec_now();\n        if (ttl < 0) ttl = 0;\n    }\n    if (ttl == -1) {\n        addReplyLongLong(c,-1);\n    } else {\n        addReplyLongLong(c,output_ms ? ttl : ((ttl+500)/1000));\n    }\n}\n\nvoid ttlCommand(client *c) {\n    ttlGenericCommand(c, 0);\n}\n\nvoid pttlCommand(client *c) {\n    ttlGenericCommand(c, 1);\n}\n\nvoid persistCommand(client *c) {\n    dictEntry *de;\n\n    fetchInternalDbByKey(c, c->argv[1]);\n    lockDbWrite(c->db);\n    de = dictFind(c->db->dict,c->argv[1]->ptr);\n    if (de == NULL) {\n        addReply(c,shared.czero);\n    } else {\n        if (removeExpire(c->db,c->argv[1])) {\n            addReply(c,shared.cone);\n            c->vel->dirty++;\n        } else {\n            addReply(c,shared.czero);\n        }\n    }\n    unlockDb(c->db);\n}\n\n/* -----------------------------------------------------------------------------\n * API to get key arguments from commands\n * ---------------------------------------------------------------------------*/\n\n/* The base case is to use the keys position as given in the command table\n * (firstkey, lastkey, step). */\nint *getKeysUsingCommandTable(struct redisCommand *cmd,robj **argv, int argc, int *numkeys) {\n    int j, i = 0, last, *keys;\n    UNUSED(argv);\n\n    if (cmd->firstkey == 0) {\n        *numkeys = 0;\n        return NULL;\n    }\n    last = cmd->lastkey;\n    if (last < 0) last = argc+last;\n    keys = dalloc(sizeof(int)*((last - cmd->firstkey)+1));\n    for (j = cmd->firstkey; j <= last; j += cmd->keystep) {\n        ASSERT(j < argc);\n        keys[i++] = j;\n    }\n    *numkeys = i;\n    return keys;\n}\n\n/* Return all the arguments that are keys in the command passed via argc / argv.\n *\n * The command returns the positions of all the key arguments inside the array,\n * so the actual return value is an heap allocated array of integers. The\n * length of the array is returned by reference into *numkeys.\n *\n * 'cmd' must be point to the corresponding entry into the redisCommand\n * table, according to the command name in argv[0].\n *\n * This function uses the command table if a command-specific helper function\n * is not required, otherwise it calls the command-specific function. */\nint *getKeysFromCommand(struct redisCommand *cmd, robj **argv, int argc, int *numkeys) {\n    if (cmd->getkeys_proc) {\n        return cmd->getkeys_proc(cmd,argv,argc,numkeys);\n    } else {\n        return getKeysUsingCommandTable(cmd,argv,argc,numkeys);\n    }\n}\n\n/* Free the result of getKeysFromCommand. */\nvoid getKeysFreeResult(int *result) {\n    dfree(result);\n}\n\n/* Helper function to extract keys from following commands:\n * ZUNIONSTORE <destkey> <num-keys> <key> <key> ... <key> <options>\n * ZINTERSTORE <destkey> <num-keys> <key> <key> ... <key> <options> */\nint *zunionInterGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys) {\n    int i, num, *keys;\n    UNUSED(cmd);\n\n    num = atoi(argv[2]->ptr);\n    /* Sanity check. Don't return any key if the command is going to\n     * reply with syntax error. */\n    if (num > (argc-3)) {\n        *numkeys = 0;\n        return NULL;\n    }\n\n    /* Keys in z{union,inter}store come from two places:\n     * argv[1] = storage key,\n     * argv[3...n] = keys to intersect */\n    keys = dalloc(sizeof(int)*(num+1));\n\n    /* Add all key positions for argv[3...n] to keys[] */\n    for (i = 0; i < num; i++) keys[i] = 3+i;\n\n    /* Finally add the argv[1] key position (the storage key target). */\n    keys[num] = 1;\n    *numkeys = num+1;  /* Total keys = {union,inter} keys + storage key */\n    return keys;\n}\n\n/* Helper function to extract keys from the following commands:\n * EVAL <script> <num-keys> <key> <key> ... <key> [more stuff]\n * EVALSHA <script> <num-keys> <key> <key> ... <key> [more stuff] */\nint *evalGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys) {\n    int i, num, *keys;\n    UNUSED(cmd);\n\n    num = atoi(argv[2]->ptr);\n    /* Sanity check. Don't return any key if the command is going to\n     * reply with syntax error. */\n    if (num > (argc-3)) {\n        *numkeys = 0;\n        return NULL;\n    }\n\n    keys = dalloc(sizeof(int)*num);\n    *numkeys = num;\n\n    /* Add all key positions for argv[3...n] to keys[] */\n    for (i = 0; i < num; i++) keys[i] = 3+i;\n\n    return keys;\n}\n\n/* Helper function to extract keys from the SORT command.\n *\n * SORT <sort-key> ... STORE <store-key> ...\n *\n * The first argument of SORT is always a key, however a list of options\n * follow in SQL-alike style. Here we parse just the minimum in order to\n * correctly identify keys in the \"STORE\" option. */\nint *sortGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys) {\n    int i, j, num, *keys, found_store = 0;\n    UNUSED(cmd);\n\n    num = 0;\n    keys = dalloc(sizeof(int)*2); /* Alloc 2 places for the worst case. */\n\n    keys[num++] = 1; /* <sort-key> is always present. */\n\n    /* Search for STORE option. By default we consider options to don't\n     * have arguments, so if we find an unknown option name we scan the\n     * next. However there are options with 1 or 2 arguments, so we\n     * provide a list here in order to skip the right number of args. */\n    struct {\n        char *name;\n        int skip;\n    } skiplist[] = {\n        {\"limit\", 2},\n        {\"get\", 1},\n        {\"by\", 1},\n        {NULL, 0} /* End of elements. */\n    };\n\n    for (i = 2; i < argc; i++) {\n        for (j = 0; skiplist[j].name != NULL; j++) {\n            if (!strcasecmp(argv[i]->ptr,skiplist[j].name)) {\n                i += skiplist[j].skip;\n                break;\n            } else if (!strcasecmp(argv[i]->ptr,\"store\") && i+1 < argc) {\n                /* Note: we don't increment \"num\" here and continue the loop\n                 * to be sure to process the *last* \"STORE\" option if multiple\n                 * ones are provided. This is same behavior as SORT. */\n                found_store = 1;\n                keys[num] = i+1; /* <store-key> */\n                break;\n            }\n        }\n    }\n    *numkeys = num + found_store;\n    return keys;\n}\n\nint *migrateGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys) {\n    int i, num, first, *keys;\n    UNUSED(cmd);\n\n    /* Assume the obvious form. */\n    first = 3;\n    num = 1;\n\n    /* But check for the extended one with the KEYS option. */\n    if (argc > 6) {\n        for (i = 6; i < argc; i++) {\n            if (!strcasecmp(argv[i]->ptr,\"keys\") &&\n                sdslen(argv[3]->ptr) == 0)\n            {\n                first = i+1;\n                num = argc-first;\n                break;\n            }\n        }\n    }\n\n    keys = dalloc(sizeof(int)*num);\n    for (i = 0; i < num; i++) keys[i] = first+i;\n    *numkeys = num;\n    return keys;\n}\n\nint fetchInternalDbByKey(client *c, robj *key) {\n    c->db = darray_get(&server.dbs, (hash_crc16(key->ptr,stringObjectLen(key))&0x3FFF)%server.dbinum+c->dictid*server.dbinum);\n    return VR_OK;\n}\n\nint fetchInternalDbById(client *c, int idx) {\n    c->db = darray_get(&server.dbs, idx+c->dictid*server.dbinum);\n    return VR_OK;\n}\n\n/* If the percentage of used slots in the HT reaches HASHTABLE_MIN_FILL\n * we resize the hash table to save memory */\nvoid tryResizeHashTablesForDb(int dbid) {\n    redisDb *db;\n\n    db = darray_get(&server.dbs, dbid);\n    lockDbWrite(db);\n    if (htNeedsResize(db->dict))\n        dictResize(db->dict);\n    if (htNeedsResize(db->expires))\n        dictResize(db->expires);\n    unlockDb(db);\n}\n\n/* Our hash table implementation performs rehashing incrementally while\n * we write/read from the hash table. Still if the server is idle, the hash\n * table will use two tables for a long time. So we try to use 1 millisecond\n * of CPU time at every call of this function to perform some rehahsing.\n *\n * The function returns 1 if some rehashing was performed, otherwise 0\n * is returned. */\nint incrementallyRehashForDb(int dbid) {\n    redisDb *db;\n\n    db = darray_get(&server.dbs, dbid);\n    lockDbWrite(db);\n    \n    /* Keys dictionary */\n    if (dictIsRehashing(db->dict)) {\n        dictRehashMilliseconds(db->dict,1);\n        unlockDb(db);\n        return 1; /* already used our millisecond for this loop... */\n    }\n    /* Expires */\n    if (dictIsRehashing(db->expires)) {\n        dictRehashMilliseconds(db->expires,1);\n        unlockDb(db);\n        return 1; /* already used our millisecond for this loop... */\n    }\n\n    unlockDb(db);\n    return 0;\n}\n\n/* Try to expire a few timed out keys. The algorithm used is adaptive and\n * will use few CPU cycles if there are few expiring keys, otherwise\n * it will get more aggressive to avoid that too much memory is used by\n * keys that can be removed from the keyspace.\n *\n * No more than CRON_DBS_PER_CALL databases are tested at every\n * iteration.\n *\n * This kind of call is used when Redis detects that timelimit_exit is\n * true, so there is more work to do, and we do it more incrementally from\n * the beforeSleep() function of the event loop.\n *\n * Expire cycle type:\n *\n * If type is ACTIVE_EXPIRE_CYCLE_FAST the function will try to run a\n * \"fast\" expire cycle that takes no longer than EXPIRE_FAST_CYCLE_DURATION\n * microseconds, and is not repeated again before the same amount of time.\n *\n * If type is ACTIVE_EXPIRE_CYCLE_SLOW, that normal expire cycle is\n * executed, where the time limit is a percentage of the REDIS_HZ period\n * as specified by the REDIS_EXPIRELOOKUPS_TIME_PERC define. */\n\nvoid activeExpireCycle(vr_backend *backend, int type) {\n    int j, iteration = 0;\n    int dbs_per_call = CRON_DBS_PER_CALL;\n    long long start = vr_usec_now(), timelimit;\n    long long expired_total = 0;\n\n    if (type == ACTIVE_EXPIRE_CYCLE_FAST) {\n        /* Don't start a fast cycle if the previous cycle did not exited\n         * for time limt. Also don't repeat a fast cycle for the same period\n         * as the fast cycle total duration itself. */\n        if (!backend->timelimit_exit) return;\n        if (start < backend->last_fast_cycle + ACTIVE_EXPIRE_CYCLE_FAST_DURATION*2) return;\n        backend->last_fast_cycle = start;\n    }\n\n    /* We usually should test CRON_DBS_PER_CALL per iteration, with\n     * two exceptions:\n     *\n     * 1) Don't test more DBs than we have.\n     * 2) If last time we hit the time limit, we want to scan all DBs\n     * in this iteration, as there is work to do in some DB and we don't want\n     * expired keys to use memory for too much time. */\n    if (dbs_per_call > server.dbnum || backend->timelimit_exit)\n        dbs_per_call = server.dbnum;\n\n    /* We can use at max ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC percentage of CPU time\n     * per iteration. Since this function gets called with a frequency of\n     * server.hz times per second, the following is the max amount of\n     * microseconds we can spend in this function. */\n    timelimit = 1000000*ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC/server.hz/100;\n    backend->timelimit_exit = 0;\n    if (timelimit <= 0) timelimit = 1;\n\n    if (type == ACTIVE_EXPIRE_CYCLE_FAST)\n        timelimit = ACTIVE_EXPIRE_CYCLE_FAST_DURATION; /* in microseconds. */\n\n    for (j = 0; j < dbs_per_call; j++) {\n        int expired;\n        redisDb *db = darray_get(&server.dbs, backend->current_db%server.dbnum);\n\n        /* Increment the DB now so we are sure if we run out of time\n         * in the current DB we'll restart from the next. This allows to\n         * distribute the time evenly across DBs. */\n        backend->current_db++;\n        \n        lockDbWrite(db);\n        /* Continue to expire if at the end of the cycle more than 25%\n         * of the keys were expired. */\n        do {\n            unsigned long num, slots;\n            long long now, ttl_sum;\n            int ttl_samples;\n            /* If there is nothing to expire try next DB ASAP. */\n            if ((num = dictSize(db->expires)) == 0) {\n                db->avg_ttl = 0;\n                break;\n            }\n            slots = dictSlots(db->expires);\n            now = vr_msec_now();\n\n            /* When there are less than 1% filled slots getting random\n             * keys is expensive, so stop here waiting for better times...\n             * The dictionary will be resized asap. */\n            if (num && slots > DICT_HT_INITIAL_SIZE &&\n                (num*100/slots < 1)) break;\n\n            /* The main collection cycle. Sample random keys among keys\n             * with an expire set, checking for expired ones. */\n            expired = 0;\n            ttl_sum = 0;\n            ttl_samples = 0;\n\n            if (num > ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP)\n                num = ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP;\n\n            while (num--) {\n                dictEntry *de;\n                long long ttl;\n\n                if ((de = dictGetRandomKey(db->expires)) == NULL) break;\n                ttl = dictGetSignedIntegerVal(de)-now;\n                if (activeExpireCycleTryExpire(db,de,now)) expired++;\n                if (ttl > 0) {\n                    /* We want the average TTL of keys yet not expired. */\n                    ttl_sum += ttl;\n                    ttl_samples++;\n                }\n            }\n\n            expired_total += expired;\n\n            /* Update the average TTL stats for this database. */\n            if (ttl_samples) {\n                long long avg_ttl = ttl_sum/ttl_samples;\n\n                /* Do a simple running average with a few samples.\n                 * We just use the current estimate with a weight of 2%\n                 * and the previous estimate with a weight of 98%. */\n                if (db->avg_ttl == 0) db->avg_ttl = avg_ttl;\n                db->avg_ttl = (db->avg_ttl/50)*49 + (avg_ttl/50);\n            }\n\n            /* We can't block forever here even if there are many keys to\n             * expire. So after a given amount of milliseconds return to the\n             * caller waiting for the other active expire cycle. */\n            iteration++;\n            if ((iteration & 0xf) == 0) { /* check once every 16 iterations. */\n                long long elapsed = vr_usec_now()-start;\n\n                //latencyAddSampleIfNeeded(\"expire-cycle\",elapsed/1000);\n                if (elapsed > timelimit) backend->timelimit_exit = 1;\n            }\n            if (backend->timelimit_exit) {\n                unlockDb(db);\n\n                if (expired_total > 0) {\n                    update_stats_add(backend->vel.stats, expiredkeys, expired_total);\n                }\n                return;\n            }\n            /* We don't repeat the cycle if there are less than 25% of keys\n             * found expired in the current DB. */\n        } while (expired > ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP/4);\n        unlockDb(db);\n    }\n\n    if (expired_total > 0) {\n        update_stats_add(backend->vel.stats, expiredkeys, expired_total);\n    }\n}\n\nint activeExpireCycleTryExpire(redisDb *db, dictEntry *de, long long now) {\n    long long t = dictGetSignedIntegerVal(de);\n    if (now > t) {\n        sds key = dictGetKey(de);\n        robj *keyobj = createStringObject(key,sdslen(key));\n        dbDelete(db,keyobj);\n        freeObject(keyobj);\n        return 1;\n    } else {\n        return 0;\n    }\n}\n\n/* This function handles 'background' operations we are required to do\n * incrementally in Redis databases, such as active key expiring, resizing,\n * rehashing. */\nvoid databasesCron(vr_backend *backend) {\n    /* Expire keys by random sampling. Not required for slaves\n     * as master will synthesize DELs for us. */\n    if (repl.masterhost == NULL)\n        activeExpireCycle(backend, ACTIVE_EXPIRE_CYCLE_SLOW);\n\n    /* Perform hash tables rehashing if needed, but only if there are no\n     * other processes saving the DB on disk. Otherwise rehashing is bad\n     * as will cause a lot of copy-on-write of memory pages. */\n    if (server.rdb_child_pid == -1 && server.aof_child_pid == -1) {\n        int dbs_per_call = CRON_DBS_PER_CALL;\n        int j;\n\n        /* Don't test more DBs than we have. */\n        if (dbs_per_call > server.dbnum) dbs_per_call = server.dbnum;\n\n        /* Resize */\n        for (j = 0; j < dbs_per_call; j++) {\n            tryResizeHashTablesForDb(backend->resize_db%server.dbnum);\n            backend->resize_db++;\n        }\n\n        /* Rehash */\n        if (server.activerehashing) {\n            for (j = 0; j < dbs_per_call; j++) {\n                int work_done = incrementallyRehashForDb(backend->rehash_db%server.dbnum);\n                backend->rehash_db++;\n                if (work_done) {\n                    /* If the function did some work, stop here, we'll do\n                     * more at the next cron loop. */\n                    break;\n                }\n            }\n        }\n    }\n}\n\n"
  },
  {
    "path": "src/vr_db.h",
    "content": "#ifndef _VR_DB_H_\n#define _VR_DB_H_\n\n/* To improve the quality of the LRU approximation we take a set of keys\n * that are good candidate for eviction across freeMemoryIfNeeded() calls.\n *\n * Entries inside the eviciton pool are taken ordered by idle time, putting\n * greater idle times to the right (ascending order).\n *\n * Empty entries have the key pointer set to NULL. */\n#define MAXMEMORY_EVICTION_POOL_SIZE 16\nstruct evictionPoolEntry {\n    unsigned long long idle;    /* Object idle time. */\n    sds key;                    /* Key name. */\n};\n\n/* Vire database representation. There are multiple databases identified\n * by integers from 0 (the default database) up to the max configured\n * database. The database number is the 'id' field in the structure. */\ntypedef struct redisDb {\n    dict *dict;                 /* The keyspace for this DB */\n    dict *expires;              /* Timeout of keys with a timeout set */\n    dict *blocking_keys;        /* Keys with clients waiting for data (BLPOP) */\n    dict *ready_keys;           /* Blocked keys that received a PUSH */\n    dict *watched_keys;         /* WATCHED keys for MULTI/EXEC CAS */\n    struct evictionPoolEntry *eviction_pool;    /* Eviction pool of keys */\n    int id;                     /* Database ID */\n    long long avg_ttl;          /* Average TTL, just for stats */\n\n    pthread_rwlock_t rwl;       /* read write lock */\n} redisDb;\n\nextern dictType dbDictType;\nextern dictType keyptrDictType;\nextern dictType keylistDictType;\n\nint redisDbInit(redisDb *db);\nint redisDbDeinit(redisDb *db);\n\nint lockDbRead(redisDb *db);\nint lockDbWrite(redisDb *db);\nint unlockDb(redisDb *db);\n\nrobj *lookupKey(redisDb *db, robj *key);\nrobj *lookupKeyRead(redisDb *db, robj *key);\nrobj *lookupKeyWrite(redisDb *db, robj *key, int *expired);\nrobj *lookupKeyReadOrReply(struct client *c, robj *key, robj *reply);\nrobj *lookupKeyWriteOrReply(struct client *c, robj *key, robj *reply, int *expired);\nvoid dbAdd(redisDb *db, robj *key, robj *val);\nvoid dbOverwrite(redisDb *db, robj *key, robj *val);\nvoid setKey(redisDb *db, robj *key, robj *val, int *expired);\nint dbExists(redisDb *db, robj *key);\nrobj *dbRandomKey(redisDb *db);\nint dbDelete(redisDb *db, robj *key);\nrobj *dbUnshareStringValue(redisDb *db, robj *key, robj *o);\nlong long emptyDb(void(callback)(void*));\nint selectDb(struct client *c, int id);\nvoid signalModifiedKey(redisDb *db, robj *key);\nvoid signalFlushedDb(int dbid);\nvoid flushdbCommand(struct client *c);\nvoid flushallCommand(struct client *c);\nvoid delCommand(struct client *c);\nvoid existsCommand(struct client *c);\nvoid selectCommand(struct client *c);\nvoid randomkeyCommand(struct client *c);\nvoid keysCommand(struct client *c);\nvoid scanCallback(void *privdata, const dictEntry *de);\nint parseScanCursorOrReply(struct client *c, robj *o, unsigned long *cursor);\nvoid scanGenericCommand(struct client *c, int scantype);\nvoid scanCommand(struct client *c);\nvoid dbsizeCommand(struct client *c);\nvoid lastsaveCommand(struct client *c);\nvoid typeCommand(struct client *c);\nvoid shutdownCommand(struct client *c);\nvoid renameGenericCommand(struct client *c, int nx);\nvoid renameCommand(struct client *c);\nvoid renamenxCommand(struct client *c);\nvoid moveCommand(struct client *c);\nint removeExpire(redisDb *db, robj *key);\nvoid setExpire(redisDb *db, robj *key, long long when);\nlong long getExpire(redisDb *db, robj *key);\nvoid propagateExpire(redisDb *db, robj *key);\nint checkIfExpired(redisDb *db, robj *key);\nint expireIfNeeded(redisDb *db, robj *key);\nvoid expireGenericCommand(struct client *c, long long basetime, int unit);\nvoid expireCommand(struct client *c);\nvoid expireatCommand(struct client *c);\nvoid pexpireCommand(struct client *c);\nvoid pexpireatCommand(struct client *c);\nvoid ttlGenericCommand(struct client *c, int output_ms);\nvoid ttlCommand(struct client *c);\nvoid pttlCommand(struct client *c);\nvoid persistCommand(struct client *c);\nint *getKeysUsingCommandTable(struct redisCommand *cmd,robj **argv, int argc, int *numkeys);\nint *getKeysFromCommand(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);\nvoid getKeysFreeResult(int *result);\nint *zunionInterGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);\nint *evalGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);\nint *sortGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);\nint *migrateGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);\n\nint fetchInternalDbByKey(struct client *c, robj *key);\nint fetchInternalDbById(struct client *c, int idx);\n\nvoid tryResizeHashTablesForDb(int dbid);\nint incrementallyRehashForDb(int dbid);\nvoid activeExpireCycle(vr_backend *backend, int type);\nint activeExpireCycleTryExpire(redisDb *db, dictEntry *de, long long now);\nvoid databasesCron(vr_backend *backend);\n\n#endif\n"
  },
  {
    "path": "src/vr_dict.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdarg.h>\n#include <limits.h>\n#include <sys/time.h>\n#include <ctype.h>\n\n#include <vr_core.h>\n\n/* Using dictEnableResize() / dictDisableResize() we make possible to\n * enable/disable resizing of the hash table as needed. This is very important\n * for Redis, as we use copy-on-write and don't want to move too much memory\n * around when there is a child performing saving operations.\n *\n * Note that even when dict_can_resize is set to 0, not all resizes are\n * prevented: a hash table is still allowed to grow if the ratio between\n * the number of elements and the buckets > dict_force_resize_ratio. */\nstatic int dict_can_resize = 1;\nstatic unsigned int dict_force_resize_ratio = 5;\n\n/* -------------------------- private prototypes ---------------------------- */\n\nstatic int _dictExpandIfNeeded(dict *ht);\nstatic unsigned long _dictNextPower(unsigned long size);\nstatic int _dictKeyIndex(dict *ht, const void *key);\nstatic int _dictInit(dict *ht, dictType *type, void *privDataPtr);\n\n/* -------------------------- hash functions -------------------------------- */\n\n/* Thomas Wang's 32 bit Mix Function */\nunsigned int dictIntHashFunction(unsigned int key)\n{\n    key += ~(key << 15);\n    key ^=  (key >> 10);\n    key +=  (key << 3);\n    key ^=  (key >> 6);\n    key += ~(key << 11);\n    key ^=  (key >> 16);\n    return key;\n}\n\nstatic uint32_t dict_hash_function_seed = 5381;\n\nvoid dictSetHashFunctionSeed(uint32_t seed) {\n    dict_hash_function_seed = seed;\n}\n\nuint32_t dictGetHashFunctionSeed(void) {\n    return dict_hash_function_seed;\n}\n\n/* MurmurHash2, by Austin Appleby\n * Note - This code makes a few assumptions about how your machine behaves -\n * 1. We can read a 4-byte value from any address without crashing\n * 2. sizeof(int) == 4\n *\n * And it has a few limitations -\n *\n * 1. It will not work incrementally.\n * 2. It will not produce the same results on little-endian and big-endian\n *    machines.\n */\nunsigned int dictGenHashFunction(const void *key, int len) {\n    /* 'm' and 'r' are mixing constants generated offline.\n     They're not really 'magic', they just happen to work well.  */\n    uint32_t seed = dict_hash_function_seed;\n    const uint32_t m = 0x5bd1e995;\n    const int r = 24;\n\n    /* Initialize the hash to a 'random' value */\n    uint32_t h = seed ^ len;\n\n    /* Mix 4 bytes at a time into the hash */\n    const unsigned char *data = (const unsigned char *)key;\n\n    while(len >= 4) {\n        uint32_t k = *(uint32_t*)data;\n\n        k *= m;\n        k ^= k >> r;\n        k *= m;\n\n        h *= m;\n        h ^= k;\n\n        data += 4;\n        len -= 4;\n    }\n\n    /* Handle the last few bytes of the input array  */\n    switch(len) {\n    case 3: h ^= data[2] << 16;\n    case 2: h ^= data[1] << 8;\n    case 1: h ^= data[0]; h *= m;\n    };\n\n    /* Do a few final mixes of the hash to ensure the last few\n     * bytes are well-incorporated. */\n    h ^= h >> 13;\n    h *= m;\n    h ^= h >> 15;\n\n    return (unsigned int)h;\n}\n\n/* And a case insensitive hash function (based on djb hash) */\nunsigned int dictGenCaseHashFunction(const unsigned char *buf, int len) {\n    unsigned int hash = (unsigned int)dict_hash_function_seed;\n\n    while (len--)\n        hash = ((hash << 5) + hash) + (tolower(*buf++)); /* hash * 33 + c */\n    return hash;\n}\n\n/* ----------------------------- API implementation ------------------------- */\n\n/* Reset a hash table already initialized with ht_init().\n * NOTE: This function should only be called by ht_destroy(). */\nstatic void _dictReset(dictht *ht)\n{\n    ht->table = NULL;\n    ht->size = 0;\n    ht->sizemask = 0;\n    ht->used = 0;\n}\n\n/* Create a new hash table */\ndict *dictCreate(dictType *type,\n        void *privDataPtr)\n{\n    dict *d = dalloc(sizeof(*d));\n\n    _dictInit(d,type,privDataPtr);\n    return d;\n}\n\n/* Initialize the hash table */\nint _dictInit(dict *d, dictType *type,\n        void *privDataPtr)\n{\n    _dictReset(&d->ht[0]);\n    _dictReset(&d->ht[1]);\n    d->type = type;\n    d->privdata = privDataPtr;\n    d->rehashidx = -1;\n    d->iterators = 0;\n    return DICT_OK;\n}\n\n/* Resize the table to the minimal size that contains all the elements,\n * but with the invariant of a USED/BUCKETS ratio near to <= 1 */\nint dictResize(dict *d)\n{\n    int minimal;\n\n    if (!dict_can_resize || dictIsRehashing(d)) return DICT_ERR;\n    minimal = d->ht[0].used;\n    if (minimal < DICT_HT_INITIAL_SIZE)\n        minimal = DICT_HT_INITIAL_SIZE;\n    return dictExpand(d, minimal);\n}\n\n/* Expand or create the hash table */\nint dictExpand(dict *d, unsigned long size)\n{\n    dictht n; /* the new hash table */\n    unsigned long realsize = _dictNextPower(size);\n\n    /* the size is invalid if it is smaller than the number of\n     * elements already inside the hash table */\n    if (dictIsRehashing(d) || d->ht[0].used > size)\n        return DICT_ERR;\n\n    /* Rehashing to the same table size is not useful. */\n    if (realsize == d->ht[0].size) return DICT_ERR;\n\n    /* Allocate the new hash table and initialize all pointers to NULL */\n    n.size = realsize;\n    n.sizemask = realsize-1;\n    n.table = dcalloc(realsize, sizeof(dictEntry*));\n    n.used = 0;\n\n    /* Is this the first initialization? If so it's not really a rehashing\n     * we just set the first hash table so that it can accept keys. */\n    if (d->ht[0].table == NULL) {\n        d->ht[0] = n;\n        return DICT_OK;\n    }\n\n    /* Prepare a second hash table for incremental rehashing */\n    d->ht[1] = n;\n    d->rehashidx = 0;\n    return DICT_OK;\n}\n\n/* Performs N steps of incremental rehashing. Returns 1 if there are still\n * keys to move from the old to the new hash table, otherwise 0 is returned.\n *\n * Note that a rehashing step consists in moving a bucket (that may have more\n * than one key as we use chaining) from the old to the new hash table, however\n * since part of the hash table may be composed of empty spaces, it is not\n * guaranteed that this function will rehash even a single bucket, since it\n * will visit at max N*10 empty buckets in total, otherwise the amount of\n * work it does would be unbound and the function may block for a long time. */\nint dictRehash(dict *d, int n) {\n    int empty_visits = n*10; /* Max number of empty buckets to visit. */\n    if (!dictIsRehashing(d)) return 0;\n\n    while(n-- && d->ht[0].used != 0) {\n        dictEntry *de, *nextde;\n\n        /* Note that rehashidx can't overflow as we are sure there are more\n         * elements because ht[0].used != 0 */\n        ASSERT(d->ht[0].size > (unsigned long)d->rehashidx);\n        while(d->ht[0].table[d->rehashidx] == NULL) {\n            d->rehashidx++;\n            if (--empty_visits == 0) return 1;\n        }\n        de = d->ht[0].table[d->rehashidx];\n        /* Move all the keys in this bucket from the old to the new hash HT */\n        while(de) {\n            unsigned int h;\n\n            nextde = de->next;\n            /* Get the index in the new hash table */\n            h = dictHashKey(d, de->key) & d->ht[1].sizemask;\n            de->next = d->ht[1].table[h];\n            d->ht[1].table[h] = de;\n            d->ht[0].used--;\n            d->ht[1].used++;\n            de = nextde;\n        }\n        d->ht[0].table[d->rehashidx] = NULL;\n        d->rehashidx++;\n    }\n\n    /* Check if we already rehashed the whole table... */\n    if (d->ht[0].used == 0) {\n        dfree(d->ht[0].table);\n        d->ht[0] = d->ht[1];\n        _dictReset(&d->ht[1]);\n        d->rehashidx = -1;\n        return 0;\n    }\n\n    /* More to rehash... */\n    return 1;\n}\n\nlong long timeInMilliseconds(void) {\n    struct timeval tv;\n\n    gettimeofday(&tv,NULL);\n    return (((long long)tv.tv_sec)*1000)+(tv.tv_usec/1000);\n}\n\n/* Rehash for an amount of time between ms milliseconds and ms+1 milliseconds */\nint dictRehashMilliseconds(dict *d, int ms) {\n    long long start = timeInMilliseconds();\n    int rehashes = 0;\n\n    while(dictRehash(d,100)) {\n        rehashes += 100;\n        if (timeInMilliseconds()-start > ms) break;\n    }\n    return rehashes;\n}\n\n/* This function performs just a step of rehashing, and only if there are\n * no safe iterators bound to our hash table. When we have iterators in the\n * middle of a rehashing we can't mess with the two hash tables otherwise\n * some element can be missed or duplicated.\n *\n * This function is called by common lookup or update operations in the\n * dictionary so that the hash table automatically migrates from H1 to H2\n * while it is actively used. */\nstatic void _dictRehashStep(dict *d) {\n    if (d->iterators == 0) dictRehash(d,1);\n}\n\n/* Add an element to the target hash table */\nint dictAdd(dict *d, void *key, void *val)\n{\n    dictEntry *entry = dictAddRaw(d,key);\n\n    if (!entry) return DICT_ERR;\n    dictSetVal(d, entry, val);\n    return DICT_OK;\n}\n\n/* Low level add. This function adds the entry but instead of setting\n * a value returns the dictEntry structure to the user, that will make\n * sure to fill the value field as he wishes.\n *\n * This function is also directly exposed to the user API to be called\n * mainly in order to store non-pointers inside the hash value, example:\n *\n * entry = dictAddRaw(dict,mykey);\n * if (entry != NULL) dictSetSignedIntegerVal(entry,1000);\n *\n * Return values:\n *\n * If key already exists NULL is returned.\n * If key was added, the hash entry is returned to be manipulated by the caller.\n */\ndictEntry *dictAddRaw(dict *d, void *key)\n{\n    int index;\n    dictEntry *entry;\n    dictht *ht;\n\n    if (dictIsRehashing(d)) _dictRehashStep(d);\n\n    /* Get the index of the new element, or -1 if\n     * the element already exists. */\n    if ((index = _dictKeyIndex(d, key)) == -1)\n        return NULL;\n\n    /* Allocate the memory and store the new entry.\n     * Insert the element in top, with the assumption that in a database\n     * system it is more likely that recently added entries are accessed\n     * more frequently. */\n    ht = dictIsRehashing(d) ? &d->ht[1] : &d->ht[0];\n    entry = dalloc(sizeof(*entry));\n    entry->next = ht->table[index];\n    ht->table[index] = entry;\n    ht->used++;\n\n    /* Set the hash entry fields. */\n    dictSetKey(d, entry, key);\n    return entry;\n}\n\n/* Add an element, discarding the old if the key already exists.\n * Return 1 if the key was added from scratch, 0 if there was already an\n * element with such key and dictReplace() just performed a value update\n * operation. */\nint dictReplace(dict *d, void *key, void *val)\n{\n    dictEntry *entry, auxentry;\n\n    /* Try to add the element. If the key\n     * does not exists dictAdd will suceed. */\n    if (dictAdd(d, key, val) == DICT_OK)\n        return 1;\n    /* It already exists, get the entry */\n    entry = dictFind(d, key);\n    /* Set the new value and free the old one. Note that it is important\n     * to do that in this order, as the value may just be exactly the same\n     * as the previous one. In this context, think to reference counting,\n     * you want to increment (set), and then decrement (free), and not the\n     * reverse. */\n    auxentry = *entry;\n    dictSetVal(d, entry, val);\n    dictFreeVal(d, &auxentry);\n    return 0;\n}\n\n/* dictReplaceRaw() is simply a version of dictAddRaw() that always\n * returns the hash entry of the specified key, even if the key already\n * exists and can't be added (in that case the entry of the already\n * existing key is returned.)\n *\n * See dictAddRaw() for more information. */\ndictEntry *dictReplaceRaw(dict *d, void *key) {\n    dictEntry *entry = dictFind(d,key);\n\n    return entry ? entry : dictAddRaw(d,key);\n}\n\n/* Search and remove an element */\nstatic int dictGenericDelete(dict *d, const void *key, int nofree)\n{\n    unsigned int h, idx;\n    dictEntry *he, *prevHe;\n    int table;\n\n    if (d->ht[0].size == 0) return DICT_ERR; /* d->ht[0].table is NULL */\n    if (dictIsRehashing(d)) _dictRehashStep(d);\n    h = dictHashKey(d, key);\n\n    for (table = 0; table <= 1; table++) {\n        idx = h & d->ht[table].sizemask;\n        he = d->ht[table].table[idx];\n        prevHe = NULL;\n        while(he) {\n            if (key==he->key || dictCompareKeys(d, key, he->key)) {\n                /* Unlink the element from the list */\n                if (prevHe)\n                    prevHe->next = he->next;\n                else\n                    d->ht[table].table[idx] = he->next;\n                if (!nofree) {\n                    dictFreeKey(d, he);\n                    dictFreeVal(d, he);\n                }\n                dfree(he);\n                d->ht[table].used--;\n                return DICT_OK;\n            }\n            prevHe = he;\n            he = he->next;\n        }\n        if (!dictIsRehashing(d)) break;\n    }\n    return DICT_ERR; /* not found */\n}\n\nint dictDelete(dict *ht, const void *key) {\n    return dictGenericDelete(ht,key,0);\n}\n\nint dictDeleteNoFree(dict *ht, const void *key) {\n    return dictGenericDelete(ht,key,1);\n}\n\n/* Destroy an entire dictionary */\nint _dictClear(dict *d, dictht *ht, void(callback)(void *)) {\n    unsigned long i;\n\n    /* Free all the elements */\n    for (i = 0; i < ht->size && ht->used > 0; i++) {\n        dictEntry *he, *nextHe;\n\n        if (callback && (i & 65535) == 0) callback(d->privdata);\n\n        if ((he = ht->table[i]) == NULL) continue;\n        while(he) {\n            nextHe = he->next;\n            dictFreeKey(d, he);\n            dictFreeVal(d, he);\n            dfree(he);\n            ht->used--;\n            he = nextHe;\n        }\n    }\n    /* Free the table and the allocated cache structure */\n    if(ht->table) dfree(ht->table);\n    /* Re-initialize the table */\n    _dictReset(ht);\n    return DICT_OK; /* never fails */\n}\n\n/* Clear & Release the hash table */\nvoid dictRelease(dict *d)\n{\n    _dictClear(d,&d->ht[0],NULL);\n    _dictClear(d,&d->ht[1],NULL);\n    dfree(d);\n}\n\ndictEntry *dictFind(dict *d, const void *key)\n{\n    dictEntry *he;\n    unsigned int h, idx, table;\n\n    if (d->ht[0].used + d->ht[1].used == 0) return NULL; /* dict is empty */\n    //if (dictIsRehashing(d)) _dictRehashStep(d);  /* we removed this line to avoild rehash the table when read this table, because  we used read-write lock */\n    h = dictHashKey(d, key);\n    for (table = 0; table <= 1; table++) {\n        idx = h & d->ht[table].sizemask;\n        he = d->ht[table].table[idx];\n        while(he) {\n            if (key==he->key || dictCompareKeys(d, key, he->key))\n                return he;\n            he = he->next;\n        }\n        if (!dictIsRehashing(d)) return NULL;\n    }\n    return NULL;\n}\n\nvoid *dictFetchValue(dict *d, const void *key) {\n    dictEntry *he;\n\n    he = dictFind(d,key);\n    return he ? dictGetVal(he) : NULL;\n}\n\n/* A fingerprint is a 64 bit number that represents the state of the dictionary\n * at a given time, it's just a few dict properties xored together.\n * When an unsafe iterator is initialized, we get the dict fingerprint, and check\n * the fingerprint again when the iterator is released.\n * If the two fingerprints are different it means that the user of the iterator\n * performed forbidden operations against the dictionary while iterating. */\nlong long dictFingerprint(dict *d) {\n    long long integers[6], hash = 0;\n    int j;\n\n    integers[0] = (long) d->ht[0].table;\n    integers[1] = d->ht[0].size;\n    integers[2] = d->ht[0].used;\n    integers[3] = (long) d->ht[1].table;\n    integers[4] = d->ht[1].size;\n    integers[5] = d->ht[1].used;\n\n    /* We hash N integers by summing every successive integer with the integer\n     * hashing of the previous sum. Basically:\n     *\n     * Result = hash(hash(hash(int1)+int2)+int3) ...\n     *\n     * This way the same set of integers in a different order will (likely) hash\n     * to a different number. */\n    for (j = 0; j < 6; j++) {\n        hash += integers[j];\n        /* For the hashing step we use Tomas Wang's 64 bit integer hash. */\n        hash = (~hash) + (hash << 21); // hash = (hash << 21) - hash - 1;\n        hash = hash ^ (hash >> 24);\n        hash = (hash + (hash << 3)) + (hash << 8); // hash * 265\n        hash = hash ^ (hash >> 14);\n        hash = (hash + (hash << 2)) + (hash << 4); // hash * 21\n        hash = hash ^ (hash >> 28);\n        hash = hash + (hash << 31);\n    }\n    return hash;\n}\n\ndictIterator *dictGetIterator(dict *d)\n{\n    dictIterator *iter = dalloc(sizeof(*iter));\n\n    iter->d = d;\n    iter->table = 0;\n    iter->index = -1;\n    iter->safe = 0;\n    iter->entry = NULL;\n    iter->nextEntry = NULL;\n    return iter;\n}\n\ndictIterator *dictGetSafeIterator(dict *d) {\n    dictIterator *i = dictGetIterator(d);\n\n    i->safe = 1;\n    return i;\n}\n\ndictEntry *dictNext(dictIterator *iter)\n{\n    while (1) {\n        if (iter->entry == NULL) {\n            dictht *ht = &iter->d->ht[iter->table];\n            if (iter->index == -1 && iter->table == 0) {\n                if (iter->safe)\n                    iter->d->iterators++;\n                else\n                    iter->fingerprint = dictFingerprint(iter->d);\n            }\n            iter->index++;\n            if (iter->index >= (long) ht->size) {\n                if (dictIsRehashing(iter->d) && iter->table == 0) {\n                    iter->table++;\n                    iter->index = 0;\n                    ht = &iter->d->ht[1];\n                } else {\n                    break;\n                }\n            }\n            iter->entry = ht->table[iter->index];\n        } else {\n            iter->entry = iter->nextEntry;\n        }\n        if (iter->entry) {\n            /* We need to save the 'next' here, the iterator user\n             * may delete the entry we are returning. */\n            iter->nextEntry = iter->entry->next;\n            return iter->entry;\n        }\n    }\n    return NULL;\n}\n\nvoid dictReleaseIterator(dictIterator *iter)\n{\n    if (!(iter->index == -1 && iter->table == 0)) {\n        if (iter->safe) {\n            iter->d->iterators--;\n        } else {\n            long long hv = dictFingerprint(iter->d);\n            ASSERT(iter->fingerprint == hv);\n        }\n    }\n    dfree(iter);\n}\n\n/* Return a random entry from the hash table. Useful to\n * implement randomized algorithms */\ndictEntry *dictGetRandomKey(dict *d)\n{\n    dictEntry *he, *orighe;\n    unsigned int h;\n    int listlen, listele;\n\n    if (dictSize(d) == 0) return NULL;\n    if (dictIsRehashing(d)) _dictRehashStep(d);\n    if (dictIsRehashing(d)) {\n        do {\n            /* We are sure there are no elements in indexes from 0\n             * to rehashidx-1 */\n            h = d->rehashidx + (random() % (d->ht[0].size +\n                                            d->ht[1].size -\n                                            d->rehashidx));\n            he = (h >= d->ht[0].size) ? d->ht[1].table[h - d->ht[0].size] :\n                                      d->ht[0].table[h];\n        } while(he == NULL);\n    } else {\n        do {\n            h = random() & d->ht[0].sizemask;\n            he = d->ht[0].table[h];\n        } while(he == NULL);\n    }\n\n    /* Now we found a non empty bucket, but it is a linked\n     * list and we need to get a random element from the list.\n     * The only sane way to do so is counting the elements and\n     * select a random index. */\n    listlen = 0;\n    orighe = he;\n    while(he) {\n        he = he->next;\n        listlen++;\n    }\n    listele = random() % listlen;\n    he = orighe;\n    while(listele--) he = he->next;\n    return he;\n}\n\n/* This function samples the dictionary to return a few keys from random\n * locations.\n *\n * It does not guarantee to return all the keys specified in 'count', nor\n * it does guarantee to return non-duplicated elements, however it will make\n * some effort to do both things.\n *\n * Returned pointers to hash table entries are stored into 'des' that\n * points to an array of dictEntry pointers. The array must have room for\n * at least 'count' elements, that is the argument we pass to the function\n * to tell how many random elements we need.\n *\n * The function returns the number of items stored into 'des', that may\n * be less than 'count' if the hash table has less than 'count' elements\n * inside, or if not enough elements were found in a reasonable amount of\n * steps.\n *\n * Note that this function is not suitable when you need a good distribution\n * of the returned items, but only when you need to \"sample\" a given number\n * of continuous elements to run some kind of algorithm or to produce\n * statistics. However the function is much faster than dictGetRandomKey()\n * at producing N elements. */\nunsigned int dictGetSomeKeys(dict *d, dictEntry **des, unsigned int count) {\n    unsigned long j; /* internal hash table id, 0 or 1. */\n    unsigned long tables; /* 1 or 2 tables? */\n    unsigned long stored = 0, maxsizemask;\n    unsigned long maxsteps;\n\n    if (dictSize(d) < count) count = dictSize(d);\n    maxsteps = count*10;\n\n    /* Try to do a rehashing work proportional to 'count'. */\n    for (j = 0; j < count; j++) {\n        if (dictIsRehashing(d))\n            _dictRehashStep(d);\n        else\n            break;\n    }\n\n    tables = dictIsRehashing(d) ? 2 : 1;\n    maxsizemask = d->ht[0].sizemask;\n    if (tables > 1 && maxsizemask < d->ht[1].sizemask)\n        maxsizemask = d->ht[1].sizemask;\n\n    /* Pick a random point inside the larger table. */\n    unsigned long i = random() & maxsizemask;\n    unsigned long emptylen = 0; /* Continuous empty entries so far. */\n    while(stored < count && maxsteps--) {\n        for (j = 0; j < tables; j++) {\n            /* Invariant of the dict.c rehashing: up to the indexes already\n             * visited in ht[0] during the rehashing, there are no populated\n             * buckets, so we can skip ht[0] for indexes between 0 and idx-1. */\n            if (tables == 2 && j == 0 && i < (unsigned long) d->rehashidx) {\n                /* Moreover, if we are currently out of range in the second\n                 * table, there will be no elements in both tables up to\n                 * the current rehashing index, so we jump if possible.\n                 * (this happens when going from big to small table). */\n                if (i >= d->ht[1].size) i = d->rehashidx;\n                continue;\n            }\n            if (i >= d->ht[j].size) continue; /* Out of range for this table. */\n            dictEntry *he = d->ht[j].table[i];\n\n            /* Count contiguous empty buckets, and jump to other\n             * locations if they reach 'count' (with a minimum of 5). */\n            if (he == NULL) {\n                emptylen++;\n                if (emptylen >= 5 && emptylen > count) {\n                    i = random() & maxsizemask;\n                    emptylen = 0;\n                }\n            } else {\n                emptylen = 0;\n                while (he) {\n                    /* Collect all the elements of the buckets found non\n                     * empty while iterating. */\n                    *des = he;\n                    des++;\n                    he = he->next;\n                    stored++;\n                    if (stored == count) return stored;\n                }\n            }\n        }\n        i = (i+1) & maxsizemask;\n    }\n    return stored;\n}\n\n/* Function to reverse bits. Algorithm from:\n * http://graphics.stanford.edu/~seander/bithacks.html#ReverseParallel */\nstatic unsigned long rev(unsigned long v) {\n    unsigned long s = 8 * sizeof(v); // bit size; must be power of 2\n    unsigned long mask = ~0;\n    while ((s >>= 1) > 0) {\n        mask ^= (mask << s);\n        v = ((v >> s) & mask) | ((v << s) & ~mask);\n    }\n    return v;\n}\n\n/* dictScan() is used to iterate over the elements of a dictionary.\n *\n * Iterating works the following way:\n *\n * 1) Initially you call the function using a cursor (v) value of 0.\n * 2) The function performs one step of the iteration, and returns the\n *    new cursor value you must use in the next call.\n * 3) When the returned cursor is 0, the iteration is complete.\n *\n * The function guarantees all elements present in the\n * dictionary get returned between the start and end of the iteration.\n * However it is possible some elements get returned multiple times.\n *\n * For every element returned, the callback argument 'fn' is\n * called with 'privdata' as first argument and the dictionary entry\n * 'de' as second argument.\n *\n * HOW IT WORKS.\n *\n * The iteration algorithm was designed by Pieter Noordhuis.\n * The main idea is to increment a cursor starting from the higher order\n * bits. That is, instead of incrementing the cursor normally, the bits\n * of the cursor are reversed, then the cursor is incremented, and finally\n * the bits are reversed again.\n *\n * This strategy is needed because the hash table may be resized between\n * iteration calls.\n *\n * dict.c hash tables are always power of two in size, and they\n * use chaining, so the position of an element in a given table is given\n * by computing the bitwise AND between Hash(key) and SIZE-1\n * (where SIZE-1 is always the mask that is equivalent to taking the rest\n *  of the division between the Hash of the key and SIZE).\n *\n * For example if the current hash table size is 16, the mask is\n * (in binary) 1111. The position of a key in the hash table will always be\n * the last four bits of the hash output, and so forth.\n *\n * WHAT HAPPENS IF THE TABLE CHANGES IN SIZE?\n *\n * If the hash table grows, elements can go anywhere in one multiple of\n * the old bucket: for example let's say we already iterated with\n * a 4 bit cursor 1100 (the mask is 1111 because hash table size = 16).\n *\n * If the hash table will be resized to 64 elements, then the new mask will\n * be 111111. The new buckets you obtain by substituting in ??1100\n * with either 0 or 1 can be targeted only by keys we already visited\n * when scanning the bucket 1100 in the smaller hash table.\n *\n * By iterating the higher bits first, because of the inverted counter, the\n * cursor does not need to restart if the table size gets bigger. It will\n * continue iterating using cursors without '1100' at the end, and also\n * without any other combination of the final 4 bits already explored.\n *\n * Similarly when the table size shrinks over time, for example going from\n * 16 to 8, if a combination of the lower three bits (the mask for size 8\n * is 111) were already completely explored, it would not be visited again\n * because we are sure we tried, for example, both 0111 and 1111 (all the\n * variations of the higher bit) so we don't need to test it again.\n *\n * WAIT... YOU HAVE *TWO* TABLES DURING REHASHING!\n *\n * Yes, this is true, but we always iterate the smaller table first, then\n * we test all the expansions of the current cursor into the larger\n * table. For example if the current cursor is 101 and we also have a\n * larger table of size 16, we also test (0)101 and (1)101 inside the larger\n * table. This reduces the problem back to having only one table, where\n * the larger one, if it exists, is just an expansion of the smaller one.\n *\n * LIMITATIONS\n *\n * This iterator is completely stateless, and this is a huge advantage,\n * including no additional memory used.\n *\n * The disadvantages resulting from this design are:\n *\n * 1) It is possible we return elements more than once. However this is usually\n *    easy to deal with in the application level.\n * 2) The iterator must return multiple elements per call, as it needs to always\n *    return all the keys chained in a given bucket, and all the expansions, so\n *    we are sure we don't miss keys moving during rehashing.\n * 3) The reverse cursor is somewhat hard to understand at first, but this\n *    comment is supposed to help.\n */\nunsigned long dictScan(dict *d,\n                       unsigned long v,\n                       dictScanFunction *fn,\n                       void *privdata)\n{\n    dictht *t0, *t1;\n    const dictEntry *de;\n    unsigned long m0, m1;\n\n    if (dictSize(d) == 0) return 0;\n\n    if (!dictIsRehashing(d)) {\n        t0 = &(d->ht[0]);\n        m0 = t0->sizemask;\n\n        /* Emit entries at cursor */\n        de = t0->table[v & m0];\n        while (de) {\n            fn(privdata, de);\n            de = de->next;\n        }\n\n    } else {\n        t0 = &d->ht[0];\n        t1 = &d->ht[1];\n\n        /* Make sure t0 is the smaller and t1 is the bigger table */\n        if (t0->size > t1->size) {\n            t0 = &d->ht[1];\n            t1 = &d->ht[0];\n        }\n\n        m0 = t0->sizemask;\n        m1 = t1->sizemask;\n\n        /* Emit entries at cursor */\n        de = t0->table[v & m0];\n        while (de) {\n            fn(privdata, de);\n            de = de->next;\n        }\n\n        /* Iterate over indices in larger table that are the expansion\n         * of the index pointed to by the cursor in the smaller table */\n        do {\n            /* Emit entries at cursor */\n            de = t1->table[v & m1];\n            while (de) {\n                fn(privdata, de);\n                de = de->next;\n            }\n\n            /* Increment bits not covered by the smaller mask */\n            v = (((v | m0) + 1) & ~m0) | (v & m0);\n\n            /* Continue while bits covered by mask difference is non-zero */\n        } while (v & (m0 ^ m1));\n    }\n\n    /* Set unmasked bits so incrementing the reversed cursor\n     * operates on the masked bits of the smaller table */\n    v |= ~m0;\n\n    /* Increment the reverse cursor */\n    v = rev(v);\n    v++;\n    v = rev(v);\n\n    return v;\n}\n\n/* ------------------------- private functions ------------------------------ */\n\n/* Expand the hash table if needed */\nstatic int _dictExpandIfNeeded(dict *d)\n{\n    /* Incremental rehashing already in progress. Return. */\n    if (dictIsRehashing(d)) return DICT_OK;\n\n    /* If the hash table is empty expand it to the initial size. */\n    if (d->ht[0].size == 0) return dictExpand(d, DICT_HT_INITIAL_SIZE);\n\n    /* If we reached the 1:1 ratio, and we are allowed to resize the hash\n     * table (global setting) or we should avoid it but the ratio between\n     * elements/buckets is over the \"safe\" threshold, we resize doubling\n     * the number of buckets. */\n    if (d->ht[0].used >= d->ht[0].size &&\n        (dict_can_resize ||\n         d->ht[0].used/d->ht[0].size > dict_force_resize_ratio))\n    {\n        return dictExpand(d, d->ht[0].used*2);\n    }\n    return DICT_OK;\n}\n\n/* Our hash table capability is a power of two */\nstatic unsigned long _dictNextPower(unsigned long size)\n{\n    unsigned long i = DICT_HT_INITIAL_SIZE;\n\n    if (size >= LONG_MAX) return LONG_MAX;\n    while(1) {\n        if (i >= size)\n            return i;\n        i *= 2;\n    }\n}\n\n/* Returns the index of a free slot that can be populated with\n * a hash entry for the given 'key'.\n * If the key already exists, -1 is returned.\n *\n * Note that if we are in the process of rehashing the hash table, the\n * index is always returned in the context of the second (new) hash table. */\nstatic int _dictKeyIndex(dict *d, const void *key)\n{\n    unsigned int h, idx, table;\n    dictEntry *he;\n\n    /* Expand the hash table if needed */\n    if (_dictExpandIfNeeded(d) == DICT_ERR)\n        return -1;\n    /* Compute the key hash value */\n    h = dictHashKey(d, key);\n    for (table = 0; table <= 1; table++) {\n        idx = h & d->ht[table].sizemask;\n        /* Search if this slot does not already contain the given key */\n        he = d->ht[table].table[idx];\n        while(he) {\n            if (key==he->key || dictCompareKeys(d, key, he->key))\n                return -1;\n            he = he->next;\n        }\n        if (!dictIsRehashing(d)) break;\n    }\n    return idx;\n}\n\nvoid dictEmpty(dict *d, void(callback)(void*)) {\n    _dictClear(d,&d->ht[0],callback);\n    _dictClear(d,&d->ht[1],callback);\n    d->rehashidx = -1;\n    d->iterators = 0;\n}\n\nvoid dictEnableResize(void) {\n    dict_can_resize = 1;\n}\n\nvoid dictDisableResize(void) {\n    dict_can_resize = 0;\n}\n\n/* ------------------------------- Debugging ---------------------------------*/\n\n#define DICT_STATS_VECTLEN 50\nsize_t _dictGetStatsHt(char *buf, size_t bufsize, dictht *ht, int tableid) {\n    unsigned long i, slots = 0, chainlen, maxchainlen = 0;\n    unsigned long totchainlen = 0;\n    unsigned long clvector[DICT_STATS_VECTLEN];\n    size_t l = 0;\n\n    if (ht->used == 0) {\n        return snprintf(buf,bufsize,\n            \"No stats available for empty dictionaries\\n\");\n    }\n\n    /* Compute stats. */\n    for (i = 0; i < DICT_STATS_VECTLEN; i++) clvector[i] = 0;\n    for (i = 0; i < ht->size; i++) {\n        dictEntry *he;\n\n        if (ht->table[i] == NULL) {\n            clvector[0]++;\n            continue;\n        }\n        slots++;\n        /* For each hash entry on this slot... */\n        chainlen = 0;\n        he = ht->table[i];\n        while(he) {\n            chainlen++;\n            he = he->next;\n        }\n        clvector[(chainlen < DICT_STATS_VECTLEN) ? chainlen : (DICT_STATS_VECTLEN-1)]++;\n        if (chainlen > maxchainlen) maxchainlen = chainlen;\n        totchainlen += chainlen;\n    }\n\n    /* Generate human readable stats. */\n    l += snprintf(buf+l,bufsize-l,\n        \"Hash table %d stats (%s):\\n\"\n        \" table size: %ld\\n\"\n        \" number of elements: %ld\\n\"\n        \" different slots: %ld\\n\"\n        \" max chain length: %ld\\n\"\n        \" avg chain length (counted): %.02f\\n\"\n        \" avg chain length (computed): %.02f\\n\"\n        \" Chain length distribution:\\n\",\n        tableid, (tableid == 0) ? \"main hash table\" : \"rehashing target\",\n        ht->size, ht->used, slots, maxchainlen,\n        (float)totchainlen/slots, (float)ht->used/slots);\n\n    for (i = 0; i < DICT_STATS_VECTLEN-1; i++) {\n        if (clvector[i] == 0) continue;\n        if (l >= bufsize) break;\n        l += snprintf(buf+l,bufsize-l,\n            \"   %s%ld: %ld (%.02f%%)\\n\",\n            (i == DICT_STATS_VECTLEN-1)?\">= \":\"\",\n            i, clvector[i], ((float)clvector[i]/ht->size)*100);\n    }\n\n    /* Unlike snprintf(), teturn the number of characters actually written. */\n    if (bufsize) buf[bufsize-1] = '\\0';\n    return strlen(buf);\n}\n\nvoid dictGetStats(char *buf, size_t bufsize, dict *d) {\n    size_t l;\n    char *orig_buf = buf;\n    size_t orig_bufsize = bufsize;\n\n    l = _dictGetStatsHt(buf,bufsize,&d->ht[0],0);\n    buf += l;\n    bufsize -= l;\n    if (dictIsRehashing(d) && bufsize > 0) {\n        _dictGetStatsHt(buf,bufsize,&d->ht[1],1);\n    }\n    /* Make sure there is a NULL term at the end. */\n    if (orig_bufsize) orig_buf[orig_bufsize-1] = '\\0';\n}\n"
  },
  {
    "path": "src/vr_dict.h",
    "content": "#ifndef _VR_DICT_H_\n#define _VR_DICT_H_\n\n#include <stdint.h>\n\n#define DICT_OK 0\n#define DICT_ERR 1\n\n/* Unused arguments generate annoying warnings... */\n#define DICT_NOTUSED(V) ((void) V)\n\ntypedef struct dictEntry {\n    void *key;\n    union {\n        void *val;\n        uint64_t u64;\n        int64_t s64;\n        double d;\n    } v;\n    struct dictEntry *next;\n} dictEntry;\n\ntypedef struct dictType {\n    unsigned int (*hashFunction)(const void *key);\n    void *(*keyDup)(void *privdata, const void *key);\n    void *(*valDup)(void *privdata, const void *obj);\n    int (*keyCompare)(void *privdata, const void *key1, const void *key2);\n    void (*keyDestructor)(void *privdata, void *key);\n    void (*valDestructor)(void *privdata, void *obj);\n} dictType;\n\n/* This is our hash table structure. Every dictionary has two of this as we\n * implement incremental rehashing, for the old to the new table. */\ntypedef struct dictht {\n    dictEntry **table;\n    unsigned long size;\n    unsigned long sizemask;\n    unsigned long used;\n} dictht;\n\ntypedef struct dict {\n    dictType *type;\n    void *privdata;\n    dictht ht[2];\n    long rehashidx; /* rehashing not in progress if rehashidx == -1 */\n    int iterators; /* number of iterators currently running */\n} dict;\n\n/* If safe is set to 1 this is a safe iterator, that means, you can call\n * dictAdd, dictFind, and other functions against the dictionary even while\n * iterating. Otherwise it is a non safe iterator, and only dictNext()\n * should be called while iterating. */\ntypedef struct dictIterator {\n    dict *d;\n    long index;\n    int table, safe;\n    dictEntry *entry, *nextEntry;\n    /* unsafe iterator fingerprint for misuse detection. */\n    long long fingerprint;\n} dictIterator;\n\ntypedef void (dictScanFunction)(void *privdata, const dictEntry *de);\n\n/* This is the initial size of every hash table */\n#define DICT_HT_INITIAL_SIZE     4\n\n/* ------------------------------- Macros ------------------------------------*/\n#define dictFreeVal(d, entry) \\\n    if ((d)->type->valDestructor) \\\n        (d)->type->valDestructor((d)->privdata, (entry)->v.val)\n\n#define dictSetVal(d, entry, _val_) do { \\\n    if ((d)->type->valDup) \\\n        entry->v.val = (d)->type->valDup((d)->privdata, _val_); \\\n    else \\\n        entry->v.val = (_val_); \\\n} while(0)\n\n#define dictSetSignedIntegerVal(entry, _val_) \\\n    do { entry->v.s64 = _val_; } while(0)\n\n#define dictSetUnsignedIntegerVal(entry, _val_) \\\n    do { entry->v.u64 = _val_; } while(0)\n\n#define dictSetDoubleVal(entry, _val_) \\\n    do { entry->v.d = _val_; } while(0)\n\n#define dictFreeKey(d, entry) \\\n    if ((d)->type->keyDestructor) \\\n        (d)->type->keyDestructor((d)->privdata, (entry)->key)\n\n#define dictSetKey(d, entry, _key_) do { \\\n    if ((d)->type->keyDup) \\\n        entry->key = (d)->type->keyDup((d)->privdata, _key_); \\\n    else \\\n        entry->key = (_key_); \\\n} while(0)\n\n#define dictCompareKeys(d, key1, key2) \\\n    (((d)->type->keyCompare) ? \\\n        (d)->type->keyCompare((d)->privdata, key1, key2) : \\\n        (key1) == (key2))\n\n#define dictHashKey(d, key) (d)->type->hashFunction(key)\n#define dictGetKey(he) ((he)->key)\n#define dictGetVal(he) ((he)->v.val)\n#define dictGetSignedIntegerVal(he) ((he)->v.s64)\n#define dictGetUnsignedIntegerVal(he) ((he)->v.u64)\n#define dictGetDoubleVal(he) ((he)->v.d)\n#define dictSlots(d) ((d)->ht[0].size+(d)->ht[1].size)\n#define dictSize(d) ((d)->ht[0].used+(d)->ht[1].used)\n#define dictIsRehashing(d) ((d)->rehashidx != -1)\n\n/* API */\ndict *dictCreate(dictType *type, void *privDataPtr);\nint dictExpand(dict *d, unsigned long size);\nint dictAdd(dict *d, void *key, void *val);\ndictEntry *dictAddRaw(dict *d, void *key);\nint dictReplace(dict *d, void *key, void *val);\ndictEntry *dictReplaceRaw(dict *d, void *key);\nint dictDelete(dict *d, const void *key);\nint dictDeleteNoFree(dict *d, const void *key);\nvoid dictRelease(dict *d);\ndictEntry * dictFind(dict *d, const void *key);\nvoid *dictFetchValue(dict *d, const void *key);\nint dictResize(dict *d);\ndictIterator *dictGetIterator(dict *d);\ndictIterator *dictGetSafeIterator(dict *d);\ndictEntry *dictNext(dictIterator *iter);\nvoid dictReleaseIterator(dictIterator *iter);\ndictEntry *dictGetRandomKey(dict *d);\nunsigned int dictGetSomeKeys(dict *d, dictEntry **des, unsigned int count);\nvoid dictGetStats(char *buf, size_t bufsize, dict *d);\nunsigned int dictGenHashFunction(const void *key, int len);\nunsigned int dictGenCaseHashFunction(const unsigned char *buf, int len);\nvoid dictEmpty(dict *d, void(callback)(void*));\nvoid dictEnableResize(void);\nvoid dictDisableResize(void);\nint dictRehash(dict *d, int n);\nint dictRehashMilliseconds(dict *d, int ms);\nvoid dictSetHashFunctionSeed(unsigned int initval);\nunsigned int dictGetHashFunctionSeed(void);\nunsigned long dictScan(dict *d, unsigned long v, dictScanFunction *fn, void *privdata);\n\n/* Hash table types */\nextern dictType dictTypeHeapStringCopyKey;\nextern dictType dictTypeHeapStrings;\nextern dictType dictTypeHeapStringCopyKeyValue;\n\n#endif /* _VR_DICT_H_ */\n"
  },
  {
    "path": "src/vr_eventloop.c",
    "content": "#include <vr_core.h>\n\nint\nvr_eventloop_init(vr_eventloop *vel, int filelimit)\n{    \n    rstatus_t status;\n    int maxclients, threads_num;\n\n    if (vel == NULL || filelimit <= 0) {\n        return VR_ERROR;\n    }\n\n    vr_thread_init(&vel->thread);\n    vel->el = NULL;\n    vel->hz = 10;\n    vel->cronloops = 0;\n    vel->unixtime = time(NULL);\n    vel->mstime = vr_msec_now();\n    vel->lruclock = getLRUClock();\n    vel->cb = NULL;\n    vel->next_client_id = 1;    /* Client IDs, start from 1 .*/\n    vel->current_client = NULL;\n    vel->clients = NULL;\n    vel->clients_pending_write = NULL;\n    vel->clients_to_close = NULL;\n    vel->clients_paused = 0;\n    vel->clients_pause_end_time = 0;\n    vel->stats = NULL;\n    vel->resident_set_size = 0;\n    vel->dirty = 0;\n    vel->bpop_blocked_clients = 0;\n    vel->unblocked_clients = NULL;\n    vel->clients_waiting_acks = NULL;\n    vel->pubsub_channels = NULL;\n    vel->pubsub_patterns = NULL;\n    vel->notify_keyspace_events = 0;\n    vel->cstable = NULL;\n\n    vel->el = aeCreateEventLoop(filelimit);\n    if (vel->el == NULL) {\n        log_error(\"create eventloop failed.\");\n        return VR_ERROR;\n    }\n\n    vel->cb = dalloc(sizeof(conn_base));\n    if (vel->cb == NULL) {\n        log_error(\"create conn_base failed: out of memory\");\n        return VR_ENOMEM;\n    }\n    status = conn_init(vel->cb);\n    if (status != VR_OK) {\n        log_error(\"init conn_base failed\");\n        return VR_ERROR;\n    }\n\n    vel->clients = dlistCreate();\n    if (vel->clients == NULL) {\n        log_error(\"create list failed: out of memory\");\n        return VR_ENOMEM;\n    }\n\n    vel->clients_pending_write = dlistCreate();\n    if (vel->clients_pending_write == NULL) {\n        log_error(\"create list failed: out of memory\");\n        return VR_ENOMEM;\n    }\n\n    vel->clients_to_close = dlistCreate();\n    if (vel->clients_to_close == NULL) {\n        log_error(\"create list failed: out of memory\");\n        return VR_ENOMEM;\n    }\n\n    vel->unblocked_clients = dlistCreate();\n    if (vel->unblocked_clients == NULL) {\n        log_error(\"create list failed: out of memory\");\n        return VR_ENOMEM;\n    }\n\n    vel->stats = dalloc(sizeof(vr_stats));\n    if (vel->stats == NULL) {\n        log_error(\"out of memory\");\n        return VR_ENOMEM;\n    }\n\n    vr_stats_init(vel->stats);\n\n    conf_cache_init(&vel->cc);\n\n    return VR_OK;\n}\n\nvoid\nvr_eventloop_deinit(vr_eventloop *vel)\n{\n    if (vel == NULL) {\n        return;\n    }\n\n    vr_thread_deinit(&vel->thread);\n\n    if (vel->el != NULL) {\n        aeDeleteEventLoop(vel->el);\n        vel->el = NULL;\n    }\n\n    if (vel->clients != NULL) {\n        client *c;\n        while (c = dlistPop(vel->clients)) {\n            freeClient(c);\n        }\n        dlistRelease(vel->clients);\n        vel->clients = NULL;\n    }\n\n    if (vel->clients_pending_write != NULL) {\n        client *c;\n        while (c = dlistPop(vel->clients_pending_write)) {}\n        dlistRelease(vel->clients_pending_write);\n        vel->clients_pending_write = NULL;\n    }\n\n    if (vel->clients_to_close != NULL) {\n        client *c;\n        while (c = dlistPop(vel->clients_to_close)) {\n            freeClient(c);\n        }\n        dlistRelease(vel->clients_to_close);\n        vel->clients_to_close = NULL;\n    }\n\n    if (vel->unblocked_clients != NULL) {\n        client *c;\n        while (c = dlistPop(vel->unblocked_clients)) {}\n        dlistRelease(vel->unblocked_clients);\n        vel->unblocked_clients = NULL;\n    }\n\n    if (vel->cb != NULL) {\n        conn_deinit(vel->cb);\n        dfree(vel->cb);\n        vel->cb = NULL;\n    }\n\n    if (vel->stats != NULL) {\n        vr_stats_deinit(vel->stats);\n        dfree(vel->stats);\n        vel->stats = NULL;\n    }\n\n    if (vel->cstable != NULL) {\n        commandStatsTableDestroy(vel->cstable);\n        vel->cstable = NULL;\n    }\n\n    conf_cache_deinit(&vel->cc);\n}\n\n"
  },
  {
    "path": "src/vr_eventloop.h",
    "content": "#ifndef _VR_EVENTLOOP_H_\n#define _VR_EVENTLOOP_H_\n\ntypedef struct vr_eventloop {\n    vr_thread thread;\n\n    aeEventLoop *el;\n    int hz;                     /* cron() calls frequency in hertz */\n    int cronloops;              /* Number of times the cron function run */\n    \n    /* time cache */\n    time_t unixtime;            /* Unix time sampled every cron cycle. */\n    long long mstime;           /* Like 'unixtime' but with milliseconds resolution. */\n\n    unsigned lruclock:LRU_BITS; /* Clock for LRU eviction */\n\n    conn_base *cb;\n\n    uint64_t next_client_id;    /* Next client unique ID. Incremental. */\n    struct client *current_client;     /* Current client, only used on crash report */\n    dlist *clients;              /* List of active clients */\n    dlist *clients_pending_write;/* There is to write or install handler. */\n    dlist *clients_to_close;     /* Clients to close asynchronously */\n\n    int clients_paused;         /* True if clients are currently paused */\n    long long clients_pause_end_time; /* Time when we undo clients_paused */\n\n    vr_stats *stats;            /* stats for this thread */\n    size_t resident_set_size;   /* RSS sampled in workerCron(). */\n\n    long long dirty;            /* Changes to DB from the last save */\n\n    /* Blocked clients */\n    unsigned int bpop_blocked_clients; /* Number of clients blocked by lists */\n    dlist *unblocked_clients;        /* list of clients to unblock before next loop */\n\n    /* Synchronous replication. */\n    dlist *clients_waiting_acks;     /* Clients waiting in WAIT command. */\n\n    /* Pubsub */\n    dict *pubsub_channels;  /* Map channels to list of subscribed clients */\n    dlist *pubsub_patterns;  /* A list of pubsub_patterns */\n    int notify_keyspace_events; /* Events to propagate via Pub/Sub. This is an\n                                   xor of NOTIFY_... flags. */\n\n    conf_cache cc; /* Cache the hot config option to improve vire speed. */\n\n    struct darray *cstable; /* type: commandStats */\n}vr_eventloop;\n\nint vr_eventloop_init(vr_eventloop *vel, int filelimit);\nvoid vr_eventloop_deinit(vr_eventloop *vel);\n\n#endif\n"
  },
  {
    "path": "src/vr_hyperloglog.c",
    "content": "#include <vr_core.h>\n\n#include <stdint.h>\n#include <math.h>\n\n/* The Redis HyperLogLog implementation is based on the following ideas:\n *\n * * The use of a 64 bit hash function as proposed in [1], in order to don't\n *   limited to cardinalities up to 10^9, at the cost of just 1 additional\n *   bit per register.\n * * The use of 16384 6-bit registers for a great level of accuracy, using\n *   a total of 12k per key.\n * * The use of the Redis string data type. No new type is introduced.\n * * No attempt is made to compress the data structure as in [1]. Also the\n *   algorithm used is the original HyperLogLog Algorithm as in [2], with\n *   the only difference that a 64 bit hash function is used, so no correction\n *   is performed for values near 2^32 as in [1].\n *\n * [1] Heule, Nunkesser, Hall: HyperLogLog in Practice: Algorithmic\n *     Engineering of a State of The Art Cardinality Estimation Algorithm.\n *\n * [2] P. Flajolet, Éric Fusy, O. Gandouet, and F. Meunier. Hyperloglog: The\n *     analysis of a near-optimal cardinality estimation algorithm.\n *\n * Redis uses two representations:\n *\n * 1) A \"dense\" representation where every entry is represented by\n *    a 6-bit integer.\n * 2) A \"sparse\" representation using run length compression suitable\n *    for representing HyperLogLogs with many registers set to 0 in\n *    a memory efficient way.\n *\n *\n * HLL header\n * ===\n *\n * Both the dense and sparse representation have a 16 byte header as follows:\n *\n * +------+---+-----+----------+\n * | HYLL | E | N/U | Cardin.  |\n * +------+---+-----+----------+\n *\n * The first 4 bytes are a magic string set to the bytes \"HYLL\".\n * \"E\" is one byte encoding, currently set to HLL_DENSE or\n * HLL_SPARSE. N/U are three not used bytes.\n *\n * The \"Cardin.\" field is a 64 bit integer stored in little endian format\n * with the latest cardinality computed that can be reused if the data\n * structure was not modified since the last computation (this is useful\n * because there are high probabilities that HLLADD operations don't\n * modify the actual data structure and hence the approximated cardinality).\n *\n * When the most significant bit in the most significant byte of the cached\n * cardinality is set, it means that the data structure was modified and\n * we can't reuse the cached value that must be recomputed.\n *\n * Dense representation\n * ===\n *\n * The dense representation used by Redis is the following:\n *\n * +--------+--------+--------+------//      //--+\n * |11000000|22221111|33333322|55444444 ....     |\n * +--------+--------+--------+------//      //--+\n *\n * The 6 bits counters are encoded one after the other starting from the\n * LSB to the MSB, and using the next bytes as needed.\n *\n * Sparse representation\n * ===\n *\n * The sparse representation encodes registers using a run length\n * encoding composed of three opcodes, two using one byte, and one using\n * of two bytes. The opcodes are called ZERO, XZERO and VAL.\n *\n * ZERO opcode is represented as 00xxxxxx. The 6-bit integer represented\n * by the six bits 'xxxxxx', plus 1, means that there are N registers set\n * to 0. This opcode can represent from 1 to 64 contiguous registers set\n * to the value of 0.\n *\n * XZERO opcode is represented by two bytes 01xxxxxx yyyyyyyy. The 14-bit\n * integer represented by the bits 'xxxxxx' as most significant bits and\n * 'yyyyyyyy' as least significant bits, plus 1, means that there are N\n * registers set to 0. This opcode can represent from 0 to 16384 contiguous\n * registers set to the value of 0.\n *\n * VAL opcode is represented as 1vvvvvxx. It contains a 5-bit integer\n * representing the value of a register, and a 2-bit integer representing\n * the number of contiguous registers set to that value 'vvvvv'.\n * To obtain the value and run length, the integers vvvvv and xx must be\n * incremented by one. This opcode can represent values from 1 to 32,\n * repeated from 1 to 4 times.\n *\n * The sparse representation can't represent registers with a value greater\n * than 32, however it is very unlikely that we find such a register in an\n * HLL with a cardinality where the sparse representation is still more\n * memory efficient than the dense representation. When this happens the\n * HLL is converted to the dense representation.\n *\n * The sparse representation is purely positional. For example a sparse\n * representation of an empty HLL is just: XZERO:16384.\n *\n * An HLL having only 3 non-zero registers at position 1000, 1020, 1021\n * respectively set to 2, 3, 3, is represented by the following three\n * opcodes:\n *\n * XZERO:1000 (Registers 0-999 are set to 0)\n * VAL:2,1    (1 register set to value 2, that is register 1000)\n * ZERO:19    (Registers 1001-1019 set to 0)\n * VAL:3,2    (2 registers set to value 3, that is registers 1020,1021)\n * XZERO:15362 (Registers 1022-16383 set to 0)\n *\n * In the example the sparse representation used just 7 bytes instead\n * of 12k in order to represent the HLL registers. In general for low\n * cardinality there is a big win in terms of space efficiency, traded\n * with CPU time since the sparse representation is slower to access:\n *\n * The following table shows average cardinality vs bytes used, 100\n * samples per cardinality (when the set was not representable because\n * of registers with too big value, the dense representation size was used\n * as a sample).\n *\n * 100 267\n * 200 485\n * 300 678\n * 400 859\n * 500 1033\n * 600 1205\n * 700 1375\n * 800 1544\n * 900 1713\n * 1000 1882\n * 2000 3480\n * 3000 4879\n * 4000 6089\n * 5000 7138\n * 6000 8042\n * 7000 8823\n * 8000 9500\n * 9000 10088\n * 10000 10591\n *\n * The dense representation uses 12288 bytes, so there is a big win up to\n * a cardinality of ~2000-3000. For bigger cardinalities the constant times\n * involved in updating the sparse representation is not justified by the\n * memory savings. The exact maximum length of the sparse representation\n * when this implementation switches to the dense representation is\n * configured via the define server.hll_sparse_max_bytes.\n */\n\nstruct hllhdr {\n    char magic[4];      /* \"HYLL\" */\n    uint8_t encoding;   /* HLL_DENSE or HLL_SPARSE. */\n    uint8_t notused[3]; /* Reserved for future use, must be zero. */\n    uint8_t card[8];    /* Cached cardinality, little endian. */\n    uint8_t registers[]; /* Data bytes. */\n};\n\n/* The cached cardinality MSB is used to signal validity of the cached value. */\n#define HLL_INVALIDATE_CACHE(hdr) (hdr)->card[7] |= (1<<7)\n#define HLL_VALID_CACHE(hdr) (((hdr)->card[7] & (1<<7)) == 0)\n\n#define HLL_P 14 /* The greater is P, the smaller the error. */\n#define HLL_REGISTERS (1<<HLL_P) /* With P=14, 16384 registers. */\n#define HLL_P_MASK (HLL_REGISTERS-1) /* Mask to index register. */\n#define HLL_BITS 6 /* Enough to count up to 63 leading zeroes. */\n#define HLL_REGISTER_MAX ((1<<HLL_BITS)-1)\n#define HLL_HDR_SIZE sizeof(struct hllhdr)\n#define HLL_DENSE_SIZE (HLL_HDR_SIZE+((HLL_REGISTERS*HLL_BITS+7)/8))\n#define HLL_DENSE 0 /* Dense encoding. */\n#define HLL_SPARSE 1 /* Sparse encoding. */\n#define HLL_RAW 255 /* Only used internally, never exposed. */\n#define HLL_MAX_ENCODING 1\n\nstatic char *invalid_hll_err = \"-INVALIDOBJ Corrupted HLL object detected\\r\\n\";\n\n/* =========================== Low level bit macros ========================= */\n\n/* Macros to access the dense representation.\n *\n * We need to get and set 6 bit counters in an array of 8 bit bytes.\n * We use macros to make sure the code is inlined since speed is critical\n * especially in order to compute the approximated cardinality in\n * HLLCOUNT where we need to access all the registers at once.\n * For the same reason we also want to avoid conditionals in this code path.\n *\n * +--------+--------+--------+------//\n * |11000000|22221111|33333322|55444444\n * +--------+--------+--------+------//\n *\n * Note: in the above representation the most significant bit (MSB)\n * of every byte is on the left. We start using bits from the LSB to MSB,\n * and so forth passing to the next byte.\n *\n * Example, we want to access to counter at pos = 1 (\"111111\" in the\n * illustration above).\n *\n * The index of the first byte b0 containing our data is:\n *\n *  b0 = 6 * pos / 8 = 0\n *\n *   +--------+\n *   |11000000|  <- Our byte at b0\n *   +--------+\n *\n * The position of the first bit (counting from the LSB = 0) in the byte\n * is given by:\n *\n *  fb = 6 * pos % 8 -> 6\n *\n * Right shift b0 of 'fb' bits.\n *\n *   +--------+\n *   |11000000|  <- Initial value of b0\n *   |00000011|  <- After right shift of 6 pos.\n *   +--------+\n *\n * Left shift b1 of bits 8-fb bits (2 bits)\n *\n *   +--------+\n *   |22221111|  <- Initial value of b1\n *   |22111100|  <- After left shift of 2 bits.\n *   +--------+\n *\n * OR the two bits, and finally AND with 111111 (63 in decimal) to\n * clean the higher order bits we are not interested in:\n *\n *   +--------+\n *   |00000011|  <- b0 right shifted\n *   |22111100|  <- b1 left shifted\n *   |22111111|  <- b0 OR b1\n *   |  111111|  <- (b0 OR b1) AND 63, our value.\n *   +--------+\n *\n * We can try with a different example, like pos = 0. In this case\n * the 6-bit counter is actually contained in a single byte.\n *\n *  b0 = 6 * pos / 8 = 0\n *\n *   +--------+\n *   |11000000|  <- Our byte at b0\n *   +--------+\n *\n *  fb = 6 * pos % 8 = 0\n *\n *  So we right shift of 0 bits (no shift in practice) and\n *  left shift the next byte of 8 bits, even if we don't use it,\n *  but this has the effect of clearing the bits so the result\n *  will not be affacted after the OR.\n *\n * -------------------------------------------------------------------------\n *\n * Setting the register is a bit more complex, let's assume that 'val'\n * is the value we want to set, already in the right range.\n *\n * We need two steps, in one we need to clear the bits, and in the other\n * we need to bitwise-OR the new bits.\n *\n * Let's try with 'pos' = 1, so our first byte at 'b' is 0,\n *\n * \"fb\" is 6 in this case.\n *\n *   +--------+\n *   |11000000|  <- Our byte at b0\n *   +--------+\n *\n * To create a AND-mask to clear the bits about this position, we just\n * initialize the mask with the value 63, left shift it of \"fs\" bits,\n * and finally invert the result.\n *\n *   +--------+\n *   |00111111|  <- \"mask\" starts at 63\n *   |11000000|  <- \"mask\" after left shift of \"ls\" bits.\n *   |00111111|  <- \"mask\" after invert.\n *   +--------+\n *\n * Now we can bitwise-AND the byte at \"b\" with the mask, and bitwise-OR\n * it with \"val\" left-shifted of \"ls\" bits to set the new bits.\n *\n * Now let's focus on the next byte b1:\n *\n *   +--------+\n *   |22221111|  <- Initial value of b1\n *   +--------+\n *\n * To build the AND mask we start again with the 63 value, right shift\n * it by 8-fb bits, and invert it.\n *\n *   +--------+\n *   |00111111|  <- \"mask\" set at 2&6-1\n *   |00001111|  <- \"mask\" after the right shift by 8-fb = 2 bits\n *   |11110000|  <- \"mask\" after bitwise not.\n *   +--------+\n *\n * Now we can mask it with b+1 to clear the old bits, and bitwise-OR\n * with \"val\" left-shifted by \"rs\" bits to set the new value.\n */\n\n/* Note: if we access the last counter, we will also access the b+1 byte\n * that is out of the array, but sds strings always have an implicit null\n * term, so the byte exists, and we can skip the conditional (or the need\n * to allocate 1 byte more explicitly). */\n\n/* Store the value of the register at position 'regnum' into variable 'target'.\n * 'p' is an array of unsigned bytes. */\n#define HLL_DENSE_GET_REGISTER(target,p,regnum) do { \\\n    uint8_t *_p = (uint8_t*) p; \\\n    unsigned long _byte = regnum*HLL_BITS/8; \\\n    unsigned long _fb = regnum*HLL_BITS&7; \\\n    unsigned long _fb8 = 8 - _fb; \\\n    unsigned long b0 = _p[_byte]; \\\n    unsigned long b1 = _p[_byte+1]; \\\n    target = ((b0 >> _fb) | (b1 << _fb8)) & HLL_REGISTER_MAX; \\\n} while(0)\n\n/* Set the value of the register at position 'regnum' to 'val'.\n * 'p' is an array of unsigned bytes. */\n#define HLL_DENSE_SET_REGISTER(p,regnum,val) do { \\\n    uint8_t *_p = (uint8_t*) p; \\\n    unsigned long _byte = regnum*HLL_BITS/8; \\\n    unsigned long _fb = regnum*HLL_BITS&7; \\\n    unsigned long _fb8 = 8 - _fb; \\\n    unsigned long _v = val; \\\n    _p[_byte] &= ~(HLL_REGISTER_MAX << _fb); \\\n    _p[_byte] |= _v << _fb; \\\n    _p[_byte+1] &= ~(HLL_REGISTER_MAX >> _fb8); \\\n    _p[_byte+1] |= _v >> _fb8; \\\n} while(0)\n\n/* Macros to access the sparse representation.\n * The macros parameter is expected to be an uint8_t pointer. */\n#define HLL_SPARSE_XZERO_BIT 0x40 /* 01xxxxxx */\n#define HLL_SPARSE_VAL_BIT 0x80 /* 1vvvvvxx */\n#define HLL_SPARSE_IS_ZERO(p) (((*(p)) & 0xc0) == 0) /* 00xxxxxx */\n#define HLL_SPARSE_IS_XZERO(p) (((*(p)) & 0xc0) == HLL_SPARSE_XZERO_BIT)\n#define HLL_SPARSE_IS_VAL(p) ((*(p)) & HLL_SPARSE_VAL_BIT)\n#define HLL_SPARSE_ZERO_LEN(p) (((*(p)) & 0x3f)+1)\n#define HLL_SPARSE_XZERO_LEN(p) (((((*(p)) & 0x3f) << 8) | (*((p)+1)))+1)\n#define HLL_SPARSE_VAL_VALUE(p) ((((*(p)) >> 2) & 0x1f)+1)\n#define HLL_SPARSE_VAL_LEN(p) (((*(p)) & 0x3)+1)\n#define HLL_SPARSE_VAL_MAX_VALUE 32\n#define HLL_SPARSE_VAL_MAX_LEN 4\n#define HLL_SPARSE_ZERO_MAX_LEN 64\n#define HLL_SPARSE_XZERO_MAX_LEN 16384\n#define HLL_SPARSE_VAL_SET(p,val,len) do { \\\n    *(p) = (((val)-1)<<2|((len)-1))|HLL_SPARSE_VAL_BIT; \\\n} while(0)\n#define HLL_SPARSE_ZERO_SET(p,len) do { \\\n    *(p) = (len)-1; \\\n} while(0)\n#define HLL_SPARSE_XZERO_SET(p,len) do { \\\n    int _l = (len)-1; \\\n    *(p) = (_l>>8) | HLL_SPARSE_XZERO_BIT; \\\n    *((p)+1) = (_l&0xff); \\\n} while(0)\n\n/* ========================= HyperLogLog algorithm  ========================= */\n\n/* Our hash function is MurmurHash2, 64 bit version.\n * It was modified for Redis in order to provide the same result in\n * big and little endian archs (endian neutral). */\nuint64_t MurmurHash64A (const void * key, int len, unsigned int seed) {\n    const uint64_t m = 0xc6a4a7935bd1e995;\n    const int r = 47;\n    uint64_t h = seed ^ (len * m);\n    const uint8_t *data = (const uint8_t *)key;\n    const uint8_t *end = data + (len-(len&7));\n\n    while(data != end) {\n        uint64_t k;\n\n#if (BYTE_ORDER == LITTLE_ENDIAN)\n        k = *((uint64_t*)data);\n#else\n        k = (uint64_t) data[0];\n        k |= (uint64_t) data[1] << 8;\n        k |= (uint64_t) data[2] << 16;\n        k |= (uint64_t) data[3] << 24;\n        k |= (uint64_t) data[4] << 32;\n        k |= (uint64_t) data[5] << 40;\n        k |= (uint64_t) data[6] << 48;\n        k |= (uint64_t) data[7] << 56;\n#endif\n\n        k *= m;\n        k ^= k >> r;\n        k *= m;\n        h ^= k;\n        h *= m;\n        data += 8;\n    }\n\n    switch(len & 7) {\n    case 7: h ^= (uint64_t)data[6] << 48;\n    case 6: h ^= (uint64_t)data[5] << 40;\n    case 5: h ^= (uint64_t)data[4] << 32;\n    case 4: h ^= (uint64_t)data[3] << 24;\n    case 3: h ^= (uint64_t)data[2] << 16;\n    case 2: h ^= (uint64_t)data[1] << 8;\n    case 1: h ^= (uint64_t)data[0];\n            h *= m;\n    };\n\n    h ^= h >> r;\n    h *= m;\n    h ^= h >> r;\n    return h;\n}\n\n/* Given a string element to add to the HyperLogLog, returns the length\n * of the pattern 000..1 of the element hash. As a side effect 'regp' is\n * set to the register index this element hashes to. */\nint hllPatLen(unsigned char *ele, size_t elesize, long *regp) {\n    uint64_t hash, bit, index;\n    int count;\n\n    /* Count the number of zeroes starting from bit HLL_REGISTERS\n     * (that is a power of two corresponding to the first bit we don't use\n     * as index). The max run can be 64-P+1 bits.\n     *\n     * Note that the final \"1\" ending the sequence of zeroes must be\n     * included in the count, so if we find \"001\" the count is 3, and\n     * the smallest count possible is no zeroes at all, just a 1 bit\n     * at the first position, that is a count of 1.\n     *\n     * This may sound like inefficient, but actually in the average case\n     * there are high probabilities to find a 1 after a few iterations. */\n    hash = MurmurHash64A(ele,elesize,0xadc83b19ULL);\n    index = hash & HLL_P_MASK; /* Register index. */\n    hash |= ((uint64_t)1<<63); /* Make sure the loop terminates. */\n    bit = HLL_REGISTERS; /* First bit not used to address the register. */\n    count = 1; /* Initialized to 1 since we count the \"00000...1\" pattern. */\n    while((hash & bit) == 0) {\n        count++;\n        bit <<= 1;\n    }\n    *regp = (int) index;\n    return count;\n}\n\n/* ================== Dense representation implementation  ================== */\n\n/* \"Add\" the element in the dense hyperloglog data structure.\n * Actually nothing is added, but the max 0 pattern counter of the subset\n * the element belongs to is incremented if needed.\n *\n * 'registers' is expected to have room for HLL_REGISTERS plus an\n * additional byte on the right. This requirement is met by sds strings\n * automatically since they are implicitly null terminated.\n *\n * The function always succeed, however if as a result of the operation\n * the approximated cardinality changed, 1 is returned. Otherwise 0\n * is returned. */\nint hllDenseAdd(uint8_t *registers, unsigned char *ele, size_t elesize) {\n    uint8_t oldcount, count;\n    long index;\n\n    /* Update the register if this element produced a longer run of zeroes. */\n    count = hllPatLen(ele,elesize,&index);\n    HLL_DENSE_GET_REGISTER(oldcount,registers,index);\n    if (count > oldcount) {\n        HLL_DENSE_SET_REGISTER(registers,index,count);\n        return 1;\n    } else {\n        return 0;\n    }\n}\n\n/* Compute SUM(2^-reg) in the dense representation.\n * PE is an array with a pre-computer table of values 2^-reg indexed by reg.\n * As a side effect the integer pointed by 'ezp' is set to the number\n * of zero registers. */\ndouble hllDenseSum(uint8_t *registers, double *PE, int *ezp) {\n    double E = 0;\n    int j, ez = 0;\n\n    /* Redis default is to use 16384 registers 6 bits each. The code works\n     * with other values by modifying the defines, but for our target value\n     * we take a faster path with unrolled loops. */\n    if (HLL_REGISTERS == 16384 && HLL_BITS == 6) {\n        uint8_t *r = registers;\n        unsigned long r0, r1, r2, r3, r4, r5, r6, r7, r8, r9,\n                      r10, r11, r12, r13, r14, r15;\n        for (j = 0; j < 1024; j++) {\n            /* Handle 16 registers per iteration. */\n            r0 = r[0] & 63; if (r0 == 0) ez++;\n            r1 = (r[0] >> 6 | r[1] << 2) & 63; if (r1 == 0) ez++;\n            r2 = (r[1] >> 4 | r[2] << 4) & 63; if (r2 == 0) ez++;\n            r3 = (r[2] >> 2) & 63; if (r3 == 0) ez++;\n            r4 = r[3] & 63; if (r4 == 0) ez++;\n            r5 = (r[3] >> 6 | r[4] << 2) & 63; if (r5 == 0) ez++;\n            r6 = (r[4] >> 4 | r[5] << 4) & 63; if (r6 == 0) ez++;\n            r7 = (r[5] >> 2) & 63; if (r7 == 0) ez++;\n            r8 = r[6] & 63; if (r8 == 0) ez++;\n            r9 = (r[6] >> 6 | r[7] << 2) & 63; if (r9 == 0) ez++;\n            r10 = (r[7] >> 4 | r[8] << 4) & 63; if (r10 == 0) ez++;\n            r11 = (r[8] >> 2) & 63; if (r11 == 0) ez++;\n            r12 = r[9] & 63; if (r12 == 0) ez++;\n            r13 = (r[9] >> 6 | r[10] << 2) & 63; if (r13 == 0) ez++;\n            r14 = (r[10] >> 4 | r[11] << 4) & 63; if (r14 == 0) ez++;\n            r15 = (r[11] >> 2) & 63; if (r15 == 0) ez++;\n\n            /* Additional parens will allow the compiler to optimize the\n             * code more with a loss of precision that is not very relevant\n             * here (floating point math is not commutative!). */\n            E += (PE[r0] + PE[r1]) + (PE[r2] + PE[r3]) + (PE[r4] + PE[r5]) +\n                 (PE[r6] + PE[r7]) + (PE[r8] + PE[r9]) + (PE[r10] + PE[r11]) +\n                 (PE[r12] + PE[r13]) + (PE[r14] + PE[r15]);\n            r += 12;\n        }\n    } else {\n        for (j = 0; j < HLL_REGISTERS; j++) {\n            unsigned long reg;\n\n            HLL_DENSE_GET_REGISTER(reg,registers,j);\n            if (reg == 0) {\n                ez++;\n                /* Increment E at the end of the loop. */\n            } else {\n                E += PE[reg]; /* Precomputed 2^(-reg[j]). */\n            }\n        }\n        E += ez; /* Add 2^0 'ez' times. */\n    }\n    *ezp = ez;\n    return E;\n}\n\n/* ================== Sparse representation implementation  ================= */\n\n/* Convert the HLL with sparse representation given as input in its dense\n * representation. Both representations are represented by SDS strings, and\n * the input representation is freed as a side effect.\n *\n * The function returns VR_OK if the sparse representation was valid,\n * otherwise VR_ERROR is returned if the representation was corrupted. */\nint hllSparseToDense(robj *o) {\n    sds sparse = o->ptr, dense;\n    struct hllhdr *hdr, *oldhdr = (struct hllhdr*)sparse;\n    int idx = 0, runlen, regval;\n    uint8_t *p = (uint8_t*)sparse, *end = p+sdslen(sparse);\n\n    /* If the representation is already the right one return ASAP. */\n    hdr = (struct hllhdr*) sparse;\n    if (hdr->encoding == HLL_DENSE) return VR_OK;\n\n    /* Create a string of the right size filled with zero bytes.\n     * Note that the cached cardinality is set to 0 as a side effect\n     * that is exactly the cardinality of an empty HLL. */\n    dense = sdsnewlen(NULL,HLL_DENSE_SIZE);\n    hdr = (struct hllhdr*) dense;\n    *hdr = *oldhdr; /* This will copy the magic and cached cardinality. */\n    hdr->encoding = HLL_DENSE;\n\n    /* Now read the sparse representation and set non-zero registers\n     * accordingly. */\n    p += HLL_HDR_SIZE;\n    while(p < end) {\n        if (HLL_SPARSE_IS_ZERO(p)) {\n            runlen = HLL_SPARSE_ZERO_LEN(p);\n            idx += runlen;\n            p++;\n        } else if (HLL_SPARSE_IS_XZERO(p)) {\n            runlen = HLL_SPARSE_XZERO_LEN(p);\n            idx += runlen;\n            p += 2;\n        } else {\n            runlen = HLL_SPARSE_VAL_LEN(p);\n            regval = HLL_SPARSE_VAL_VALUE(p);\n            while(runlen--) {\n                HLL_DENSE_SET_REGISTER(hdr->registers,idx,regval);\n                idx++;\n            }\n            p++;\n        }\n    }\n\n    /* If the sparse representation was valid, we expect to find idx\n     * set to HLL_REGISTERS. */\n    if (idx != HLL_REGISTERS) {\n        sdsfree(dense);\n        return VR_ERROR;\n    }\n\n    /* Free the old representation and set the new one. */\n    sdsfree(o->ptr);\n    o->ptr = dense;\n    return VR_OK;\n}\n\n/* \"Add\" the element in the sparse hyperloglog data structure.\n * Actually nothing is added, but the max 0 pattern counter of the subset\n * the element belongs to is incremented if needed.\n *\n * The object 'o' is the String object holding the HLL. The function requires\n * a reference to the object in order to be able to enlarge the string if\n * needed.\n *\n * On success, the function returns 1 if the cardinality changed, or 0\n * if the register for this element was not updated.\n * On error (if the representation is invalid) -1 is returned.\n *\n * As a side effect the function may promote the HLL representation from\n * sparse to dense: this happens when a register requires to be set to a value\n * not representable with the sparse representation, or when the resulting\n * size would be greater than server.hll_sparse_max_bytes. */\nint hllSparseAdd(robj *o, unsigned char *ele, size_t elesize) {\n    struct hllhdr *hdr;\n    uint8_t oldcount, count, *sparse, *end, *p, *prev, *next;\n    long index, first, span;\n    long is_zero = 0, is_xzero = 0, is_val = 0, runlen = 0;\n\n    /* Update the register if this element produced a longer run of zeroes. */\n    count = hllPatLen(ele,elesize,&index);\n\n    /* If the count is too big to be representable by the sparse representation\n     * switch to dense representation. */\n    if (count > HLL_SPARSE_VAL_MAX_VALUE) goto promote;\n\n    /* When updating a sparse representation, sometimes we may need to\n     * enlarge the buffer for up to 3 bytes in the worst case (XZERO split\n     * into XZERO-VAL-XZERO). Make sure there is enough space right now\n     * so that the pointers we take during the execution of the function\n     * will be valid all the time. */\n    o->ptr = sdsMakeRoomFor(o->ptr,3);\n\n    /* Step 1: we need to locate the opcode we need to modify to check\n     * if a value update is actually needed. */\n    sparse = p = ((uint8_t*)o->ptr) + HLL_HDR_SIZE;\n    end = p + sdslen(o->ptr) - HLL_HDR_SIZE;\n\n    first = 0;\n    prev = NULL; /* Points to previos opcode at the end of the loop. */\n    next = NULL; /* Points to the next opcode at the end of the loop. */\n    span = 0;\n    while(p < end) {\n        long oplen;\n\n        /* Set span to the number of registers covered by this opcode.\n         *\n         * This is the most performance critical loop of the sparse\n         * representation. Sorting the conditionals from the most to the\n         * least frequent opcode in many-bytes sparse HLLs is faster. */\n        oplen = 1;\n        if (HLL_SPARSE_IS_ZERO(p)) {\n            span = HLL_SPARSE_ZERO_LEN(p);\n        } else if (HLL_SPARSE_IS_VAL(p)) {\n            span = HLL_SPARSE_VAL_LEN(p);\n        } else { /* XZERO. */\n            span = HLL_SPARSE_XZERO_LEN(p);\n            oplen = 2;\n        }\n        /* Break if this opcode covers the register as 'index'. */\n        if (index <= first+span-1) break;\n        prev = p;\n        p += oplen;\n        first += span;\n    }\n    if (span == 0) return -1; /* Invalid format. */\n\n    next = HLL_SPARSE_IS_XZERO(p) ? p+2 : p+1;\n    if (next >= end) next = NULL;\n\n    /* Cache current opcode type to avoid using the macro again and\n     * again for something that will not change.\n     * Also cache the run-length of the opcode. */\n    if (HLL_SPARSE_IS_ZERO(p)) {\n        is_zero = 1;\n        runlen = HLL_SPARSE_ZERO_LEN(p);\n    } else if (HLL_SPARSE_IS_XZERO(p)) {\n        is_xzero = 1;\n        runlen = HLL_SPARSE_XZERO_LEN(p);\n    } else {\n        is_val = 1;\n        runlen = HLL_SPARSE_VAL_LEN(p);\n    }\n\n    /* Step 2: After the loop:\n     *\n     * 'first' stores to the index of the first register covered\n     *  by the current opcode, which is pointed by 'p'.\n     *\n     * 'next' ad 'prev' store respectively the next and previous opcode,\n     *  or NULL if the opcode at 'p' is respectively the last or first.\n     *\n     * 'span' is set to the number of registers covered by the current\n     *  opcode.\n     *\n     * There are different cases in order to update the data structure\n     * in place without generating it from scratch:\n     *\n     * A) If it is a VAL opcode already set to a value >= our 'count'\n     *    no update is needed, regardless of the VAL run-length field.\n     *    In this case PFADD returns 0 since no changes are performed.\n     *\n     * B) If it is a VAL opcode with len = 1 (representing only our\n     *    register) and the value is less than 'count', we just update it\n     *    since this is a trivial case. */\n    if (is_val) {\n        oldcount = HLL_SPARSE_VAL_VALUE(p);\n        /* Case A. */\n        if (oldcount >= count) return 0;\n\n        /* Case B. */\n        if (runlen == 1) {\n            HLL_SPARSE_VAL_SET(p,count,1);\n            goto updated;\n        }\n    }\n\n    /* C) Another trivial to handle case is a ZERO opcode with a len of 1.\n     * We can just replace it with a VAL opcode with our value and len of 1. */\n    if (is_zero && runlen == 1) {\n        HLL_SPARSE_VAL_SET(p,count,1);\n        goto updated;\n    }\n\n    /* D) General case.\n     *\n     * The other cases are more complex: our register requires to be updated\n     * and is either currently represented by a VAL opcode with len > 1,\n     * by a ZERO opcode with len > 1, or by an XZERO opcode.\n     *\n     * In those cases the original opcode must be split into muliple\n     * opcodes. The worst case is an XZERO split in the middle resuling into\n     * XZERO - VAL - XZERO, so the resulting sequence max length is\n     * 5 bytes.\n     *\n     * We perform the split writing the new sequence into the 'new' buffer\n     * with 'newlen' as length. Later the new sequence is inserted in place\n     * of the old one, possibly moving what is on the right a few bytes\n     * if the new sequence is longer than the older one. */\n    uint8_t seq[5], *n = seq;\n    int last = first+span-1; /* Last register covered by the sequence. */\n    int len;\n\n    if (is_zero || is_xzero) {\n        /* Handle splitting of ZERO / XZERO. */\n        if (index != first) {\n            len = index-first;\n            if (len > HLL_SPARSE_ZERO_MAX_LEN) {\n                HLL_SPARSE_XZERO_SET(n,len);\n                n += 2;\n            } else {\n                HLL_SPARSE_ZERO_SET(n,len);\n                n++;\n            }\n        }\n        HLL_SPARSE_VAL_SET(n,count,1);\n        n++;\n        if (index != last) {\n            len = last-index;\n            if (len > HLL_SPARSE_ZERO_MAX_LEN) {\n                HLL_SPARSE_XZERO_SET(n,len);\n                n += 2;\n            } else {\n                HLL_SPARSE_ZERO_SET(n,len);\n                n++;\n            }\n        }\n    } else {\n        /* Handle splitting of VAL. */\n        int curval = HLL_SPARSE_VAL_VALUE(p);\n\n        if (index != first) {\n            len = index-first;\n            HLL_SPARSE_VAL_SET(n,curval,len);\n            n++;\n        }\n        HLL_SPARSE_VAL_SET(n,count,1);\n        n++;\n        if (index != last) {\n            len = last-index;\n            HLL_SPARSE_VAL_SET(n,curval,len);\n            n++;\n        }\n    }\n\n    /* Step 3: substitute the new sequence with the old one.\n     *\n     * Note that we already allocated space on the sds string\n     * calling sdsMakeRoomFor(). */\n     int seqlen = n-seq;\n     int oldlen = is_xzero ? 2 : 1;\n     int deltalen = seqlen-oldlen;\n\n     if (deltalen > 0 &&\n         sdslen(o->ptr)+deltalen > server.hll_sparse_max_bytes) goto promote;\n     if (deltalen && next) memmove(next+deltalen,next,end-next);\n     sdsIncrLen(o->ptr,deltalen);\n     memcpy(p,seq,seqlen);\n     end += deltalen;\n\nupdated:\n    /* Step 4: Merge adjacent values if possible.\n     *\n     * The representation was updated, however the resulting representation\n     * may not be optimal: adjacent VAL opcodes can sometimes be merged into\n     * a single one. */\n    p = prev ? prev : sparse;\n    int scanlen = 5; /* Scan up to 5 upcodes starting from prev. */\n    while (p < end && scanlen--) {\n        if (HLL_SPARSE_IS_XZERO(p)) {\n            p += 2;\n            continue;\n        } else if (HLL_SPARSE_IS_ZERO(p)) {\n            p++;\n            continue;\n        }\n        /* We need two adjacent VAL opcodes to try a merge, having\n         * the same value, and a len that fits the VAL opcode max len. */\n        if (p+1 < end && HLL_SPARSE_IS_VAL(p+1)) {\n            int v1 = HLL_SPARSE_VAL_VALUE(p);\n            int v2 = HLL_SPARSE_VAL_VALUE(p+1);\n            if (v1 == v2) {\n                int len = HLL_SPARSE_VAL_LEN(p)+HLL_SPARSE_VAL_LEN(p+1);\n                if (len <= HLL_SPARSE_VAL_MAX_LEN) {\n                    HLL_SPARSE_VAL_SET(p+1,v1,len);\n                    memmove(p,p+1,end-p);\n                    sdsIncrLen(o->ptr,-1);\n                    end--;\n                    /* After a merge we reiterate without incrementing 'p'\n                     * in order to try to merge the just merged value with\n                     * a value on its right. */\n                    continue;\n                }\n            }\n        }\n        p++;\n    }\n\n    /* Invalidate the cached cardinality. */\n    hdr = o->ptr;\n    HLL_INVALIDATE_CACHE(hdr);\n    return 1;\n\npromote: /* Promote to dense representation. */\n    if (hllSparseToDense(o) == VR_ERROR) return -1; /* Corrupted HLL. */\n    hdr = o->ptr;\n\n    /* We need to call hllDenseAdd() to perform the operation after the\n     * conversion. However the result must be 1, since if we need to\n     * convert from sparse to dense a register requires to be updated.\n     *\n     * Note that this in turn means that PFADD will make sure the command\n     * is propagated to slaves / AOF, so if there is a sparse -> dense\n     * convertion, it will be performed in all the slaves as well. */\n    int dense_retval = hllDenseAdd(hdr->registers, ele, elesize);\n    ASSERT(dense_retval == 1);\n    return dense_retval;\n}\n\n/* Compute SUM(2^-reg) in the sparse representation.\n * PE is an array with a pre-computer table of values 2^-reg indexed by reg.\n * As a side effect the integer pointed by 'ezp' is set to the number\n * of zero registers. */\ndouble hllSparseSum(uint8_t *sparse, int sparselen, double *PE, int *ezp, int *invalid) {\n    double E = 0;\n    int ez = 0, idx = 0, runlen, regval;\n    uint8_t *end = sparse+sparselen, *p = sparse;\n\n    while(p < end) {\n        if (HLL_SPARSE_IS_ZERO(p)) {\n            runlen = HLL_SPARSE_ZERO_LEN(p);\n            idx += runlen;\n            ez += runlen;\n            /* Increment E at the end of the loop. */\n            p++;\n        } else if (HLL_SPARSE_IS_XZERO(p)) {\n            runlen = HLL_SPARSE_XZERO_LEN(p);\n            idx += runlen;\n            ez += runlen;\n            /* Increment E at the end of the loop. */\n            p += 2;\n        } else {\n            runlen = HLL_SPARSE_VAL_LEN(p);\n            regval = HLL_SPARSE_VAL_VALUE(p);\n            idx += runlen;\n            E += PE[regval]*runlen;\n            p++;\n        }\n    }\n    if (idx != HLL_REGISTERS && invalid) *invalid = 1;\n    E += ez; /* Add 2^0 'ez' times. */\n    *ezp = ez;\n    return E;\n}\n\n/* ========================= HyperLogLog Count ==============================\n * This is the core of the algorithm where the approximated count is computed.\n * The function uses the lower level hllDenseSum() and hllSparseSum() functions\n * as helpers to compute the SUM(2^-reg) part of the computation, which is\n * representation-specific, while all the rest is common. */\n\n/* Implements the SUM operation for uint8_t data type which is only used\n * internally as speedup for PFCOUNT with multiple keys. */\ndouble hllRawSum(uint8_t *registers, double *PE, int *ezp) {\n    double E = 0;\n    int j, ez = 0;\n    uint64_t *word = (uint64_t*) registers;\n    uint8_t *bytes;\n\n    for (j = 0; j < HLL_REGISTERS/8; j++) {\n        if (*word == 0) {\n            ez += 8;\n        } else {\n            bytes = (uint8_t*) word;\n            if (bytes[0]) E += PE[bytes[0]]; else ez++;\n            if (bytes[1]) E += PE[bytes[1]]; else ez++;\n            if (bytes[2]) E += PE[bytes[2]]; else ez++;\n            if (bytes[3]) E += PE[bytes[3]]; else ez++;\n            if (bytes[4]) E += PE[bytes[4]]; else ez++;\n            if (bytes[5]) E += PE[bytes[5]]; else ez++;\n            if (bytes[6]) E += PE[bytes[6]]; else ez++;\n            if (bytes[7]) E += PE[bytes[7]]; else ez++;\n        }\n        word++;\n    }\n    E += ez; /* 2^(-reg[j]) is 1 when m is 0, add it 'ez' times for every\n                zero register in the HLL. */\n    *ezp = ez;\n    return E;\n}\n\n/* Return the approximated cardinality of the set based on the harmonic\n * mean of the registers values. 'hdr' points to the start of the SDS\n * representing the String object holding the HLL representation.\n *\n * If the sparse representation of the HLL object is not valid, the integer\n * pointed by 'invalid' is set to non-zero, otherwise it is left untouched.\n *\n * hllCount() supports a special internal-only encoding of HLL_RAW, that\n * is, hdr->registers will point to an uint8_t array of HLL_REGISTERS element.\n * This is useful in order to speedup PFCOUNT when called against multiple\n * keys (no need to work with 6-bit integers encoding). */\nstatic uint64_t hllCount(struct hllhdr *hdr, int *invalid) {\n    double m = HLL_REGISTERS;\n    double E, alpha = 0.7213/(1+1.079/m);\n    int j, ez; /* Number of registers equal to 0. */\n\n    /* We precompute 2^(-reg[j]) in a small table in order to\n     * speedup the computation of SUM(2^-register[0..i]). */\n    static int initialized = 0;\n    static double PE[64];\n    if (!initialized) {\n        PE[0] = 1; /* 2^(-reg[j]) is 1 when m is 0. */\n        for (j = 1; j < 64; j++) {\n            /* 2^(-reg[j]) is the same as 1/2^reg[j]. */\n            PE[j] = 1.0/(1ULL << j);\n        }\n        initialized = 1;\n    }\n\n    /* Compute SUM(2^-register[0..i]). */\n    if (hdr->encoding == HLL_DENSE) {\n        E = hllDenseSum(hdr->registers,PE,&ez);\n    } else if (hdr->encoding == HLL_SPARSE) {\n        E = hllSparseSum(hdr->registers,\n                         sdslen((sds)hdr)-HLL_HDR_SIZE,PE,&ez,invalid);\n    } else if (hdr->encoding == HLL_RAW) {\n        E = hllRawSum(hdr->registers,PE,&ez);\n    } else {\n        serverPanic(\"Unknown HyperLogLog encoding in hllCount()\");\n    }\n\n    /* Muliply the inverse of E for alpha_m * m^2 to have the raw estimate. */\n    E = (1/E)*alpha*m*m;\n\n    /* Use the LINEARCOUNTING algorithm for small cardinalities.\n     * For larger values but up to 72000 HyperLogLog raw approximation is\n     * used since linear counting error starts to increase. However HyperLogLog\n     * shows a strong bias in the range 2.5*16384 - 72000, so we try to\n     * compensate for it. */\n    if (E < m*2.5 && ez != 0) {\n        E = m*log(m/ez); /* LINEARCOUNTING() */\n    } else if (m == 16384 && E < 72000) {\n        /* We did polynomial regression of the bias for this range, this\n         * way we can compute the bias for a given cardinality and correct\n         * according to it. Only apply the correction for P=14 that's what\n         * we use and the value the correction was verified with. */\n        double bias = 5.9119*1.0e-18*(E*E*E*E)\n                      -1.4253*1.0e-12*(E*E*E)+\n                      1.2940*1.0e-7*(E*E)\n                      -5.2921*1.0e-3*E+\n                      83.3216;\n        E -= E*(bias/100);\n    }\n    /* We don't apply the correction for E > 1/30 of 2^32 since we use\n     * a 64 bit function and 6 bit counters. To apply the correction for\n     * 1/30 of 2^64 is not needed since it would require a huge set\n     * to approach such a value. */\n    return (uint64_t) E;\n}\n\n/* Call hllDenseAdd() or hllSparseAdd() according to the HLL encoding. */\nint hllAdd(robj *o, unsigned char *ele, size_t elesize) {\n    struct hllhdr *hdr = o->ptr;\n    switch(hdr->encoding) {\n    case HLL_DENSE: return hllDenseAdd(hdr->registers,ele,elesize);\n    case HLL_SPARSE: return hllSparseAdd(o,ele,elesize);\n    default: return -1; /* Invalid representation. */\n    }\n}\n\n/* Merge by computing MAX(registers[i],hll[i]) the HyperLogLog 'hll'\n * with an array of uint8_t HLL_REGISTERS registers pointed by 'max'.\n *\n * The hll object must be already validated via isHLLObjectOrReply()\n * or in some other way.\n *\n * If the HyperLogLog is sparse and is found to be invalid, VR_ERROR\n * is returned, otherwise the function always succeeds. */\nint hllMerge(uint8_t *max, robj *hll) {\n    struct hllhdr *hdr = hll->ptr;\n    int i;\n\n    if (hdr->encoding == HLL_DENSE) {\n        uint8_t val;\n\n        for (i = 0; i < HLL_REGISTERS; i++) {\n            HLL_DENSE_GET_REGISTER(val,hdr->registers,i);\n            if (val > max[i]) max[i] = val;\n        }\n    } else {\n        uint8_t *p = hll->ptr, *end = p + sdslen(hll->ptr);\n        long runlen, regval;\n\n        p += HLL_HDR_SIZE;\n        i = 0;\n        while(p < end) {\n            if (HLL_SPARSE_IS_ZERO(p)) {\n                runlen = HLL_SPARSE_ZERO_LEN(p);\n                i += runlen;\n                p++;\n            } else if (HLL_SPARSE_IS_XZERO(p)) {\n                runlen = HLL_SPARSE_XZERO_LEN(p);\n                i += runlen;\n                p += 2;\n            } else {\n                runlen = HLL_SPARSE_VAL_LEN(p);\n                regval = HLL_SPARSE_VAL_VALUE(p);\n                while(runlen--) {\n                    if (regval > max[i]) max[i] = regval;\n                    i++;\n                }\n                p++;\n            }\n        }\n        if (i != HLL_REGISTERS) return VR_ERROR;\n    }\n    return VR_OK;\n}\n\n/* ========================== HyperLogLog commands ========================== */\n\n/* Create an HLL object. We always create the HLL using sparse encoding.\n * This will be upgraded to the dense representation as needed. */\nrobj *createHLLObject(void) {\n    robj *o;\n    struct hllhdr *hdr;\n    sds s;\n    uint8_t *p;\n    int sparselen = HLL_HDR_SIZE +\n                    (((HLL_REGISTERS+(HLL_SPARSE_XZERO_MAX_LEN-1)) /\n                     HLL_SPARSE_XZERO_MAX_LEN)*2);\n    int aux;\n\n    /* Populate the sparse representation with as many XZERO opcodes as\n     * needed to represent all the registers. */\n    aux = HLL_REGISTERS;\n    s = sdsnewlen(NULL,sparselen);\n    p = (uint8_t*)s + HLL_HDR_SIZE;\n    while(aux) {\n        int xzero = HLL_SPARSE_XZERO_MAX_LEN;\n        if (xzero > aux) xzero = aux;\n        HLL_SPARSE_XZERO_SET(p,xzero);\n        p += 2;\n        aux -= xzero;\n    }\n    ASSERT((p-(uint8_t*)s) == sparselen);\n\n    /* Create the actual object. */\n    o = createObject(OBJ_STRING,s);\n    hdr = o->ptr;\n    memcpy(hdr->magic,\"HYLL\",4);\n    hdr->encoding = HLL_SPARSE;\n    return o;\n}\n\n/* Check if the object is a String with a valid HLL representation.\n * Return VR_OK if this is true, otherwise reply to the client\n * with an error and return VR_ERROR. */\nint isHLLObjectOrReply(client *c, robj *o) {\n    struct hllhdr *hdr;\n\n    /* Key exists, check type */\n    if (checkType(c,o,OBJ_STRING))\n        return VR_ERROR; /* Error already sent. */\n\n    if (stringObjectLen(o) < sizeof(*hdr)) goto invalid;\n    hdr = o->ptr;\n\n    /* Magic should be \"HYLL\". */\n    if (hdr->magic[0] != 'H' || hdr->magic[1] != 'Y' ||\n        hdr->magic[2] != 'L' || hdr->magic[3] != 'L') goto invalid;\n\n    if (hdr->encoding > HLL_MAX_ENCODING) goto invalid;\n\n    /* Dense representation string length should match exactly. */\n    if (hdr->encoding == HLL_DENSE &&\n        stringObjectLen(o) != HLL_DENSE_SIZE) goto invalid;\n\n    /* All tests passed. */\n    return VR_OK;\n\ninvalid:\n    addReplySds(c,\n        sdsnew(\"-WRONGTYPE Key is not a valid \"\n               \"HyperLogLog string value.\\r\\n\"));\n    return VR_ERROR;\n}\n\n/* PFADD var ele ele ele ... ele => :0 or :1 */\nvoid pfaddCommand(client *c) {\n    robj *o;\n    struct hllhdr *hdr;\n    int updated = 0, j;\n    int expired = 0;\n\n    fetchInternalDbByKey(c, c->argv[1]);\n    lockDbWrite(c->db);\n    o = lookupKeyWrite(c->db,c->argv[1],&expired);\n    if (o == NULL) {\n        /* Create the key with a string value of the exact length to\n         * hold our HLL data structure. sdsnewlen() when NULL is passed\n         * is guaranteed to return bytes initialized to zero. */\n        o = createHLLObject();\n        dbAdd(c->db,c->argv[1],o);\n        updated++;\n    } else {\n        if (isHLLObjectOrReply(c,o) != VR_OK) {\n            unlockDb(c->db);\n            if (expired) update_stats_add(c->vel->stats,expiredkeys,1);\n            return;\n        }\n        o = dbUnshareStringValue(c->db,c->argv[1],o);\n    }\n    /* Perform the low level ADD operation for every element. */\n    for (j = 2; j < c->argc; j++) {\n        int retval = hllAdd(o, (unsigned char*)c->argv[j]->ptr,\n                               sdslen(c->argv[j]->ptr));\n        switch(retval) {\n        case 1:\n            updated++;\n            break;\n        case -1:\n            unlockDb(c->db);\n            if (expired) update_stats_add(c->vel->stats,expiredkeys,1);\n            addReplySds(c,sdsnew(invalid_hll_err));\n            return;\n        }\n    }\n    hdr = o->ptr;\n    if (updated) {\n        signalModifiedKey(c->db,c->argv[1]);\n        notifyKeyspaceEvent(NOTIFY_STRING,\"pfadd\",c->argv[1],c->db->id);\n        c->vel->dirty++;\n        HLL_INVALIDATE_CACHE(hdr);\n    }\n    addReply(c, updated ? shared.cone : shared.czero);\n    unlockDb(c->db);\n    if (expired) update_stats_add(c->vel->stats,expiredkeys,1);\n}\n\n/* PFCOUNT var -> approximated cardinality of set. */\nvoid pfcountCommand(client *c) {\n    robj *o;\n    struct hllhdr *hdr;\n    uint64_t card;\n    int expired;\n\n    /* Case 1: multi-key keys, cardinality of the union.\n     *\n     * When multiple keys are specified, PFCOUNT actually computes\n     * the cardinality of the merge of the N HLLs specified. */\n    if (c->argc > 2) {\n        uint8_t max[HLL_HDR_SIZE+HLL_REGISTERS], *registers;\n        int j;\n\n        /* Compute an HLL with M[i] = MAX(M[i]_j). */\n        memset(max,0,sizeof(max));\n        hdr = (struct hllhdr*) max;\n        hdr->encoding = HLL_RAW; /* Special internal-only encoding. */\n        registers = max + HLL_HDR_SIZE;\n        for (j = 1; j < c->argc; j++) {\n            /* Check type and size. */\n            fetchInternalDbByKey(c,c->argv[j]);\n            lockDbRead(c->db);\n            robj *o = lookupKeyRead(c->db,c->argv[j]);\n            if (o == NULL) {\n                unlockDb(c->db);\n                continue; /* Assume empty HLL for non existing var.*/\n            }\n            if (isHLLObjectOrReply(c,o) != VR_OK) {\n                unlockDb(c->db);\n                return;\n            }\n            /* Merge with this HLL with our 'max' HHL by setting max[i]\n             * to MAX(max[i],hll[i]). */\n            if (hllMerge(registers,o) == VR_ERROR) {\n                unlockDb(c->db);\n                addReplySds(c,sdsnew(invalid_hll_err));\n                return;\n            }\n\n            unlockDb(c->db);\n        }\n\n        /* Compute cardinality of the resulting set. */\n        addReplyLongLong(c,hllCount(hdr,NULL));\n        return;\n    }\n\n    /* Case 2: cardinality of the single HLL.\n     *\n     * The user specified a single key. Either return the cached value\n     * or compute one and update the cache. */\n    fetchInternalDbByKey(c,c->argv[1]);\n    lockDbWrite(c->db);\n    o = lookupKeyWrite(c->db,c->argv[1],&expired);\n    if (o == NULL) {\n        /* No key? Cardinality is zero since no element was added, otherwise\n         * we would have a key as HLLADD creates it as a side effect. */\n        addReply(c,shared.czero);\n    } else {\n        if (isHLLObjectOrReply(c,o) != VR_OK) {\n            unlockDb(c->db);\n            if (expired) update_stats_add(c->vel->stats,expiredkeys,1);\n            return;\n        }\n        o = dbUnshareStringValue(c->db,c->argv[1],o);\n\n        /* Check if the cached cardinality is valid. */\n        hdr = o->ptr;\n        if (HLL_VALID_CACHE(hdr)) {\n            /* Just return the cached value. */\n            card = (uint64_t)hdr->card[0];\n            card |= (uint64_t)hdr->card[1] << 8;\n            card |= (uint64_t)hdr->card[2] << 16;\n            card |= (uint64_t)hdr->card[3] << 24;\n            card |= (uint64_t)hdr->card[4] << 32;\n            card |= (uint64_t)hdr->card[5] << 40;\n            card |= (uint64_t)hdr->card[6] << 48;\n            card |= (uint64_t)hdr->card[7] << 56;\n        } else {\n            int invalid = 0;\n            /* Recompute it and update the cached value. */\n            card = hllCount(hdr,&invalid);\n            if (invalid) {\n                unlockDb(c->db);\n                if (expired) update_stats_add(c->vel->stats,expiredkeys,1);\n                addReplySds(c,sdsnew(invalid_hll_err));\n                return;\n            }\n            hdr->card[0] = card & 0xff;\n            hdr->card[1] = (card >> 8) & 0xff;\n            hdr->card[2] = (card >> 16) & 0xff;\n            hdr->card[3] = (card >> 24) & 0xff;\n            hdr->card[4] = (card >> 32) & 0xff;\n            hdr->card[5] = (card >> 40) & 0xff;\n            hdr->card[6] = (card >> 48) & 0xff;\n            hdr->card[7] = (card >> 56) & 0xff;\n            /* This is not considered a read-only command even if the\n             * data structure is not modified, since the cached value\n             * may be modified and given that the HLL is a Redis string\n             * we need to propagate the change. */\n            signalModifiedKey(c->db,c->argv[1]);\n            server.dirty++;\n            c->vel->dirty++;\n        }\n        addReplyLongLong(c,card);\n    }\n\n    unlockDb(c->db);\n    if (expired) update_stats_add(c->vel->stats,expiredkeys,1);\n}\n\n/* PFMERGE dest src1 src2 src3 ... srcN => OK */\nvoid pfmergeCommand(client *c) {\n    uint8_t max[HLL_REGISTERS];\n    struct hllhdr *hdr;\n    int j;\n\n    /* Compute an HLL with M[i] = MAX(M[i]_j).\n     * We we the maximum into the max array of registers. We'll write\n     * it to the target variable later. */\n    memset(max,0,sizeof(max));\n    for (j = 1; j < c->argc; j++) {\n        /* Check type and size. */\n        robj *o = lookupKeyRead(c->db,c->argv[j]);\n        if (o == NULL) continue; /* Assume empty HLL for non existing var. */\n        if (isHLLObjectOrReply(c,o) != VR_OK) return;\n\n        /* Merge with this HLL with our 'max' HHL by setting max[i]\n         * to MAX(max[i],hll[i]). */\n        if (hllMerge(max,o) == VR_ERROR) {\n            addReplySds(c,sdsnew(invalid_hll_err));\n            return;\n        }\n    }\n\n    /* Create / unshare the destination key's value if needed. */\n    robj *o = lookupKeyWrite(c->db,c->argv[1],NULL);\n    if (o == NULL) {\n        /* Create the key with a string value of the exact length to\n         * hold our HLL data structure. sdsnewlen() when NULL is passed\n         * is guaranteed to return bytes initialized to zero. */\n        o = createHLLObject();\n        dbAdd(c->db,c->argv[1],o);\n    } else {\n        /* If key exists we are sure it's of the right type/size\n         * since we checked when merging the different HLLs, so we\n         * don't check again. */\n        o = dbUnshareStringValue(c->db,c->argv[1],o);\n    }\n\n    /* Only support dense objects as destination. */\n    if (hllSparseToDense(o) == VR_ERROR) {\n        addReplySds(c,sdsnew(invalid_hll_err));\n        return;\n    }\n\n    /* Write the resulting HLL to the destination HLL registers and\n     * invalidate the cached value. */\n    hdr = o->ptr;\n    for (j = 0; j < HLL_REGISTERS; j++) {\n        HLL_DENSE_SET_REGISTER(hdr->registers,j,max[j]);\n    }\n    HLL_INVALIDATE_CACHE(hdr);\n\n    signalModifiedKey(c->db,c->argv[1]);\n    /* We generate an PFADD event for PFMERGE for semantical simplicity\n     * since in theory this is a mass-add of elements. */\n    notifyKeyspaceEvent(NOTIFY_STRING,\"pfadd\",c->argv[1],c->db->id);\n    server.dirty++;\n    addReply(c,shared.ok);\n}\n\n/* ========================== Testing / Debugging  ========================== */\n\n/* PFSELFTEST\n * This command performs a self-test of the HLL registers implementation.\n * Something that is not easy to test from within the outside. */\n#define HLL_TEST_CYCLES 1000\nvoid pfselftestCommand(client *c) {\n    unsigned int j, i;\n    sds bitcounters = sdsnewlen(NULL,HLL_DENSE_SIZE);\n    struct hllhdr *hdr = (struct hllhdr*) bitcounters, *hdr2;\n    robj *o = NULL;\n    uint8_t bytecounters[HLL_REGISTERS];\n\n    /* Test 1: access registers.\n     * The test is conceived to test that the different counters of our data\n     * structure are accessible and that setting their values both result in\n     * the correct value to be retained and not affect adjacent values. */\n    for (j = 0; j < HLL_TEST_CYCLES; j++) {\n        /* Set the HLL counters and an array of unsigned byes of the\n         * same size to the same set of random values. */\n        for (i = 0; i < HLL_REGISTERS; i++) {\n            unsigned int r = rand() & HLL_REGISTER_MAX;\n\n            bytecounters[i] = r;\n            HLL_DENSE_SET_REGISTER(hdr->registers,i,r);\n        }\n        /* Check that we are able to retrieve the same values. */\n        for (i = 0; i < HLL_REGISTERS; i++) {\n            unsigned int val;\n\n            HLL_DENSE_GET_REGISTER(val,hdr->registers,i);\n            if (val != bytecounters[i]) {\n                addReplyErrorFormat(c,\n                    \"TESTFAILED Register %d should be %d but is %d\",\n                    i, (int) bytecounters[i], (int) val);\n                goto cleanup;\n            }\n        }\n    }\n\n    /* Test 2: approximation error.\n     * The test adds unique elements and check that the estimated value\n     * is always reasonable bounds.\n     *\n     * We check that the error is smaller than a few times than the expected\n     * standard error, to make it very unlikely for the test to fail because\n     * of a \"bad\" run.\n     *\n     * The test is performed with both dense and sparse HLLs at the same\n     * time also verifying that the computed cardinality is the same. */\n    memset(hdr->registers,0,HLL_DENSE_SIZE-HLL_HDR_SIZE);\n    o = createHLLObject();\n    double relerr = 1.04/sqrt(HLL_REGISTERS);\n    int64_t checkpoint = 1;\n    uint64_t seed = (uint64_t)rand() | (uint64_t)rand() << 32;\n    uint64_t ele;\n    for (j = 1; j <= 10000000; j++) {\n        ele = j ^ seed;\n        hllDenseAdd(hdr->registers,(unsigned char*)&ele,sizeof(ele));\n        hllAdd(o,(unsigned char*)&ele,sizeof(ele));\n\n        /* Make sure that for small cardinalities we use sparse\n         * encoding. */\n        if (j == checkpoint && j < server.hll_sparse_max_bytes/2) {\n            hdr2 = o->ptr;\n            if (hdr2->encoding != HLL_SPARSE) {\n                addReplyError(c, \"TESTFAILED sparse encoding not used\");\n                goto cleanup;\n            }\n        }\n\n        /* Check that dense and sparse representations agree. */\n        if (j == checkpoint && hllCount(hdr,NULL) != hllCount(o->ptr,NULL)) {\n                addReplyError(c, \"TESTFAILED dense/sparse disagree\");\n                goto cleanup;\n        }\n\n        /* Check error. */\n        if (j == checkpoint) {\n            int64_t abserr = checkpoint - (int64_t)hllCount(hdr,NULL);\n            uint64_t maxerr = ceil(relerr*6*checkpoint);\n\n            /* Adjust the max error we expect for cardinality 10\n             * since from time to time it is statistically likely to get\n             * much higher error due to collision, resulting into a false\n             * positive. */\n            if (j == 10) maxerr = 1;\n\n            if (abserr < 0) abserr = -abserr;\n            if (abserr > (int64_t)maxerr) {\n                addReplyErrorFormat(c,\n                    \"TESTFAILED Too big error. card:%llu abserr:%llu\",\n                    (unsigned long long) checkpoint,\n                    (unsigned long long) abserr);\n                goto cleanup;\n            }\n            checkpoint *= 10;\n        }\n    }\n\n    /* Success! */\n    addReply(c,shared.ok);\n\ncleanup:\n    sdsfree(bitcounters);\n    if (o) decrRefCount(o);\n}\n\n/* PFDEBUG <subcommand> <key> ... args ...\n * Different debugging related operations about the HLL implementation. */\nvoid pfdebugCommand(client *c) {\n    char *cmd = c->argv[1]->ptr;\n    struct hllhdr *hdr;\n    robj *o;\n    int j;\n\n    o = lookupKeyWrite(c->db,c->argv[2],NULL);\n    if (o == NULL) {\n        addReplyError(c,\"The specified key does not exist\");\n        return;\n    }\n    if (isHLLObjectOrReply(c,o) != VR_OK) return;\n    o = dbUnshareStringValue(c->db,c->argv[2],o);\n    hdr = o->ptr;\n\n    /* PFDEBUG GETREG <key> */\n    if (!strcasecmp(cmd,\"getreg\")) {\n        if (c->argc != 3) goto arityerr;\n\n        if (hdr->encoding == HLL_SPARSE) {\n            if (hllSparseToDense(o) == VR_ERROR) {\n                addReplySds(c,sdsnew(invalid_hll_err));\n                return;\n            }\n            server.dirty++; /* Force propagation on encoding change. */\n        }\n\n        hdr = o->ptr;\n        addReplyMultiBulkLen(c,HLL_REGISTERS);\n        for (j = 0; j < HLL_REGISTERS; j++) {\n            uint8_t val;\n\n            HLL_DENSE_GET_REGISTER(val,hdr->registers,j);\n            addReplyLongLong(c,val);\n        }\n    }\n    /* PFDEBUG DECODE <key> */\n    else if (!strcasecmp(cmd,\"decode\")) {\n        if (c->argc != 3) goto arityerr;\n\n        uint8_t *p = o->ptr, *end = p+sdslen(o->ptr);\n        sds decoded = sdsempty();\n\n        if (hdr->encoding != HLL_SPARSE) {\n            addReplyError(c,\"HLL encoding is not sparse\");\n            return;\n        }\n\n        p += HLL_HDR_SIZE;\n        while(p < end) {\n            int runlen, regval;\n\n            if (HLL_SPARSE_IS_ZERO(p)) {\n                runlen = HLL_SPARSE_ZERO_LEN(p);\n                p++;\n                decoded = sdscatprintf(decoded,\"z:%d \",runlen);\n            } else if (HLL_SPARSE_IS_XZERO(p)) {\n                runlen = HLL_SPARSE_XZERO_LEN(p);\n                p += 2;\n                decoded = sdscatprintf(decoded,\"Z:%d \",runlen);\n            } else {\n                runlen = HLL_SPARSE_VAL_LEN(p);\n                regval = HLL_SPARSE_VAL_VALUE(p);\n                p++;\n                decoded = sdscatprintf(decoded,\"v:%d,%d \",regval,runlen);\n            }\n        }\n        decoded = sdstrim(decoded,\" \");\n        addReplyBulkCBuffer(c,decoded,sdslen(decoded));\n        sdsfree(decoded);\n    }\n    /* PFDEBUG ENCODING <key> */\n    else if (!strcasecmp(cmd,\"encoding\")) {\n        char *encodingstr[2] = {\"dense\",\"sparse\"};\n        if (c->argc != 3) goto arityerr;\n\n        addReplyStatus(c,encodingstr[hdr->encoding]);\n    }\n    /* PFDEBUG TODENSE <key> */\n    else if (!strcasecmp(cmd,\"todense\")) {\n        int conv = 0;\n        if (c->argc != 3) goto arityerr;\n\n        if (hdr->encoding == HLL_SPARSE) {\n            if (hllSparseToDense(o) == VR_ERROR) {\n                addReplySds(c,sdsnew(invalid_hll_err));\n                return;\n            }\n            conv = 1;\n            server.dirty++; /* Force propagation on encoding change. */\n        }\n        addReply(c,conv ? shared.cone : shared.czero);\n    } else {\n        addReplyErrorFormat(c,\"Unknown PFDEBUG subcommand '%s'\", cmd);\n    }\n    return;\n\narityerr:\n    addReplyErrorFormat(c,\n        \"Wrong number of arguments for the '%s' subcommand\",cmd);\n}\n\n"
  },
  {
    "path": "src/vr_hyperloglog.h",
    "content": "#ifndef _VR_HYPERLOGLOG_H_\n#define _VR_HYPERLOGLOG_H_\n\nuint64_t MurmurHash64A (const void * key, int len, unsigned int seed);\nint hllPatLen(unsigned char *ele, size_t elesize, long *regp);\nint hllDenseAdd(uint8_t *registers, unsigned char *ele, size_t elesize);\ndouble hllDenseSum(uint8_t *registers, double *PE, int *ezp);\nint hllSparseToDense(robj *o);\nint hllSparseAdd(robj *o, unsigned char *ele, size_t elesize);\ndouble hllSparseSum(uint8_t *sparse, int sparselen, double *PE, int *ezp, int *invalid);\ndouble hllRawSum(uint8_t *registers, double *PE, int *ezp);\nint hllAdd(robj *o, unsigned char *ele, size_t elesize);\nint hllMerge(uint8_t *max, robj *hll);\nrobj *createHLLObject(void);\nint isHLLObjectOrReply(struct client *c, robj *o);\nvoid pfaddCommand(struct client *c);\nvoid pfcountCommand(struct client *c);\nvoid pfmergeCommand(struct client *c);\nvoid pfselftestCommand(struct client *c);\nvoid pfdebugCommand(struct client *c);\n\n#endif\n"
  },
  {
    "path": "src/vr_intset.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include <vr_core.h>\n\n/* Note that these encodings are ordered, so:\n * INTSET_ENC_INT16 < INTSET_ENC_INT32 < INTSET_ENC_INT64. */\n#define INTSET_ENC_INT16 (sizeof(int16_t))\n#define INTSET_ENC_INT32 (sizeof(int32_t))\n#define INTSET_ENC_INT64 (sizeof(int64_t))\n\n/* Return the required encoding for the provided value. */\nstatic uint8_t _intsetValueEncoding(int64_t v) {\n    if (v < INT32_MIN || v > INT32_MAX)\n        return INTSET_ENC_INT64;\n    else if (v < INT16_MIN || v > INT16_MAX)\n        return INTSET_ENC_INT32;\n    else\n        return INTSET_ENC_INT16;\n}\n\n/* Return the value at pos, given an encoding. */\nstatic int64_t _intsetGetEncoded(intset *is, int pos, uint8_t enc) {\n    int64_t v64;\n    int32_t v32;\n    int16_t v16;\n\n    if (enc == INTSET_ENC_INT64) {\n        memcpy(&v64,((int64_t*)is->contents)+pos,sizeof(v64));\n        memrev64ifbe(&v64);\n        return v64;\n    } else if (enc == INTSET_ENC_INT32) {\n        memcpy(&v32,((int32_t*)is->contents)+pos,sizeof(v32));\n        memrev32ifbe(&v32);\n        return v32;\n    } else {\n        memcpy(&v16,((int16_t*)is->contents)+pos,sizeof(v16));\n        memrev16ifbe(&v16);\n        return v16;\n    }\n}\n\n/* Return the value at pos, using the configured encoding. */\nstatic int64_t _intsetGet(intset *is, int pos) {\n    return _intsetGetEncoded(is,pos,intrev32ifbe(is->encoding));\n}\n\n/* Set the value at pos, using the configured encoding. */\nstatic void _intsetSet(intset *is, int pos, int64_t value) {\n    uint32_t encoding = intrev32ifbe(is->encoding);\n\n    if (encoding == INTSET_ENC_INT64) {\n        ((int64_t*)is->contents)[pos] = value;\n        memrev64ifbe(((int64_t*)is->contents)+pos);\n    } else if (encoding == INTSET_ENC_INT32) {\n        ((int32_t*)is->contents)[pos] = value;\n        memrev32ifbe(((int32_t*)is->contents)+pos);\n    } else {\n        ((int16_t*)is->contents)[pos] = value;\n        memrev16ifbe(((int16_t*)is->contents)+pos);\n    }\n}\n\n/* Create an empty intset. */\nintset *intsetNew(void) {\n    intset *is = dalloc(sizeof(intset));\n    is->encoding = intrev32ifbe(INTSET_ENC_INT16);\n    is->length = 0;\n    return is;\n}\n\n/* Resize the intset */\nstatic intset *intsetResize(intset *is, uint32_t len) {\n    uint32_t size = len*intrev32ifbe(is->encoding);\n    is = drealloc(is,sizeof(intset)+size);\n    return is;\n}\n\n/* Search for the position of \"value\". Return 1 when the value was found and\n * sets \"pos\" to the position of the value within the intset. Return 0 when\n * the value is not present in the intset and sets \"pos\" to the position\n * where \"value\" can be inserted. */\nstatic uint8_t intsetSearch(intset *is, int64_t value, uint32_t *pos) {\n    int min = 0, max = intrev32ifbe(is->length)-1, mid = -1;\n    int64_t cur = -1;\n\n    /* The value can never be found when the set is empty */\n    if (intrev32ifbe(is->length) == 0) {\n        if (pos) *pos = 0;\n        return 0;\n    } else {\n        /* Check for the case where we know we cannot find the value,\n         * but do know the insert position. */\n        if (value > _intsetGet(is,intrev32ifbe(is->length)-1)) {\n            if (pos) *pos = intrev32ifbe(is->length);\n            return 0;\n        } else if (value < _intsetGet(is,0)) {\n            if (pos) *pos = 0;\n            return 0;\n        }\n    }\n\n    while(max >= min) {\n        mid = ((unsigned int)min + (unsigned int)max) >> 1;\n        cur = _intsetGet(is,mid);\n        if (value > cur) {\n            min = mid+1;\n        } else if (value < cur) {\n            max = mid-1;\n        } else {\n            break;\n        }\n    }\n\n    if (value == cur) {\n        if (pos) *pos = mid;\n        return 1;\n    } else {\n        if (pos) *pos = min;\n        return 0;\n    }\n}\n\n/* Upgrades the intset to a larger encoding and inserts the given integer. */\nstatic intset *intsetUpgradeAndAdd(intset *is, int64_t value) {\n    uint8_t curenc = intrev32ifbe(is->encoding);\n    uint8_t newenc = _intsetValueEncoding(value);\n    int length = intrev32ifbe(is->length);\n    int prepend = value < 0 ? 1 : 0;\n\n    /* First set new encoding and resize */\n    is->encoding = intrev32ifbe(newenc);\n    is = intsetResize(is,intrev32ifbe(is->length)+1);\n\n    /* Upgrade back-to-front so we don't overwrite values.\n     * Note that the \"prepend\" variable is used to make sure we have an empty\n     * space at either the beginning or the end of the intset. */\n    while(length--)\n        _intsetSet(is,length+prepend,_intsetGetEncoded(is,length,curenc));\n\n    /* Set the value at the beginning or the end. */\n    if (prepend)\n        _intsetSet(is,0,value);\n    else\n        _intsetSet(is,intrev32ifbe(is->length),value);\n    is->length = intrev32ifbe(intrev32ifbe(is->length)+1);\n    return is;\n}\n\nstatic void intsetMoveTail(intset *is, uint32_t from, uint32_t to) {\n    void *src, *dst;\n    uint32_t bytes = intrev32ifbe(is->length)-from;\n    uint32_t encoding = intrev32ifbe(is->encoding);\n\n    if (encoding == INTSET_ENC_INT64) {\n        src = (int64_t*)is->contents+from;\n        dst = (int64_t*)is->contents+to;\n        bytes *= sizeof(int64_t);\n    } else if (encoding == INTSET_ENC_INT32) {\n        src = (int32_t*)is->contents+from;\n        dst = (int32_t*)is->contents+to;\n        bytes *= sizeof(int32_t);\n    } else {\n        src = (int16_t*)is->contents+from;\n        dst = (int16_t*)is->contents+to;\n        bytes *= sizeof(int16_t);\n    }\n    memmove(dst,src,bytes);\n}\n\n/* Insert an integer in the intset */\nintset *intsetAdd(intset *is, int64_t value, uint8_t *success) {\n    uint8_t valenc = _intsetValueEncoding(value);\n    uint32_t pos;\n    if (success) *success = 1;\n\n    /* Upgrade encoding if necessary. If we need to upgrade, we know that\n     * this value should be either appended (if > 0) or prepended (if < 0),\n     * because it lies outside the range of existing values. */\n    if (valenc > intrev32ifbe(is->encoding)) {\n        /* This always succeeds, so we don't need to curry *success. */\n        return intsetUpgradeAndAdd(is,value);\n    } else {\n        /* Abort if the value is already present in the set.\n         * This call will populate \"pos\" with the right position to insert\n         * the value when it cannot be found. */\n        if (intsetSearch(is,value,&pos)) {\n            if (success) *success = 0;\n            return is;\n        }\n\n        is = intsetResize(is,intrev32ifbe(is->length)+1);\n        if (pos < intrev32ifbe(is->length)) intsetMoveTail(is,pos,pos+1);\n    }\n\n    _intsetSet(is,pos,value);\n    is->length = intrev32ifbe(intrev32ifbe(is->length)+1);\n    return is;\n}\n\n/* Delete integer from intset */\nintset *intsetRemove(intset *is, int64_t value, int *success) {\n    uint8_t valenc = _intsetValueEncoding(value);\n    uint32_t pos;\n    if (success) *success = 0;\n\n    if (valenc <= intrev32ifbe(is->encoding) && intsetSearch(is,value,&pos)) {\n        uint32_t len = intrev32ifbe(is->length);\n\n        /* We know we can delete */\n        if (success) *success = 1;\n\n        /* Overwrite value with tail and update length */\n        if (pos < (len-1)) intsetMoveTail(is,pos+1,pos);\n        is = intsetResize(is,len-1);\n        is->length = intrev32ifbe(len-1);\n    }\n    return is;\n}\n\n/* Determine whether a value belongs to this set */\nuint8_t intsetFind(intset *is, int64_t value) {\n    uint8_t valenc = _intsetValueEncoding(value);\n    return valenc <= intrev32ifbe(is->encoding) && intsetSearch(is,value,NULL);\n}\n\n/* Return random member */\nint64_t intsetRandom(intset *is) {\n    return _intsetGet(is,rand()%intrev32ifbe(is->length));\n}\n\n/* Sets the value to the value at the given position. When this position is\n * out of range the function returns 0, when in range it returns 1. */\nuint8_t intsetGet(intset *is, uint32_t pos, int64_t *value) {\n    if (pos < intrev32ifbe(is->length)) {\n        *value = _intsetGet(is,pos);\n        return 1;\n    }\n    return 0;\n}\n\n/* Return intset length */\nuint32_t intsetLen(intset *is) {\n    return intrev32ifbe(is->length);\n}\n\n/* Return intset blob size in bytes. */\nsize_t intsetBlobLen(intset *is) {\n    return sizeof(intset)+intrev32ifbe(is->length)*intrev32ifbe(is->encoding);\n}\n"
  },
  {
    "path": "src/vr_intset.h",
    "content": "#ifndef _VR_INTSET_H_\n#define _VR_INTSET_H_\n\n#include <stdint.h>\n\ntypedef struct intset {\n    uint32_t encoding;\n    uint32_t length;\n    int8_t contents[];\n} intset;\n\nintset *intsetNew(void);\nintset *intsetAdd(intset *is, int64_t value, uint8_t *success);\nintset *intsetRemove(intset *is, int64_t value, int *success);\nuint8_t intsetFind(intset *is, int64_t value);\nint64_t intsetRandom(intset *is);\nuint8_t intsetGet(intset *is, uint32_t pos, int64_t *value);\nuint32_t intsetLen(intset *is);\nsize_t intsetBlobLen(intset *is);\n\n#endif\n"
  },
  {
    "path": "src/vr_listen.c",
    "content": "#include <sys/stat.h>\n#include <sys/un.h>\n\n#include <vr_core.h>\n\nvr_listen *\nvr_listen_create(sds listen_str)\n{\n    rstatus_t status;\n    vr_listen *vlisten;\n    uint8_t *p, *name;\n    uint32_t namelen;\n\n    if (listen_str == NULL) {\n        return NULL;\n    }\n\n    vlisten = dalloc(sizeof(struct vr_listen));\n    if (vlisten == NULL) {\n        return NULL;\n    }\n\n    vlisten->name = NULL;\n    vlisten->port = 0;\n    memset(&vlisten->info, 0, sizeof(vlisten->info));\n    vlisten->sd = -1;\n    \n    if (listen_str == '/') {\n        uint8_t *q, *start, *perm;\n        uint32_t permlen;\n\n        /* parse \"socket_path permissions\" from the end */\n        p = listen_str + sdslen(listen_str) - 1;\n        start = listen_str;\n        q = vr_strrchr(p, start, ' ');\n        if (q == NULL) {\n            /* no permissions field, so use defaults */\n            name = listen_str;\n            namelen = sdslen(listen_str);\n        } else {\n            perm = q + 1;\n            permlen = (uint32_t)(p - perm + 1);\n\n            p = q - 1;\n            name = start;\n            namelen = (uint32_t)(p - start + 1);\n\n            errno = 0;\n            vlisten->perm = (mode_t)strtol((char *)perm, NULL, 8);\n            if (errno || vlisten->perm > 0777) {\n                log_error(\"config file has an invalid file permission in \\\"socket_path permission\\\" format string\");\n                vr_listen_destroy(vlisten);\n                return NULL;\n            }\n        }\n    } else {\n        uint8_t *q, *start, *port;\n        uint32_t portlen;\n\n        /* parse \"hostname:port\" from the end */\n        p = listen_str + sdslen(listen_str) - 1;\n        start = listen_str;\n        q = vr_strrchr(p, start, ':');\n        if (q == NULL) {\n            log_error(\"config file has an invalid \\\"hostname:port\\\" format string\");\n            vr_listen_destroy(vlisten);\n            return NULL;\n        }\n\n        port = q + 1;\n        portlen = (uint32_t)(p - port + 1);\n\n        p = q - 1;\n\n        name = start;\n        namelen = (uint32_t)(p - start + 1);\n\n        vlisten->port = vr_atoi(port, portlen);\n        if (vlisten->port < 0 || !vr_valid_port(vlisten->port)) {\n            log_error(\"config file has an invalid port in \\\"hostname:port\\\" format string\");\n            vr_listen_destroy(vlisten);\n            return NULL;\n        }\n    }\n\n    vlisten->name = sdsnewlen(name, namelen);\n    if (vlisten->name == NULL) {\n        log_error(\"create a sds string failed: out of memory.\");\n        vr_listen_destroy(vlisten);\n        return NULL;\n    }\n\n    status = vr_resolve(vlisten->name, vlisten->port, &vlisten->info);\n    if (status != VR_OK) {\n        vr_listen_destroy(vlisten);\n        return NULL;\n    }\n\n    return vlisten;\n}\n\nvoid\nvr_listen_destroy(vr_listen *vliston)\n{\n    if (vliston == NULL) {\n        return;\n    }\n\n    if (vliston->name) {\n        sdsfree(vliston->name);\n        vliston->name = NULL;\n    }\n\n    if (vliston->sd > 0) {\n        close(vliston->sd);\n        vliston->sd = -1;\n    }\n    \n    dfree(vliston);\n}\n\nstatic rstatus_t\nvr_listen_reuse(vr_listen *p)\n{\n    rstatus_t status;\n    struct sockaddr_un *un;\n\n    switch (p->info.family) {\n    case AF_INET:\n    case AF_INET6:\n        status = vr_set_reuseaddr(p->sd);\n        break;\n\n    case AF_UNIX:\n        /*\n         * bind() will fail if the pathname already exist. So, we call unlink()\n         * to delete the pathname, in case it already exists. If it does not\n         * exist, unlink() returns error, which we ignore\n         */\n        un = (struct sockaddr_un *) &p->info.addr;\n        unlink(un->sun_path);\n        status = VR_OK;\n        break;\n\n    default:\n        NOT_REACHED();\n        status = VR_ERROR;\n    }\n\n    return status;\n}\n\nrstatus_t\nvr_listen_begin(vr_listen *vlisten)\n{\n    rstatus_t status;\n\n    vlisten->sd = socket(vlisten->info.family, SOCK_STREAM, 0);\n    if (vlisten->sd < 0) {\n        log_error(\"socket failed: %s\", strerror(errno));\n        return VR_ERROR;\n    }\n\n    status = vr_listen_reuse(vlisten);\n    if (status < 0) {\n        log_error(\"reuse of addr %s for listening on p %d failed: %s\",\n                  vlisten->name, vlisten->sd, strerror(errno));\n        return VR_ERROR;\n    }\n\n    status = bind(vlisten->sd, (struct sockaddr *)&vlisten->info.addr, vlisten->info.addrlen);\n    if (status < 0) {\n        log_error(\"bind on p %d to addr %s failed: %s\", vlisten->sd,\n                  vlisten->name, strerror(errno));\n        return VR_ERROR;\n    }\n\n    if (vlisten->info.family == AF_UNIX && vlisten->perm) {\n        struct sockaddr_un *un = (struct sockaddr_un *)&vlisten->info.addr;\n        status = chmod(un->sun_path, vlisten->perm);\n        if (status < 0) {\n            log_error(\"chmod on p %d on addr %s failed: %s\", vlisten->sd,\n                      vlisten->name, strerror(errno));\n            return VR_ERROR;\n        }\n    }\n\n    status = listen(vlisten->sd, 512);\n    if (status < 0) {\n        log_error(\"listen on p %d on addr %s failed: %s\", vlisten->sd,\n                  vlisten->name, strerror(errno));\n        return VR_ERROR;\n    }\n\n    status = vr_set_nonblocking(vlisten->sd);\n    if (status < 0) {\n        log_error(\"set nonblock on p %d on addr %s failed: %s\", vlisten->sd,\n                  vlisten->name, strerror(errno));\n        return VR_ERROR;\n    }\n\n    return VR_OK;\n}\n\nint\nvr_listen_accept(vr_listen *vlisten)\n{\n    rstatus_t status;\n    int sd;\n    int maxclients;\n    \n    ASSERT(vlisten->sd > 0);\n    \n    log_debug(LOG_DEBUG,\"client_accept\");\n\n    conf_server_get(CONFIG_SOPN_MAXCLIENTS,&maxclients);\n    for (;;) {\n        sd = accept(vlisten->sd, NULL, NULL);\n        if (sd < 0) {\n            if (errno == EINTR) {\n                log_debug(LOG_VERB, \"accept on p %d not ready - eintr\", vlisten->sd);\n                continue;\n            }\n\n            if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ECONNABORTED) {\n                log_debug(LOG_VERB, \"accept on p %d not ready - eagain\", vlisten->sd);\n                return -1;\n            }\n            \n            if (errno == EMFILE || errno == ENFILE) {\n                log_debug(LOG_CRIT, \"accept on p %d \"\n                          \"max client connections %d \"\n                          \"curr client connections %d failed: %s\",\n                          vlisten->sd, maxclients, \n                          current_clients(), strerror(errno));\n                return -1;\n            }\n\n            log_warn(\"accept on p %d failed: %s\", vlisten->sd, strerror(errno));\n\n            return -1;\n        }\n\n        break;\n    }\n\n    if (current_clients() >= maxclients) {\n        log_debug(LOG_CRIT, \"client connections %d exceed limit %d\",\n                  current_clients(), maxclients);\n        status = close(sd);\n        if (status < 0) {\n            log_error(\"close c %d failed, ignored: %s\", sd, strerror(errno));\n        }\n\n        update_stats_add(master.vel.stats, rejected_conn, 1);\n        \n        return -1;\n    }\n\n    return sd;\n}\n"
  },
  {
    "path": "src/vr_listen.h",
    "content": "#ifndef _VR_LISTEN_H_\n#define _VR_LISTEN_H_\n\ntypedef struct vr_listen {\n    sds name;               /* hostname:port */\n    int port;               /* port */\n    mode_t perm;            /* socket permissions */\n    struct sockinfo info;   /* listen socket info */\n    int sd;                 /* socket descriptor */\n}vr_listen;\n\nvr_listen *vr_listen_create(sds linten_str);\nvoid vr_listen_destroy(vr_listen *vliston);\nrstatus_t vr_listen_begin(struct vr_listen *vlisten);\nint vr_listen_accept(vr_listen *vlisten);\n\n#endif\n"
  },
  {
    "path": "src/vr_lzf.h",
    "content": "#ifndef _VR_LZF_H_\n#define _VR_LZF_H_\n\n/***********************************************************************\n**\n**\tlzf -- an extremely fast/free compression/decompression-method\n**\thttp://liblzf.plan9.de/\n**\n**\tThis algorithm is believed to be patent-free.\n**\n***********************************************************************/\n\n#define LZF_VERSION 0x0105 /* 1.5, API version */\n\n/*\n * Compress in_len bytes stored at the memory block starting at\n * in_data and write the result to out_data, up to a maximum length\n * of out_len bytes.\n *\n * If the output buffer is not large enough or any error occurs return 0,\n * otherwise return the number of bytes used, which might be considerably\n * more than in_len (but less than 104% of the original size), so it\n * makes sense to always use out_len == in_len - 1), to ensure _some_\n * compression, and store the data uncompressed otherwise (with a flag, of\n * course.\n *\n * lzf_compress might use different algorithms on different systems and\n * even different runs, thus might result in different compressed strings\n * depending on the phase of the moon or similar factors. However, all\n * these strings are architecture-independent and will result in the\n * original data when decompressed using lzf_decompress.\n *\n * The buffers must not be overlapping.\n *\n * If the option LZF_STATE_ARG is enabled, an extra argument must be\n * supplied which is not reflected in this header file. Refer to lzfP.h\n * and lzf_c.c.\n *\n */\nunsigned int\nlzf_compress (const void *const in_data,  unsigned int in_len,\n              void             *out_data, unsigned int out_len);\n\n/*\n * Decompress data compressed with some version of the lzf_compress\n * function and stored at location in_data and length in_len. The result\n * will be stored at out_data up to a maximum of out_len characters.\n *\n * If the output buffer is not large enough to hold the decompressed\n * data, a 0 is returned and errno is set to E2BIG. Otherwise the number\n * of decompressed bytes (i.e. the original length of the data) is\n * returned.\n *\n * If an error in the compressed data is detected, a zero is returned and\n * errno is set to EINVAL.\n *\n * This function is very fast, about as fast as a copying loop.\n */\nunsigned int\nlzf_decompress (const void *const in_data,  unsigned int in_len,\n                void             *out_data, unsigned int out_len);\n\n#endif\n\n"
  },
  {
    "path": "src/vr_lzfP.h",
    "content": "#ifndef _VR_LZFP_H_\n#define _VR_LZFP_H_\n\n#define STANDALONE 1 /* at the moment, this is ok. */\n\n#ifndef STANDALONE\n#include <vr_lzf.h>\n#endif\n\n/*\n * Size of hashtable is (1 << HLOG) * sizeof (char *)\n * decompression is independent of the hash table size\n * the difference between 15 and 14 is very small\n * for small blocks (and 14 is usually a bit faster).\n * For a low-memory/faster configuration, use HLOG == 13;\n * For best compression, use 15 or 16 (or more, up to 22).\n */\n#ifndef HLOG\n# define HLOG 16\n#endif\n\n/*\n * Sacrifice very little compression quality in favour of compression speed.\n * This gives almost the same compression as the default code, and is\n * (very roughly) 15% faster. This is the preferred mode of operation.\n */\n#ifndef VERY_FAST\n# define VERY_FAST 1\n#endif\n\n/*\n * Sacrifice some more compression quality in favour of compression speed.\n * (roughly 1-2% worse compression for large blocks and\n * 9-10% for small, redundant, blocks and >>20% better speed in both cases)\n * In short: when in need for speed, enable this for binary data,\n * possibly disable this for text data.\n */\n#ifndef ULTRA_FAST\n# define ULTRA_FAST 0\n#endif\n\n/*\n * Unconditionally aligning does not cost very much, so do it if unsure\n */\n#ifndef STRICT_ALIGN\n# define STRICT_ALIGN !(defined(__i386) || defined (__amd64))\n#endif\n\n/*\n * You may choose to pre-set the hash table (might be faster on some\n * modern cpus and large (>>64k) blocks, and also makes compression\n * deterministic/repeatable when the configuration otherwise is the same).\n */\n#ifndef INIT_HTAB\n# define INIT_HTAB 0\n#endif\n\n/*\n * Avoid assigning values to errno variable? for some embedding purposes\n * (linux kernel for example), this is necessary. NOTE: this breaks\n * the documentation in lzf.h. Avoiding errno has no speed impact.\n */\n#ifndef AVOID_ERRNO\n# define AVOID_ERRNO 0\n#endif\n\n/*\n * Whether to pass the LZF_STATE variable as argument, or allocate it\n * on the stack. For small-stack environments, define this to 1.\n * NOTE: this breaks the prototype in lzf.h.\n */\n#ifndef LZF_STATE_ARG\n# define LZF_STATE_ARG 0\n#endif\n\n/*\n * Whether to add extra checks for input validity in lzf_decompress\n * and return EINVAL if the input stream has been corrupted. This\n * only shields against overflowing the input buffer and will not\n * detect most corrupted streams.\n * This check is not normally noticeable on modern hardware\n * (<1% slowdown), but might slow down older cpus considerably.\n */\n#ifndef CHECK_INPUT\n# define CHECK_INPUT 1\n#endif\n\n/*\n * Whether to store pointers or offsets inside the hash table. On\n * 64 bit architetcures, pointers take up twice as much space,\n * and might also be slower. Default is to autodetect.\n */\n/*#define LZF_USER_OFFSETS autodetect */\n\n/*****************************************************************************/\n/* nothing should be changed below */\n\n#ifdef __cplusplus\n# include <cstring>\n# include <climits>\nusing namespace std;\n#else\n# include <string.h>\n# include <limits.h>\n#endif\n\n#ifndef LZF_USE_OFFSETS\n# if defined (WIN32)\n#  define LZF_USE_OFFSETS defined(_M_X64)\n# else\n#  if __cplusplus > 199711L\n#   include <cstdint>\n#  else\n#   include <stdint.h>\n#  endif\n#  define LZF_USE_OFFSETS (UINTPTR_MAX > 0xffffffffU)\n# endif\n#endif\n\ntypedef unsigned char u8;\n\n#if LZF_USE_OFFSETS\n# define LZF_HSLOT_BIAS ((const u8 *)in_data)\n  typedef unsigned int LZF_HSLOT;\n#else\n# define LZF_HSLOT_BIAS 0\n  typedef const u8 *LZF_HSLOT;\n#endif\n\ntypedef LZF_HSLOT LZF_STATE[1 << (HLOG)];\n\n#if !STRICT_ALIGN\n/* for unaligned accesses we need a 16 bit datatype. */\n# if USHRT_MAX == 65535\n    typedef unsigned short u16;\n# elif UINT_MAX == 65535\n    typedef unsigned int u16;\n# else\n#  undef STRICT_ALIGN\n#  define STRICT_ALIGN 1\n# endif\n#endif\n\n#if ULTRA_FAST\n# undef VERY_FAST\n#endif\n\n#endif\n\n"
  },
  {
    "path": "src/vr_lzf_c.c",
    "content": "#include <vr_lzfP.h>\n\n#define HSIZE (1 << (HLOG))\n\n/*\n * don't play with this unless you benchmark!\n * the data format is not dependent on the hash function.\n * the hash function might seem strange, just believe me,\n * it works ;)\n */\n#ifndef FRST\n# define FRST(p) (((p[0]) << 8) | p[1])\n# define NEXT(v,p) (((v) << 8) | p[2])\n# if ULTRA_FAST\n#  define IDX(h) ((( h             >> (3*8 - HLOG)) - h  ) & (HSIZE - 1))\n# elif VERY_FAST\n#  define IDX(h) ((( h             >> (3*8 - HLOG)) - h*5) & (HSIZE - 1))\n# else\n#  define IDX(h) ((((h ^ (h << 5)) >> (3*8 - HLOG)) - h*5) & (HSIZE - 1))\n# endif\n#endif\n/*\n * IDX works because it is very similar to a multiplicative hash, e.g.\n * ((h * 57321 >> (3*8 - HLOG)) & (HSIZE - 1))\n * the latter is also quite fast on newer CPUs, and compresses similarly.\n *\n * the next one is also quite good, albeit slow ;)\n * (int)(cos(h & 0xffffff) * 1e6)\n */\n\n#if 0\n/* original lzv-like hash function, much worse and thus slower */\n# define FRST(p) (p[0] << 5) ^ p[1]\n# define NEXT(v,p) ((v) << 5) ^ p[2]\n# define IDX(h) ((h) & (HSIZE - 1))\n#endif\n\n#define        MAX_LIT        (1 <<  5)\n#define        MAX_OFF        (1 << 13)\n#define        MAX_REF        ((1 << 8) + (1 << 3))\n\n#if __GNUC__ >= 3\n# define expect(expr,value)         __builtin_expect ((expr),(value))\n# define inline                     inline\n#else\n# define expect(expr,value)         (expr)\n# define inline                     static\n#endif\n\n#define expect_false(expr) expect ((expr) != 0, 0)\n#define expect_true(expr)  expect ((expr) != 0, 1)\n\n/*\n * compressed format\n *\n * 000LLLLL <L+1>    ; literal, L+1=1..33 octets\n * LLLooooo oooooooo ; backref L+1=1..7 octets, o+1=1..4096 offset\n * 111ooooo LLLLLLLL oooooooo ; backref L+8 octets, o+1=1..4096 offset\n *\n */\n\nunsigned int\nlzf_compress (const void *const in_data, unsigned int in_len,\n\t      void *out_data, unsigned int out_len\n#if LZF_STATE_ARG\n              , LZF_STATE htab\n#endif\n              )\n{\n#if !LZF_STATE_ARG\n  LZF_STATE htab;\n#endif\n  const u8 *ip = (const u8 *)in_data;\n        u8 *op = (u8 *)out_data;\n  const u8 *in_end  = ip + in_len;\n        u8 *out_end = op + out_len;\n  const u8 *ref;\n\n  /* off requires a type wide enough to hold a general pointer difference.\n   * ISO C doesn't have that (size_t might not be enough and ptrdiff_t only\n   * works for differences within a single object). We also assume that no\n   * no bit pattern traps. Since the only platform that is both non-POSIX\n   * and fails to support both assumptions is windows 64 bit, we make a\n   * special workaround for it.\n   */\n#if defined (WIN32) && defined (_M_X64)\n  unsigned _int64 off; /* workaround for missing POSIX compliance */\n#else\n  unsigned long off;\n#endif\n  unsigned int hval;\n  int lit;\n\n  if (!in_len || !out_len)\n    return 0;\n\n#if INIT_HTAB\n  memset (htab, 0, sizeof (htab));\n#endif\n\n  lit = 0; op++; /* start run */\n\n  hval = FRST (ip);\n  while (ip < in_end - 2)\n    {\n      LZF_HSLOT *hslot;\n\n      hval = NEXT (hval, ip);\n      hslot = htab + IDX (hval);\n      ref = *hslot + LZF_HSLOT_BIAS; *hslot = ip - LZF_HSLOT_BIAS;\n\n      if (1\n#if INIT_HTAB\n          && ref < ip /* the next test will actually take care of this, but this is faster */\n#endif\n          && (off = ip - ref - 1) < MAX_OFF\n          && ref > (u8 *)in_data\n          && ref[2] == ip[2]\n#if STRICT_ALIGN\n          && ((ref[1] << 8) | ref[0]) == ((ip[1] << 8) | ip[0])\n#else\n          && *(u16 *)ref == *(u16 *)ip\n#endif\n        )\n        {\n          /* match found at *ref++ */\n          unsigned int len = 2;\n          unsigned int maxlen = in_end - ip - len;\n          maxlen = maxlen > MAX_REF ? MAX_REF : maxlen;\n\n          if (expect_false (op + 3 + 1 >= out_end)) /* first a faster conservative test */\n            if (op - !lit + 3 + 1 >= out_end) /* second the exact but rare test */\n              return 0;\n\n          op [- lit - 1] = lit - 1; /* stop run */\n          op -= !lit; /* undo run if length is zero */\n\n          for (;;)\n            {\n              if (expect_true (maxlen > 16))\n                {\n                  len++; if (ref [len] != ip [len]) break;\n                  len++; if (ref [len] != ip [len]) break;\n                  len++; if (ref [len] != ip [len]) break;\n                  len++; if (ref [len] != ip [len]) break;\n\n                  len++; if (ref [len] != ip [len]) break;\n                  len++; if (ref [len] != ip [len]) break;\n                  len++; if (ref [len] != ip [len]) break;\n                  len++; if (ref [len] != ip [len]) break;\n\n                  len++; if (ref [len] != ip [len]) break;\n                  len++; if (ref [len] != ip [len]) break;\n                  len++; if (ref [len] != ip [len]) break;\n                  len++; if (ref [len] != ip [len]) break;\n\n                  len++; if (ref [len] != ip [len]) break;\n                  len++; if (ref [len] != ip [len]) break;\n                  len++; if (ref [len] != ip [len]) break;\n                  len++; if (ref [len] != ip [len]) break;\n                }\n\n              do\n                len++;\n              while (len < maxlen && ref[len] == ip[len]);\n\n              break;\n            }\n\n          len -= 2; /* len is now #octets - 1 */\n          ip++;\n\n          if (len < 7)\n            {\n              *op++ = (off >> 8) + (len << 5);\n            }\n          else\n            {\n              *op++ = (off >> 8) + (  7 << 5);\n              *op++ = len - 7;\n            }\n\n          *op++ = off;\n\n          lit = 0; op++; /* start run */\n\n          ip += len + 1;\n\n          if (expect_false (ip >= in_end - 2))\n            break;\n\n#if ULTRA_FAST || VERY_FAST\n          --ip;\n# if VERY_FAST && !ULTRA_FAST\n          --ip;\n# endif\n          hval = FRST (ip);\n\n          hval = NEXT (hval, ip);\n          htab[IDX (hval)] = ip - LZF_HSLOT_BIAS;\n          ip++;\n\n# if VERY_FAST && !ULTRA_FAST\n          hval = NEXT (hval, ip);\n          htab[IDX (hval)] = ip - LZF_HSLOT_BIAS;\n          ip++;\n# endif\n#else\n          ip -= len + 1;\n\n          do\n            {\n              hval = NEXT (hval, ip);\n              htab[IDX (hval)] = ip - LZF_HSLOT_BIAS;\n              ip++;\n            }\n          while (len--);\n#endif\n        }\n      else\n        {\n          /* one more literal byte we must copy */\n          if (expect_false (op >= out_end))\n            return 0;\n\n          lit++; *op++ = *ip++;\n\n          if (expect_false (lit == MAX_LIT))\n            {\n              op [- lit - 1] = lit - 1; /* stop run */\n              lit = 0; op++; /* start run */\n            }\n        }\n    }\n\n  if (op + 3 > out_end) /* at most 3 bytes can be missing here */\n    return 0;\n\n  while (ip < in_end)\n    {\n      lit++; *op++ = *ip++;\n\n      if (expect_false (lit == MAX_LIT))\n        {\n          op [- lit - 1] = lit - 1; /* stop run */\n          lit = 0; op++; /* start run */\n        }\n    }\n\n  op [- lit - 1] = lit - 1; /* end run */\n  op -= !lit; /* undo run if length is zero */\n\n  return op - (u8 *)out_data;\n}\n\n"
  },
  {
    "path": "src/vr_lzf_d.c",
    "content": "#include <vr_lzfP.h>\n\n#if AVOID_ERRNO\n# define SET_ERRNO(n)\n#else\n# include <errno.h>\n# define SET_ERRNO(n) errno = (n)\n#endif\n\n#if USE_REP_MOVSB /* small win on amd, big loss on intel */\n#if (__i386 || __amd64) && __GNUC__ >= 3\n# define lzf_movsb(dst, src, len)                \\\n   asm (\"rep movsb\"                              \\\n        : \"=D\" (dst), \"=S\" (src), \"=c\" (len)     \\\n        :  \"0\" (dst),  \"1\" (src),  \"2\" (len));\n#endif\n#endif\n\nunsigned int\nlzf_decompress (const void *const in_data,  unsigned int in_len,\n                void             *out_data, unsigned int out_len)\n{\n  u8 const *ip = (const u8 *)in_data;\n  u8       *op = (u8 *)out_data;\n  u8 const *const in_end  = ip + in_len;\n  u8       *const out_end = op + out_len;\n\n  do\n    {\n      unsigned int ctrl = *ip++;\n\n      if (ctrl < (1 << 5)) /* literal run */\n        {\n          ctrl++;\n\n          if (op + ctrl > out_end)\n            {\n              SET_ERRNO (E2BIG);\n              return 0;\n            }\n\n#if CHECK_INPUT\n          if (ip + ctrl > in_end)\n            {\n              SET_ERRNO (EINVAL);\n              return 0;\n            }\n#endif\n\n#ifdef lzf_movsb\n          lzf_movsb (op, ip, ctrl);\n#else\n          switch (ctrl)\n            {\n              case 32: *op++ = *ip++; case 31: *op++ = *ip++; case 30: *op++ = *ip++; case 29: *op++ = *ip++;\n              case 28: *op++ = *ip++; case 27: *op++ = *ip++; case 26: *op++ = *ip++; case 25: *op++ = *ip++;\n              case 24: *op++ = *ip++; case 23: *op++ = *ip++; case 22: *op++ = *ip++; case 21: *op++ = *ip++;\n              case 20: *op++ = *ip++; case 19: *op++ = *ip++; case 18: *op++ = *ip++; case 17: *op++ = *ip++;\n              case 16: *op++ = *ip++; case 15: *op++ = *ip++; case 14: *op++ = *ip++; case 13: *op++ = *ip++;\n              case 12: *op++ = *ip++; case 11: *op++ = *ip++; case 10: *op++ = *ip++; case  9: *op++ = *ip++;\n              case  8: *op++ = *ip++; case  7: *op++ = *ip++; case  6: *op++ = *ip++; case  5: *op++ = *ip++;\n              case  4: *op++ = *ip++; case  3: *op++ = *ip++; case  2: *op++ = *ip++; case  1: *op++ = *ip++;\n            }\n#endif\n        }\n      else /* back reference */\n        {\n          unsigned int len = ctrl >> 5;\n\n          u8 *ref = op - ((ctrl & 0x1f) << 8) - 1;\n\n#if CHECK_INPUT\n          if (ip >= in_end)\n            {\n              SET_ERRNO (EINVAL);\n              return 0;\n            }\n#endif\n          if (len == 7)\n            {\n              len += *ip++;\n#if CHECK_INPUT\n              if (ip >= in_end)\n                {\n                  SET_ERRNO (EINVAL);\n                  return 0;\n                }\n#endif\n            }\n\n          ref -= *ip++;\n\n          if (op + len + 2 > out_end)\n            {\n              SET_ERRNO (E2BIG);\n              return 0;\n            }\n\n          if (ref < (u8 *)out_data)\n            {\n              SET_ERRNO (EINVAL);\n              return 0;\n            }\n\n#ifdef lzf_movsb\n          len += 2;\n          lzf_movsb (op, ref, len);\n#else\n          switch (len)\n            {\n              default:\n                len += 2;\n\n                if (op >= ref + len)\n                  {\n                    /* disjunct areas */\n                    memcpy (op, ref, len);\n                    op += len;\n                  }\n                else\n                  {\n                    /* overlapping, use octte by octte copying */\n                    do\n                      *op++ = *ref++;\n                    while (--len);\n                  }\n\n                break;\n\n              case 9: *op++ = *ref++;\n              case 8: *op++ = *ref++;\n              case 7: *op++ = *ref++;\n              case 6: *op++ = *ref++;\n              case 5: *op++ = *ref++;\n              case 4: *op++ = *ref++;\n              case 3: *op++ = *ref++;\n              case 2: *op++ = *ref++;\n              case 1: *op++ = *ref++;\n              case 0: *op++ = *ref++; /* two octets more */\n                      *op++ = *ref++;\n            }\n#endif\n        }\n    }\n  while (ip < in_end);\n\n  return op - (u8 *)out_data;\n}\n\n"
  },
  {
    "path": "src/vr_master.c",
    "content": "#include <vr_core.h>\n\nvr_master master;\n\nstatic int setup_master(void);\nstatic void *master_thread_run(void *args);\n\nint\nmaster_init(vr_conf *conf)\n{\n    rstatus_t status;\n    uint32_t j;\n    sds *host, listen_str;\n    vr_listen **vlisten;\n    int threads_num;\n    int filelimit;\n\n    master.cbsul = NULL;\n    pthread_mutex_init(&master.cbsullock, NULL);\n\n    conf_server_get(CONFIG_SOPN_THREADS,&threads_num);\n    filelimit = threads_num*2+CONFIG_MIN_RESERVED_FDS;\n    vr_eventloop_init(&master.vel,filelimit);\n    master.vel.thread.fun_run = master_thread_run;\n\n    darray_init(&master.listens,darray_n(&cserver->binds),sizeof(vr_listen*));\n\n    for (j = 0; j < darray_n(&cserver->binds); j ++) {\n        host = darray_get(&cserver->binds,j);\n        listen_str = sdsdup(*host);\n        listen_str = sdscatfmt(listen_str, \":%i\", cserver->port);\n        vlisten = darray_push(&master.listens);\n        *vlisten = vr_listen_create(listen_str);\n        if (*vlisten == NULL) {\n            darray_pop(&master.listens);\n            log_error(\"Create listen %s failed\", listen_str);\n            sdsfree(listen_str);\n            return VR_ERROR;\n        }\n        sdsfree(listen_str);\n    }\n\n    for (j = 0; j < darray_n(&master.listens); j ++) {\n        vlisten = darray_get(&master.listens, j);\n        status = vr_listen_begin(*vlisten);\n        if (status != VR_OK) {\n            log_error(\"Begin listen to %s failed\", (*vlisten)->name);\n            return VR_ERROR;\n        }\n    }\n\n    master.cbsul = dlistCreate();\n    if (master.cbsul == NULL) {\n        log_error(\"Create list failed: out of memory\");\n        return VR_ENOMEM;\n    }\n\n    setup_master();\n\n    return VR_OK;\n}\n\nvoid\nmaster_deinit(void)\n{\n    vr_listen **vlisten;\n    \n    vr_eventloop_deinit(&master.vel);\n\n    while (darray_n(&master.listens) > 0) {\n        vlisten = darray_pop(&master.listens);\n        vr_listen_destroy(*vlisten);\n    }\n    darray_deinit(&master.listens);\n    \n}\n\nstatic void\nclient_accept(aeEventLoop *el, int fd, void *privdata, int mask) {\n    int sd;\n    vr_listen *vlisten = privdata;\n\n    while((sd = vr_listen_accept(vlisten)) > 0) {\n        dispatch_conn_new(vlisten, sd);\n    }\n}\n\nstatic void\ncbsul_push(struct connswapunit *su)\n{\n    pthread_mutex_lock(&master.cbsullock);\n    dlistPush(master.cbsul, su);\n    pthread_mutex_unlock(&master.cbsullock);\n}\n\nstatic struct connswapunit *\ncbsul_pop(void)\n{\n    struct connswapunit *su = NULL;\n\n    pthread_mutex_lock(&master.cbsullock);\n    su = dlistPop(master.cbsul);\n    pthread_mutex_unlock(&master.cbsullock);\n    \n    return su;\n}\n\nvoid\ndispatch_conn_exist(client *c, int tid)\n{\n    struct connswapunit *su = csui_new();\n    char buf[1];\n    vr_worker *worker;\n\n    if (su == NULL) {\n        freeClient(c);\n        /* given that malloc failed this may also fail, but let's try */\n        log_error(\"Failed to allocate memory for connection swap object\\n\");\n        return ;\n    }\n\n    su->num = tid;\n    su->data = c;\n    \n    unlinkClientFromEventloop(c);\n\n    cbsul_push(su);\n\n    worker = darray_get(&workers, (uint32_t)c->curidx);\n\n    /* Back to master */\n    buf[0] = 'b';\n    if (vr_write(worker->socketpairs[1], buf, 1) != 1) {\n        log_error(\"Notice the worker failed.\");\n    }\n}\n\nstatic void\nthread_event_process(aeEventLoop *el, int fd, void *privdata, int mask) {\n\n    rstatus_t status;\n    vr_worker *worker = privdata;\n    char buf[1];\n    int idx;\n    client *c;\n    struct connswapunit *su;\n\n    ASSERT(el == master.vel.el);\n    ASSERT(fd == worker->socketpairs[0]);\n\n    if (vr_read(fd, buf, 1) != 1) {\n        log_warn(\"Can't read for worker(id:%d) socketpairs[1](%d)\", \n            worker->vel.thread.id, fd);\n        buf[0] = 'b';\n    }\n    \n    switch (buf[0]) {\n    case 'b':\n        su = cbsul_pop();\n        if (su == NULL) {\n            log_warn(\"Pop from connection back swap list is null\");\n            return;\n        }\n        \n        idx = su->num;\n        su->num = worker->id;\n        worker = darray_get(&workers, (uint32_t)idx);\n        csul_push(worker, su);\n\n        /* Jump to the target worker. */\n        buf[0] = 'j';\n        if (vr_write(worker->socketpairs[0], buf, 1) != 1) {\n            log_error(\"Notice the worker failed.\");\n        }\n        break;\n    default:\n        log_error(\"read error char '%c' for worker(id:%d) socketpairs[0](%d)\", \n            buf[0], worker->vel.thread.id, worker->socketpairs[1]);\n        break;\n    }\n}\n\nstatic int\nsetup_master(void)\n{\n    rstatus_t status;\n    uint32_t j;\n    vr_listen **vlisten;\n    vr_worker *worker;\n\n    for (j = 0; j < darray_n(&workers); j ++) {\n        worker = darray_get(&workers, j);\n        status = aeCreateFileEvent(master.vel.el, worker->socketpairs[0], \n            AE_READABLE, thread_event_process, worker);\n        if (status == AE_ERR) {\n            log_error(\"Unrecoverable error creating master ipfd file event.\");\n            return VR_ERROR;\n        }\n    }\n\n    for (j = 0; j < darray_n(&master.listens); j ++) {\n        vlisten = darray_get(&master.listens,j);\n        status = aeCreateFileEvent(master.vel.el, (*vlisten)->sd, AE_READABLE, \n            client_accept, *vlisten);\n        if (status == AE_ERR) {\n            log_error(\"Unrecoverable error creating master ipfd file event.\");\n            return VR_ERROR;\n        }\n    }\n    \n    return VR_OK;\n}\n\nstatic void *\nmaster_thread_run(void *args)\n{    \n    /* vire master run */\n    aeMain(master.vel.el);\n\n    return NULL;\n}\n\nint\nmaster_run(void)\n{\n    vr_thread_start(&master.vel.thread);\n    return VR_OK;\n}\n"
  },
  {
    "path": "src/vr_master.h",
    "content": "#ifndef _VR_MASTER_H_\n#define _VR_MASTER_H_\n\ntypedef struct vr_master {\n\n    vr_eventloop vel;\n    \n    struct darray listens;   /* type: vr_listen */\n\n    dlist *cbsul;    /* Connect back swap unit list */\n    pthread_mutex_t cbsullock;   /* swap unit list locker */\n}vr_master;\n\nextern vr_master master;\n\nint master_init(vr_conf *conf);\nvoid master_deinit(void);\n\nvoid dispatch_conn_exist(struct client *c, int tid);\n\nint master_run(void);\n\n#endif\n"
  },
  {
    "path": "src/vr_multi.c",
    "content": "#include <vr_core.h>\n\n/* ===================== WATCH (CAS alike for MULTI/EXEC) ===================\n *\n * The implementation uses a per-DB hash table mapping keys to list of clients\n * WATCHing those keys, so that given a key that is going to be modified\n * we can mark all the associated clients as dirty.\n *\n * Also every client contains a list of WATCHed keys so that's possible to\n * un-watch such keys when the client is freed or when UNWATCH is called. */\n\n/* In the client->watched_keys list we need to use watchedKey structures\n * as in order to identify a key in Redis we need both the key name and the\n * DB */\ntypedef struct watchedKey {\n    robj *key;\n    redisDb *db;\n} watchedKey;\n\n/* Unwatch all the keys watched by this client. To clean the EXEC dirty\n * flag is up to the caller. */\nvoid unwatchAllKeys(client *c) {\n    dlistIter li;\n    dlistNode *ln;\n\n    if (dlistLength(c->watched_keys) == 0) return;\n    dlistRewind(c->watched_keys,&li);\n    while((ln = dlistNext(&li))) {\n        dlist *clients;\n        watchedKey *wk;\n\n        /* Lookup the watched key -> clients list and remove the client\n         * from the list */\n        wk = dlistNodeValue(ln);\n        clients = dictFetchValue(wk->db->watched_keys, wk->key);\n        serverAssertWithInfo(c,NULL,clients != NULL);\n        dlistDelNode(clients,dlistSearchKey(clients,c));\n        /* Kill the entry at all if this was the only client */\n        if (dlistLength(clients) == 0)\n            dictDelete(wk->db->watched_keys, wk->key);\n        /* Remove this watched key from the client->watched list */\n        dlistDelNode(c->watched_keys,ln);\n        decrRefCount(wk->key);\n        dfree(wk);\n    }\n}\n\n/* Client state initialization for MULTI/EXEC */\nvoid initClientMultiState(client *c) {\n    c->mstate.commands = NULL;\n    c->mstate.count = 0;\n}\n\n/* Release all the resources associated with MULTI/EXEC state */\nvoid freeClientMultiState(client *c) {\n    int j;\n\n    for (j = 0; j < c->mstate.count; j++) {\n        int i;\n        multiCmd *mc = c->mstate.commands+j;\n\n        for (i = 0; i < mc->argc; i++)\n            decrRefCount(mc->argv[i]);\n        dfree(mc->argv);\n    }\n    if (c->mstate.commands) dfree(c->mstate.commands);\n}\n\n/* Add a new command into the MULTI commands queue */\nvoid queueMultiCommand(client *c) {\n    multiCmd *mc;\n    int j;\n\n    c->mstate.commands = drealloc(c->mstate.commands,\n            sizeof(multiCmd)*(c->mstate.count+1));\n    mc = c->mstate.commands+c->mstate.count;\n    mc->cmd = c->cmd;\n    mc->argc = c->argc;\n    mc->argv = dalloc(sizeof(robj*)*c->argc);\n    memcpy(mc->argv,c->argv,sizeof(robj*)*c->argc);\n    for (j = 0; j < c->argc; j++)\n        incrRefCount(mc->argv[j]);\n    c->mstate.count++;\n}\n\n/* Flag the transacation as DIRTY_EXEC so that EXEC will fail.\n * Should be called every time there is an error while queueing a command. */\nvoid flagTransaction(client *c) {\n    if (c->flags & CLIENT_MULTI)\n        c->flags |= CLIENT_DIRTY_EXEC;\n}\n\nvoid execCommand(client *c) {\n    addReply(c,shared.ok);\n}\n\nvoid discardCommand(client *c) {\n    if (!(c->flags & CLIENT_MULTI)) {\n        addReplyError(c,\"DISCARD without MULTI\");\n        return;\n    }\n    discardTransaction(c);\n    addReply(c,shared.ok);\n}\n\nvoid discardTransaction(client *c) {\n    freeClientMultiState(c);\n    initClientMultiState(c);\n    c->flags &= ~(CLIENT_MULTI|CLIENT_DIRTY_CAS|CLIENT_DIRTY_EXEC);\n    unwatchAllKeys(c);\n}\n\nvoid multiCommand(client *c) {\n    if (c->flags & CLIENT_MULTI) {\n        addReplyError(c,\"MULTI calls can not be nested\");\n        return;\n    }\n    c->flags |= CLIENT_MULTI;\n    addReply(c,shared.ok);\n}\n\n/* Watch for the specified key */\nvoid watchForKey(client *c, robj *key) {\n    dlist *clients = NULL;\n    dlistIter li;\n    dlistNode *ln;\n    watchedKey *wk;\n\n    /* Check if we are already watching for this key */\n    dlistRewind(c->watched_keys,&li);\n    while((ln = dlistNext(&li))) {\n        wk = dlistNodeValue(ln);\n        if (wk->db == c->db && equalStringObjects(key,wk->key))\n            return; /* Key already watched */\n    }\n    /* This key is not already watched in this DB. Let's add it */\n    clients = dictFetchValue(c->db->watched_keys,key);\n    if (!clients) {\n        clients = dlistCreate();\n        dictAdd(c->db->watched_keys,key,clients);\n        incrRefCount(key);\n    }\n    dlistAddNodeTail(clients,c);\n    /* Add the new key to the list of keys watched by this client */\n    wk = dalloc(sizeof(*wk));\n    wk->key = key;\n    wk->db = c->db;\n    incrRefCount(key);\n    dlistAddNodeTail(c->watched_keys,wk);\n}\n\nvoid watchCommand(client *c) {\n    int j;\n\n    if (c->flags & CLIENT_MULTI) {\n        addReplyError(c,\"WATCH inside MULTI is not allowed\");\n        return;\n    }\n    for (j = 1; j < c->argc; j++)\n        watchForKey(c,c->argv[j]);\n    addReply(c,shared.ok);\n}\n\n/* \"Touch\" a key, so that if this key is being WATCHed by some client the\n * next EXEC will fail. */\nvoid touchWatchedKey(redisDb *db, robj *key) {\n    dlist *clients;\n    dlistIter li;\n    dlistNode *ln;\n\n    if (dictSize(db->watched_keys) == 0) return;\n    clients = dictFetchValue(db->watched_keys, key);\n    if (!clients) return;\n\n    /* Mark all the clients watching this key as CLIENT_DIRTY_CAS */\n    /* Check if we are already watching for this key */\n    dlistRewind(clients,&li);\n    while((ln = dlistNext(&li))) {\n        client *c = dlistNodeValue(ln);\n\n        c->flags |= CLIENT_DIRTY_CAS;\n    }\n}\n\n/* On FLUSHDB or FLUSHALL all the watched keys that are present before the\n * flush but will be deleted as effect of the flushing operation should\n * be touched. \"dbid\" is the DB that's getting the flush. -1 if it is\n * a FLUSHALL operation (all the DBs flushed). */\nvoid touchWatchedKeysOnFlush(int dbid) {\n    dlistIter li1, li2;\n    dlistNode *ln;\n\n    /* For every client, check all the waited keys */\n    dlistRewind(server.clients,&li1);\n    while((ln = dlistNext(&li1))) {\n        client *c = dlistNodeValue(ln);\n        dlistRewind(c->watched_keys,&li2);\n        while((ln = dlistNext(&li2))) {\n            watchedKey *wk = dlistNodeValue(ln);\n\n            /* For every watched key matching the specified DB, if the\n             * key exists, mark the client as dirty, as the key will be\n             * removed. */\n            if (dbid == -1 || wk->db->id == dbid) {\n                if (dictFind(wk->db->dict, wk->key->ptr) != NULL)\n                    c->flags |= CLIENT_DIRTY_CAS;\n            }\n        }\n    }\n}\n\n"
  },
  {
    "path": "src/vr_multi.h",
    "content": "#ifndef _VR_MULTI_H_\n#define _VR_MULTI_H_\n\n/* Client MULTI/EXEC state */\ntypedef struct multiCmd {\n    robj **argv;\n    int argc;\n    struct redisCommand *cmd;\n} multiCmd;\n\ntypedef struct multiState {\n    multiCmd *commands;     /* Array of MULTI commands */\n    int count;              /* Total number of MULTI commands */\n    int minreplicas;        /* MINREPLICAS for synchronous replication */\n    time_t minreplicas_timeout; /* MINREPLICAS timeout as unixtime. */\n} multiState;\n\nvoid unwatchAllKeys(struct client *c);\nvoid initClientMultiState(struct client *c);\nvoid freeClientMultiState(struct client *c);\nvoid queueMultiCommand(struct client *c);\n\nvoid flagTransaction(struct client *c);\nvoid execCommand(struct client *c);\nvoid discardCommand(struct client *c);\nvoid discardTransaction(struct client *c);\nvoid multiCommand(struct client *c);\nvoid watchForKey(struct client *c, robj *key);\nvoid watchCommand(struct client *c);\nvoid touchWatchedKey(redisDb *db, robj *key);\nvoid touchWatchedKeysOnFlush(int dbid) ;\n\n#endif\n"
  },
  {
    "path": "src/vr_notify.c",
    "content": "#include <vr_core.h>\n\n/* This file implements keyspace events notification via Pub/Sub ad\n * described at http://redis.io/topics/keyspace-events. */\n\n/* Turn a string representing notification classes into an integer\n * representing notification classes flags xored.\n *\n * The function returns -1 if the input contains characters not mapping to\n * any class. */\nint keyspaceEventsStringToFlags(char *classes) {\n    char *p = classes;\n    int c, flags = 0;\n\n    while((c = *p++) != '\\0') {\n        switch(c) {\n        case 'A': flags |= NOTIFY_ALL; break;\n        case 'g': flags |= NOTIFY_GENERIC; break;\n        case '$': flags |= NOTIFY_STRING; break;\n        case 'l': flags |= NOTIFY_LIST; break;\n        case 's': flags |= NOTIFY_SET; break;\n        case 'h': flags |= NOTIFY_HASH; break;\n        case 'z': flags |= NOTIFY_ZSET; break;\n        case 'x': flags |= NOTIFY_EXPIRED; break;\n        case 'e': flags |= NOTIFY_EVICTED; break;\n        case 'K': flags |= NOTIFY_KEYSPACE; break;\n        case 'E': flags |= NOTIFY_KEYEVENT; break;\n        default: return -1;\n        }\n    }\n    return flags;\n}\n\n/* This function does exactly the revese of the function above: it gets\n * as input an integer with the xored flags and returns a string representing\n * the selected classes. The string returned is an sds string that needs to\n * be released with sdsfree(). */\nsds keyspaceEventsFlagsToString(int flags) {\n    sds res;\n\n    res = sdsempty();\n    if ((flags & NOTIFY_ALL) == NOTIFY_ALL) {\n        res = sdscatlen(res,\"A\",1);\n    } else {\n        if (flags & NOTIFY_GENERIC) res = sdscatlen(res,\"g\",1);\n        if (flags & NOTIFY_STRING) res = sdscatlen(res,\"$\",1);\n        if (flags & NOTIFY_LIST) res = sdscatlen(res,\"l\",1);\n        if (flags & NOTIFY_SET) res = sdscatlen(res,\"s\",1);\n        if (flags & NOTIFY_HASH) res = sdscatlen(res,\"h\",1);\n        if (flags & NOTIFY_ZSET) res = sdscatlen(res,\"z\",1);\n        if (flags & NOTIFY_EXPIRED) res = sdscatlen(res,\"x\",1);\n        if (flags & NOTIFY_EVICTED) res = sdscatlen(res,\"e\",1);\n    }\n    if (flags & NOTIFY_KEYSPACE) res = sdscatlen(res,\"K\",1);\n    if (flags & NOTIFY_KEYEVENT) res = sdscatlen(res,\"E\",1);\n    return res;\n}\n\n/* The API provided to the rest of the Redis core is a simple function:\n *\n * notifyKeyspaceEvent(char *event, robj *key, int dbid);\n *\n * 'event' is a C string representing the event name.\n * 'key' is a Redis object representing the key name.\n * 'dbid' is the database ID where the key lives.  */\nvoid notifyKeyspaceEvent(int type, char *event, robj *key, int dbid) {\n    sds chan;\n    robj *chanobj, *eventobj;\n    int len = -1;\n    char buf[24];\n\n    /* If notifications for this class of events are off, return ASAP. */\n    if (!(server.notify_keyspace_events & type)) return;\n\n    eventobj = createStringObject(event,strlen(event));\n\n    /* __keyspace@<db>__:<key> <event> notifications. */\n    if (server.notify_keyspace_events & NOTIFY_KEYSPACE) {\n        chan = sdsnewlen(\"__keyspace@\",11);\n        len = ll2string(buf,sizeof(buf),dbid);\n        chan = sdscatlen(chan, buf, len);\n        chan = sdscatlen(chan, \"__:\", 3);\n        chan = sdscatsds(chan, key->ptr);\n        chanobj = createObject(OBJ_STRING, chan);\n        pubsubPublishMessage(chanobj, eventobj);\n        decrRefCount(chanobj);\n    }\n\n    /* __keyevente@<db>__:<event> <key> notifications. */\n    if (server.notify_keyspace_events & NOTIFY_KEYEVENT) {\n        chan = sdsnewlen(\"__keyevent@\",11);\n        if (len == -1) len = ll2string(buf,sizeof(buf),dbid);\n        chan = sdscatlen(chan, buf, len);\n        chan = sdscatlen(chan, \"__:\", 3);\n        chan = sdscatsds(chan, eventobj->ptr);\n        chanobj = createObject(OBJ_STRING, chan);\n        pubsubPublishMessage(chanobj, key);\n        decrRefCount(chanobj);\n    }\n    decrRefCount(eventobj);\n}\n\n\n"
  },
  {
    "path": "src/vr_notify.h",
    "content": "#ifndef _VR_NOTIFY_H_\n#define _VR_NOTIFY_H_\n\n/* Keyspace changes notification classes. Every class is associated with a\n * character for configuration purposes. */\n#define NOTIFY_KEYSPACE (1<<0)    /* K */\n#define NOTIFY_KEYEVENT (1<<1)    /* E */\n#define NOTIFY_GENERIC (1<<2)     /* g */\n#define NOTIFY_STRING (1<<3)      /* $ */\n#define NOTIFY_LIST (1<<4)        /* l */\n#define NOTIFY_SET (1<<5)         /* s */\n#define NOTIFY_HASH (1<<6)        /* h */\n#define NOTIFY_ZSET (1<<7)        /* z */\n#define NOTIFY_EXPIRED (1<<8)     /* x */\n#define NOTIFY_EVICTED (1<<9)     /* e */\n#define NOTIFY_ALL (NOTIFY_GENERIC | NOTIFY_STRING | NOTIFY_LIST | NOTIFY_SET | NOTIFY_HASH | NOTIFY_ZSET | NOTIFY_EXPIRED | NOTIFY_EVICTED)      /* A */\n\n\n#endif\n"
  },
  {
    "path": "src/vr_object.c",
    "content": "#include <math.h>\n#include <ctype.h>\n\n#include <vr_core.h>\n\n#ifdef __CYGWIN__\n#define strtold(a,b) ((long double)strtod((a),(b)))\n#endif\n\nrobj *createObject(int type, void *ptr) {\n    robj *o = dalloc(sizeof(*o));\n    o->type = type;\n    o->encoding = OBJ_ENCODING_RAW;\n    o->ptr = ptr;\n    o->constant = 0;\n    o->refcount = -1;\n    o->lru = 0;\n    return o;\n}\n\n/* Create a string object with encoding OBJ_ENCODING_RAW, that is a plain\n * string object where o->ptr points to a proper sds string. */\nrobj *createRawStringObject(const char *ptr, size_t len) {\n    return createObject(OBJ_STRING,sdsnewlen(ptr,len));\n}\n\n/* Create a string object with encoding OBJ_ENCODING_EMBSTR, that is\n * an object where the sds string is actually an unmodifiable string\n * allocated in the same chunk as the object itself. */\nrobj *createEmbeddedStringObject(const char *ptr, size_t len) {\n    robj *o = dalloc(sizeof(robj)+sizeof(struct sdshdr8)+len+1);\n    struct sdshdr8 *sh = (void*)(o+1);\n\n    o->type = OBJ_STRING;\n    o->encoding = OBJ_ENCODING_EMBSTR;\n    o->ptr = sh+1;\n    o->constant = 0;\n    o->refcount = -1;\n    o->lru = 0;\n\n    sh->len = len;\n    sh->alloc = len;\n    sh->flags = SDS_TYPE_8;\n    if (ptr) {\n        memcpy(sh->buf,ptr,len);\n        sh->buf[len] = '\\0';\n    } else {\n        memset(sh->buf,0,len+1);\n    }\n    return o;\n}\n\n/* Create a string object with EMBSTR encoding if it is smaller than\n * REIDS_ENCODING_EMBSTR_SIZE_LIMIT, otherwise the RAW encoding is\n * used.\n *\n * The current limit of 39 is chosen so that the biggest string object\n * we allocate as EMBSTR will still fit into the 64 byte arena of jemalloc. */\n#define OBJ_ENCODING_EMBSTR_SIZE_LIMIT 44\nrobj *createStringObject(const char *ptr, size_t len) {\n    if (len <= OBJ_ENCODING_EMBSTR_SIZE_LIMIT)\n        return createEmbeddedStringObject(ptr,len);\n    else\n        return createRawStringObject(ptr,len);\n}\n\nrobj *createStringObjectFromLongLong(long long value) {\n    robj *o;\n    if (value >= 0 && value < OBJ_SHARED_INTEGERS) {\n        o = shared.integers[value];\n    } else {\n        if (value >= LONG_MIN && value <= LONG_MAX) {\n            o = createObject(OBJ_STRING, NULL);\n            o->encoding = OBJ_ENCODING_INT;\n            o->ptr = (void*)((long)value);\n        } else {\n            o = createObject(OBJ_STRING,sdsfromlonglong(value));\n        }\n    }\n    return o;\n}\n\n/* Create a string object from a long double. If humanfriendly is non-zero\n * it does not use exponential format and trims trailing zeroes at the end,\n * however this results in loss of precision. Otherwise exp format is used\n * and the output of snprintf() is not modified.\n *\n * The 'humanfriendly' option is used for INCRBYFLOAT and HINCRBYFLOAT. */\nrobj *createStringObjectFromLongDouble(long double value, int humanfriendly) {\n    char buf[256];\n    int len;\n\n    if (isinf(value)) {\n        /* Libc in odd systems (Hi Solaris!) will format infinite in a\n         * different way, so better to handle it in an explicit way. */\n        if (value > 0) {\n            memcpy(buf,\"inf\",3);\n            len = 3;\n        } else {\n            memcpy(buf,\"-inf\",4);\n            len = 4;\n        }\n    } else if (humanfriendly) {\n        /* We use 17 digits precision since with 128 bit floats that precision\n         * after rounding is able to represent most small decimal numbers in a\n         * way that is \"non surprising\" for the user (that is, most small\n         * decimal numbers will be represented in a way that when converted\n         * back into a string are exactly the same as what the user typed.) */\n        len = snprintf(buf,sizeof(buf),\"%.17Lf\", value);\n        /* Now remove trailing zeroes after the '.' */\n        if (strchr(buf,'.') != NULL) {\n            char *p = buf+len-1;\n            while(*p == '0') {\n                p--;\n                len--;\n            }\n            if (*p == '.') len--;\n        }\n    } else {\n        len = snprintf(buf,sizeof(buf),\"%.17Lg\", value);\n    }\n    return createStringObject(buf,len);\n}\n\n/* Duplicate a string object, with the guarantee that the returned object\n * has the same encoding as the original one.\n *\n * This function also guarantees that duplicating a small integere object\n * (or a string object that contains a representation of a small integer)\n * will always result in a fresh object that is unshared (refcount == 1).\n *\n * The resulting object always has refcount set to 1. */\nrobj *dupStringObject(robj *o) {\n    robj *d;\n\n    ASSERT(o->type == OBJ_STRING);\n\n    switch(o->encoding) {\n    case OBJ_ENCODING_RAW:\n        return createRawStringObject(o->ptr,sdslen(o->ptr));\n    case OBJ_ENCODING_EMBSTR:\n        return createEmbeddedStringObject(o->ptr,sdslen(o->ptr));\n    case OBJ_ENCODING_INT:\n        d = createObject(OBJ_STRING, NULL);\n        d->encoding = OBJ_ENCODING_INT;\n        d->ptr = o->ptr;\n        return d;\n    default:\n        serverPanic(\"Wrong encoding.\");\n        break;\n    }\n}\n\nrobj *dupStringObjectUnconstant(robj *o) {\n    if (o->constant) return o;\n    return dupStringObject(o);\n}\n\nrobj *createQuicklistObject(void) {\n    quicklist *l = quicklistCreate();\n    robj *o = createObject(OBJ_LIST,l);\n    o->encoding = OBJ_ENCODING_QUICKLIST;\n    return o;\n}\n\nrobj *createZiplistObject(void) {\n    unsigned char *zl = ziplistNew();\n    robj *o = createObject(OBJ_LIST,zl);\n    o->encoding = OBJ_ENCODING_ZIPLIST;\n    return o;\n}\n\nrobj *createSetObject(void) {\n    dict *d = dictCreate(&setDictType,NULL);\n    robj *o = createObject(OBJ_SET,d);\n    o->encoding = OBJ_ENCODING_HT;\n    return o;\n}\n\nrobj *createIntsetObject(void) {\n    intset *is = intsetNew();\n    robj *o = createObject(OBJ_SET,is);\n    o->encoding = OBJ_ENCODING_INTSET;\n    return o;\n}\n\nrobj *createHashObject(void) {\n    unsigned char *zl = ziplistNew();\n    robj *o = createObject(OBJ_HASH, zl);\n    o->encoding = OBJ_ENCODING_ZIPLIST;\n    return o;\n}\n\n/* The member object is stored in the zs->dict. \n  * You can use the member in the zs->zsl just \n  * when zs->dict was not released.\n  */\nrobj *createZsetObject(void) {\n    zset *zs = dalloc(sizeof(*zs));\n    robj *o;\n\n    zs->dict = dictCreate(&zsetDictType,NULL);\n    zs->zsl = zslCreate();\n    o = createObject(OBJ_ZSET,zs);\n    o->encoding = OBJ_ENCODING_SKIPLIST;\n    return o;\n}\n\nrobj *createZsetZiplistObject(void) {\n    unsigned char *zl = ziplistNew();\n    robj *o = createObject(OBJ_ZSET,zl);\n    o->encoding = OBJ_ENCODING_ZIPLIST;\n    return o;\n}\n\nvoid freeStringObject(robj *o) {\n    if (o->encoding == OBJ_ENCODING_RAW) {\n        sdsfree(o->ptr);\n    }\n}\n\nvoid freeListObject(robj *o) {\n    switch (o->encoding) {\n    case OBJ_ENCODING_QUICKLIST:\n        quicklistRelease(o->ptr);\n        break;\n    default:\n        serverPanic(\"Unknown list encoding type\");\n    }\n}\n\nvoid freeSetObject(robj *o) {\n    switch (o->encoding) {\n    case OBJ_ENCODING_HT:\n        dictRelease((dict*) o->ptr);\n        break;\n    case OBJ_ENCODING_INTSET:\n        dfree(o->ptr);\n        break;\n    default:\n        serverPanic(\"Unknown set encoding type\");\n    }\n}\n\nvoid freeZsetObject(robj *o) {\n    zset *zs;\n    switch (o->encoding) {\n    case OBJ_ENCODING_SKIPLIST:\n        zs = o->ptr;\n        dictRelease(zs->dict);\n        zslFree(zs->zsl);\n        dfree(zs);\n        break;\n    case OBJ_ENCODING_ZIPLIST:\n        dfree(o->ptr);\n        break;\n    default:\n        serverPanic(\"Unknown sorted set encoding\");\n    }\n}\n\nvoid freeHashObject(robj *o) {\n    switch (o->encoding) {\n    case OBJ_ENCODING_HT:\n        dictRelease((dict*) o->ptr);\n        break;\n    case OBJ_ENCODING_ZIPLIST:\n        dfree(o->ptr);\n        break;\n    default:\n        serverPanic(\"Unknown hash encoding type\");\n        break;\n    }\n}\n\nvoid incrRefCount(robj *o) {\n    o->refcount++;\n}\n\nvoid decrRefCount(robj *o) {\n    if (o->refcount <= 0) serverPanic(\"decrRefCount against refcount <= 0\");\n    if (o->refcount == 1) {\n        switch(o->type) {\n        case OBJ_STRING: freeStringObject(o); break;\n        case OBJ_LIST: freeListObject(o); break;\n        case OBJ_SET: freeSetObject(o); break;\n        case OBJ_ZSET: freeZsetObject(o); break;\n        case OBJ_HASH: freeHashObject(o); break;\n        default: serverPanic(\"Unknown object type\"); break;\n        }\n        dfree(o);\n    } else {\n        o->refcount--;\n    }\n}\n\n/* This variant of decrRefCount() gets its argument as void, and is useful\n * as free method in data structures that expect a 'void free_object(void*)'\n * prototype for the free method. */\nvoid decrRefCountVoid(void *o) {\n    decrRefCount(o);\n}\n\nvoid freeObject(robj *o) {\n    if (o->constant) return;\n    \n    switch(o->type) {\n    case OBJ_STRING: freeStringObject(o); break;\n    case OBJ_LIST: freeListObject(o); break;\n    case OBJ_SET: freeSetObject(o); break;\n    case OBJ_ZSET: freeZsetObject(o); break;\n    case OBJ_HASH: freeHashObject(o); break;\n    default: serverPanic(\"Unknown object type\"); break;\n    }\n    dfree(o);\n}\n\nvoid freeObjectVoid(void *o) {\n    freeObject(o);\n}\n\n/* This function set the ref count to zero without freeing the object.\n * It is useful in order to pass a new object to functions incrementing\n * the ref count of the received object. Example:\n *\n *    functionThatWillIncrementRefCount(resetRefCount(CreateObject(...)));\n *\n * Otherwise you need to resort to the less elegant pattern:\n *\n *    *obj = createObject(...);\n *    functionThatWillIncrementRefCount(obj);\n *    decrRefCount(obj);\n */\nrobj *resetRefCount(robj *obj) {\n    obj->refcount = 0;\n    return obj;\n}\n\nint checkType(client *c, robj *o, int type) {\n    if (o->type != type) {\n        addReply(c,shared.wrongtypeerr);\n        return 1;\n    }\n    return 0;\n}\n\nint isObjectRepresentableAsLongLong(robj *o, long long *llval) {\n    serverAssertWithInfo(NULL,o,o->type == OBJ_STRING);\n    if (o->encoding == OBJ_ENCODING_INT) {\n        if (llval) *llval = (long) o->ptr;\n        return VR_OK;\n    } else {\n        return string2ll(o->ptr,sdslen(o->ptr),llval) ? VR_OK : VR_ERROR;\n    }\n}\n\n/* Try to encode a string object in order to save space */\nrobj *tryObjectEncoding(robj *o) {\n    long value;\n    sds s = o->ptr;\n    size_t len;\n\n    /* Make sure this is a string object, the only type we encode\n     * in this function. Other types use encoded memory efficient\n     * representations but are handled by the commands implementing\n     * the type. */\n    serverAssertWithInfo(NULL,o,o->type == OBJ_STRING);\n\n    /* We try some specialized encoding only for objects that are\n     * RAW or EMBSTR encoded, in other words objects that are still\n     * in represented by an actually array of chars. */\n    if (!sdsEncodedObject(o)) return o;\n\n    /* It's constant object, not encode it.  */\n    if (o->constant) return o;\n\n    /* Check if we can represent this string as a long integer.\n     * Note that we are sure that a string larger than 21 chars is not\n     * representable as a 32 nor 64 bit integer. */\n    len = sdslen(s);\n    if (len <= 21 && string2l(s,len,&value)) {\n        /* This object is encodable as a long. Try to use a shared object.\n         * Note that we avoid using shared integers when maxmemory is used\n         * because every object needs to have a private LRU field for the LRU\n         * algorithm to work well. \n         * Now we do not support LRU, so just comment it. */\n        if (/*(server.maxmemory == 0 ||\n             (server.maxmemory_policy != MAXMEMORY_VOLATILE_LRU &&\n              server.maxmemory_policy != MAXMEMORY_ALLKEYS_LRU)) && */\n            value >= 0 &&\n            value < OBJ_SHARED_INTEGERS)\n        {\n            freeObject(o);\n            return shared.integers[value];\n        } else {\n            if (o->encoding == OBJ_ENCODING_RAW) sdsfree(o->ptr);\n            o->encoding = OBJ_ENCODING_INT;\n            o->ptr = (void*) value;\n            return o;\n        }\n    }\n\n    /* If the string is small and is still RAW encoded,\n     * try the EMBSTR encoding which is more efficient.\n     * In this representation the object and the SDS string are allocated\n     * in the same chunk of memory to save space and cache misses. */\n    if (len <= OBJ_ENCODING_EMBSTR_SIZE_LIMIT) {\n        robj *emb;\n\n        if (o->encoding == OBJ_ENCODING_EMBSTR) return o;\n        emb = createEmbeddedStringObject(s,sdslen(s));\n        freeObject(o);\n        return emb;\n    }\n\n    /* We can't encode the object...\n     *\n     * Do the last try, and at least optimize the SDS string inside\n     * the string object to require little space, in case there\n     * is more than 10% of free space at the end of the SDS string.\n     *\n     * We do that only for relatively large strings as this branch\n     * is only entered if the length of the string is greater than\n     * OBJ_ENCODING_EMBSTR_SIZE_LIMIT. */\n    if (o->encoding == OBJ_ENCODING_RAW &&\n        sdsavail(s) > len/10)\n    {\n        o->ptr = sdsRemoveFreeSpace(o->ptr);\n    }\n\n    /* Return the original object. */\n    return o;\n}\n\n/* Get a decoded version of an encoded object (returned as a new object).\n * If the object is already raw-encoded just increment the ref count. */\nrobj *getDecodedObject(robj *o) {\n    robj *dec;\n\n    if (sdsEncodedObject(o)) {\n        return o;\n    }\n    if (o->type == OBJ_STRING && o->encoding == OBJ_ENCODING_INT) {\n        char buf[32];\n\n        ll2string(buf,32,(long)o->ptr);\n        dec = createStringObject(buf,strlen(buf));\n        return dec;\n    } else {\n        serverPanic(\"Unknown encoding type\");\n    }\n}\n\n/* Compare two string objects via strcmp() or strcoll() depending on flags.\n * Note that the objects may be integer-encoded. In such a case we\n * use ll2string() to get a string representation of the numbers on the stack\n * and compare the strings, it's much faster than calling getDecodedObject().\n *\n * Important note: when REDIS_COMPARE_BINARY is used a binary-safe comparison\n * is used. */\n\n#define REDIS_COMPARE_BINARY (1<<0)\n#define REDIS_COMPARE_COLL (1<<1)\n\nint compareStringObjectsWithFlags(robj *a, robj *b, int flags) {\n    serverAssertWithInfo(NULL,a,a->type == OBJ_STRING && b->type == OBJ_STRING);\n    char bufa[128], bufb[128], *astr, *bstr;\n    size_t alen, blen, minlen;\n\n    if (a == b) return 0;\n    if (sdsEncodedObject(a)) {\n        astr = a->ptr;\n        alen = sdslen(astr);\n    } else {\n        alen = ll2string(bufa,sizeof(bufa),(long) a->ptr);\n        astr = bufa;\n    }\n    if (sdsEncodedObject(b)) {\n        bstr = b->ptr;\n        blen = sdslen(bstr);\n    } else {\n        blen = ll2string(bufb,sizeof(bufb),(long) b->ptr);\n        bstr = bufb;\n    }\n    if (flags & REDIS_COMPARE_COLL) {\n        return strcoll(astr,bstr);\n    } else {\n        int cmp;\n\n        minlen = (alen < blen) ? alen : blen;\n        cmp = memcmp(astr,bstr,minlen);\n        if (cmp == 0) return alen-blen;\n        return cmp;\n    }\n}\n\n/* Wrapper for compareStringObjectsWithFlags() using binary comparison. */\nint compareStringObjects(robj *a, robj *b) {\n    return compareStringObjectsWithFlags(a,b,REDIS_COMPARE_BINARY);\n}\n\n/* Wrapper for compareStringObjectsWithFlags() using collation. */\nint collateStringObjects(robj *a, robj *b) {\n    return compareStringObjectsWithFlags(a,b,REDIS_COMPARE_COLL);\n}\n\n/* Equal string objects return 1 if the two objects are the same from the\n * point of view of a string comparison, otherwise 0 is returned. Note that\n * this function is faster then checking for (compareStringObject(a,b) == 0)\n * because it can perform some more optimization. */\nint equalStringObjects(robj *a, robj *b) {\n    if (a->encoding == OBJ_ENCODING_INT &&\n        b->encoding == OBJ_ENCODING_INT){\n        /* If both strings are integer encoded just check if the stored\n         * long is the same. */\n        return a->ptr == b->ptr;\n    } else {\n        return compareStringObjects(a,b) == 0;\n    }\n}\n\nsize_t stringObjectLen(robj *o) {\n    serverAssertWithInfo(NULL,o,o->type == OBJ_STRING);\n    if (sdsEncodedObject(o)) {\n        return sdslen(o->ptr);\n    } else {\n        return sdigits10((long)o->ptr);\n    }\n}\n\nint getDoubleFromObject(robj *o, double *target) {\n    double value;\n    char *eptr;\n\n    if (o == NULL) {\n        value = 0;\n    } else {\n        serverAssertWithInfo(NULL,o,o->type == OBJ_STRING);\n        if (sdsEncodedObject(o)) {\n            errno = 0;\n            value = strtod(o->ptr, &eptr);\n            if (isspace(((char*)o->ptr)[0]) ||\n                eptr[0] != '\\0' ||\n                (errno == ERANGE &&\n                    (value == HUGE_VAL || value == -HUGE_VAL || value == 0)) ||\n                errno == EINVAL ||\n                isnan(value))\n                return VR_ERROR;\n        } else if (o->encoding == OBJ_ENCODING_INT) {\n            value = (long)o->ptr;\n        } else {\n            serverPanic(\"Unknown string encoding\");\n        }\n    }\n    *target = value;\n    return VR_OK;\n}\n\nint getDoubleFromObjectOrReply(client *c, robj *o, double *target, const char *msg) {\n    double value;\n    if (getDoubleFromObject(o, &value) != VR_OK) {\n        if (msg != NULL) {\n            addReplyError(c,(char*)msg);\n        } else {\n            addReplyError(c,\"value is not a valid float\");\n        }\n        return VR_ERROR;\n    }\n    *target = value;\n    return VR_OK;\n}\n\nint getLongDoubleFromObject(robj *o, long double *target) {\n    long double value;\n    char *eptr;\n\n    if (o == NULL) {\n        value = 0;\n    } else {\n        serverAssertWithInfo(NULL,o,o->type == OBJ_STRING);\n        if (sdsEncodedObject(o)) {\n            errno = 0;\n            value = strtold(o->ptr, &eptr);\n            if (isspace(((char*)o->ptr)[0]) || eptr[0] != '\\0' ||\n                errno == ERANGE || isnan(value))\n                return VR_ERROR;\n        } else if (o->encoding == OBJ_ENCODING_INT) {\n            value = (long)o->ptr;\n        } else {\n            serverPanic(\"Unknown string encoding\");\n        }\n    }\n    *target = value;\n    return VR_OK;\n}\n\nint getLongDoubleFromObjectOrReply(client *c, robj *o, long double *target, const char *msg) {\n    long double value;\n    if (getLongDoubleFromObject(o, &value) != VR_OK) {\n        if (msg != NULL) {\n            addReplyError(c,(char*)msg);\n        } else {\n            addReplyError(c,\"value is not a valid float\");\n        }\n        return VR_ERROR;\n    }\n    *target = value;\n    return VR_OK;\n}\n\nint getLongLongFromObject(robj *o, long long *target) {\n    long long value;\n    char *eptr;\n\n    if (o == NULL) {\n        value = 0;\n    } else {\n        serverAssertWithInfo(NULL,o,o->type == OBJ_STRING);\n        if (sdsEncodedObject(o)) {\n            errno = 0;\n            value = strtoll(o->ptr, &eptr, 10);\n            if (isspace(((char*)o->ptr)[0]) || eptr[0] != '\\0' ||\n                errno == ERANGE)\n                return VR_ERROR;\n        } else if (o->encoding == OBJ_ENCODING_INT) {\n            value = (long)o->ptr;\n        } else {\n            serverPanic(\"Unknown string encoding\");\n        }\n    }\n    if (target) *target = value;\n    return VR_OK;\n}\n\nint getLongLongFromObjectOrReply(client *c, robj *o, long long *target, const char *msg) {\n    long long value;\n    if (getLongLongFromObject(o, &value) != VR_OK) {\n        if (msg != NULL) {\n            addReplyError(c,(char*)msg);\n        } else {\n            addReplyError(c,\"value is not an integer or out of range\");\n        }\n        return VR_ERROR;\n    }\n    *target = value;\n    return VR_OK;\n}\n\nint getLongFromObjectOrReply(client *c, robj *o, long *target, const char *msg) {\n    long long value;\n\n    if (getLongLongFromObjectOrReply(c, o, &value, msg) != VR_OK) return VR_ERROR;\n    if (value < LONG_MIN || value > LONG_MAX) {\n        if (msg != NULL) {\n            addReplyError(c,(char*)msg);\n        } else {\n            addReplyError(c,\"value is out of range\");\n        }\n        return VR_ERROR;\n    }\n    *target = value;\n    return VR_OK;\n}\n\nchar *strEncoding(int encoding) {\n    switch(encoding) {\n    case OBJ_ENCODING_RAW: return \"raw\";\n    case OBJ_ENCODING_INT: return \"int\";\n    case OBJ_ENCODING_HT: return \"hashtable\";\n    case OBJ_ENCODING_QUICKLIST: return \"quicklist\";\n    case OBJ_ENCODING_ZIPLIST: return \"ziplist\";\n    case OBJ_ENCODING_INTSET: return \"intset\";\n    case OBJ_ENCODING_SKIPLIST: return \"skiplist\";\n    case OBJ_ENCODING_EMBSTR: return \"embstr\";\n    default: return \"unknown\";\n    }\n}\n\n/* Given an object returns the min number of milliseconds the object was never\n * requested, using an approximated LRU algorithm. */\nunsigned long long estimateObjectIdleTime(robj *o) {\n    unsigned long long lruclock = LRU_CLOCK();\n    if (lruclock >= o->lru) {\n        return (lruclock - o->lru) * LRU_CLOCK_RESOLUTION;\n    } else {\n        return (lruclock + (LRU_CLOCK_MAX - o->lru)) *\n                    LRU_CLOCK_RESOLUTION;\n    }\n}\n\n/* Return the amount of memory used by the sds string at object->ptr\n * for a string object. */\nsize_t getStringObjectSdsUsedMemory(robj *o) {\n    serverAssertWithInfo(NULL,o,o->type == OBJ_STRING);\n    switch(o->encoding) {\n    case OBJ_ENCODING_RAW: return sdsZmallocSize(o->ptr);\n    case OBJ_ENCODING_EMBSTR: return dmalloc_size(o)-sizeof(robj);\n    default: return 0; /* Just integer encoding for now. */\n    }\n}\n\n/* This is a helper function for the OBJECT command. We need to lookup keys\n * without any modification of LRU or other parameters. */\nrobj *objectCommandLookup(client *c, robj *key) {\n    dictEntry *de;\n\n    if ((de = dictFind(c->db->dict,key->ptr)) == NULL) return NULL;\n    return (robj*) dictGetVal(de);\n}\n\nrobj *objectCommandLookupOrReply(client *c, robj *key, robj *reply) {\n    robj *o = objectCommandLookup(c,key);\n\n    if (!o) addReply(c, reply);\n    return o;\n}\n\n/* Object command allows to inspect the internals of an Redis Object.\n * Usage: OBJECT <refcount|encoding|idletime> <key> */\nvoid objectCommand(client *c) {\n    robj *o;\n\n    if (!strcasecmp(c->argv[1]->ptr,\"encoding\") && c->argc == 3) {\n        fetchInternalDbByKey(c,c->argv[2]);\n        lockDbRead(c->db);\n        if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk))\n                == NULL) {\n            unlockDb(c->db);\n            return;\n        }\n        addReplyBulkCString(c,strEncoding(o->encoding));\n        unlockDb(c->db);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"idletime\") && c->argc == 3) {\n        fetchInternalDbByKey(c,c->argv[2]);\n        lockDbRead(c->db);\n        if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk))\n                == NULL) {\n            unlockDb(c->db);\n            return;\n        }\n        addReplyLongLong(c,estimateObjectIdleTime(o)/1000);\n        unlockDb(c->db);\n    } else {\n        addReplyError(c,\"Syntax error. Try OBJECT (encoding|idletime)\");\n    }\n}\n"
  },
  {
    "path": "src/vr_object.h",
    "content": "#ifndef _VR_OBJECT_H_\n#define _VR_OBJECT_H_\n\n#define OBJ_SHARED_INTEGERS 10000\n#define OBJ_SHARED_BULKHDR_LEN 32\n\n/* Object types */\n#define OBJ_STRING 0\n#define OBJ_LIST 1\n#define OBJ_SET 2\n#define OBJ_ZSET 3\n#define OBJ_HASH 4\n\n/* Objects encoding. Some kind of objects like Strings and Hashes can be\n * internally represented in multiple ways. The 'encoding' field of the object\n * is set to one of this fields for this object. */\n#define OBJ_ENCODING_RAW 0     /* Raw representation */\n#define OBJ_ENCODING_INT 1     /* Encoded as integer */\n#define OBJ_ENCODING_HT 2      /* Encoded as hash table */\n#define OBJ_ENCODING_ZIPMAP 3  /* Encoded as zipmap */\n#define OBJ_ENCODING_LINKEDLIST 4 /* Encoded as regular linked list */\n#define OBJ_ENCODING_ZIPLIST 5 /* Encoded as ziplist */\n#define OBJ_ENCODING_INTSET 6  /* Encoded as intset */\n#define OBJ_ENCODING_SKIPLIST 7  /* Encoded as skiplist */\n#define OBJ_ENCODING_EMBSTR 8  /* Embedded sds string encoding */\n#define OBJ_ENCODING_QUICKLIST 9 /* Encoded as linked list of ziplists */\n\n#define OBJ_HASH_KEY 1\n#define OBJ_HASH_VALUE 2\n\n#define sdsEncodedObject(objptr) (objptr->encoding == OBJ_ENCODING_RAW || objptr->encoding == OBJ_ENCODING_EMBSTR)\n\n/* The actual Redis Object */\n#define LRU_BITS 24\n#define LRU_CLOCK_MAX ((1<<LRU_BITS)-1) /* Max value of obj->lru */\n#define LRU_CLOCK_RESOLUTION 1000 /* LRU clock resolution in ms */\ntypedef struct vr_object {\n    unsigned type:4;\n    unsigned encoding:4;\n    unsigned lru:LRU_BITS; /* lru time (relative to server.lruclock) */\n    unsigned constant:1;\n    int refcount;\n    void *ptr;\n} robj;\n\nvoid decrRefCount(robj *o);\nvoid decrRefCountVoid(void *o);\nvoid incrRefCount(robj *o);\nrobj *resetRefCount(robj *obj);\nvoid freeObject(robj *o);\nvoid freeObjectVoid(void *o);\nvoid freeStringObject(robj *o);\nvoid freeListObject(robj *o);\nvoid freeSetObject(robj *o);\nvoid freeZsetObject(robj *o);\nvoid freeHashObject(robj *o);\nrobj *createObject(int type, void *ptr);\nrobj *createStringObject(const char *ptr, size_t len);\nrobj *createRawStringObject(const char *ptr, size_t len);\nrobj *createEmbeddedStringObject(const char *ptr, size_t len);\nrobj *dupStringObject(robj *o);\nrobj *dupStringObjectUnconstant(robj *o);\nint isObjectRepresentableAsLongLong(robj *o, long long *llongval);\nrobj *tryObjectEncoding(robj *o);\nrobj *getDecodedObject(robj *o);\nsize_t stringObjectLen(robj *o);\nrobj *createStringObjectFromLongLong(long long value);\nrobj *createStringObjectFromLongDouble(long double value, int humanfriendly);\nrobj *createQuicklistObject(void);\nrobj *createZiplistObject(void);\nrobj *createSetObject(void);\nrobj *createIntsetObject(void);\nrobj *createHashObject(void);\nrobj *createZsetObject(void);\nrobj *createZsetZiplistObject(void);\nint getLongFromObjectOrReply(struct client *c, robj *o, long *target, const char *msg);\nint checkType(struct client *c, robj *o, int type);\nint getLongLongFromObjectOrReply(struct client *c, robj *o, long long *target, const char *msg);\nint getDoubleFromObjectOrReply(struct client *c, robj *o, double *target, const char *msg);\nint getLongLongFromObject(robj *o, long long *target);\nint getLongDoubleFromObject(robj *o, long double *target);\nint getLongDoubleFromObjectOrReply(struct client *c, robj *o, long double *target, const char *msg);\nchar *strEncoding(int encoding);\nint compareStringObjects(robj *a, robj *b);\nint collateStringObjects(robj *a, robj *b);\nint equalStringObjects(robj *a, robj *b);\nunsigned long long estimateObjectIdleTime(robj *o);\n\nsize_t getStringObjectSdsUsedMemory(robj *o);\n\nrobj *objectCommandLookup(struct client *c, robj *key);\nrobj *objectCommandLookupOrReply(struct client *c, robj *key, robj *reply);\nvoid objectCommand(struct client *c);\n\n#endif\n"
  },
  {
    "path": "src/vr_pubsub.c",
    "content": "#include <vr_core.h>\n\n/* Unsubscribe a client from a channel. Returns 1 if the operation succeeded, or\n * 0 if the client was not subscribed to the specified channel. */\nint pubsubUnsubscribeChannel(client *c, robj *channel, int notify) {\n    dictEntry *de;\n    dlist *clients;\n    dlistNode *ln;\n    int retval = 0;\n\n    /* Remove the channel from the client -> channels hash table */\n    incrRefCount(channel); /* channel may be just a pointer to the same object\n                            we have in the hash tables. Protect it... */\n    if (dictDelete(c->pubsub_channels,channel) == DICT_OK) {\n        retval = 1;\n        /* Remove the client from the channel -> clients list hash table */\n        de = dictFind(c->vel->pubsub_channels,channel);\n        serverAssertWithInfo(c,NULL,de != NULL);\n        clients = dictGetVal(de);\n        ln = dlistSearchKey(clients,c);\n        serverAssertWithInfo(c,NULL,ln != NULL);\n        dlistDelNode(clients,ln);\n        if (dlistLength(clients) == 0) {\n            /* Free the list and associated hash entry at all if this was\n             * the latest client, so that it will be possible to abuse\n             * Redis PUBSUB creating millions of channels. */\n            dictDelete(c->vel->pubsub_channels,channel);\n        }\n    }\n    /* Notify the client */\n    if (notify) {\n        addReply(c,shared.mbulkhdr[3]);\n        addReply(c,shared.unsubscribebulk);\n        addReplyBulk(c,channel);\n        addReplyLongLong(c,dictSize(c->pubsub_channels)+\n                       dlistLength(c->pubsub_patterns));\n\n    }\n    decrRefCount(channel); /* it is finally safe to release it */\n    return retval;\n}\n\n/* Unsubscribe from all the channels. Return the number of channels the\n * client was subscribed to. */\nint pubsubUnsubscribeAllChannels(client *c, int notify) {\n    dictIterator *di = dictGetSafeIterator(c->pubsub_channels);\n    dictEntry *de;\n    int count = 0;\n\n    while((de = dictNext(di)) != NULL) {\n        robj *channel = dictGetKey(de);\n\n        count += pubsubUnsubscribeChannel(c,channel,notify);\n    }\n    /* We were subscribed to nothing? Still reply to the client. */\n    if (notify && count == 0) {\n        addReply(c,shared.mbulkhdr[3]);\n        addReply(c,shared.unsubscribebulk);\n        addReply(c,shared.nullbulk);\n        addReplyLongLong(c,dictSize(c->pubsub_channels)+\n                       dlistLength(c->pubsub_patterns));\n    }\n    dictReleaseIterator(di);\n    return count;\n}\n\n/* Unsubscribe a client from a channel. Returns 1 if the operation succeeded, or\n * 0 if the client was not subscribed to the specified channel. */\nint pubsubUnsubscribePattern(client *c, robj *pattern, int notify) {\n    dlistNode *ln;\n    pubsubPattern pat;\n    int retval = 0;\n\n    incrRefCount(pattern); /* Protect the object. May be the same we remove */\n    if ((ln = dlistSearchKey(c->pubsub_patterns,pattern)) != NULL) {\n        retval = 1;\n        dlistDelNode(c->pubsub_patterns,ln);\n        pat.client = c;\n        pat.pattern = pattern;\n        ln = dlistSearchKey(c->vel->pubsub_patterns,&pat);\n        dlistDelNode(c->vel->pubsub_patterns,ln);\n    }\n    /* Notify the client */\n    if (notify) {\n        addReply(c,shared.mbulkhdr[3]);\n        addReply(c,shared.punsubscribebulk);\n        addReplyBulk(c,pattern);\n        addReplyLongLong(c,dictSize(c->pubsub_channels)+\n                       dlistLength(c->pubsub_patterns));\n    }\n    decrRefCount(pattern);\n    return retval;\n}\n\n/* Unsubscribe from all the patterns. Return the number of patterns the\n * client was subscribed from. */\nint pubsubUnsubscribeAllPatterns(client *c, int notify) {\n    dlistNode *ln;\n    dlistIter li;\n    int count = 0;\n\n    dlistRewind(c->pubsub_patterns,&li);\n    while ((ln = dlistNext(&li)) != NULL) {\n        robj *pattern = ln->value;\n\n        count += pubsubUnsubscribePattern(c,pattern,notify);\n    }\n    if (notify && count == 0) {\n        /* We were subscribed to nothing? Still reply to the client. */\n        addReply(c,shared.mbulkhdr[3]);\n        addReply(c,shared.punsubscribebulk);\n        addReply(c,shared.nullbulk);\n        addReplyLongLong(c,dictSize(c->pubsub_channels)+\n                       dlistLength(c->pubsub_patterns));\n    }\n    return count;\n}\n\n/*-----------------------------------------------------------------------------\n * Pubsub commands implementation\n *----------------------------------------------------------------------------*/\n\n\n/* Subscribe a client to a channel. Returns 1 if the operation succeeded, or\n * 0 if the client was already subscribed to that channel. */\nint pubsubSubscribeChannel(client *c, robj *channel) {\n    dictEntry *de;\n    dlist *clients = NULL;\n    int retval = 0;\n\n    /* Add the channel to the client -> channels hash table */\n    if (dictAdd(c->pubsub_channels,channel,NULL) == DICT_OK) {\n        retval = 1;\n        incrRefCount(channel);\n        /* Add the client to the channel -> list of clients hash table */\n        de = dictFind(c->vel->pubsub_channels,channel);\n        if (de == NULL) {\n            clients = dlistCreate();\n            dictAdd(c->vel->pubsub_channels,channel,clients);\n            incrRefCount(channel);\n        } else {\n            clients = dictGetVal(de);\n        }\n        dlistAddNodeTail(clients,c);\n    }\n    /* Notify the client */\n    addReply(c,shared.mbulkhdr[3]);\n    addReply(c,shared.subscribebulk);\n    addReplyBulk(c,channel);\n    addReplyLongLong(c,clientSubscriptionsCount(c));\n    return retval;\n}\n\nvoid subscribeCommand(client *c) {\n    int j;\n\n    for (j = 1; j < c->argc; j++)\n        pubsubSubscribeChannel(c,c->argv[j]);\n    c->flags |= CLIENT_PUBSUB;\n}\n\n/* Return the number of channels + patterns a client is subscribed to. */\nint clientSubscriptionsCount(client *c) {\n    return dictSize(c->pubsub_channels)+\n           dlistLength(c->pubsub_patterns);\n}\n\nvoid unsubscribeCommand(client *c) {\n    if (c->argc == 1) {\n        pubsubUnsubscribeAllChannels(c,1);\n    } else {\n        int j;\n\n        for (j = 1; j < c->argc; j++)\n            pubsubUnsubscribeChannel(c,c->argv[j],1);\n    }\n    if (clientSubscriptionsCount(c) == 0) c->flags &= ~CLIENT_PUBSUB;\n}\n\n/* Subscribe a client to a pattern. Returns 1 if the operation succeeded, or 0 if the client was already subscribed to that pattern. */\nint pubsubSubscribePattern(client *c, robj *pattern) {\n    int retval = 0;\n\n    if (dlistSearchKey(c->pubsub_patterns,pattern) == NULL) {\n        retval = 1;\n        pubsubPattern *pat;\n        dlistAddNodeTail(c->pubsub_patterns,pattern);\n        incrRefCount(pattern);\n        pat = dalloc(sizeof(*pat));\n        pat->pattern = getDecodedObject(pattern);\n        pat->client = c;\n        dlistAddNodeTail(c->vel->pubsub_patterns,pat);\n    }\n    /* Notify the client */\n    addReply(c,shared.mbulkhdr[3]);\n    addReply(c,shared.psubscribebulk);\n    addReplyBulk(c,pattern);\n    addReplyLongLong(c,clientSubscriptionsCount(c));\n    return retval;\n}\n\nvoid psubscribeCommand(client *c) {\n    int j;\n\n    for (j = 1; j < c->argc; j++)\n        pubsubSubscribePattern(c,c->argv[j]);\n    c->flags |= CLIENT_PUBSUB;\n}\n\nvoid punsubscribeCommand(client *c) {\n    if (c->argc == 1) {\n        pubsubUnsubscribeAllPatterns(c,1);\n    } else {\n        int j;\n\n        for (j = 1; j < c->argc; j++)\n            pubsubUnsubscribePattern(c,c->argv[j],1);\n    }\n    if (clientSubscriptionsCount(c) == 0) c->flags &= ~CLIENT_PUBSUB;\n}\n\n/* Publish a message */\nint pubsubPublishMessage(robj *channel, robj *message) {\n    int receivers = 0;\n    dictEntry *de;\n    dlistNode *ln;\n    dlistIter li;\n\n    /* Send to clients listening for that channel */\n    de = dictFind(server.pubsub_channels,channel);\n    if (de) {\n        dlist *list = dictGetVal(de);\n        dlistNode *ln;\n        dlistIter li;\n\n        dlistRewind(list,&li);\n        while ((ln = dlistNext(&li)) != NULL) {\n            client *c = ln->value;\n\n            addReply(c,shared.mbulkhdr[3]);\n            addReply(c,shared.messagebulk);\n            addReplyBulk(c,channel);\n            addReplyBulk(c,message);\n            receivers++;\n        }\n    }\n    /* Send to clients listening to matching channels */\n    if (dlistLength(server.pubsub_patterns)) {\n        dlistRewind(server.pubsub_patterns,&li);\n        channel = getDecodedObject(channel);\n        while ((ln = dlistNext(&li)) != NULL) {\n            pubsubPattern *pat = ln->value;\n\n            if (stringmatchlen((char*)pat->pattern->ptr,\n                                sdslen(pat->pattern->ptr),\n                                (char*)channel->ptr,\n                                sdslen(channel->ptr),0)) {\n                addReply(pat->client,shared.mbulkhdr[4]);\n                addReply(pat->client,shared.pmessagebulk);\n                addReplyBulk(pat->client,pat->pattern);\n                addReplyBulk(pat->client,channel);\n                addReplyBulk(pat->client,message);\n                receivers++;\n            }\n        }\n        decrRefCount(channel);\n    }\n    return receivers;\n}\n\n"
  },
  {
    "path": "src/vr_pubsub.h",
    "content": "#ifndef _VR_PUBSUB_H_\n#define _VR_PUBSUB_H_\n\ntypedef struct pubsubPattern {\n    client *client;\n    robj *pattern;\n} pubsubPattern;\n\nint pubsubUnsubscribeChannel(client *c, robj *channel, int notify);\nint pubsubUnsubscribeAllChannels(client *c, int notify);\nint pubsubUnsubscribePattern(client *c, robj *pattern, int notify);\nint pubsubUnsubscribeAllPatterns(client *c, int notify);\nint pubsubSubscribeChannel(client *c, robj *channel);\nint clientSubscriptionsCount(client *c);\nvoid subscribeCommand(client *c);\nvoid unsubscribeCommand(client *c);\nvoid psubscribeCommand(client *c);\nvoid punsubscribeCommand(client *c);\nint pubsubSubscribePattern(client *c, robj *pattern);\nint pubsubPublishMessage(robj *channel, robj *message);\n\n#endif\n"
  },
  {
    "path": "src/vr_quicklist.c",
    "content": "#include <string.h> /* for memcpy */\n\n#include <vr_core.h>\n\n#if defined(REDIS_TEST) || defined(REDIS_TEST_VERBOSE)\n#include <stdio.h> /* for printf (debug printing), snprintf (genstr) */\n#endif\n\n#ifndef REDIS_STATIC\n#define REDIS_STATIC static\n#endif\n\n/* Optimization levels for size-based filling */\nstatic const size_t optimization_level[] = {4096, 8192, 16384, 32768, 65536};\n\n/* Maximum size in bytes of any multi-element ziplist.\n * Larger values will live in their own isolated ziplists. */\n#define SIZE_SAFETY_LIMIT 8192\n\n/* Minimum ziplist size in bytes for attempting compression. */\n#define MIN_COMPRESS_BYTES 48\n\n/* Minimum size reduction in bytes to store compressed quicklistNode data.\n * This also prevents us from storing compression if the compression\n * resulted in a larger size than the original data. */\n#define MIN_COMPRESS_IMPROVE 8\n\n/* If not verbose testing, remove all debug printing. */\n#ifndef REDIS_TEST_VERBOSE\n#define D(...)\n#else\n#define D(...)                                                                 \\\n    do {                                                                       \\\n        printf(\"%s:%s:%d:\\t\", __FILE__, __FUNCTION__, __LINE__);               \\\n        printf(__VA_ARGS__);                                                   \\\n        printf(\"\\n\");                                                          \\\n    } while (0);\n#endif\n\n/* Simple way to give quicklistEntry structs default values with one call. */\n#define initEntry(e)                                                           \\\n    do {                                                                       \\\n        (e)->zi = (e)->value = NULL;                                           \\\n        (e)->longval = -123456789;                                             \\\n        (e)->quicklist = NULL;                                                 \\\n        (e)->node = NULL;                                                      \\\n        (e)->offset = 123456789;                                               \\\n        (e)->sz = 0;                                                           \\\n    } while (0)\n\n#if __GNUC__ >= 3\n#define likely(x) __builtin_expect(!!(x), 1)\n#define unlikely(x) __builtin_expect(!!(x), 0)\n#else\n#define likely(x) (x)\n#define unlikely(x) (x)\n#endif\n\n/* Create a new quicklist.\n * Free with quicklistRelease(). */\nquicklist *quicklistCreate(void) {\n    struct quicklist *quicklist;\n\n    quicklist = dalloc(sizeof(*quicklist));\n    quicklist->head = quicklist->tail = NULL;\n    quicklist->len = 0;\n    quicklist->count = 0;\n    quicklist->compress = 0;\n    quicklist->fill = -2;\n    return quicklist;\n}\n\n#define COMPRESS_MAX (1 << 16)\nvoid quicklistSetCompressDepth(quicklist *quicklist, int compress) {\n    if (compress > COMPRESS_MAX) {\n        compress = COMPRESS_MAX;\n    } else if (compress < 0) {\n        compress = 0;\n    }\n    quicklist->compress = compress;\n}\n\n#define FILL_MAX (1 << 15)\nvoid quicklistSetFill(quicklist *quicklist, int fill) {\n    if (fill > FILL_MAX) {\n        fill = FILL_MAX;\n    } else if (fill < -5) {\n        fill = -5;\n    }\n    quicklist->fill = fill;\n}\n\nvoid quicklistSetOptions(quicklist *quicklist, int fill, int depth) {\n    quicklistSetFill(quicklist, fill);\n    quicklistSetCompressDepth(quicklist, depth);\n}\n\n/* Create a new quicklist with some default parameters. */\nquicklist *quicklistNew(int fill, int compress) {\n    quicklist *quicklist = quicklistCreate();\n    quicklistSetOptions(quicklist, fill, compress);\n    return quicklist;\n}\n\nREDIS_STATIC quicklistNode *quicklistCreateNode(void) {\n    quicklistNode *node;\n    node = dalloc(sizeof(*node));\n    node->zl = NULL;\n    node->count = 0;\n    node->sz = 0;\n    node->next = node->prev = NULL;\n    node->encoding = QUICKLIST_NODE_ENCODING_RAW;\n    node->container = QUICKLIST_NODE_CONTAINER_ZIPLIST;\n    node->recompress = 0;\n    return node;\n}\n\n/* Return cached quicklist count */\nunsigned int quicklistCount(quicklist *ql) { return ql->count; }\n\n/* Free entire quicklist. */\nvoid quicklistRelease(quicklist *quicklist) {\n    unsigned long len;\n    quicklistNode *current, *next;\n\n    current = quicklist->head;\n    len = quicklist->len;\n    while (len--) {\n        next = current->next;\n\n        dfree(current->zl);\n        quicklist->count -= current->count;\n\n        dfree(current);\n\n        quicklist->len--;\n        current = next;\n    }\n    dfree(quicklist);\n}\n\n/* Compress the ziplist in 'node' and update encoding details.\n * Returns 1 if ziplist compressed successfully.\n * Returns 0 if compression failed or if ziplist too small to compress. */\nREDIS_STATIC int __quicklistCompressNode(quicklistNode *node) {\n#ifdef REDIS_TEST\n    node->attempted_compress = 1;\n#endif\n\n    /* Don't bother compressing small values */\n    if (node->sz < MIN_COMPRESS_BYTES)\n        return 0;\n\n    quicklistLZF *lzf = dalloc(sizeof(*lzf) + node->sz);\n\n    /* Cancel if compression fails or doesn't compress small enough */\n    if (((lzf->sz = lzf_compress(node->zl, node->sz, lzf->compressed,\n                                 node->sz)) == 0) ||\n        lzf->sz + MIN_COMPRESS_IMPROVE >= node->sz) {\n        /* lzf_compress aborts/rejects compression if value not compressable. */\n        dfree(lzf);\n        return 0;\n    }\n    lzf = drealloc(lzf, sizeof(*lzf) + lzf->sz);\n    dfree(node->zl);\n    node->zl = (unsigned char *)lzf;\n    node->encoding = QUICKLIST_NODE_ENCODING_LZF;\n    node->recompress = 0;\n    return 1;\n}\n\n/* Compress only uncompressed nodes. */\n#define quicklistCompressNode(_node)                                           \\\n    do {                                                                       \\\n        if ((_node) && (_node)->encoding == QUICKLIST_NODE_ENCODING_RAW) {     \\\n            __quicklistCompressNode((_node));                                  \\\n        }                                                                      \\\n    } while (0)\n\n/* Uncompress the ziplist in 'node' and update encoding details.\n * Returns 1 on successful decode, 0 on failure to decode. */\nREDIS_STATIC int __quicklistDecompressNode(quicklistNode *node) {\n#ifdef REDIS_TEST\n    node->attempted_compress = 0;\n#endif\n\n    void *decompressed = dalloc(node->sz);\n    quicklistLZF *lzf = (quicklistLZF *)node->zl;\n    if (lzf_decompress(lzf->compressed, lzf->sz, decompressed, node->sz) == 0) {\n        /* Someone requested decompress, but we can't decompress.  Not good. */\n        dfree(decompressed);\n        return 0;\n    }\n    dfree(lzf);\n    node->zl = decompressed;\n    node->encoding = QUICKLIST_NODE_ENCODING_RAW;\n    return 1;\n}\n\n/* Decompress only compressed nodes. */\n#define quicklistDecompressNode(_node)                                         \\\n    do {                                                                       \\\n        if ((_node) && (_node)->encoding == QUICKLIST_NODE_ENCODING_LZF) {     \\\n            __quicklistDecompressNode((_node));                                \\\n        }                                                                      \\\n    } while (0)\n\n/* Force node to not be immediately re-compresable */\n#define quicklistDecompressNodeForUse(_node)                                   \\\n    do {                                                                       \\\n        if ((_node) && (_node)->encoding == QUICKLIST_NODE_ENCODING_LZF) {     \\\n            __quicklistDecompressNode((_node));                                \\\n            (_node)->recompress = 1;                                           \\\n        }                                                                      \\\n    } while (0)\n\n/* Extract the raw LZF data from this quicklistNode.\n * Pointer to LZF data is assigned to '*data'.\n * Return value is the length of compressed LZF data. */\nsize_t quicklistGetLzf(const quicklistNode *node, void **data) {\n    quicklistLZF *lzf = (quicklistLZF *)node->zl;\n    *data = lzf->compressed;\n    return lzf->sz;\n}\n\n#define quicklistAllowsCompression(_ql) ((_ql)->compress != 0)\n\n/* Force 'quicklist' to meet compression guidelines set by compress depth.\n * The only way to guarantee interior nodes get compressed is to iterate\n * to our \"interior\" compress depth then compress the next node we find.\n * If compress depth is larger than the entire list, we return immediately. */\nREDIS_STATIC void __quicklistCompress(const quicklist *quicklist,\n                                      quicklistNode *node) {\n    /* If length is less than our compress depth (from both sides),\n     * we can't compress anything. */\n    if (!quicklistAllowsCompression(quicklist) ||\n        quicklist->len < (unsigned int)(quicklist->compress * 2))\n        return;\n\n#if 0\n    /* Optimized cases for small depth counts */\n    if (quicklist->compress == 1) {\n        quicklistNode *h = quicklist->head, *t = quicklist->tail;\n        quicklistDecompressNode(h);\n        quicklistDecompressNode(t);\n        if (h != node && t != node)\n            quicklistCompressNode(node);\n        return;\n    } else if (quicklist->compress == 2) {\n        quicklistNode *h = quicklist->head, *hn = h->next, *hnn = hn->next;\n        quicklistNode *t = quicklist->tail, *tp = t->prev, *tpp = tp->prev;\n        quicklistDecompressNode(h);\n        quicklistDecompressNode(hn);\n        quicklistDecompressNode(t);\n        quicklistDecompressNode(tp);\n        if (h != node && hn != node && t != node && tp != node) {\n            quicklistCompressNode(node);\n        }\n        if (hnn != t) {\n            quicklistCompressNode(hnn);\n        }\n        if (tpp != h) {\n            quicklistCompressNode(tpp);\n        }\n        return;\n    }\n#endif\n\n    /* Iterate until we reach compress depth for both sides of the list.a\n     * Note: because we do length checks at the *top* of this function,\n     *       we can skip explicit null checks below. Everything exists. */\n    quicklistNode *forward = quicklist->head;\n    quicklistNode *reverse = quicklist->tail;\n    int depth = 0;\n    int in_depth = 0;\n    while (depth++ < quicklist->compress) {\n        quicklistDecompressNode(forward);\n        quicklistDecompressNode(reverse);\n\n        if (forward == node || reverse == node)\n            in_depth = 1;\n\n        if (forward == reverse)\n            return;\n\n        forward = forward->next;\n        reverse = reverse->prev;\n    }\n\n    if (!in_depth)\n        quicklistCompressNode(node);\n\n    if (depth > 2) {\n        /* At this point, forward and reverse are one node beyond depth */\n        quicklistCompressNode(forward);\n        quicklistCompressNode(reverse);\n    }\n}\n\n#define quicklistCompress(_ql, _node)                                          \\\n    do {                                                                       \\\n        if ((_node)->recompress)                                               \\\n            quicklistCompressNode((_node));                                    \\\n        else                                                                   \\\n            __quicklistCompress((_ql), (_node));                               \\\n    } while (0)\n\n/* If we previously used quicklistDecompressNodeForUse(), just recompress. */\n#define quicklistRecompressOnly(_ql, _node)                                    \\\n    do {                                                                       \\\n        if ((_node)->recompress)                                               \\\n            quicklistCompressNode((_node));                                    \\\n    } while (0)\n\n/* Insert 'new_node' after 'old_node' if 'after' is 1.\n * Insert 'new_node' before 'old_node' if 'after' is 0.\n * Note: 'new_node' is *always* uncompressed, so if we assign it to\n *       head or tail, we do not need to uncompress it. */\nREDIS_STATIC void __quicklistInsertNode(quicklist *quicklist,\n                                        quicklistNode *old_node,\n                                        quicklistNode *new_node, int after) {\n    if (after) {\n        new_node->prev = old_node;\n        if (old_node) {\n            new_node->next = old_node->next;\n            if (old_node->next)\n                old_node->next->prev = new_node;\n            old_node->next = new_node;\n        }\n        if (quicklist->tail == old_node)\n            quicklist->tail = new_node;\n    } else {\n        new_node->next = old_node;\n        if (old_node) {\n            new_node->prev = old_node->prev;\n            if (old_node->prev)\n                old_node->prev->next = new_node;\n            old_node->prev = new_node;\n        }\n        if (quicklist->head == old_node)\n            quicklist->head = new_node;\n    }\n    /* If this insert creates the only element so far, initialize head/tail. */\n    if (quicklist->len == 0) {\n        quicklist->head = quicklist->tail = new_node;\n    }\n\n    if (old_node)\n        quicklistCompress(quicklist, old_node);\n\n    quicklist->len++;\n}\n\n/* Wrappers for node inserting around existing node. */\nREDIS_STATIC void _quicklistInsertNodeBefore(quicklist *quicklist,\n                                             quicklistNode *old_node,\n                                             quicklistNode *new_node) {\n    __quicklistInsertNode(quicklist, old_node, new_node, 0);\n}\n\nREDIS_STATIC void _quicklistInsertNodeAfter(quicklist *quicklist,\n                                            quicklistNode *old_node,\n                                            quicklistNode *new_node) {\n    __quicklistInsertNode(quicklist, old_node, new_node, 1);\n}\n\nREDIS_STATIC int\n_quicklistNodeSizeMeetsOptimizationRequirement(const size_t sz,\n                                               const int fill) {\n    if (fill >= 0)\n        return 0;\n\n    size_t offset = (-fill) - 1;\n    if (offset < (sizeof(optimization_level) / sizeof(*optimization_level))) {\n        if (sz <= optimization_level[offset]) {\n            return 1;\n        } else {\n            return 0;\n        }\n    } else {\n        return 0;\n    }\n}\n\n#define sizeMeetsSafetyLimit(sz) ((sz) <= SIZE_SAFETY_LIMIT)\n\nREDIS_STATIC int _quicklistNodeAllowInsert(const quicklistNode *node,\n                                           const int fill, const size_t sz) {\n    if (unlikely(!node))\n        return 0;\n\n    int ziplist_overhead;\n    /* size of previous offset */\n    if (sz < 254)\n        ziplist_overhead = 1;\n    else\n        ziplist_overhead = 5;\n\n    /* size of forward offset */\n    if (sz < 64)\n        ziplist_overhead += 1;\n    else if (likely(sz < 16384))\n        ziplist_overhead += 2;\n    else\n        ziplist_overhead += 5;\n\n    /* new_sz overestimates if 'sz' encodes to an integer type */\n    unsigned int new_sz = node->sz + sz + ziplist_overhead;\n    if (likely(_quicklistNodeSizeMeetsOptimizationRequirement(new_sz, fill)))\n        return 1;\n    else if (!sizeMeetsSafetyLimit(new_sz))\n        return 0;\n    else if ((int)node->count < fill)\n        return 1;\n    else\n        return 0;\n}\n\nREDIS_STATIC int _quicklistNodeAllowMerge(const quicklistNode *a,\n                                          const quicklistNode *b,\n                                          const int fill) {\n    if (!a || !b)\n        return 0;\n\n    /* approximate merged ziplist size (- 11 to remove one ziplist\n     * header/trailer) */\n    unsigned int merge_sz = a->sz + b->sz - 11;\n    if (likely(_quicklistNodeSizeMeetsOptimizationRequirement(merge_sz, fill)))\n        return 1;\n    else if (!sizeMeetsSafetyLimit(merge_sz))\n        return 0;\n    else if ((int)(a->count + b->count) <= fill)\n        return 1;\n    else\n        return 0;\n}\n\n#define quicklistNodeUpdateSz(node)                                            \\\n    do {                                                                       \\\n        (node)->sz = ziplistBlobLen((node)->zl);                               \\\n    } while (0)\n\n/* Add new entry to head node of quicklist.\n *\n * Returns 0 if used existing head.\n * Returns 1 if new head created. */\nint quicklistPushHead(quicklist *quicklist, void *value, size_t sz) {\n    quicklistNode *orig_head = quicklist->head;\n    if (likely(\n            _quicklistNodeAllowInsert(quicklist->head, quicklist->fill, sz))) {\n        quicklist->head->zl =\n            ziplistPush(quicklist->head->zl, value, sz, ZIPLIST_HEAD);\n        quicklistNodeUpdateSz(quicklist->head);\n    } else {\n        quicklistNode *node = quicklistCreateNode();\n        node->zl = ziplistPush(ziplistNew(), value, sz, ZIPLIST_HEAD);\n\n        quicklistNodeUpdateSz(node);\n        _quicklistInsertNodeBefore(quicklist, quicklist->head, node);\n    }\n    quicklist->count++;\n    quicklist->head->count++;\n    return (orig_head != quicklist->head);\n}\n\n/* Add new entry to tail node of quicklist.\n *\n * Returns 0 if used existing tail.\n * Returns 1 if new tail created. */\nint quicklistPushTail(quicklist *quicklist, void *value, size_t sz) {\n    quicklistNode *orig_tail = quicklist->tail;\n    if (likely(\n            _quicklistNodeAllowInsert(quicklist->tail, quicklist->fill, sz))) {\n        quicklist->tail->zl =\n            ziplistPush(quicklist->tail->zl, value, sz, ZIPLIST_TAIL);\n        quicklistNodeUpdateSz(quicklist->tail);\n    } else {\n        quicklistNode *node = quicklistCreateNode();\n        node->zl = ziplistPush(ziplistNew(), value, sz, ZIPLIST_TAIL);\n\n        quicklistNodeUpdateSz(node);\n        _quicklistInsertNodeAfter(quicklist, quicklist->tail, node);\n    }\n    quicklist->count++;\n    quicklist->tail->count++;\n    return (orig_tail != quicklist->tail);\n}\n\n/* Create new node consisting of a pre-formed ziplist.\n * Used for loading RDBs where entire ziplists have been stored\n * to be retrieved later. */\nvoid quicklistAppendZiplist(quicklist *quicklist, unsigned char *zl) {\n    quicklistNode *node = quicklistCreateNode();\n\n    node->zl = zl;\n    node->count = ziplistLen(node->zl);\n    node->sz = ziplistBlobLen(zl);\n\n    _quicklistInsertNodeAfter(quicklist, quicklist->tail, node);\n    quicklist->count += node->count;\n}\n\n/* Append all values of ziplist 'zl' individually into 'quicklist'.\n *\n * This allows us to restore old RDB ziplists into new quicklists\n * with smaller ziplist sizes than the saved RDB ziplist.\n *\n * Returns 'quicklist' argument. Frees passed-in ziplist 'zl' */\nquicklist *quicklistAppendValuesFromZiplist(quicklist *quicklist,\n                                            unsigned char *zl) {\n    unsigned char *value;\n    unsigned int sz;\n    long long longval;\n    char longstr[32] = {0};\n\n    unsigned char *p = ziplistIndex(zl, 0);\n    while (ziplistGet(p, &value, &sz, &longval)) {\n        if (!value) {\n            /* Write the longval as a string so we can re-add it */\n            sz = ll2string(longstr, sizeof(longstr), longval);\n            value = (unsigned char *)longstr;\n        }\n        quicklistPushTail(quicklist, value, sz);\n        p = ziplistNext(zl, p);\n    }\n    dfree(zl);\n    return quicklist;\n}\n\n/* Create new (potentially multi-node) quicklist from a single existing ziplist.\n *\n * Returns new quicklist.  Frees passed-in ziplist 'zl'. */\nquicklist *quicklistCreateFromZiplist(int fill, int compress,\n                                      unsigned char *zl) {\n    return quicklistAppendValuesFromZiplist(quicklistNew(fill, compress), zl);\n}\n\n#define quicklistDeleteIfEmpty(ql, n)                                          \\\n    do {                                                                       \\\n        if ((n)->count == 0) {                                                 \\\n            __quicklistDelNode((ql), (n));                                     \\\n            (n) = NULL;                                                        \\\n        }                                                                      \\\n    } while (0)\n\nREDIS_STATIC void __quicklistDelNode(quicklist *quicklist,\n                                     quicklistNode *node) {\n    if (node->next)\n        node->next->prev = node->prev;\n    if (node->prev)\n        node->prev->next = node->next;\n\n    if (node == quicklist->tail) {\n        quicklist->tail = node->prev;\n    }\n\n    if (node == quicklist->head) {\n        quicklist->head = node->next;\n    }\n\n    /* If we deleted a node within our compress depth, we\n     * now have compressed nodes needing to be decompressed. */\n    __quicklistCompress(quicklist, NULL);\n\n    quicklist->count -= node->count;\n\n    dfree(node->zl);\n    dfree(node);\n    quicklist->len--;\n}\n\n/* Delete one entry from list given the node for the entry and a pointer\n * to the entry in the node.\n *\n * Note: quicklistDelIndex() *requires* uncompressed nodes because you\n *       already had to get *p from an uncompressed node somewhere.\n *\n * Returns 1 if the entire node was deleted, 0 if node still exists.\n * Also updates in/out param 'p' with the next offset in the ziplist. */\nREDIS_STATIC int quicklistDelIndex(quicklist *quicklist, quicklistNode *node,\n                                   unsigned char **p) {\n    int gone = 0;\n\n    node->zl = ziplistDelete(node->zl, p);\n    node->count--;\n    if (node->count == 0) {\n        gone = 1;\n        __quicklistDelNode(quicklist, node);\n    } else {\n        quicklistNodeUpdateSz(node);\n    }\n    quicklist->count--;\n    /* If we deleted the node, the original node is no longer valid */\n    return gone ? 1 : 0;\n}\n\n/* Delete one element represented by 'entry'\n *\n * 'entry' stores enough metadata to delete the proper position in\n * the correct ziplist in the correct quicklist node. */\nvoid quicklistDelEntry(quicklistIter *iter, quicklistEntry *entry) {\n    quicklistNode *prev = entry->node->prev;\n    quicklistNode *next = entry->node->next;\n    int deleted_node = quicklistDelIndex((quicklist *)entry->quicklist,\n                                         entry->node, &entry->zi);\n\n    /* after delete, the zi is now invalid for any future usage. */\n    iter->zi = NULL;\n\n    /* If current node is deleted, we must update iterator node and offset. */\n    if (deleted_node) {\n        if (iter->direction == AL_START_HEAD) {\n            iter->current = next;\n            iter->offset = 0;\n        } else if (iter->direction == AL_START_TAIL) {\n            iter->current = prev;\n            iter->offset = -1;\n        }\n    }\n    /* else if (!deleted_node), no changes needed.\n     * we already reset iter->zi above, and the existing iter->offset\n     * doesn't move again because:\n     *   - [1, 2, 3] => delete offset 1 => [1, 3]: next element still offset 1\n     *   - [1, 2, 3] => delete offset 0 => [2, 3]: next element still offset 0\n     *  if we deleted the last element at offet N and now\n     *  length of this ziplist is N-1, the next call into\n     *  quicklistNext() will jump to the next node. */\n}\n\n/* Replace quicklist entry at offset 'index' by 'data' with length 'sz'.\n *\n * Returns 1 if replace happened.\n * Returns 0 if replace failed and no changes happened. */\nint quicklistReplaceAtIndex(quicklist *quicklist, long index, void *data,\n                            int sz) {\n    quicklistEntry entry;\n    if (likely(quicklistIndex(quicklist, index, &entry))) {\n        /* quicklistIndex provides an uncompressed node */\n        entry.node->zl = ziplistDelete(entry.node->zl, &entry.zi);\n        entry.node->zl = ziplistInsert(entry.node->zl, entry.zi, data, sz);\n        quicklistCompress(quicklist, entry.node);\n        return 1;\n    } else {\n        return 0;\n    }\n}\n\n/* Given two nodes, try to merge their ziplists.\n *\n * This helps us not have a quicklist with 3 element ziplists if\n * our fill factor can handle much higher levels.\n *\n * Note: 'a' must be to the LEFT of 'b'.\n *\n * After calling this function, both 'a' and 'b' should be considered\n * unusable.  The return value from this function must be used\n * instead of re-using any of the quicklistNode input arguments.\n *\n * Returns the input node picked to merge against or NULL if\n * merging was not possible. */\nREDIS_STATIC quicklistNode *_quicklistZiplistMerge(quicklist *quicklist,\n                                                   quicklistNode *a,\n                                                   quicklistNode *b) {\n    D(\"Requested merge (a,b) (%u, %u)\", a->count, b->count);\n\n    quicklistDecompressNode(a);\n    quicklistDecompressNode(b);\n    if ((ziplistMerge(&a->zl, &b->zl))) {\n        /* We merged ziplists! Now remove the unused quicklistNode. */\n        quicklistNode *keep = NULL, *nokeep = NULL;\n        if (!a->zl) {\n            nokeep = a;\n            keep = b;\n        } else if (!b->zl) {\n            nokeep = b;\n            keep = a;\n        }\n        keep->count = ziplistLen(keep->zl);\n        quicklistNodeUpdateSz(keep);\n\n        nokeep->count = 0;\n        __quicklistDelNode(quicklist, nokeep);\n        quicklistCompress(quicklist, keep);\n        return keep;\n    } else {\n        /* else, the merge returned NULL and nothing changed. */\n        return NULL;\n    }\n}\n\n/* Attempt to merge ziplists within two nodes on either side of 'center'.\n *\n * We attempt to merge:\n *   - (center->prev->prev, center->prev)\n *   - (center->next, center->next->next)\n *   - (center->prev, center)\n *   - (center, center->next)\n */\nREDIS_STATIC void _quicklistMergeNodes(quicklist *quicklist,\n                                       quicklistNode *center) {\n    int fill = quicklist->fill;\n    quicklistNode *prev, *prev_prev, *next, *next_next, *target;\n    prev = prev_prev = next = next_next = target = NULL;\n\n    if (center->prev) {\n        prev = center->prev;\n        if (center->prev->prev)\n            prev_prev = center->prev->prev;\n    }\n\n    if (center->next) {\n        next = center->next;\n        if (center->next->next)\n            next_next = center->next->next;\n    }\n\n    /* Try to merge prev_prev and prev */\n    if (_quicklistNodeAllowMerge(prev, prev_prev, fill)) {\n        _quicklistZiplistMerge(quicklist, prev_prev, prev);\n        prev_prev = prev = NULL; /* they could have moved, invalidate them. */\n    }\n\n    /* Try to merge next and next_next */\n    if (_quicklistNodeAllowMerge(next, next_next, fill)) {\n        _quicklistZiplistMerge(quicklist, next, next_next);\n        next = next_next = NULL; /* they could have moved, invalidate them. */\n    }\n\n    /* Try to merge center node and previous node */\n    if (_quicklistNodeAllowMerge(center, center->prev, fill)) {\n        target = _quicklistZiplistMerge(quicklist, center->prev, center);\n        center = NULL; /* center could have been deleted, invalidate it. */\n    } else {\n        /* else, we didn't merge here, but target needs to be valid below. */\n        target = center;\n    }\n\n    /* Use result of center merge (or original) to merge with next node. */\n    if (_quicklistNodeAllowMerge(target, target->next, fill)) {\n        _quicklistZiplistMerge(quicklist, target, target->next);\n    }\n}\n\n/* Split 'node' into two parts, parameterized by 'offset' and 'after'.\n *\n * The 'after' argument controls which quicklistNode gets returned.\n * If 'after'==1, returned node has elements after 'offset'.\n *                input node keeps elements up to 'offset', including 'offset'.\n * If 'after'==0, returned node has elements up to 'offset', including 'offset'.\n *                input node keeps elements after 'offset'.\n *\n * If 'after'==1, returned node will have elements _after_ 'offset'.\n *                The returned node will have elements [OFFSET+1, END].\n *                The input node keeps elements [0, OFFSET].\n *\n * If 'after'==0, returned node will keep elements up to and including 'offset'.\n *                The returned node will have elements [0, OFFSET].\n *                The input node keeps elements [OFFSET+1, END].\n *\n * The input node keeps all elements not taken by the returned node.\n *\n * Returns newly created node or NULL if split not possible. */\nREDIS_STATIC quicklistNode *_quicklistSplitNode(quicklistNode *node, int offset,\n                                                int after) {\n    size_t zl_sz = node->sz;\n\n    quicklistNode *new_node = quicklistCreateNode();\n    new_node->zl = dalloc(zl_sz);\n\n    /* Copy original ziplist so we can split it */\n    memcpy(new_node->zl, node->zl, zl_sz);\n\n    /* -1 here means \"continue deleting until the list ends\" */\n    int orig_start = after ? offset + 1 : 0;\n    int orig_extent = after ? -1 : offset;\n    int new_start = after ? 0 : offset;\n    int new_extent = after ? offset + 1 : -1;\n\n    D(\"After %d (%d); ranges: [%d, %d], [%d, %d]\", after, offset, orig_start,\n      orig_extent, new_start, new_extent);\n\n    node->zl = ziplistDeleteRange(node->zl, orig_start, orig_extent);\n    node->count = ziplistLen(node->zl);\n    quicklistNodeUpdateSz(node);\n\n    new_node->zl = ziplistDeleteRange(new_node->zl, new_start, new_extent);\n    new_node->count = ziplistLen(new_node->zl);\n    quicklistNodeUpdateSz(new_node);\n\n    D(\"After split lengths: orig (%d), new (%d)\", node->count, new_node->count);\n    return new_node;\n}\n\n/* Insert a new entry before or after existing entry 'entry'.\n *\n * If after==1, the new value is inserted after 'entry', otherwise\n * the new value is inserted before 'entry'. */\nREDIS_STATIC void _quicklistInsert(quicklist *quicklist, quicklistEntry *entry,\n                                   void *value, const size_t sz, int after) {\n    int full = 0, at_tail = 0, at_head = 0, full_next = 0, full_prev = 0;\n    int fill = quicklist->fill;\n    quicklistNode *node = entry->node;\n    quicklistNode *new_node = NULL;\n\n    if (!node) {\n        /* we have no reference node, so let's create only node in the list */\n        D(\"No node given!\");\n        new_node = quicklistCreateNode();\n        new_node->zl = ziplistPush(ziplistNew(), value, sz, ZIPLIST_HEAD);\n        __quicklistInsertNode(quicklist, NULL, new_node, after);\n        new_node->count++;\n        quicklist->count++;\n        return;\n    }\n\n    /* Populate accounting flags for easier boolean checks later */\n    if (!_quicklistNodeAllowInsert(node, fill, sz)) {\n        D(\"Current node is full with count %d with requested fill %lu\",\n          node->count, fill);\n        full = 1;\n    }\n\n    if (after && (entry->offset == node->count)) {\n        D(\"At Tail of current ziplist\");\n        at_tail = 1;\n        if (!_quicklistNodeAllowInsert(node->next, fill, sz)) {\n            D(\"Next node is full too.\");\n            full_next = 1;\n        }\n    }\n\n    if (!after && (entry->offset == 0)) {\n        D(\"At Head\");\n        at_head = 1;\n        if (!_quicklistNodeAllowInsert(node->prev, fill, sz)) {\n            D(\"Prev node is full too.\");\n            full_prev = 1;\n        }\n    }\n\n    /* Now determine where and how to insert the new element */\n    if (!full && after) {\n        D(\"Not full, inserting after current position.\");\n        quicklistDecompressNodeForUse(node);\n        unsigned char *next = ziplistNext(node->zl, entry->zi);\n        if (next == NULL) {\n            node->zl = ziplistPush(node->zl, value, sz, ZIPLIST_TAIL);\n        } else {\n            node->zl = ziplistInsert(node->zl, next, value, sz);\n        }\n        node->count++;\n        quicklistNodeUpdateSz(node);\n        quicklistRecompressOnly(quicklist, node);\n    } else if (!full && !after) {\n        D(\"Not full, inserting before current position.\");\n        quicklistDecompressNodeForUse(node);\n        node->zl = ziplistInsert(node->zl, entry->zi, value, sz);\n        node->count++;\n        quicklistNodeUpdateSz(node);\n        quicklistRecompressOnly(quicklist, node);\n    } else if (full && at_tail && node->next && !full_next && after) {\n        /* If we are: at tail, next has free space, and inserting after:\n         *   - insert entry at head of next node. */\n        D(\"Full and tail, but next isn't full; inserting next node head\");\n        new_node = node->next;\n        quicklistDecompressNodeForUse(new_node);\n        new_node->zl = ziplistPush(new_node->zl, value, sz, ZIPLIST_HEAD);\n        new_node->count++;\n        quicklistNodeUpdateSz(new_node);\n        quicklistRecompressOnly(quicklist, new_node);\n    } else if (full && at_head && node->prev && !full_prev && !after) {\n        /* If we are: at head, previous has free space, and inserting before:\n         *   - insert entry at tail of previous node. */\n        D(\"Full and head, but prev isn't full, inserting prev node tail\");\n        new_node = node->prev;\n        quicklistDecompressNodeForUse(new_node);\n        new_node->zl = ziplistPush(new_node->zl, value, sz, ZIPLIST_TAIL);\n        new_node->count++;\n        quicklistNodeUpdateSz(new_node);\n        quicklistRecompressOnly(quicklist, new_node);\n    } else if (full && ((at_tail && node->next && full_next && after) ||\n                        (at_head && node->prev && full_prev && !after))) {\n        /* If we are: full, and our prev/next is full, then:\n         *   - create new node and attach to quicklist */\n        D(\"\\tprovisioning new node...\");\n        new_node = quicklistCreateNode();\n        new_node->zl = ziplistPush(ziplistNew(), value, sz, ZIPLIST_HEAD);\n        new_node->count++;\n        quicklistNodeUpdateSz(new_node);\n        __quicklistInsertNode(quicklist, node, new_node, after);\n    } else if (full) {\n        /* else, node is full we need to split it. */\n        /* covers both after and !after cases */\n        D(\"\\tsplitting node...\");\n        quicklistDecompressNodeForUse(node);\n        new_node = _quicklistSplitNode(node, entry->offset, after);\n        new_node->zl = ziplistPush(new_node->zl, value, sz,\n                                   after ? ZIPLIST_HEAD : ZIPLIST_TAIL);\n        new_node->count++;\n        quicklistNodeUpdateSz(new_node);\n        __quicklistInsertNode(quicklist, node, new_node, after);\n        _quicklistMergeNodes(quicklist, node);\n    }\n\n    quicklist->count++;\n}\n\nvoid quicklistInsertBefore(quicklist *quicklist, quicklistEntry *entry,\n                           void *value, const size_t sz) {\n    _quicklistInsert(quicklist, entry, value, sz, 0);\n}\n\nvoid quicklistInsertAfter(quicklist *quicklist, quicklistEntry *entry,\n                          void *value, const size_t sz) {\n    _quicklistInsert(quicklist, entry, value, sz, 1);\n}\n\n/* Delete a range of elements from the quicklist.\n *\n * elements may span across multiple quicklistNodes, so we\n * have to be careful about tracking where we start and end.\n *\n * Returns 1 if entries were deleted, 0 if nothing was deleted. */\nint quicklistDelRange(quicklist *quicklist, const long start,\n                      const long count) {\n    if (count <= 0)\n        return 0;\n\n    unsigned long extent = count; /* range is inclusive of start position */\n\n    if (start >= 0 && extent > (quicklist->count - start)) {\n        /* if requesting delete more elements than exist, limit to list size. */\n        extent = quicklist->count - start;\n    } else if (start < 0 && extent > (unsigned long)(-start)) {\n        /* else, if at negative offset, limit max size to rest of list. */\n        extent = -start; /* c.f. LREM -29 29; just delete until end. */\n    }\n\n    quicklistEntry entry;\n    if (!quicklistIndex(quicklist, start, &entry))\n        return 0;\n\n    D(\"Quicklist delete request for start %ld, count %ld, extent: %ld\", start,\n      count, extent);\n    quicklistNode *node = entry.node;\n\n    /* iterate over next nodes until everything is deleted. */\n    while (extent) {\n        quicklistNode *next = node->next;\n\n        unsigned long del;\n        int delete_entire_node = 0;\n        if (entry.offset == 0 && extent >= node->count) {\n            /* If we are deleting more than the count of this node, we\n             * can just delete the entire node without ziplist math. */\n            delete_entire_node = 1;\n            del = node->count;\n        } else if (entry.offset >= 0 && extent >= node->count) {\n            /* If deleting more nodes after this one, calculate delete based\n             * on size of current node. */\n            del = node->count - entry.offset;\n        } else if (entry.offset < 0) {\n            /* If offset is negative, we are in the first run of this loop\n             * and we are deleting the entire range\n             * from this start offset to end of list.  Since the Negative\n             * offset is the number of elements until the tail of the list,\n             * just use it directly as the deletion count. */\n            del = -entry.offset;\n\n            /* If the positive offset is greater than the remaining extent,\n             * we only delete the remaining extent, not the entire offset.\n             */\n            if (del > extent)\n                del = extent;\n        } else {\n            /* else, we are deleting less than the extent of this node, so\n             * use extent directly. */\n            del = extent;\n        }\n\n        D(\"[%ld]: asking to del: %ld because offset: %d; (ENTIRE NODE: %d), \"\n          \"node count: %u\",\n          extent, del, entry.offset, delete_entire_node, node->count);\n\n        if (delete_entire_node) {\n            __quicklistDelNode(quicklist, node);\n        } else {\n            quicklistDecompressNodeForUse(node);\n            node->zl = ziplistDeleteRange(node->zl, entry.offset, del);\n            quicklistNodeUpdateSz(node);\n            node->count -= del;\n            quicklist->count -= del;\n            quicklistDeleteIfEmpty(quicklist, node);\n            if (node)\n                quicklistRecompressOnly(quicklist, node);\n        }\n\n        extent -= del;\n\n        node = next;\n\n        entry.offset = 0;\n    }\n    return 1;\n}\n\n/* Passthrough to ziplistCompare() */\nint quicklistCompare(unsigned char *p1, unsigned char *p2, int p2_len) {\n    return ziplistCompare(p1, p2, p2_len);\n}\n\n/* Returns a quicklist iterator 'iter'. After the initialization every\n * call to quicklistNext() will return the next element of the quicklist. */\nquicklistIter *quicklistGetIterator(const quicklist *quicklist, int direction) {\n    quicklistIter *iter;\n\n    iter = dalloc(sizeof(*iter));\n\n    if (direction == AL_START_HEAD) {\n        iter->current = quicklist->head;\n        iter->offset = 0;\n    } else if (direction == AL_START_TAIL) {\n        iter->current = quicklist->tail;\n        iter->offset = -1;\n    }\n\n    iter->direction = direction;\n    iter->quicklist = quicklist;\n\n    iter->zi = NULL;\n\n    return iter;\n}\n\n/* Initialize an iterator at a specific offset 'idx' and make the iterator\n * return nodes in 'direction' direction. */\nquicklistIter *quicklistGetIteratorAtIdx(const quicklist *quicklist,\n                                         const int direction,\n                                         const long long idx) {\n    quicklistEntry entry;\n\n    if (quicklistIndex(quicklist, idx, &entry)) {\n        quicklistIter *base = quicklistGetIterator(quicklist, direction);\n        base->zi = NULL;\n        base->current = entry.node;\n        base->offset = entry.offset;\n        return base;\n    } else {\n        return NULL;\n    }\n}\n\n/* Release iterator.\n * If we still have a valid current node, then re-encode current node. */\nvoid quicklistReleaseIterator(quicklistIter *iter) {\n    if (iter->current)\n        quicklistCompress(iter->quicklist, iter->current);\n\n    dfree(iter);\n}\n\n/* Get next element in iterator.\n *\n * Note: You must NOT insert into the list while iterating over it.\n * You *may* delete from the list while iterating using the\n * quicklistDelEntry() function.\n * If you insert into the quicklist while iterating, you should\n * re-create the iterator after your addition.\n *\n * iter = quicklistGetIterator(quicklist,<direction>);\n * quicklistEntry entry;\n * while (quicklistNext(iter, &entry)) {\n *     if (entry.value)\n *          [[ use entry.value with entry.sz ]]\n *     else\n *          [[ use entry.longval ]]\n * }\n *\n * Populates 'entry' with values for this iteration.\n * Returns 0 when iteration is complete or if iteration not possible.\n * If return value is 0, the contents of 'entry' are not valid.\n */\nint quicklistNext(quicklistIter *iter, quicklistEntry *entry) {\n    initEntry(entry);\n\n    if (!iter) {\n        D(\"Returning because no iter!\");\n        return 0;\n    }\n\n    entry->quicklist = iter->quicklist;\n    entry->node = iter->current;\n\n    if (!iter->current) {\n        D(\"Returning because current node is NULL\")\n        return 0;\n    }\n\n    unsigned char *(*nextFn)(unsigned char *, unsigned char *) = NULL;\n    int offset_update = 0;\n\n    if (!iter->zi) {\n        /* If !zi, use current index. */\n        quicklistDecompressNodeForUse(iter->current);\n        iter->zi = ziplistIndex(iter->current->zl, iter->offset);\n    } else {\n        /* else, use existing iterator offset and get prev/next as necessary. */\n        if (iter->direction == AL_START_HEAD) {\n            nextFn = ziplistNext;\n            offset_update = 1;\n        } else if (iter->direction == AL_START_TAIL) {\n            nextFn = ziplistPrev;\n            offset_update = -1;\n        }\n        iter->zi = nextFn(iter->current->zl, iter->zi);\n        iter->offset += offset_update;\n    }\n\n    entry->zi = iter->zi;\n    entry->offset = iter->offset;\n\n    if (iter->zi) {\n        /* Populate value from existing ziplist position */\n        ziplistGet(entry->zi, &entry->value, &entry->sz, &entry->longval);\n        return 1;\n    } else {\n        /* We ran out of ziplist entries.\n         * Pick next node, update offset, then re-run retrieval. */\n        quicklistCompress(iter->quicklist, iter->current);\n        if (iter->direction == AL_START_HEAD) {\n            /* Forward traversal */\n            D(\"Jumping to start of next node\");\n            iter->current = iter->current->next;\n            iter->offset = 0;\n        } else if (iter->direction == AL_START_TAIL) {\n            /* Reverse traversal */\n            D(\"Jumping to end of previous node\");\n            iter->current = iter->current->prev;\n            iter->offset = -1;\n        }\n        iter->zi = NULL;\n        return quicklistNext(iter, entry);\n    }\n}\n\n/* Duplicate the quicklist.\n * On success a copy of the original quicklist is returned.\n *\n * The original quicklist both on success or error is never modified.\n *\n * Returns newly allocated quicklist. */\nquicklist *quicklistDup(quicklist *orig) {\n    quicklist *copy;\n    quicklistNode *current;\n\n    copy = quicklistNew(orig->fill, orig->compress);\n\n    for (current = orig->head; current;\n         current = current->next) {\n        quicklistNode *node = quicklistCreateNode();\n\n        if (node->encoding == QUICKLIST_NODE_ENCODING_LZF) {\n            quicklistLZF *lzf = (quicklistLZF *)node->zl;\n            size_t lzf_sz = sizeof(*lzf) + lzf->sz;\n            node->zl = dalloc(lzf_sz);\n            memcpy(node->zl, current->zl, lzf_sz);\n        } else if (node->encoding == QUICKLIST_NODE_ENCODING_RAW) {\n            node->zl = dalloc(current->sz);\n            memcpy(node->zl, current->zl, current->sz);\n        }\n\n        node->count = current->count;\n        copy->count += node->count;\n        node->sz = current->sz;\n        node->encoding = current->encoding;\n\n        _quicklistInsertNodeAfter(copy, copy->tail, node);\n    }\n\n    /* copy->count must equal orig->count here */\n    return copy;\n}\n\n/* Populate 'entry' with the element at the specified zero-based index\n * where 0 is the head, 1 is the element next to head\n * and so on. Negative integers are used in order to count\n * from the tail, -1 is the last element, -2 the penultimate\n * and so on. If the index is out of range 0 is returned.\n *\n * Returns 1 if element found\n * Returns 0 if element not found */\nint quicklistIndex(const quicklist *quicklist, const long long idx,\n                   quicklistEntry *entry) {\n    quicklistNode *n;\n    unsigned long long accum = 0;\n    unsigned long long index;\n    int forward = idx < 0 ? 0 : 1; /* < 0 -> reverse, 0+ -> forward */\n\n    initEntry(entry);\n    entry->quicklist = quicklist;\n\n    if (!forward) {\n        index = (-idx) - 1;\n        n = quicklist->tail;\n    } else {\n        index = idx;\n        n = quicklist->head;\n    }\n\n    if (index >= quicklist->count)\n        return 0;\n\n    while (likely(n)) {\n        if ((accum + n->count) > index) {\n            break;\n        } else {\n            D(\"Skipping over (%p) %u at accum %lld\", (void *)n, n->count,\n              accum);\n            accum += n->count;\n            n = forward ? n->next : n->prev;\n        }\n    }\n\n    if (!n)\n        return 0;\n\n    D(\"Found node: %p at accum %llu, idx %llu, sub+ %llu, sub- %llu\", (void *)n,\n      accum, index, index - accum, (-index) - 1 + accum);\n\n    entry->node = n;\n    if (forward) {\n        /* forward = normal head-to-tail offset. */\n        entry->offset = index - accum;\n    } else {\n        /* reverse = need negative offset for tail-to-head, so undo\n         * the result of the original if (index < 0) above. */\n        entry->offset = (-index) - 1 + accum;\n    }\n\n    quicklistDecompressNodeForUse(entry->node);\n    entry->zi = ziplistIndex(entry->node->zl, entry->offset);\n    ziplistGet(entry->zi, &entry->value, &entry->sz, &entry->longval);\n    /* The caller will use our result, so we don't re-compress here.\n     * The caller can recompress or delete the node as needed. */\n    return 1;\n}\n\n/* Rotate quicklist by moving the tail element to the head. */\nvoid quicklistRotate(quicklist *quicklist) {\n    if (quicklist->count <= 1)\n        return;\n\n    /* First, get the tail entry */\n    unsigned char *p = ziplistIndex(quicklist->tail->zl, -1);\n    unsigned char *value;\n    long long longval;\n    unsigned int sz;\n    char longstr[32] = {0};\n    ziplistGet(p, &value, &sz, &longval);\n\n    /* If value found is NULL, then ziplistGet populated longval instead */\n    if (!value) {\n        /* Write the longval as a string so we can re-add it */\n        sz = ll2string(longstr, sizeof(longstr), longval);\n        value = (unsigned char *)longstr;\n    }\n\n    /* Add tail entry to head (must happen before tail is deleted). */\n    quicklistPushHead(quicklist, value, sz);\n\n    /* If quicklist has only one node, the head ziplist is also the\n     * tail ziplist and PushHead() could have reallocated our single ziplist,\n     * which would make our pre-existing 'p' unusable. */\n    if (quicklist->len == 1) {\n        p = ziplistIndex(quicklist->tail->zl, -1);\n    }\n\n    /* Remove tail entry. */\n    quicklistDelIndex(quicklist, quicklist->tail, &p);\n}\n\n/* pop from quicklist and return result in 'data' ptr.  Value of 'data'\n * is the return value of 'saver' function pointer if the data is NOT a number.\n *\n * If the quicklist element is a long long, then the return value is returned in\n * 'sval'.\n *\n * Return value of 0 means no elements available.\n * Return value of 1 means check 'data' and 'sval' for values.\n * If 'data' is set, use 'data' and 'sz'.  Otherwise, use 'sval'. */\nint quicklistPopCustom(quicklist *quicklist, int where, unsigned char **data,\n                       unsigned int *sz, long long *sval,\n                       void *(*saver)(unsigned char *data, unsigned int sz)) {\n    unsigned char *p;\n    unsigned char *vstr;\n    unsigned int vlen;\n    long long vlong;\n    int pos = (where == QUICKLIST_HEAD) ? 0 : -1;\n\n    if (quicklist->count == 0)\n        return 0;\n\n    if (data)\n        *data = NULL;\n    if (sz)\n        *sz = 0;\n    if (sval)\n        *sval = -123456789;\n\n    quicklistNode *node;\n    if (where == QUICKLIST_HEAD && quicklist->head) {\n        node = quicklist->head;\n    } else if (where == QUICKLIST_TAIL && quicklist->tail) {\n        node = quicklist->tail;\n    } else {\n        return 0;\n    }\n\n    p = ziplistIndex(node->zl, pos);\n    if (ziplistGet(p, &vstr, &vlen, &vlong)) {\n        if (vstr) {\n            if (data)\n                *data = saver(vstr, vlen);\n            if (sz)\n                *sz = vlen;\n        } else {\n            if (data)\n                *data = NULL;\n            if (sval)\n                *sval = vlong;\n        }\n        quicklistDelIndex(quicklist, node, &p);\n        return 1;\n    }\n    return 0;\n}\n\n/* Return a malloc'd copy of data passed in */\nREDIS_STATIC void *_quicklistSaver(unsigned char *data, unsigned int sz) {\n    unsigned char *vstr;\n    if (data) {\n        vstr = dalloc(sz);\n        memcpy(vstr, data, sz);\n        return vstr;\n    }\n    return NULL;\n}\n\n/* Default pop function\n *\n * Returns malloc'd value from quicklist */\nint quicklistPop(quicklist *quicklist, int where, unsigned char **data,\n                 unsigned int *sz, long long *slong) {\n    unsigned char *vstr;\n    unsigned int vlen;\n    long long vlong;\n    if (quicklist->count == 0)\n        return 0;\n    int ret = quicklistPopCustom(quicklist, where, &vstr, &vlen, &vlong,\n                                 _quicklistSaver);\n    if (data)\n        *data = vstr;\n    if (slong)\n        *slong = vlong;\n    if (sz)\n        *sz = vlen;\n    return ret;\n}\n\n/* Wrapper to allow argument-based switching between HEAD/TAIL pop */\nvoid quicklistPush(quicklist *quicklist, void *value, const size_t sz,\n                   int where) {\n    if (where == QUICKLIST_HEAD) {\n        quicklistPushHead(quicklist, value, sz);\n    } else if (where == QUICKLIST_TAIL) {\n        quicklistPushTail(quicklist, value, sz);\n    }\n}\n"
  },
  {
    "path": "src/vr_quicklist.h",
    "content": "#ifndef _VR_QUICKLIST_H_\n#define _VR_QUICKLIST_H_\n\n/* Node, quicklist, and Iterator are the only data structures used currently. */\n\n/* quicklistNode is a 32 byte struct describing a ziplist for a quicklist.\n * We use bit fields keep the quicklistNode at 32 bytes.\n * count: 16 bits, max 65536 (max zl bytes is 65k, so max count actually < 32k).\n * encoding: 2 bits, RAW=1, LZF=2.\n * container: 2 bits, NONE=1, ZIPLIST=2.\n * recompress: 1 bit, bool, true if node is temporarry decompressed for usage.\n * attempted_compress: 1 bit, boolean, used for verifying during testing.\n * extra: 12 bits, free for future use; pads out the remainder of 32 bits */\ntypedef struct quicklistNode {\n    struct quicklistNode *prev;\n    struct quicklistNode *next;\n    unsigned char *zl;\n    unsigned int sz;             /* ziplist size in bytes */\n    unsigned int count : 16;     /* count of items in ziplist */\n    unsigned int encoding : 2;   /* RAW==1 or LZF==2 */\n    unsigned int container : 2;  /* NONE==1 or ZIPLIST==2 */\n    unsigned int recompress : 1; /* was this node previous compressed? */\n    unsigned int attempted_compress : 1; /* node can't compress; too small */\n    unsigned int extra : 10; /* more bits to steal for future usage */\n} quicklistNode;\n\n/* quicklistLZF is a 4+N byte struct holding 'sz' followed by 'compressed'.\n * 'sz' is byte length of 'compressed' field.\n * 'compressed' is LZF data with total (compressed) length 'sz'\n * NOTE: uncompressed length is stored in quicklistNode->sz.\n * When quicklistNode->zl is compressed, node->zl points to a quicklistLZF */\ntypedef struct quicklistLZF {\n    unsigned int sz; /* LZF size in bytes*/\n    char compressed[];\n} quicklistLZF;\n\n/* quicklist is a 32 byte struct (on 64-bit systems) describing a quicklist.\n * 'count' is the number of total entries.\n * 'len' is the number of quicklist nodes.\n * 'compress' is: -1 if compression disabled, otherwise it's the number\n *                of quicklistNodes to leave uncompressed at ends of quicklist.\n * 'fill' is the user-requested (or default) fill factor. */\ntypedef struct quicklist {\n    quicklistNode *head;\n    quicklistNode *tail;\n    unsigned long count;        /* total count of all entries in all ziplists */\n    unsigned int len;           /* number of quicklistNodes */\n    int fill : 16;              /* fill factor for individual nodes */\n    unsigned int compress : 16; /* depth of end nodes not to compress;0=off */\n} quicklist;\n\ntypedef struct quicklistIter {\n    const quicklist *quicklist;\n    quicklistNode *current;\n    unsigned char *zi;\n    long offset; /* offset in current ziplist */\n    int direction;\n} quicklistIter;\n\ntypedef struct quicklistEntry {\n    const quicklist *quicklist;\n    quicklistNode *node;\n    unsigned char *zi;\n    unsigned char *value;\n    unsigned int sz;\n    long long longval;\n    int offset;\n} quicklistEntry;\n\n#define QUICKLIST_HEAD 0\n#define QUICKLIST_TAIL -1\n\n/* quicklist node encodings */\n#define QUICKLIST_NODE_ENCODING_RAW 1\n#define QUICKLIST_NODE_ENCODING_LZF 2\n\n/* quicklist compression disable */\n#define QUICKLIST_NOCOMPRESS 0\n\n/* quicklist container formats */\n#define QUICKLIST_NODE_CONTAINER_NONE 1\n#define QUICKLIST_NODE_CONTAINER_ZIPLIST 2\n\n#define quicklistNodeIsCompressed(node)                                        \\\n    ((node)->encoding == QUICKLIST_NODE_ENCODING_LZF)\n\n/* Prototypes */\nquicklist *quicklistCreate(void);\nquicklist *quicklistNew(int fill, int compress);\nvoid quicklistSetCompressDepth(quicklist *quicklist, int depth);\nvoid quicklistSetFill(quicklist *quicklist, int fill);\nvoid quicklistSetOptions(quicklist *quicklist, int fill, int depth);\nvoid quicklistRelease(quicklist *quicklist);\nint quicklistPushHead(quicklist *quicklist, void *value, const size_t sz);\nint quicklistPushTail(quicklist *quicklist, void *value, const size_t sz);\nvoid quicklistPush(quicklist *quicklist, void *value, const size_t sz,\n                   int where);\nvoid quicklistAppendZiplist(quicklist *quicklist, unsigned char *zl);\nquicklist *quicklistAppendValuesFromZiplist(quicklist *quicklist,\n                                            unsigned char *zl);\nquicklist *quicklistCreateFromZiplist(int fill, int compress,\n                                      unsigned char *zl);\nvoid quicklistInsertAfter(quicklist *quicklist, quicklistEntry *node,\n                          void *value, const size_t sz);\nvoid quicklistInsertBefore(quicklist *quicklist, quicklistEntry *node,\n                           void *value, const size_t sz);\nvoid quicklistDelEntry(quicklistIter *iter, quicklistEntry *entry);\nint quicklistReplaceAtIndex(quicklist *quicklist, long index, void *data,\n                            int sz);\nint quicklistDelRange(quicklist *quicklist, const long start, const long stop);\nquicklistIter *quicklistGetIterator(const quicklist *quicklist, int direction);\nquicklistIter *quicklistGetIteratorAtIdx(const quicklist *quicklist,\n                                         int direction, const long long idx);\nint quicklistNext(quicklistIter *iter, quicklistEntry *node);\nvoid quicklistReleaseIterator(quicklistIter *iter);\nquicklist *quicklistDup(quicklist *orig);\nint quicklistIndex(const quicklist *quicklist, const long long index,\n                   quicklistEntry *entry);\nvoid quicklistRewind(quicklist *quicklist, quicklistIter *li);\nvoid quicklistRewindTail(quicklist *quicklist, quicklistIter *li);\nvoid quicklistRotate(quicklist *quicklist);\nint quicklistPopCustom(quicklist *quicklist, int where, unsigned char **data,\n                       unsigned int *sz, long long *sval,\n                       void *(*saver)(unsigned char *data, unsigned int sz));\nint quicklistPop(quicklist *quicklist, int where, unsigned char **data,\n                 unsigned int *sz, long long *slong);\nunsigned int quicklistCount(quicklist *ql);\nint quicklistCompare(unsigned char *p1, unsigned char *p2, int p2_len);\nsize_t quicklistGetLzf(const quicklistNode *node, void **data);\n\n/* Directions for iterators */\n#define AL_START_HEAD 0\n#define AL_START_TAIL 1\n\n#endif /* __QUICKLIST_H__ */\n"
  },
  {
    "path": "src/vr_rbtree.c",
    "content": "#include <vr_core.h>\n\nvoid\nrbtree_node_init(struct rbnode *node)\n{\n    node->left = NULL;\n    node->right = NULL;\n    node->parent = NULL;\n    node->key = 0ULL;\n    node->data = NULL;\n    /* color is left uninitialized */\n}\n\nvoid\nrbtree_init(struct rbtree *tree, struct rbnode *node)\n{\n    rbtree_node_init(node);\n    rbtree_black(node);\n    tree->root = node;\n    tree->sentinel = node;\n}\n\nstatic struct rbnode *\nrbtree_node_min(struct rbnode *node, struct rbnode *sentinel)\n{\n    /* traverse left links */\n\n    while (node->left != sentinel) {\n        node = node->left;\n    }\n\n    return node;\n}\n\nstruct rbnode *\nrbtree_min(struct rbtree *tree)\n{\n    struct rbnode *node = tree->root;\n    struct rbnode *sentinel = tree->sentinel;\n\n    /* empty tree */\n\n    if (node == sentinel) {\n        return NULL;\n    }\n\n    return rbtree_node_min(node, sentinel);\n}\n\nstatic void\nrbtree_left_rotate(struct rbnode **root, struct rbnode *sentinel,\n                   struct rbnode *node)\n{\n    struct rbnode *temp;\n\n    temp = node->right;\n    node->right = temp->left;\n\n    if (temp->left != sentinel) {\n        temp->left->parent = node;\n    }\n\n    temp->parent = node->parent;\n\n    if (node == *root) {\n        *root = temp;\n    } else if (node == node->parent->left) {\n        node->parent->left = temp;\n    } else {\n        node->parent->right = temp;\n    }\n\n    temp->left = node;\n    node->parent = temp;\n}\n\nstatic void\nrbtree_right_rotate(struct rbnode **root, struct rbnode *sentinel,\n                    struct rbnode *node)\n{\n    struct rbnode *temp;\n\n    temp = node->left;\n    node->left = temp->right;\n\n    if (temp->right != sentinel) {\n        temp->right->parent = node;\n    }\n\n    temp->parent = node->parent;\n\n    if (node == *root) {\n        *root = temp;\n    } else if (node == node->parent->right) {\n        node->parent->right = temp;\n    } else {\n        node->parent->left = temp;\n    }\n\n    temp->right = node;\n    node->parent = temp;\n}\n\nvoid\nrbtree_insert(struct rbtree *tree, struct rbnode *node)\n{\n    struct rbnode **root = &tree->root;\n    struct rbnode *sentinel = tree->sentinel;\n    struct rbnode *temp, **p;\n\n    /* empty tree */\n\n    if (*root == sentinel) {\n        node->parent = NULL;\n        node->left = sentinel;\n        node->right = sentinel;\n        rbtree_black(node);\n        *root = node;\n        return;\n    }\n\n    /* a binary tree insert */\n\n    temp = *root;\n    for (;;) {\n\n        p = (node->key < temp->key) ? &temp->left : &temp->right;\n        if (*p == sentinel) {\n            break;\n        }\n        temp = *p;\n    }\n\n    *p = node;\n    node->parent = temp;\n    node->left = sentinel;\n    node->right = sentinel;\n    rbtree_red(node);\n\n    /* re-balance tree */\n\n    while (node != *root && rbtree_is_red(node->parent)) {\n\n        if (node->parent == node->parent->parent->left) {\n            temp = node->parent->parent->right;\n\n            if (rbtree_is_red(temp)) {\n                rbtree_black(node->parent);\n                rbtree_black(temp);\n                rbtree_red(node->parent->parent);\n                node = node->parent->parent;\n            } else {\n                if (node == node->parent->right) {\n                    node = node->parent;\n                    rbtree_left_rotate(root, sentinel, node);\n                }\n\n                rbtree_black(node->parent);\n                rbtree_red(node->parent->parent);\n                rbtree_right_rotate(root, sentinel, node->parent->parent);\n            }\n        } else {\n            temp = node->parent->parent->left;\n\n            if (rbtree_is_red(temp)) {\n                rbtree_black(node->parent);\n                rbtree_black(temp);\n                rbtree_red(node->parent->parent);\n                node = node->parent->parent;\n            } else {\n                if (node == node->parent->left) {\n                    node = node->parent;\n                    rbtree_right_rotate(root, sentinel, node);\n                }\n\n                rbtree_black(node->parent);\n                rbtree_red(node->parent->parent);\n                rbtree_left_rotate(root, sentinel, node->parent->parent);\n            }\n        }\n    }\n\n    rbtree_black(*root);\n}\n\nvoid\nrbtree_delete(struct rbtree *tree, struct rbnode *node)\n{\n    struct rbnode **root = &tree->root;\n    struct rbnode *sentinel = tree->sentinel;\n    struct rbnode *subst, *temp, *w;\n    uint8_t red;\n\n    /* a binary tree delete */\n\n    if (node->left == sentinel) {\n        temp = node->right;\n        subst = node;\n    } else if (node->right == sentinel) {\n        temp = node->left;\n        subst = node;\n    } else {\n        subst = rbtree_node_min(node->right, sentinel);\n        temp = subst->right;\n    }\n\n    if (subst == *root) {\n        *root = temp;\n        rbtree_black(temp);\n\n        rbtree_node_init(node);\n\n        return;\n    }\n\n    red = rbtree_is_red(subst);\n\n    if (subst == subst->parent->left) {\n        subst->parent->left = temp;\n    } else {\n        subst->parent->right = temp;\n    }\n\n    if (subst == node) {\n        temp->parent = subst->parent;\n    } else {\n\n        if (subst->parent == node) {\n            temp->parent = subst;\n        } else {\n            temp->parent = subst->parent;\n        }\n\n        subst->left = node->left;\n        subst->right = node->right;\n        subst->parent = node->parent;\n        rbtree_copy_color(subst, node);\n\n        if (node == *root) {\n            *root = subst;\n        } else {\n            if (node == node->parent->left) {\n                node->parent->left = subst;\n            } else {\n                node->parent->right = subst;\n            }\n        }\n\n        if (subst->left != sentinel) {\n            subst->left->parent = subst;\n        }\n\n        if (subst->right != sentinel) {\n            subst->right->parent = subst;\n        }\n    }\n\n    rbtree_node_init(node);\n\n    if (red) {\n        return;\n    }\n\n    /* a delete fixup */\n\n    while (temp != *root && rbtree_is_black(temp)) {\n\n        if (temp == temp->parent->left) {\n            w = temp->parent->right;\n\n            if (rbtree_is_red(w)) {\n                rbtree_black(w);\n                rbtree_red(temp->parent);\n                rbtree_left_rotate(root, sentinel, temp->parent);\n                w = temp->parent->right;\n            }\n\n            if (rbtree_is_black(w->left) && rbtree_is_black(w->right)) {\n                rbtree_red(w);\n                temp = temp->parent;\n            } else {\n                if (rbtree_is_black(w->right)) {\n                    rbtree_black(w->left);\n                    rbtree_red(w);\n                    rbtree_right_rotate(root, sentinel, w);\n                    w = temp->parent->right;\n                }\n\n                rbtree_copy_color(w, temp->parent);\n                rbtree_black(temp->parent);\n                rbtree_black(w->right);\n                rbtree_left_rotate(root, sentinel, temp->parent);\n                temp = *root;\n            }\n\n        } else {\n            w = temp->parent->left;\n\n            if (rbtree_is_red(w)) {\n                rbtree_black(w);\n                rbtree_red(temp->parent);\n                rbtree_right_rotate(root, sentinel, temp->parent);\n                w = temp->parent->left;\n            }\n\n            if (rbtree_is_black(w->left) && rbtree_is_black(w->right)) {\n                rbtree_red(w);\n                temp = temp->parent;\n            } else {\n                if (rbtree_is_black(w->left)) {\n                    rbtree_black(w->right);\n                    rbtree_red(w);\n                    rbtree_left_rotate(root, sentinel, w);\n                    w = temp->parent->left;\n                }\n\n                rbtree_copy_color(w, temp->parent);\n                rbtree_black(temp->parent);\n                rbtree_black(w->left);\n                rbtree_right_rotate(root, sentinel, temp->parent);\n                temp = *root;\n            }\n        }\n    }\n\n    rbtree_black(temp);\n}\n"
  },
  {
    "path": "src/vr_rbtree.h",
    "content": "#ifndef _VR_RBTREE_\n#define _VR_RBTREE_\n\n#define rbtree_red(_node)           ((_node)->color = 1)\n#define rbtree_black(_node)         ((_node)->color = 0)\n#define rbtree_is_red(_node)        ((_node)->color)\n#define rbtree_is_black(_node)      (!rbtree_is_red(_node))\n#define rbtree_copy_color(_n1, _n2) ((_n1)->color = (_n2)->color)\n\nstruct rbnode {\n    struct rbnode *left;     /* left link */\n    struct rbnode *right;    /* right link */\n    struct rbnode *parent;   /* parent link */\n    int64_t       key;       /* key for ordering */\n    void          *data;     /* opaque data */\n    uint8_t       color;     /* red | black */\n};\n\nstruct rbtree {\n    struct rbnode *root;     /* root node */\n    struct rbnode *sentinel; /* nil node */\n};\n\nvoid rbtree_node_init(struct rbnode *node);\nvoid rbtree_init(struct rbtree *tree, struct rbnode *node);\nstruct rbnode *rbtree_min(struct rbtree *tree);\nvoid rbtree_insert(struct rbtree *tree, struct rbnode *node);\nvoid rbtree_delete(struct rbtree *tree, struct rbnode *node);\n\n#endif\n"
  },
  {
    "path": "src/vr_rdb.c",
    "content": "#include <vr_core.h>\n\n/* Save the DB on disk. Return C_ERR on error, C_OK on success. */\nint rdbSave(char *filename) {\n    return VR_OK;\n}\n\nvoid rdbRemoveTempFile(pid_t childpid) {\n    char tmpfile[256];\n\n    snprintf(tmpfile,sizeof(tmpfile),\"temp-%d.rdb\", (int) childpid);\n    unlink(tmpfile);\n}\n"
  },
  {
    "path": "src/vr_rdb.h",
    "content": "#ifndef _VR_RDB_H_\n#define _VR_RDB_H_\n\n/* Defines related to the dump file format. To store 32 bits lengths for short\n * keys requires a lot of space, so we check the most significant 2 bits of\n * the first byte to interpreter the length:\n *\n * 00|000000 => if the two MSB are 00 the len is the 6 bits of this byte\n * 01|000000 00000000 =>  01, the len is 14 byes, 6 bits + 8 bits of next byte\n * 10|000000 [32 bit integer] => if it's 10, a full 32 bit len will follow\n * 11|000000 this means: specially encoded object will follow. The six bits\n *           number specify the kind of object that follows.\n *           See the RDB_ENC_* defines.\n *\n * Lengths up to 63 are stored using a single byte, most DB keys, and may\n * values, will fit inside. */\n#define RDB_6BITLEN 0\n#define RDB_14BITLEN 1\n#define RDB_32BITLEN 2\n#define RDB_ENCVAL 3\n#define RDB_LENERR UINT_MAX\n\n/* When a length of a string object stored on disk has the first two bits\n * set, the remaining two bits specify a special encoding for the object\n * accordingly to the following defines: */\n#define RDB_ENC_INT8 0        /* 8 bit signed integer */\n#define RDB_ENC_INT16 1       /* 16 bit signed integer */\n#define RDB_ENC_INT32 2       /* 32 bit signed integer */\n#define RDB_ENC_LZF 3         /* string compressed with FASTLZ */\n\nstruct saveparam {\n    time_t seconds;\n    int changes;\n};\n\nint rdbSave(char *filename);\nvoid rdbRemoveTempFile(pid_t childpid);\n\n#endif\n"
  },
  {
    "path": "src/vr_replication.c",
    "content": "#include <vr_core.h>\n\nstruct vr_replication repl;\n\nint vr_replication_init(void)\n{\n    vr_eventloop_init(&repl.vel,1000);\n\n    repl.role = REPLICATION_ROLE_MASTER;\n    repl.master = NULL;\n    repl.cached_master = NULL;\n    repl.slaves = NULL;\n    repl.repl_no_slaves_since = 0;\n    repl.repl_min_slaves_to_write = 0;\n    repl.repl_min_slaves_max_lag = 0;\n    repl.repl_good_slaves_count = 0;\n    repl.repl_state = REPL_STATE_NONE;\n    repl.repl_down_since = 0;\n\n    /* Replication partial resync backlog */\n    repl.repl_backlog = NULL;\n    repl.repl_backlog_size = CONFIG_DEFAULT_REPL_BACKLOG_SIZE;\n    repl.repl_backlog_histlen = 0;\n    repl.repl_backlog_idx = 0;\n    repl.repl_backlog_off = 0;\n    repl.repl_backlog_time_limit = CONFIG_DEFAULT_REPL_BACKLOG_TIME_LIMIT;\n    repl.repl_no_slaves_since = time(NULL);\n\n    repl.slaves = dlistCreate();\n\n    return VR_OK;\n}\n\nvoid vr_replication_deinit(void)\n{\n    vr_eventloop_deinit(&repl.vel);\n\n    if (repl.master != NULL) {\n        freeClient(repl.master);\n        repl.master = NULL;\n    }\n\n    if (repl.cached_master != NULL) {\n        freeClient(repl.cached_master);\n        repl.cached_master = NULL;\n    }\n\n    if (repl.repl_backlog != NULL) {\n        dfree(repl.repl_backlog);\n        repl.repl_backlog = NULL;\n    }\n\n    if (repl.slaves != NULL) {\n        client *slave;\n        while (slave = dlistPop(repl.slaves)) {\n            freeClient(slave);\n        }\n        dlistRelease(repl.slaves);\n        repl.slaves = NULL;\n    }\n}\n\n/* This is called by unblockClient() to perform the blocking op type\n * specific cleanup. We just remove the client from the list of clients\n * waiting for replica acks. Never call it directly, call unblockClient()\n * instead. */\nvoid unblockClientWaitingReplicas(client *c) {\n    dlistNode *ln = dlistSearchKey(c->vel->clients_waiting_acks,c);\n    ASSERT(ln != NULL);\n    dlistDelNode(c->vel->clients_waiting_acks,ln);\n}\n\n/* ------------------------- MIN-SLAVES-TO-WRITE  --------------------------- */\n\n/* This function counts the number of slaves with lag <= min-slaves-max-lag.\n * If the option is active, the server will prevent writes if there are not\n * enough connected slaves with the specified lag (or less). */\nvoid refreshGoodSlavesCount(void) {\n    dlistIter li;\n    dlistNode *ln;\n    int good = 0;\n\n    if (!repl.repl_min_slaves_to_write ||\n        !repl.repl_min_slaves_max_lag) return;\n\n    dlistRewind(repl.slaves,&li);\n    while((ln = dlistNext(&li))) {\n        client *slave = ln->value;\n        time_t lag = repl.vel.unixtime - slave->repl_ack_time;\n\n        if (slave->replstate == SLAVE_STATE_ONLINE &&\n            lag <= repl.repl_min_slaves_max_lag) good++;\n    }\n    repl.repl_good_slaves_count = good;\n}\n\n/* This function is called when the slave lose the connection with the\n * master into an unexpected way. */\nvoid replicationHandleMasterDisconnection(void) {\n    repl.master = NULL;\n    repl.repl_state = REPL_STATE_CONNECT;\n    repl.repl_down_since = repl.vel.unixtime;\n    /* We lost connection with our master, don't disconnect slaves yet,\n     * maybe we'll be able to PSYNC with our master later. We'll disconnect\n     * the slaves only if we'll have to do a full resync with our master. */\n}\n\n\n/* ---------------------- MASTER CACHING FOR PSYNC -------------------------- */\n\n/* In order to implement partial synchronization we need to be able to cache\n * our master's client structure after a transient disconnection.\n * It is cached into server.cached_master and flushed away using the following\n * functions. */\n\n/* This function is called by freeClient() in order to cache the master\n * client structure instead of destryoing it. freeClient() will return\n * ASAP after this function returns, so every action needed to avoid problems\n * with a client that is really \"suspended\" has to be done by this function.\n *\n * The other functions that will deal with the cached master are:\n *\n * replicationDiscardCachedMaster() that will make sure to kill the client\n * as for some reason we don't want to use it in the future.\n *\n * replicationResurrectCachedMaster() that is used after a successful PSYNC\n * handshake in order to reactivate the cached master.\n */\nvoid replicationCacheMaster(client *c) {\n    ASSERT(repl.master != NULL && repl.cached_master == NULL);\n    log_debug(LOG_NOTICE,\"Caching the disconnected master state.\");\n\n    /* Unlink the client from the server structures. */\n    unlinkClient(c);\n\n    /* Save the master. Server.master will be set to null later by\n     * replicationHandleMasterDisconnection(). */\n    repl.cached_master = repl.master;\n\n    /* Invalidate the Peer ID cache. */\n    if (c->peerid) {\n        sdsfree(c->peerid);\n        c->peerid = NULL;\n    }\n\n    /* Caching the master happens instead of the actual freeClient() call,\n     * so make sure to adjust the replication state. This function will\n     * also set server.master to NULL. */\n    replicationHandleMasterDisconnection();\n}\n\n/* Return the pointer to a string representing the slave ip:listening_port\n * pair. Mostly useful for logging, since we want to log a slave using its\n * IP address and it's listening port which is more clear for the user, for\n * example: \"Closing connection with slave 10.1.2.3:6380\". */\nchar *replicationGetSlaveName(client *c) {\n    static char buf[VR_INET_PEER_ID_LEN];\n    char ip[VR_INET_ADDRSTRLEN];\n\n    ip[0] = '\\0';\n    buf[0] = '\\0';\n    \n    return buf;\n}\n\n/* REPLCONF <option> <value> <option> <value> ...\n * This command is used by a slave in order to configure the replication\n * process before starting it with the SYNC command.\n *\n * Currently the only use of this command is to communicate to the master\n * what is the listening port of the Slave redis instance, so that the\n * master can accurately list slaves and their listening ports in\n * the INFO output.\n *\n * In the future the same command can be used in order to configure\n * the replication to initiate an incremental replication instead of a\n * full resync. */\nvoid replconfCommand(client *c) {\n    int j;\n\n    if ((c->argc % 2) == 0) {\n        /* Number of arguments must be odd to make sure that every\n         * option has a corresponding value. */\n        addReply(c,shared.syntaxerr);\n        return;\n    }\n\n    /* Process every option-value pair. */\n    for (j = 1; j < c->argc; j+=2) {\n        if (!strcasecmp(c->argv[j]->ptr,\"listening-port\")) {\n            long port;\n\n            if ((getLongFromObjectOrReply(c,c->argv[j+1],\n                    &port,NULL) != VR_OK))\n                return;\n            c->slave_listening_port = port;\n        } else if (!strcasecmp(c->argv[j]->ptr,\"capa\")) {\n            /* Ignore capabilities not understood by this master. */\n            if (!strcasecmp(c->argv[j+1]->ptr,\"eof\"))\n                c->slave_capa |= SLAVE_CAPA_EOF;\n        } else if (!strcasecmp(c->argv[j]->ptr,\"ack\")) {\n            /* REPLCONF ACK is used by slave to inform the master the amount\n             * of replication stream that it processed so far. It is an\n             * internal only command that normal clients should never use. */\n            long long offset;\n\n            if (!(c->flags & CLIENT_SLAVE)) return;\n            if ((getLongLongFromObject(c->argv[j+1], &offset) != VR_OK))\n                return;\n            if (offset > c->repl_ack_off)\n                c->repl_ack_off = offset;\n            c->repl_ack_time = c->vel->unixtime;\n            /* If this was a diskless replication, we need to really put\n             * the slave online when the first ACK is received (which\n             * confirms slave is online and ready to get more data). */\n            if (c->repl_put_online_on_ack && c->replstate == SLAVE_STATE_ONLINE)\n                putSlaveOnline(c);\n            /* Note: this command does not reply anything! */\n            return;\n        } else if (!strcasecmp(c->argv[j]->ptr,\"getack\")) {\n            /* REPLCONF GETACK is used in order to request an ACK ASAP\n             * to the slave. */\n            if (repl.masterhost && repl.master) replicationSendAck();\n            /* Note: this command does not reply anything! */\n        } else {\n            addReplyErrorFormat(c,\"Unrecognized REPLCONF option: %s\",\n                (char*)c->argv[j]->ptr);\n            return;\n        }\n    }\n    addReply(c,shared.ok);\n}\n\n/* This function puts a slave in the online state, and should be called just\n * after a slave received the RDB file for the initial synchronization, and\n * we are finally ready to send the incremental stream of commands.\n *\n * It does a few things:\n *\n * 1) Put the slave in ONLINE state (useless when the function is called\n *    because state is already ONLINE but repl_put_online_on_ack is true).\n * 2) Make sure the writable event is re-installed, since calling the SYNC\n *    command disables it, so that we can accumulate output buffer without\n *    sending it to the slave.\n * 3) Update the count of good slaves. */\nvoid putSlaveOnline(client *slave) {\n    slave->replstate = SLAVE_STATE_ONLINE;\n    slave->repl_put_online_on_ack = 0;\n    slave->repl_ack_time = slave->vel->unixtime; /* Prevent false timeout. */\n    if (aeCreateFileEvent(slave->vel->el, slave->conn->sd, AE_WRITABLE,\n        sendReplyToClient, slave) == AE_ERR) {\n        log_warn(\"unable to register writable event for slave bulk transfer: %s\", strerror(errno));\n        freeClient(slave);\n        return;\n    }\n    refreshGoodSlavesCount();\n    log_debug(LOG_NOTICE,\"Synchronization with slave %s succeeded\",\n        replicationGetSlaveName(slave));\n}\n\n/* Send a REPLCONF ACK command to the master to inform it about the current\n * processed offset. If we are not connected with a master, the command has\n * no effects. */\nvoid replicationSendAck(void) {\n    client *c = repl.master;\n\n    if (c != NULL) {\n        c->flags |= CLIENT_MASTER_FORCE_REPLY;\n        addReplyMultiBulkLen(c,3);\n        addReplyBulkCString(c,\"REPLCONF\");\n        addReplyBulkCString(c,\"ACK\");\n        addReplyBulkLongLong(c,c->reploff);\n        c->flags &= ~CLIENT_MASTER_FORCE_REPLY;\n    }\n}\n\nvoid replicationFeedMonitors(client *c, dlist *monitors, int dictid, robj **argv, int argc) {\n    dlistNode *ln;\n    dlistIter li;\n    int j;\n    sds cmdrepr = sdsnew(\"+\");\n    robj *cmdobj;\n    struct timeval tv;\n\n    gettimeofday(&tv,NULL);\n    cmdrepr = sdscatprintf(cmdrepr,\"%ld.%06ld \",(long)tv.tv_sec,(long)tv.tv_usec);\n    if (c->flags & CLIENT_LUA) {\n        cmdrepr = sdscatprintf(cmdrepr,\"[%d lua] \",dictid);\n    } else if (c->flags & CLIENT_UNIX_SOCKET) {\n        cmdrepr = sdscatprintf(cmdrepr,\"[%d unix:%s] \",dictid,server.unixsocket);\n    } else {\n        cmdrepr = sdscatprintf(cmdrepr,\"[%d %s] \",dictid,getClientPeerId(c));\n    }\n\n    for (j = 0; j < argc; j++) {\n        if (argv[j]->encoding == OBJ_ENCODING_INT) {\n            cmdrepr = sdscatprintf(cmdrepr, \"\\\"%ld\\\"\", (long)argv[j]->ptr);\n        } else {\n            cmdrepr = sdscatrepr(cmdrepr,(char*)argv[j]->ptr,\n                        sdslen(argv[j]->ptr));\n        }\n        if (j != argc-1)\n            cmdrepr = sdscatlen(cmdrepr,\" \",1);\n    }\n    cmdrepr = sdscatlen(cmdrepr,\"\\r\\n\",2);\n    cmdobj = createObject(OBJ_STRING,cmdrepr);\n\n    dlistRewind(monitors,&li);\n    while((ln = dlistNext(&li))) {\n        client *monitor = ln->value;\n        addReply(monitor,cmdobj);\n    }\n    decrRefCount(cmdobj);\n}\n\n/* Add data to the replication backlog.\n * This function also increments the global replication offset stored at\n * server.master_repl_offset, because there is no case where we want to feed\n * the backlog without incrementing the buffer. */\nvoid feedReplicationBacklog(void *ptr, size_t len) {\n    unsigned char *p = ptr;\n\n    repl.master_repl_offset += len;\n\n    /* This is a circular buffer, so write as much data we can at every\n     * iteration and rewind the \"idx\" index if we reach the limit. */\n    while(len) {\n        size_t thislen = repl.repl_backlog_size - repl.repl_backlog_idx;\n        if (thislen > len) thislen = len;\n        memcpy(repl.repl_backlog+repl.repl_backlog_idx,p,thislen);\n        repl.repl_backlog_idx += thislen;\n        if (repl.repl_backlog_idx == repl.repl_backlog_size)\n            repl.repl_backlog_idx = 0;\n        len -= thislen;\n        p += thislen;\n        repl.repl_backlog_histlen += thislen;\n    }\n    if (repl.repl_backlog_histlen > repl.repl_backlog_size)\n        repl.repl_backlog_histlen = repl.repl_backlog_size;\n    /* Set the offset of the first byte we have in the backlog. */\n    repl.repl_backlog_off = repl.master_repl_offset -\n                              repl.repl_backlog_histlen + 1;\n}\n\n/* Wrapper for feedReplicationBacklog() that takes Redis string objects\n * as input. */\nvoid feedReplicationBacklogWithObject(robj *o) {\n    char llstr[LONG_STR_SIZE];\n    void *p;\n    size_t len;\n\n    if (o->encoding == OBJ_ENCODING_INT) {\n        len = ll2string(llstr,sizeof(llstr),(long)o->ptr);\n        p = llstr;\n    } else {\n        len = sdslen(o->ptr);\n        p = o->ptr;\n    }\n    feedReplicationBacklog(p,len);\n}\n\nvoid replicationFeedSlaves(dlist *slaves, int dictid, robj **argv, int argc) {\n    dlistNode *ln;\n    dlistIter li;\n    int j, len;\n    char llstr[LONG_STR_SIZE];\n\n    /* If there aren't slaves, and there is no backlog buffer to populate,\n     * we can return ASAP. */\n    if (repl.repl_backlog == NULL && dlistLength(slaves) == 0) return;\n\n    /* We can't have slaves attached and no backlog. */\n    ASSERT(!(dlistLength(slaves) != 0 && repl.repl_backlog == NULL));\n\n    /* Send SELECT command to every slave if needed. */\n    if (repl.slaveseldb != dictid) {\n        robj *selectcmd;\n\n        /* For a few DBs we have pre-computed SELECT command. */\n        if (dictid >= 0 && dictid < PROTO_SHARED_SELECT_CMDS) {\n            selectcmd = shared.select[dictid];\n        } else {\n            int dictid_len;\n\n            dictid_len = ll2string(llstr,sizeof(llstr),dictid);\n            selectcmd = createObject(OBJ_STRING,\n                sdscatprintf(sdsempty(),\n                \"*2\\r\\n$6\\r\\nSELECT\\r\\n$%d\\r\\n%s\\r\\n\",\n                dictid_len, llstr));\n        }\n\n        /* Add the SELECT command into the backlog. */\n        if (repl.repl_backlog) feedReplicationBacklogWithObject(selectcmd);\n\n        /* Send it to slaves. */\n        dlistRewind(slaves,&li);\n        while((ln = dlistNext(&li))) {\n            client *slave = ln->value;\n            if (slave->replstate == SLAVE_STATE_WAIT_BGSAVE_START) continue;\n            addReply(slave,selectcmd);\n        }\n\n        if (dictid < 0 || dictid >= PROTO_SHARED_SELECT_CMDS)\n            decrRefCount(selectcmd);\n    }\n    repl.slaveseldb = dictid;\n\n    /* Write the command to the replication backlog if any. */\n    if (repl.repl_backlog) {\n        char aux[LONG_STR_SIZE+3];\n\n        /* Add the multi bulk reply length. */\n        aux[0] = '*';\n        len = ll2string(aux+1,sizeof(aux)-1,argc);\n        aux[len+1] = '\\r';\n        aux[len+2] = '\\n';\n        feedReplicationBacklog(aux,len+3);\n\n        for (j = 0; j < argc; j++) {\n            long objlen = stringObjectLen(argv[j]);\n\n            /* We need to feed the buffer with the object as a bulk reply\n             * not just as a plain string, so create the $..CRLF payload len\n             * and add the final CRLF */\n            aux[0] = '$';\n            len = ll2string(aux+1,sizeof(aux)-1,objlen);\n            aux[len+1] = '\\r';\n            aux[len+2] = '\\n';\n            feedReplicationBacklog(aux,len+3);\n            feedReplicationBacklogWithObject(argv[j]);\n            feedReplicationBacklog(aux+len+1,2);\n        }\n    }\n\n    /* Write the command to every slave. */\n    dlistRewind(repl.slaves,&li);\n    while((ln = dlistNext(&li))) {\n        client *slave = ln->value;\n\n        /* Don't feed slaves that are still waiting for BGSAVE to start */\n        if (slave->replstate == SLAVE_STATE_WAIT_BGSAVE_START) continue;\n\n        /* Feed slaves that are waiting for the initial SYNC (so these commands\n         * are queued in the output buffer until the initial SYNC completes),\n         * or are already in sync with the master. */\n\n        /* Add the multi bulk length. */\n        addReplyMultiBulkLen(slave,argc);\n\n        /* Finally any additional argument that was not stored inside the\n         * static buffer if any (from j to argc). */\n        for (j = 0; j < argc; j++)\n            addReplyBulk(slave,argv[j]);\n    }\n}\n\n"
  },
  {
    "path": "src/vr_replication.h",
    "content": "#ifndef _VR_REPLICATION_H_\n#define _VR_REPLICATION_H_\n\n/* Slave replication state. Used in server.repl_state for slaves to remember\n * what to do next. */\n#define REPL_STATE_NONE 0 /* No active replication */\n#define REPL_STATE_CONNECT 1 /* Must connect to master */\n#define REPL_STATE_CONNECTING 2 /* Connecting to master */\n/* --- Handshake states, must be ordered --- */\n#define REPL_STATE_RECEIVE_PONG 3 /* Wait for PING reply */\n#define REPL_STATE_SEND_AUTH 4 /* Send AUTH to master */\n#define REPL_STATE_RECEIVE_AUTH 5 /* Wait for AUTH reply */\n#define REPL_STATE_SEND_PORT 6 /* Send REPLCONF listening-port */\n#define REPL_STATE_RECEIVE_PORT 7 /* Wait for REPLCONF reply */\n#define REPL_STATE_SEND_CAPA 8 /* Send REPLCONF capa */\n#define REPL_STATE_RECEIVE_CAPA 9 /* Wait for REPLCONF reply */\n#define REPL_STATE_SEND_PSYNC 10 /* Send PSYNC */\n#define REPL_STATE_RECEIVE_PSYNC 11 /* Wait for PSYNC reply */\n/* --- End of handshake states --- */\n#define REPL_STATE_TRANSFER 12 /* Receiving .rdb from master */\n#define REPL_STATE_CONNECTED 13 /* Connected to master */\n\n/* State of slaves from the POV of the master. Used in client->replstate.\n * In SEND_BULK and ONLINE state the slave receives new updates\n * in its output queue. In the WAIT_BGSAVE states instead the server is waiting\n * to start the next background saving in order to send updates to it. */\n#define SLAVE_STATE_WAIT_BGSAVE_START 6 /* We need to produce a new RDB file. */\n#define SLAVE_STATE_WAIT_BGSAVE_END 7 /* Waiting RDB file creation to finish. */\n#define SLAVE_STATE_SEND_BULK 8 /* Sending RDB file to slave. */\n#define SLAVE_STATE_ONLINE 9 /* RDB file transmitted, sending just updates. */\n\n/* Slave capabilities. */\n#define SLAVE_CAPA_NONE 0\n#define SLAVE_CAPA_EOF (1<<0)   /* Can parse the RDB EOF streaming format. */\n\n/* Synchronous read timeout - slave side */\n#define CONFIG_REPL_SYNCIO_TIMEOUT 5\n\n#define REPLICATION_ROLE_MASTER 0\n#define REPLICATION_ROLE_SLAVE  1\n\n#define CONFIG_DEFAULT_REPL_BACKLOG_SIZE (1024*1024)    /* 1mb */\n#define CONFIG_DEFAULT_REPL_BACKLOG_TIME_LIMIT (60*60)  /* 1 hour */\n#define CONFIG_REPL_BACKLOG_MIN_SIZE (1024*16)          /* 16k */\n\nstruct vr_replication {\n    vr_eventloop vel;\n\n    int role;               /* Master/slave? */\n\n    /* Replication (master) */\n    dlist *slaves;           /* List of slaves */\n    int slaveseldb;                 /* Last SELECTed DB in replication output */\n    long long master_repl_offset;   /* Global replication offset */\n    int repl_ping_slave_period;     /* Master pings the slave every N seconds */\n    char *repl_backlog;             /* Replication backlog for partial syncs */\n    long long repl_backlog_size;    /* Backlog circular buffer size */\n    long long repl_backlog_histlen; /* Backlog actual data length */\n    long long repl_backlog_idx;     /* Backlog circular buffer current offset */\n    long long repl_backlog_off;     /* Replication offset of first byte in the\n                                       backlog buffer. */\n    time_t repl_backlog_time_limit; /* Time without slaves after the backlog\n                                       gets released. */\n    time_t repl_no_slaves_since;    /* We have no slaves since that time.\n                                       Only valid if server.slaves len is 0. */\n    int repl_min_slaves_to_write;   /* Min number of slaves to write. */\n    int repl_min_slaves_max_lag;    /* Max lag of <count> slaves to write. */\n    int repl_good_slaves_count;     /* Number of slaves with lag <= max_lag. */\n    int repl_diskless_sync;         /* Send RDB to slaves sockets directly. */\n    int repl_diskless_sync_delay;   /* Delay to start a diskless repl BGSAVE. */\n\n    /* Replication (slave) */\n    char *masterauth;               /* AUTH with this password with master */\n    char *masterhost;               /* Hostname of master */\n    int masterport;                 /* Port of master */\n    int repl_timeout;               /* Timeout after N seconds of master idle */\n    client *master;     /* Client that is master for this slave */\n    client *cached_master; /* Cached master to be reused for PSYNC. */\n    int repl_syncio_timeout; /* Timeout for synchronous I/O calls */\n    int repl_state;          /* Replication status if the instance is a slave */\n    off_t repl_transfer_size; /* Size of RDB to read from master during sync. */\n    off_t repl_transfer_read; /* Amount of RDB read from master during sync. */\n    off_t repl_transfer_last_fsync_off; /* Offset when we fsync-ed last time. */\n    int repl_transfer_s;     /* Slave -> Master SYNC socket */\n    int repl_transfer_fd;    /* Slave -> Master SYNC temp file descriptor */\n    char *repl_transfer_tmpfile; /* Slave-> master SYNC temp file name */\n    time_t repl_transfer_lastio; /* Unix time of the latest read, for timeout */\n    int repl_serve_stale_data; /* Serve stale data when link is down? */\n    int repl_slave_ro;          /* Slave is read only? */\n    time_t repl_down_since; /* Unix time at which link with master went down */\n    int repl_disable_tcp_nodelay;   /* Disable TCP_NODELAY after SYNC? */\n    int slave_priority;             /* Reported in INFO and used by Sentinel. */\n    char repl_master_runid[CONFIG_RUN_ID_SIZE+1];  /* Master run id for PSYNC. */\n    long long repl_master_initial_offset;         /* Master PSYNC offset. */\n};\n\nextern struct vr_replication repl;\n\nint vr_replication_init(void);\nvoid vr_replication_deinit(void);\n\nvoid unblockClientWaitingReplicas(client *c);\nvoid refreshGoodSlavesCount(void);\nvoid replicationHandleMasterDisconnection(void);\nvoid replicationCacheMaster(client *c);\nchar *replicationGetSlaveName(client *c);\nvoid replconfCommand(client *c);\nvoid putSlaveOnline(client *slave);\nvoid replicationSendAck(void);\nvoid replicationFeedMonitors(client *c, dlist *monitors, int dictid, robj **argv, int argc);\nvoid replicationFeedSlaves(dlist *slaves, int dictid, robj **argv, int argc);\nvoid feedReplicationBacklogWithObject(robj *o);\nvoid feedReplicationBacklog(void *ptr, size_t len);\n\n#endif\n"
  },
  {
    "path": "src/vr_scripting.c",
    "content": "#include <vr_core.h>\n\nvoid scriptCommand(client *c) {\n    addReply(c,shared.ok);\n}\n\n\n"
  },
  {
    "path": "src/vr_scripting.h",
    "content": "#ifndef _VR_SCRIPTING_H_\n#define _VR_SCRIPTING_H_\n\nvoid scriptCommand(client *c);\n\n#endif\n"
  },
  {
    "path": "src/vr_server.c",
    "content": "#include <sys/utsname.h>\n\n#include <vr_core.h>\n\n/* Global vars */\nstruct vr_server server; /* server global state */\n\n/* Our shared \"common\" objects */\nstruct sharedObjectsStruct shared;\n\nunsigned int\ndictStrHash(const void *key) {\n    return dictGenHashFunction((unsigned char*)key, strlen((char*)key));\n}\n\nunsigned int\ndictStrCaseHash(const void *key) {\n    return dictGenCaseHashFunction((unsigned char*)key, strlen((char*)key));\n}\n\nunsigned int\ndictSdsHash(const void *key) {\n    return dictGenHashFunction((unsigned char*)key, sdslen((char*)key));\n}\n\nunsigned int\ndictSdsCaseHash(const void *key) {\n    return dictGenCaseHashFunction((unsigned char*)key, sdslen((char*)key));\n}\n\nint\ndictStrKeyCompare(void *privdata, const void *key1,\n        const void *key2)\n{\n    int l1,l2;\n    DICT_NOTUSED(privdata);\n\n    l1 = strlen((char*)key1);\n    l2 = strlen((char*)key2);\n    if (l1 != l2) return 0;\n    return memcmp(key1, key2, l1) == 0;\n}\n\n/* A case insensitive version used for the config option lookup table and other\n * places where case insensitive non binary-safe comparison is needed. */\nint\ndictStrKeyCaseCompare(void *privdata, const void *key1,\n        const void *key2)\n{\n    DICT_NOTUSED(privdata);\n\n    return strcasecmp(key1, key2) == 0;\n}\n\nint\ndictSdsKeyCompare(void *privdata, const void *key1,\n        const void *key2)\n{\n    int l1,l2;\n    DICT_NOTUSED(privdata);\n\n    l1 = sdslen((sds)key1);\n    l2 = sdslen((sds)key2);\n    if (l1 != l2) return 0;\n    return memcmp(key1, key2, l1) == 0;\n}\n\n/* A case insensitive version used for the command lookup table and other\n * places where case insensitive non binary-safe comparison is needed. */\nint\ndictSdsKeyCaseCompare(void *privdata, const void *key1,\n        const void *key2)\n{\n    DICT_NOTUSED(privdata);\n\n    return strcasecmp(key1, key2) == 0;\n}\n\nvoid *\ndictSdsKeyDupFromStr(void *privdata, const void *key)\n{\n    DICT_NOTUSED(privdata);\n\n    return sdsnew(key); /* key is c string */\n}\n\nvoid\ndictSdsDestructor(void *privdata, void *val)\n{\n    DICT_NOTUSED(privdata);\n\n    sdsfree(val);\n}\n\nvoid\ndictObjectDestructor(void *privdata, void *val)\n{\n    DICT_NOTUSED(privdata);\n\n    if (val == NULL) return; /* Values of swapped out keys as set to NULL */\n    freeObject(val);\n}\n\nint\ndictEncObjKeyCompare(void *privdata, const void *key1,\n        const void *key2)\n{\n    robj *o1 = (robj*) key1, *o2 = (robj*) key2;\n    robj *o1_new, *o2_new;\n    int cmp;\n\n    if (o1->encoding == OBJ_ENCODING_INT &&\n        o2->encoding == OBJ_ENCODING_INT)\n            return o1->ptr == o2->ptr;\n\n    o1_new = getDecodedObject(o1);\n    o2_new = getDecodedObject(o2);\n    cmp = dictSdsKeyCompare(privdata,o1_new->ptr,o2_new->ptr);\n    if (o1_new != o1)  freeObject(o1_new);\n    if (o2_new != o2)  freeObject(o2_new);\n    return cmp;\n}\n\nunsigned int\ndictEncObjHash(const void *key) {\n    robj *o = (robj*) key;\n\n    if (sdsEncodedObject(o)) {\n        return dictGenHashFunction(o->ptr, sdslen((sds)o->ptr));\n    } else {\n        if (o->encoding == OBJ_ENCODING_INT) {\n            char buf[32];\n            int len;\n\n            len = ll2string(buf,32,(long)o->ptr);\n            return dictGenHashFunction((unsigned char*)buf, len);\n        } else {\n            unsigned int hash;\n            robj *o_new;\n\n            o_new = getDecodedObject(o);\n            hash = dictGenHashFunction(o_new->ptr, sdslen((sds)o_new->ptr));\n            if (o_new!= o) freeObject(o_new);\n            return hash;\n        }\n    }\n}\n\nunsigned int\ndictObjHash(const void *key) {\n    const robj *o = key;\n    return dictGenHashFunction(o->ptr, sdslen((sds)o->ptr));\n}\n\nint\ndictObjKeyCompare(void *privdata, const void *key1,\n        const void *key2)\n{\n    const robj *o1 = key1, *o2 = key2;\n    return dictSdsKeyCompare(privdata,o1->ptr,o2->ptr);\n}\n\nvoid\ndictListDestructor(void *privdata, void *val)\n{\n    DICT_NOTUSED(privdata);\n    dlistRelease((dlist*)val);\n}\n\n/* Hash type hash table (note that small hashes are represented with ziplists) */\ndictType hashDictType = {\n    dictEncObjHash,             /* hash function */\n    NULL,                       /* key dup */\n    NULL,                       /* val dup */\n    dictEncObjKeyCompare,       /* key compare */\n    dictObjectDestructor,  /* key destructor */\n    dictObjectDestructor   /* val destructor */\n};\n\n/* Sets type hash table */\ndictType setDictType = {\n    dictEncObjHash,            /* hash function */\n    NULL,                      /* key dup */\n    NULL,                      /* val dup */\n    dictEncObjKeyCompare,      /* key compare */\n    dictObjectDestructor, /* key destructor */\n    NULL                       /* val destructor */\n};\n\n/* Sorted sets hash (note: a skiplist is used in addition to the hash table) */\ndictType zsetDictType = {\n    dictEncObjHash,            /* hash function */\n    NULL,                      /* key dup */\n    NULL,                      /* val dup */\n    dictEncObjKeyCompare,      /* key compare */\n    dictObjectDestructor,      /* key destructor */\n    NULL                       /* val destructor */\n};\n\n/* =========================== Server initialization ======================== */\n\nstatic void createSharedObjects(void) {\n    int j;\n    robj **obj;\n\n    shared.crlf = createObject(OBJ_STRING,sdsnew(\"\\r\\n\"));\n    shared.ok = createObject(OBJ_STRING,sdsnew(\"+OK\\r\\n\"));\n    shared.err = createObject(OBJ_STRING,sdsnew(\"-ERR\\r\\n\"));\n    shared.emptybulk = createObject(OBJ_STRING,sdsnew(\"$0\\r\\n\\r\\n\"));\n    shared.czero = createObject(OBJ_STRING,sdsnew(\":0\\r\\n\"));\n    shared.cone = createObject(OBJ_STRING,sdsnew(\":1\\r\\n\"));\n    shared.cnegone = createObject(OBJ_STRING,sdsnew(\":-1\\r\\n\"));\n    shared.nullbulk = createObject(OBJ_STRING,sdsnew(\"$-1\\r\\n\"));\n    shared.nullmultibulk = createObject(OBJ_STRING,sdsnew(\"*-1\\r\\n\"));\n    shared.emptymultibulk = createObject(OBJ_STRING,sdsnew(\"*0\\r\\n\"));\n    shared.pong = createObject(OBJ_STRING,sdsnew(\"+PONG\\r\\n\"));\n    shared.queued = createObject(OBJ_STRING,sdsnew(\"+QUEUED\\r\\n\"));\n    shared.emptyscan = createObject(OBJ_STRING,sdsnew(\"*2\\r\\n$1\\r\\n0\\r\\n*0\\r\\n\"));\n    shared.wrongtypeerr = createObject(OBJ_STRING,sdsnew(\n        \"-WRONGTYPE Operation against a key holding the wrong kind of value\\r\\n\"));\n    shared.nokeyerr = createObject(OBJ_STRING,sdsnew(\n        \"-ERR no such key\\r\\n\"));\n    shared.syntaxerr = createObject(OBJ_STRING,sdsnew(\n        \"-ERR syntax error\\r\\n\"));\n    shared.sameobjecterr = createObject(OBJ_STRING,sdsnew(\n        \"-ERR source and destination objects are the same\\r\\n\"));\n    shared.outofrangeerr = createObject(OBJ_STRING,sdsnew(\n        \"-ERR index out of range\\r\\n\"));\n    shared.noscripterr = createObject(OBJ_STRING,sdsnew(\n        \"-NOSCRIPT No matching script. Please use EVAL.\\r\\n\"));\n    shared.loadingerr = createObject(OBJ_STRING,sdsnew(\n        \"-LOADING Redis is loading the dataset in memory\\r\\n\"));\n    shared.slowscripterr = createObject(OBJ_STRING,sdsnew(\n        \"-BUSY Redis is busy running a script. You can only call SCRIPT KILL or SHUTDOWN NOSAVE.\\r\\n\"));\n    shared.masterdownerr = createObject(OBJ_STRING,sdsnew(\n        \"-MASTERDOWN Link with MASTER is down and slave-serve-stale-data is set to 'no'.\\r\\n\"));\n    shared.bgsaveerr = createObject(OBJ_STRING,sdsnew(\n        \"-MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist on disk. Commands that may modify the data set are disabled. Please check Redis logs for details about the error.\\r\\n\"));\n    shared.roslaveerr = createObject(OBJ_STRING,sdsnew(\n        \"-READONLY You can't write against a read only slave.\\r\\n\"));\n    shared.noautherr = createObject(OBJ_STRING,sdsnew(\n        \"-NOAUTH Authentication required.\\r\\n\"));\n    shared.noadminerr = createObject(OBJ_STRING,sdsnew(\n        \"-NOADMIN Authentication required.\\r\\n\"));\n    shared.oomerr = createObject(OBJ_STRING,sdsnew(\n        \"-OOM command not allowed when used memory > 'maxmemory'.\\r\\n\"));\n    shared.execaborterr = createObject(OBJ_STRING,sdsnew(\n        \"-EXECABORT Transaction discarded because of previous errors.\\r\\n\"));\n    shared.noreplicaserr = createObject(OBJ_STRING,sdsnew(\n        \"-NOREPLICAS Not enough good slaves to write.\\r\\n\"));\n    shared.busykeyerr = createObject(OBJ_STRING,sdsnew(\n        \"-BUSYKEY Target key name already exists.\\r\\n\"));\n    shared.space = createObject(OBJ_STRING,sdsnew(\" \"));\n    shared.colon = createObject(OBJ_STRING,sdsnew(\":\"));\n    shared.plus = createObject(OBJ_STRING,sdsnew(\"+\"));\n\n    for (j = 0; j < PROTO_SHARED_SELECT_CMDS; j++) {\n        char dictid_str[64];\n        int dictid_len;\n\n        dictid_len = ll2string(dictid_str,sizeof(dictid_str),j);\n        shared.select[j] = createObject(OBJ_STRING,\n            sdscatprintf(sdsempty(),\n                \"*2\\r\\n$6\\r\\nSELECT\\r\\n$%d\\r\\n%s\\r\\n\",\n                dictid_len, dictid_str));\n    }\n    shared.messagebulk = createStringObject(\"$7\\r\\nmessage\\r\\n\",13);\n    shared.pmessagebulk = createStringObject(\"$8\\r\\npmessage\\r\\n\",14);\n    shared.subscribebulk = createStringObject(\"$9\\r\\nsubscribe\\r\\n\",15);\n    shared.unsubscribebulk = createStringObject(\"$11\\r\\nunsubscribe\\r\\n\",18);\n    shared.psubscribebulk = createStringObject(\"$10\\r\\npsubscribe\\r\\n\",17);\n    shared.punsubscribebulk = createStringObject(\"$12\\r\\npunsubscribe\\r\\n\",19);\n    shared.del = createStringObject(\"DEL\",3);\n    shared.rpop = createStringObject(\"RPOP\",4);\n    shared.lpop = createStringObject(\"LPOP\",4);\n    shared.lpush = createStringObject(\"LPUSH\",5);\n    for (j = 0; j < OBJ_SHARED_INTEGERS; j++) {\n        shared.integers[j] = createObject(OBJ_STRING,(void*)(long)j);\n        shared.integers[j]->encoding = OBJ_ENCODING_INT;\n    }\n    for (j = 0; j < OBJ_SHARED_BULKHDR_LEN; j++) {\n        shared.mbulkhdr[j] = createObject(OBJ_STRING,\n            sdscatprintf(sdsempty(),\"*%d\\r\\n\",j));\n        shared.bulkhdr[j] = createObject(OBJ_STRING,\n            sdscatprintf(sdsempty(),\"$%d\\r\\n\",j));\n    }\n    /* The following two shared objects, minstring and maxstrings, are not\n     * actually used for their value but as a special object meaning\n     * respectively the minimum possible string and the maximum possible\n     * string in string comparisons for the ZRANGEBYLEX command. */\n    shared.minstring = createStringObject(\"minstring\",9);\n    shared.maxstring = createStringObject(\"maxstring\",9);\n\n    shared.outofcomplexitylimit = createObject(OBJ_STRING,sdsnew(\n        \"-ERR Out of max time complexity limit.\\r\\n\"));\n\n    /* Set this objects to constant */\n    for (obj = &shared; *obj != NULL; obj ++) {\n        (*obj)->constant = 1;\n    }\n}\n\nint\ninit_server(struct instance *nci)\n{\n    int ret;\n    uint32_t i;\n    redisDb *db;\n    \n    server.pid = getpid();\n    server.arch_bits = (sizeof(long) == 8) ? 64 : 32;\n    server.starttime = time(NULL);\n    get_random_hex_chars(server.runid, CONFIG_RUN_ID_SIZE);\n\n    server.commands = dictCreate(&commandTableDictType,NULL);\n    populateCommandTable();\n    server.delCommand = lookupCommandByCString(\"del\");\n    server.multiCommand = lookupCommandByCString(\"multi\");\n    server.lpushCommand = lookupCommandByCString(\"lpush\");\n    server.lpopCommand = lookupCommandByCString(\"lpop\");\n    server.rpopCommand = lookupCommandByCString(\"rpop\");\n    server.sremCommand = lookupCommandByCString(\"srem\");\n    server.execCommand = lookupCommandByCString(\"exec\");\n\n    conf = conf_create(nci->conf_filename);\n\n    ret = populateCommandsNeedAdminpass();\n    if (ret != VR_OK) {\n        log_error(\"Populate need adminpass commands failed\");\n        return VR_ERROR;\n    }\n\n    server.configfile = getAbsolutePath(nci->conf_filename);\n    server.hz = 10;\n    server.dblnum = cserver->databases;\n    server.dbinum = cserver->internal_dbs_per_databases;\n    server.dbnum = server.dblnum*server.dbinum;\n    darray_init(&server.dbs, server.dbnum, sizeof(redisDb));\n    server.pidfile = nci->pid_filename;\n    server.executable = NULL;\n    server.activerehashing = CONFIG_DEFAULT_ACTIVE_REHASHING;\n    \n    server.client_max_querybuf_len = PROTO_MAX_QUERYBUF_LEN;\n\n    for (i = 0; i < server.dbnum; i ++) {\n        db = darray_push(&server.dbs);\n        redisDbInit(db);\n    }\n\n    server.clients = dlistCreate();\n    \n    server.monitors = dlistCreate();\n\n    server.loading = 0;\n\n    server.lua_timedout = 0;\n\n    server.aof_state = AOF_OFF;\n\n    server.stop_writes_on_bgsave_err = 0;\n\n    server.ready_keys = dlistCreate();\n\n    server.system_memory_size = dalloc_get_memory_size();\n\n    server.rdb_child_pid = -1;\n    server.aof_child_pid = -1;\n\n    server.hash_max_ziplist_entries = OBJ_HASH_MAX_ZIPLIST_ENTRIES;\n    server.hash_max_ziplist_value = OBJ_HASH_MAX_ZIPLIST_VALUE;\n    server.list_max_ziplist_size = OBJ_LIST_MAX_ZIPLIST_SIZE;\n    server.list_compress_depth = OBJ_LIST_COMPRESS_DEPTH;\n    server.set_max_intset_entries = OBJ_SET_MAX_INTSET_ENTRIES;\n    server.zset_max_ziplist_entries = OBJ_ZSET_MAX_ZIPLIST_ENTRIES;\n    server.zset_max_ziplist_value = OBJ_ZSET_MAX_ZIPLIST_VALUE;\n    server.hll_sparse_max_bytes = CONFIG_DEFAULT_HLL_SPARSE_MAX_BYTES;\n\n    server.notify_keyspace_events = 0;\n\n    slowlogInit();\n    vr_replication_init();\n    \n    createSharedObjects();\n\n    server.port = cserver->port;\n    \n    /* Init worker first */\n    ret = workers_init(nci->thread_num);\n    if (ret != VR_OK) {\n        log_error(\"Init worker threads failed\");\n        return VR_ERROR;\n    }\n\n    /* Init master after worker init */\n    ret = master_init(conf);\n    if (ret != VR_OK) {\n        log_error(\"Init master thread failed\");\n        return VR_ERROR;\n    }\n\n    ret = backends_init(1);\n    if (ret != VR_OK) {\n        log_error(\"Init backend threads failed\");\n        return VR_ERROR;\n    }\n\n    log_debug(LOG_NOTICE, \"memory alloc lock type: %s\", malloc_lock_type());\n    log_debug(LOG_NOTICE, \"malloc lib: %s\", DMALLOC_LIB);\n\n    log_debug(LOG_NOTICE, \"stats lock type: %s\", STATS_LOCK_TYPE);\n\n    return VR_OK;\n}\n\nunsigned int getLRUClock(void) {\n    return (vr_msec_now()/LRU_CLOCK_RESOLUTION) & LRU_CLOCK_MAX;\n}\n\n/* This is an helper function for freeMemoryIfNeeded(), it is used in order\n * to populate the evictionPool with a few entries every time we want to\n * expire a key. Keys with idle time smaller than one of the current\n * keys are added. Keys are always added if there are free entries.\n *\n * We insert keys on place in ascending order, so keys with the smaller\n * idle time are on the left, and keys with the higher idle time on the\n * right. */\n\n#define EVICTION_SAMPLES_ARRAY_SIZE 16\nvoid evictionPoolPopulate(dict *sampledict, dict *keydict, \n    struct evictionPoolEntry *pool, int maxmemory_samples) {\n    int j, k, count;\n    dictEntry *_samples[EVICTION_SAMPLES_ARRAY_SIZE];\n    dictEntry **samples;\n\n    /* Try to use a static buffer: this function is a big hit...\n     * Note: it was actually measured that this helps. */\n    if (maxmemory_samples <= EVICTION_SAMPLES_ARRAY_SIZE) {\n        samples = _samples;\n    } else {\n        samples = dalloc(sizeof(samples[0])*maxmemory_samples);\n    }\n\n    count = dictGetSomeKeys(sampledict,samples,maxmemory_samples);\n    for (j = 0; j < count; j++) {\n        unsigned long long idle;\n        sds key;\n        robj *o;\n        dictEntry *de;\n\n        de = samples[j];\n        key = dictGetKey(de);\n        /* If the dictionary we are sampling from is not the main\n         * dictionary (but the expires one) we need to lookup the key\n         * again in the key dictionary to obtain the value object. */\n        if (sampledict != keydict) de = dictFind(keydict, key);\n        o = dictGetVal(de);\n        idle = estimateObjectIdleTime(o);\n\n        /* Insert the element inside the pool.\n         * First, find the first empty bucket or the first populated\n         * bucket that has an idle time smaller than our idle time. */\n        k = 0;\n        while (k < MAXMEMORY_EVICTION_POOL_SIZE &&\n               pool[k].key &&\n               pool[k].idle < idle) k++;\n        if (k == 0 && pool[MAXMEMORY_EVICTION_POOL_SIZE-1].key != NULL) {\n            /* Can't insert if the element is < the worst element we have\n             * and there are no empty buckets. */\n            continue;\n        } else if (k < MAXMEMORY_EVICTION_POOL_SIZE && pool[k].key == NULL) {\n            /* Inserting into empty position. No setup needed before insert. */\n        } else {\n            /* Inserting in the middle. Now k points to the first element\n             * greater than the element to insert.  */\n            if (pool[MAXMEMORY_EVICTION_POOL_SIZE-1].key == NULL) {\n                /* Free space on the right? Insert at k shifting\n                 * all the elements from k to end to the right. */\n                memmove(pool+k+1,pool+k,\n                    sizeof(pool[0])*(MAXMEMORY_EVICTION_POOL_SIZE-k-1));\n            } else {\n                /* No free space on right? Insert at k-1 */\n                k--;\n                /* Shift all elements on the left of k (included) to the\n                 * left, so we discard the element with smaller idle time. */\n                sdsfree(pool[0].key);\n                memmove(pool,pool+1,sizeof(pool[0])*k);\n            }\n        }\n        pool[k].key = sdsdup(key);\n        pool[k].idle = idle;\n    }\n    if (samples != _samples) dfree(samples);\n}\n\nint freeMemoryIfNeeded(vr_eventloop *vel) {\n    size_t mem_used, mem_tofree, mem_freed;\n    mstime_t latency, eviction_latency;\n    int keys_freed = 0;\n    long long maxmemory;\n    int maxmemory_policy, maxmemory_samples;\n    int ret;\n\n    maxmemory = vel->cc.maxmemory;\n    if (dalloc_used_memory() <= maxmemory)\n        return VR_OK;\n\n    conf_server_get(CONFIG_SOPN_MAXMEMORYP, &maxmemory_policy);\n    if (maxmemory_policy == MAXMEMORY_NO_EVICTION)\n        return VR_ERROR; /* We need to free memory, but policy forbids. */\n\n    conf_server_get(CONFIG_SOPN_MAXMEMORYS, &maxmemory_samples);\n    while (1) {\n        int j, k;\n\n        for (j = 0; j < server.dbnum; j++) {\n            long bestval = 0; /* just to prevent warning */\n            sds bestkey = NULL;\n            dictEntry *de;\n            redisDb *db = darray_get(&server.dbs, j);\n            dict *dict;\n\n            lockDbWrite(db);\n            if (maxmemory_policy == MAXMEMORY_ALLKEYS_LRU ||\n                maxmemory_policy == MAXMEMORY_ALLKEYS_RANDOM)\n            {\n                dict = db->dict;\n            } else {\n                dict = db->expires;\n            }\n            if (dictSize(dict) == 0) {\n                unlockDb(db);\n                continue;\n            }\n\n            /* volatile-random and allkeys-random policy */\n            if (maxmemory_policy == MAXMEMORY_ALLKEYS_RANDOM ||\n                maxmemory_policy == MAXMEMORY_VOLATILE_RANDOM)\n            {\n                de = dictGetRandomKey(dict);\n                bestkey = dictGetKey(de);\n            }\n\n            /* volatile-lru and allkeys-lru policy */\n            else if (maxmemory_policy == MAXMEMORY_ALLKEYS_LRU ||\n                maxmemory_policy == MAXMEMORY_VOLATILE_LRU)\n            {\n                struct evictionPoolEntry *pool = db->eviction_pool;\n\n                while(bestkey == NULL) {\n                    evictionPoolPopulate(dict, db->dict, db->eviction_pool, maxmemory_samples);\n                    /* Go backward from best to worst element to evict. */\n                    for (k = MAXMEMORY_EVICTION_POOL_SIZE-1; k >= 0; k--) {\n                        if (pool[k].key == NULL) continue;\n                        de = dictFind(dict,pool[k].key);\n\n                        /* Remove the entry from the pool. */\n                        sdsfree(pool[k].key);\n                        /* Shift all elements on its right to left. */\n                        memmove(pool+k,pool+k+1,\n                            sizeof(pool[0])*(MAXMEMORY_EVICTION_POOL_SIZE-k-1));\n                        /* Clear the element on the right which is empty\n                         * since we shifted one position to the left.  */\n                        pool[MAXMEMORY_EVICTION_POOL_SIZE-1].key = NULL;\n                        pool[MAXMEMORY_EVICTION_POOL_SIZE-1].idle = 0;\n\n                        /* If the key exists, is our pick. Otherwise it is\n                         * a ghost and we need to try the next element. */\n                        if (de) {\n                            bestkey = dictGetKey(de);\n                            break;\n                        } else {\n                            /* Ghost... */\n                            continue;\n                        }\n                    }\n                }\n            }\n\n            /* volatile-ttl */\n            else if (maxmemory_policy == MAXMEMORY_VOLATILE_TTL) {\n                for (k = 0; k < maxmemory_samples; k++) {\n                    sds thiskey;\n                    long thisval;\n\n                    de = dictGetRandomKey(dict);\n                    thiskey = dictGetKey(de);\n                    thisval = (long) dictGetVal(de);\n\n                    /* Expire sooner (minor expire unix timestamp) is better\n                     * candidate for deletion */\n                    if (bestkey == NULL || thisval < bestval) {\n                        bestkey = thiskey;\n                        bestval = thisval;\n                    }\n                }\n            }\n\n            /* Finally remove the selected key. */\n            if (bestkey) {\n                robj *keyobj = createStringObject(bestkey,sdslen(bestkey));\n                dbDelete(db,keyobj);\n                freeObject(keyobj);\n                keys_freed++;\n            }\n            \n            unlockDb(db);\n\n            conf_server_get(CONFIG_SOPN_MAXMEMORY, &maxmemory);\n            if (dalloc_used_memory() <= maxmemory) {\n                goto stop;\n            }\n        }\n        \n        if (!keys_freed) {\n            return VR_ERROR; /* nothing to free... */\n        }\n\n        update_stats_add(vel->stats, evictedkeys, keys_freed);\n        keys_freed = 0;\n    }\n\nstop:\n    update_stats_add(vel->stats, evictedkeys, keys_freed);\n    return VR_OK;\n}\n\n/* The PING command. It works in a different way if the client is in\n * in Pub/Sub mode. */\nvoid pingCommand(client *c) {\n    /* The command takes zero or one arguments. */\n    if (c->argc > 2) {\n        addReplyErrorFormat(c,\"wrong number of arguments for '%s' command\",\n            c->cmd->name);\n        return;\n    }\n\n    if (c->flags & CLIENT_PUBSUB) {\n        addReply(c,shared.mbulkhdr[2]);\n        addReplyBulkCBuffer(c,\"pong\",4);\n        if (c->argc == 1)\n            addReplyBulkCBuffer(c,\"\",0);\n        else\n            addReplyBulk(c,c->argv[1]);\n    } else {\n        if (c->argc == 1)\n            addReply(c,shared.pong);\n        else\n            addReplyBulk(c,c->argv[1]);\n    }\n}\n\n/* Return zero if strings are the same, non-zero if they are not.\n * The comparison is performed in a way that prevents an attacker to obtain\n * information about the nature of the strings just monitoring the execution\n * time of the function.\n *\n * Note that limiting the comparison length to strings up to 512 bytes we\n * can avoid leaking any information about the password length and any\n * possible branch misprediction related leak.\n */\nint time_independent_strcmp(char *a, char *b) {\n    char bufa[CONFIG_AUTHPASS_MAX_LEN], bufb[CONFIG_AUTHPASS_MAX_LEN];\n    /* The above two strlen perform len(a) + len(b) operations where either\n     * a or b are fixed (our password) length, and the difference is only\n     * relative to the length of the user provided string, so no information\n     * leak is possible in the following two lines of code. */\n    unsigned int alen = strlen(a);\n    unsigned int blen = strlen(b);\n    unsigned int j;\n    int diff = 0;\n\n    /* We can't compare strings longer than our static buffers.\n     * Note that this will never pass the first test in practical circumstances\n     * so there is no info leak. */\n    if (alen > sizeof(bufa) || blen > sizeof(bufb)) return 1;\n\n    memset(bufa,0,sizeof(bufa));        /* Constant time. */\n    memset(bufb,0,sizeof(bufb));        /* Constant time. */\n    /* Again the time of the following two copies is proportional to\n     * len(a) + len(b) so no info is leaked. */\n    memcpy(bufa,a,alen);\n    memcpy(bufb,b,blen);\n\n    /* Always compare all the chars in the two buffers without\n     * conditional expressions. */\n    for (j = 0; j < sizeof(bufa); j++) {\n        diff |= (bufa[j] ^ bufb[j]);\n    }\n    /* Length must be equal as well. */\n    diff |= alen ^ blen;\n    return diff; /* If zero strings are the same. */\n}\n\nvoid authCommand(client *c) {\n    sds requirepass;\n\n    conf_server_get(CONFIG_SOPN_REQUIREPASS,&requirepass);\n    if (!requirepass) {\n        addReplyError(c,\"Client sent AUTH, but no password is set\");\n        return;\n    } else if (!time_independent_strcmp(c->argv[1]->ptr, requirepass)) {\n        if (!c->authenticated)\n            c->authenticated = 1;\n        addReply(c,shared.ok);\n    } else {\n        c->authenticated = 0;\n        addReplyError(c,\"invalid password\");\n    }\n    sdsfree(requirepass);\n}\n\nvoid adminCommand(client *c) {\n    sds adminpass;\n\n    conf_server_get(CONFIG_SOPN_ADMINPASS,&adminpass);\n    if (!adminpass) {\n        addReplyError(c,\"Client sent ADMIN, but no password is set\");\n        return;\n    } else if (!time_independent_strcmp(c->argv[1]->ptr, adminpass)) {\n        c->authenticated = 2;\n        addReply(c,shared.ok);\n    } else {\n        c->authenticated = 0;\n        addReplyError(c,\"invalid password\");\n    }\n    sdsfree(adminpass);\n}\n\nint htNeedsResize(dict *dict) {\n    long long size, used;\n\n    size = dictSlots(dict);\n    used = dictSize(dict);\n    return (size && used && size > DICT_HT_INITIAL_SIZE &&\n            (used*100/size < HASHTABLE_MIN_FILL));\n}\n\nstruct keys_statistics {\n    long long keys_all;\n    long long vkeys_all;\n    long long avg_ttl_all;\n    int nexist;\n};\n\n/* Create the string returned by the INFO command. This is decoupled\n * by the INFO command itself as we need to report the same information\n * on memory corruption problems. */\nsds genVireInfoString(vr_eventloop *vel, char *section) {\n    sds info = sdsempty();\n    time_t uptime = time(NULL)-server.starttime;\n    int j, k, numcommands;\n    struct rusage self_ru;\n    unsigned long lol, bib;\n    int allsections = 0, defsections = 0;\n    int sections = 0;\n    struct darray *kss = NULL;  /* type: keys_statistics */\n\n    if (section == NULL) section = \"default\";\n    allsections = strcasecmp(section,\"all\") == 0;\n    defsections = strcasecmp(section,\"default\") == 0;\n\n    getrusage(RUSAGE_SELF, &self_ru);\n\n    /* Server */\n    if (allsections || defsections || !strcasecmp(section,\"server\")) {\n        static int call_uname = 1;\n        static struct utsname name;\n        char *mode;\n\n        mode = \"standalone\";\n\n        if (sections++) info = sdscat(info,\"\\r\\n\");\n\n        if (call_uname) {\n            /* Uname can be slow and is always the same output. Cache it. */\n            uname(&name);\n            call_uname = 0;\n        }\n\n        info = sdscatprintf(info,\n            \"# Server\\r\\n\"\n            \"vire_version:%s\\r\\n\"\n            \"vire_mode:%s\\r\\n\"\n            \"os:%s %s %s\\r\\n\"\n            \"arch_bits:%d\\r\\n\"\n            \"multiplexing_api:%s\\r\\n\"\n            \"gcc_version:%d.%d.%d\\r\\n\"\n            \"process_id:%ld\\r\\n\"\n            \"run_id:%s\\r\\n\"\n            \"tcp_port:%d\\r\\n\"\n            \"uptime_in_seconds:%jd\\r\\n\"\n            \"uptime_in_days:%jd\\r\\n\"\n            \"hz:%d\\r\\n\"\n            \"executable:%s\\r\\n\"\n            \"config_file:%s\\r\\n\"\n            \"databases:%d\\r\\n\"\n            \"internal_databases:%d\\r\\n\",\n            VR_VERSION_STRING,\n            mode,\n            name.sysname, name.release, name.machine,\n            server.arch_bits,\n            aeGetApiName(),\n#ifdef __GNUC__\n            __GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__,\n#else\n            0,0,0,\n#endif\n            (long) getpid(),\n            server.runid,\n            server.port,\n            (intmax_t)uptime,\n            (intmax_t)(uptime/(3600*24)),\n            server.hz,\n            server.executable ? server.executable : \"\",\n            server.configfile ? server.configfile : \"\",\n            server.dblnum,\n            server.dbinum);\n    }\n\n    /* Clients */\n    if (allsections || defsections || !strcasecmp(section,\"clients\")) {\n        if (sections++) info = sdscat(info,\"\\r\\n\");\n        info = sdscatprintf(info,\n            \"# Clients\\r\\n\"\n            \"connected_clients:%d\\r\\n\",\n            current_clients());\n    }\n\n    /* Memory */\n    if (allsections || defsections || !strcasecmp(section,\"memory\")) {\n        uint32_t idx;\n        vr_worker *worker;\n        char hmem[64];\n        char peak_hmem[64];\n        char total_system_hmem[64];\n        char used_memory_lua_hmem[64];\n        char used_memory_rss_hmem[64];\n        char maxmemory_hmem[64];\n        size_t vr_used_memory = dalloc_used_memory();\n        size_t total_system_mem = server.system_memory_size;\n        const char *evict_policy;\n        size_t peak_memory = 0, peak_memory_for_one_worker;\n        long long maxmemory;\n        int maxmemory_policy;\n\n        /* Peak memory is updated from time to time by workerCron() so it\n         * may happen that the instantaneous value is slightly bigger than\n         * the peak value. This may confuse users, so we update the peak\n         * if found smaller than the current memory usage. */\n        for (idx = 0; idx < darray_n(&workers); idx ++) {\n            worker = darray_get(&workers, idx);\n            update_stats_get(worker->vel.stats, peak_memory, \n                &peak_memory_for_one_worker);\n            if (peak_memory < peak_memory_for_one_worker)\n                peak_memory = peak_memory_for_one_worker;\n        }\n        if (vr_used_memory > peak_memory) {\n            peak_memory = vr_used_memory;\n            update_stats_set(vel->stats, peak_memory, vr_used_memory);\n        }\n\n        conf_server_get(CONFIG_SOPN_MAXMEMORY,&maxmemory);\n        conf_server_get(CONFIG_SOPN_MAXMEMORYP,&maxmemory_policy);\n        evict_policy = get_evictpolicy_strings(maxmemory_policy);\n    \n        bytesToHuman(hmem,vr_used_memory);\n        bytesToHuman(peak_hmem,peak_memory);\n        bytesToHuman(total_system_hmem,total_system_mem);\n        bytesToHuman(used_memory_rss_hmem,vel->resident_set_size);\n        bytesToHuman(maxmemory_hmem,maxmemory);\n\n        if (sections++) info = sdscat(info,\"\\r\\n\");\n        info = sdscatprintf(info,\n            \"# Memory\\r\\n\"\n            \"used_memory:%zu\\r\\n\"\n            \"used_memory_human:%s\\r\\n\"\n            \"used_memory_rss:%zu\\r\\n\"\n            \"used_memory_rss_human:%s\\r\\n\"\n            \"used_memory_peak:%zu\\r\\n\"\n            \"used_memory_peak_human:%s\\r\\n\"\n            \"total_system_memory:%lu\\r\\n\"\n            \"total_system_memory_human:%s\\r\\n\"\n            \"maxmemory:%lld\\r\\n\"\n            \"maxmemory_human:%s\\r\\n\"\n            \"maxmemory_policy:%s\\r\\n\"\n            \"mem_fragmentation_ratio:%.2f\\r\\n\"\n            \"mem_allocator:%s\\r\\n\",\n            vr_used_memory,\n            hmem,\n            vel->resident_set_size,\n            used_memory_rss_hmem,\n            peak_memory,\n            peak_hmem,\n            (unsigned long)total_system_mem,\n            total_system_hmem,\n            maxmemory,\n            maxmemory_hmem,\n            evict_policy,\n            (float)vel->resident_set_size/vr_used_memory,\n            DMALLOC_LIB\n            );\n    }\n\n    /* Stats */\n    if (allsections || defsections || !strcasecmp(section,\"stats\")) {\n        uint32_t idx;\n        vr_stats *stats;\n        long long stat_numconnections=0, stat_numcommands=0;\n        long long stat_net_input_bytes=0, stat_net_output_bytes=0;\n        long long stat_rejected_conn=0;\n        long long stat_expiredkeys=0;\n        long long stat_evictedkeys=0;\n        long long stat_keyspace_hits=0, stat_keyspace_misses=0;\n        long long stat_numcommands_ops=0;\n        float stat_net_input_bytes_ops=0, stat_net_output_bytes_ops=0;\n\n        for (idx = 0; idx < darray_n(&workers); idx ++) {\n            long long stats_value;\n            vr_worker *worker = darray_get(&workers, idx);\n            stats = worker->vel.stats;\n\n            update_stats_get(stats, numcommands, &stats_value);\n            stat_numcommands += stats_value;\n            update_stats_get(stats, numconnections, &stats_value);\n            stat_numconnections += stats_value;\n            update_stats_get(stats, expiredkeys, &stats_value);\n            stat_expiredkeys += stats_value;\n            update_stats_get(stats, evictedkeys, &stats_value);\n            stat_evictedkeys += stats_value;\n            update_stats_get(stats, net_input_bytes, &stats_value);\n            stat_net_input_bytes += stats_value;\n            update_stats_get(stats, net_output_bytes, &stats_value);\n            stat_net_output_bytes += stats_value;\n            update_stats_get(stats, keyspace_hits, &stats_value);\n            stat_keyspace_hits += stats_value;\n            update_stats_get(stats, keyspace_misses, &stats_value);\n            stat_keyspace_misses += stats_value;\n            \n            stat_numcommands_ops += getInstantaneousMetric(stats, STATS_METRIC_COMMAND);\n            stat_net_input_bytes_ops += (float)getInstantaneousMetric(stats, STATS_METRIC_NET_INPUT)/1024;\n            stat_net_output_bytes_ops += (float)getInstantaneousMetric(stats, STATS_METRIC_NET_OUTPUT)/1024;\n        }\n        for (idx = 0; idx < darray_n(&backends); idx ++) {\n            long long stats_value;\n            vr_backend *backend = darray_get(&backends, idx);\n            stats = backend->vel.stats;\n\n            update_stats_get(stats, expiredkeys, &stats_value);\n            stat_expiredkeys += stats_value;\n        }\n        update_stats_get(master.vel.stats, rejected_conn, &stat_rejected_conn);\n        \n        if (sections++) info = sdscat(info,\"\\r\\n\");\n        info = sdscatprintf(info,\n            \"# Stats\\r\\n\"\n            \"total_connections_received:%lld\\r\\n\"\n            \"total_commands_processed:%lld\\r\\n\"\n            \"instantaneous_ops_per_sec:%lld\\r\\n\"\n            \"total_net_input_bytes:%lld\\r\\n\"\n            \"total_net_output_bytes:%lld\\r\\n\"\n            \"instantaneous_input_kbps:%.2f\\r\\n\"\n            \"instantaneous_output_kbps:%.2f\\r\\n\"\n            \"rejected_connections:%lld\\r\\n\"\n            \"expired_keys:%lld\\r\\n\"\n            \"evicted_keys:%lld\\r\\n\"\n            \"keyspace_hits:%lld\\r\\n\"\n            \"keyspace_misses:%lld\\r\\n\",\n            stat_numconnections,\n            stat_numcommands,\n            stat_numcommands_ops,\n            stat_net_input_bytes,\n            stat_net_output_bytes,\n            stat_net_input_bytes_ops,\n            stat_net_output_bytes_ops,\n            stat_rejected_conn,\n            stat_expiredkeys,\n            stat_evictedkeys,\n            stat_keyspace_hits,\n            stat_keyspace_misses);\n    }\n\n    /* CPU */\n    if (allsections || defsections || !strcasecmp(section,\"cpu\")) {\n        if (sections++) info = sdscat(info,\"\\r\\n\");\n        info = sdscatprintf(info,\n        \"# CPU\\r\\n\"\n        \"used_cpu_sys:%.2f\\r\\n\"\n        \"used_cpu_user:%.2f\\r\\n\",\n        (float)self_ru.ru_stime.tv_sec+(float)self_ru.ru_stime.tv_usec/1000000,\n        (float)self_ru.ru_utime.tv_sec+(float)self_ru.ru_utime.tv_usec/1000000);\n    }\n    \n    /* Internal */\n    if (allsections || !strcasecmp(section,\"internal\")) {\n        redisDb *db;\n        struct keys_statistics *ks;\n        long long keys, vkeys, avg_ttl;\n        \n        kss = darray_create(server.dblnum, sizeof(struct keys_statistics));\n        \n        if (sections++) info = sdscat(info,\"\\r\\n\");\n        info = sdscatprintf(info, \"# Internal\\r\\n\");\n        for (j = 0; j < server.dblnum; j++) {\n            ks = darray_push(kss);\n            ks->keys_all = ks->vkeys_all = ks->avg_ttl_all = 0;\n            ks->nexist = 0;\n            for (k = 0; k < server.dbinum; k ++) {\n                db = darray_get(&server.dbs, (uint32_t)(j*server.dbinum+k));\n                lockDbRead(db);\n                keys = dictSize(db->dict);\n                vkeys = dictSize(db->expires);\n                avg_ttl = db->avg_ttl;\n                unlockDb(db);\n                if (keys || vkeys) {\n                    info = sdscatprintf(info,\n                        \"db%d-%d:keys=%lld,expires=%lld,avg_ttl=%lld\\r\\n\",\n                        j, k, keys, vkeys, db->avg_ttl);\n                }\n                ks->keys_all += keys;\n                ks->vkeys_all += vkeys;\n                ks->avg_ttl_all += avg_ttl;\n                if (avg_ttl > 0) ks->nexist ++;\n            }\n        }\n    }\n\n    /* Key space */\n    if (allsections || defsections || !strcasecmp(section,\"keyspace\")) {\n        redisDb *db;\n        struct keys_statistics *ks;\n        long long keys_all, vkeys_all, avg_ttl_all;\n        int nexist;\n        \n        if (sections++) info = sdscat(info,\"\\r\\n\");\n        info = sdscatprintf(info, \"# Keyspace\\r\\n\");\n        if (kss == NULL) {\n            for (j = 0; j < server.dblnum; j++) {\n                keys_all = vkeys_all = avg_ttl_all = 0;\n                nexist = 0;\n                for (k = 0; k < server.dbinum; k ++) {\n                    db = darray_get(&server.dbs, (uint32_t)(j*server.dbinum+k));\n                    lockDbRead(db);\n                    keys_all += dictSize(db->dict);\n                    vkeys_all += dictSize(db->expires);\n                    avg_ttl_all += db->avg_ttl;\n                    if (db->avg_ttl > 0) nexist ++;\n                    unlockDb(db);\n                }\n                if (keys_all || vkeys_all) {\n                    info = sdscatprintf(info,\n                        \"db%d:keys=%lld,expires=%lld,avg_ttl=%lld\\r\\n\",\n                        j, keys_all, vkeys_all, nexist>0?(avg_ttl_all/nexist):0);\n                }\n            }\n        } else {\n            for (j = 0; j < server.dblnum; j ++) {\n                ks = darray_get(kss, j);\n                if (ks->keys_all || ks->vkeys_all) {\n                    info = sdscatprintf(info,\n                        \"db%d:keys=%lld,expires=%lld,avg_ttl=%lld\\r\\n\",\n                        j, ks->keys_all, ks->vkeys_all, \n                        ks->nexist>0?(ks->avg_ttl_all/ks->nexist):0);\n                }\n            }\n        }\n    }\n\n    if (kss != NULL) {\n        kss->nelem = 0;\n        darray_destroy(kss);\n        kss = NULL;\n    }\n\n    return info;\n}\n\nvoid infoCommand(client *c) {\n    char *section = c->argc == 2 ? c->argv[1]->ptr : \"default\";\n\n    if (c->argc > 2) {\n        addReply(c,shared.syntaxerr);\n        return;\n    }\n    addReplyBulkSds(c, genVireInfoString(c->vel, section));\n}\n\nvoid echoCommand(client *c) {\n    addReplyBulk(c,c->argv[1]);\n}\n\nvoid timeCommand(client *c) {\n    struct timeval tv;\n\n    /* gettimeofday() can only fail if &tv is a bad address so we\n     * don't check for errors. */\n    gettimeofday(&tv,NULL);\n    addReplyMultiBulkLen(c,2);\n    addReplyBulkLongLong(c,tv.tv_sec);\n    addReplyBulkLongLong(c,tv.tv_usec);\n}\n\n/* This function will try to raise the max number of open files accordingly to\n * the configured max number of clients. It also reserves a number of file\n * descriptors (CONFIG_MIN_RESERVED_FDS) for extra operations of\n * persistence, listening sockets, log files and so forth.\n *\n * If it will not be possible to set the limit accordingly to the configured\n * max number of clients, the function will do the reverse setting\n * server.maxclients to the value that we can actually handle. */\nint adjustOpenFilesLimit(int maxclients) {\n    rlim_t maxfiles, finallimit = 0;\n    rlim_t oldlimit;\n    int finalmaxclients;\n    struct rlimit limit;\n    int threads;\n\n    conf_server_get(CONFIG_SOPN_THREADS,&threads);\n    maxfiles = maxclients+threads*2+CONFIG_MIN_RESERVED_FDS;\n    if (getrlimit(RLIMIT_NOFILE,&limit) == -1) {\n        log_warn(\"Unable to obtain the current NOFILE limit (%s), assuming 1024 and setting the max clients configuration accordingly.\",\n            strerror(errno));\n        oldlimit = 1024;\n        finallimit = oldlimit;\n    } else {\n        oldlimit = limit.rlim_cur;\n\n        /* Set the max number of files if the current limit is not enough\n         * for our needs. */\n        if (oldlimit < maxfiles) {\n            rlim_t bestlimit;\n            int setrlimit_error = 0;\n\n            /* Try to set the file limit to match 'maxfiles' or at least\n             * to the higher value supported less than maxfiles. */\n            bestlimit = maxfiles;\n            while(bestlimit > oldlimit) {\n                rlim_t decr_step = 16;\n\n                limit.rlim_cur = bestlimit;\n                limit.rlim_max = bestlimit;\n                if (setrlimit(RLIMIT_NOFILE,&limit) != -1) break;\n                setrlimit_error = errno;\n\n                /* We failed to set file limit to 'bestlimit'. Try with a\n                 * smaller limit decrementing by a few FDs per iteration. */\n                if (bestlimit < decr_step) break;\n                bestlimit -= decr_step;\n            }\n\n            /* Assume that the limit we get initially is still valid if\n             * our last try was even lower. */\n            if (bestlimit < oldlimit) bestlimit = oldlimit;\n\n            finallimit = bestlimit;\n            if (bestlimit < maxfiles) {\n                log_warn(\"You requested maxclients of %d \"\n                    \"requiring at least %llu max file descriptors.\",\n                    maxclients,\n                    (unsigned long long) maxfiles);\n                log_warn(\"Server can't set maximum open files \"\n                    \"to %llu because of OS error: %s.\",\n                    (unsigned long long) maxfiles, strerror(setrlimit_error));\n                log_warn(\"Current maximum open files is %llu. \",\n                    (unsigned long long) bestlimit);\n            } else {\n                log_warn(\"Increased maximum number of open files \"\n                    \"to %llu (it was originally set to %llu).\",\n                    (unsigned long long) maxfiles,\n                    (unsigned long long) oldlimit);\n            }\n        } else {\n            finallimit = maxfiles;\n        }\n    }\n\n    finalmaxclients = finallimit-threads*2-CONFIG_MIN_RESERVED_FDS;\n    if (finalmaxclients < 1) {\n        log_warn(\"Your current 'ulimit -n' \"\n            \"of %llu is not enough for the server to start. \"\n            \"Please increase your open file limit to at least \"\n            \"%llu. Exiting.\",\n            (unsigned long long) oldlimit,\n            (unsigned long long) maxfiles);\n        return -1;\n    }\n    \n    if (finallimit < maxfiles) {\n        conf_value *cv = conf_value_create(CONF_VALUE_TYPE_STRING);\n        cv->value = sdsfromlonglong((long long)finalmaxclients);\n        conf_server_set(CONFIG_SOPN_MAXCLIENTS,cv);\n        conf_value_destroy(cv);\n        log_warn(\"maxclients has been reduced to %d to compensate for \"\n            \"low ulimit. \"\n            \"If you need higher maxclients increase 'ulimit -n'.\",\n            finalmaxclients);\n    }\n    \n    return (int)finallimit;\n}\n"
  },
  {
    "path": "src/vr_server.h",
    "content": "#ifndef _VR_SERVER_H_\n#define _VR_SERVER_H_\n\n#define CONFIG_MIN_RESERVED_FDS 32 /* For extra operations of\n                                            * listening sockets, log files and so forth*/\n\n#define CONFIG_AUTHPASS_MAX_LEN 512\n#define PROTO_SHARED_SELECT_CMDS 10\n#define CRON_DBS_PER_CALL 16\n\n#define ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP 20 /* Loopkups per loop. */\n#define ACTIVE_EXPIRE_CYCLE_FAST_DURATION 1000 /* Microseconds */\n#define ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC 25 /* CPU max % for keys collection */\n#define ACTIVE_EXPIRE_CYCLE_SLOW 0\n#define ACTIVE_EXPIRE_CYCLE_FAST 1\n\n#define SCAN_TYPE_KEY   0\n#define SCAN_TYPE_HASH  1\n#define SCAN_TYPE_SET   2\n#define SCAN_TYPE_ZSET  3\n\n/* Redis maxmemory strategies */\n#define CONFIG_DEFAULT_MAXMEMORY_POLICY MAXMEMORY_NO_EVICTION\n\n/* Zip structure related defaults */\n#define OBJ_HASH_MAX_ZIPLIST_ENTRIES 512\n#define OBJ_HASH_MAX_ZIPLIST_VALUE 64\n#define OBJ_SET_MAX_INTSET_ENTRIES 512\n#define OBJ_ZSET_MAX_ZIPLIST_ENTRIES 128\n#define OBJ_ZSET_MAX_ZIPLIST_VALUE 64\n\n/* List defaults */\n#define OBJ_LIST_MAX_ZIPLIST_SIZE -2\n#define OBJ_LIST_COMPRESS_DEPTH 0\n\n/* HyperLogLog defines */\n#define CONFIG_DEFAULT_HLL_SPARSE_MAX_BYTES 3000\n\n/* List related stuff */\n#define LIST_HEAD 0\n#define LIST_TAIL 1\n\n#define ZSKIPLIST_MAXLEVEL 32 /* Should be enough for 2^32 elements */\n#define ZSKIPLIST_P 0.25      /* Skiplist P = 1/4 */\n\n/* Units */\n#define UNIT_SECONDS 0\n#define UNIT_MILLISECONDS 1\n\n/* Hash table parameters */\n#define HASHTABLE_MIN_FILL        10      /* Minimal hash table fill 10% */\n\n/* Using the following macro you can run code inside serverCron() with the\n * specified period, specified in milliseconds.\n * The actual resolution depends on server.hz. */\n#define run_with_period(_ms_, cronloops) if ((_ms_ <= 1000/server.hz) || !(cronloops%((_ms_)/(1000/server.hz))))\n\n/* Macro used to obtain the current LRU clock.\n * If the current resolution is lower than the frequency we refresh the\n * LRU clock (as it should be in production servers) we return the\n * precomputed value, otherwise we need to resort to a function call. */\n#define LRU_CLOCK() ((1000/server.hz <= LRU_CLOCK_RESOLUTION) ? server.lruclock : getLRUClock())\n\n/* The following structure represents a node in the server.ready_keys list,\n * where we accumulate all the keys that had clients blocked with a blocking\n * operation such as B[LR]POP, but received new data in the context of the\n * last executed command.\n *\n * After the execution of every command or script, we run this list to check\n * if as a result we should serve data to clients blocked, unblocking them.\n * Note that server.ready_keys will not have duplicates as there dictionary\n * also called ready_keys in every structure representing a Redis database,\n * where we make sure to remember if a given key was already added in the\n * server.ready_keys list. */\ntypedef struct readyList {\n    redisDb *db;\n    robj *key;\n} readyList;\n\nstruct vr_server {\n    aeEventLoop *el;\n    dlist *clients;\n    \n    /* General */\n    pid_t pid;                  /* Main process pid. */\n    char *executable;           /* Absolute executable file path. */\n    char *configfile;           /* Absolute config file path, or NULL */\n    int hz;                     /* serverCron() calls frequency in hertz */\n\n    struct darray dbs;           /* database array, type: redisDB */\n    int dbnum;                  /* Total number of DBs */\n    int dblnum;                 /* Logical number of configured DBs */\n    int dbinum;                 /* Number of internal DBs for per logical DB */\n    \n    dict *commands;             /* Command table */\n    dict *orig_commands;        /* Command table before command renaming. */\n    \n    unsigned lruclock:LRU_BITS; /* Clock for LRU eviction */\n    int activerehashing;        /* Incremental rehash in serverCron() */\n\n    char *pidfile;              /* PID file path */\n    int arch_bits;              /* 32 or 64 depending on sizeof(long) */\n    char runid[CONFIG_RUN_ID_SIZE+1];  /* ID always different at every exec. */\n    \n    /* Networking */\n    int port;                   /* TCP listening port */\n    int tcp_backlog;            /* TCP listen() backlog */\n\n    int tcpkeepalive;               /* Set SO_KEEPALIVE if non-zero. */\n    size_t client_max_querybuf_len; /* Limit for client query buffer length */\n\n    /* Zip structure config, see redis.conf for more information  */\n    size_t hash_max_ziplist_entries;\n    size_t hash_max_ziplist_value;\n    size_t set_max_intset_entries;\n    size_t zset_max_ziplist_entries;\n    size_t zset_max_ziplist_value;\n    size_t hll_sparse_max_bytes;\n    /* List parameters */\n    int list_max_ziplist_size;\n    int list_compress_depth;\n    \n    clientBufferLimitsConfig client_obuf_limits[CLIENT_TYPE_OBUF_COUNT];\n\n    dlist *monitors;    /* List of slaves and MONITORs */\n\n    time_t starttime;       /* Server start time */\n\n    /* time cache */\n    time_t unixtime;        /* Unix time sampled every cron cycle. */\n    long long mstime;       /* Like 'unixtime' but with milliseconds resolution. */\n\n    char *unixsocket;           /* UNIX socket path */\n\n    /* RDB / AOF loading information */\n    int loading;                /* We are loading data from disk if true */\n    off_t loading_total_bytes;\n    off_t loading_loaded_bytes;\n    time_t loading_start_time;\n    off_t loading_process_events_interval_bytes;\n\n    /* AOF persistence */\n    int aof_state;                  /* AOF_(ON|OFF|WAIT_REWRITE) */\n    int aof_fsync;                  /* Kind of fsync() policy */\n    char *aof_filename;             /* Name of the AOF file */\n    int aof_no_fsync_on_rewrite;    /* Don't fsync if a rewrite is in prog. */\n    int aof_rewrite_perc;           /* Rewrite AOF if % growth is > M and... */\n    off_t aof_rewrite_min_size;     /* the AOF file is at least N bytes. */\n    off_t aof_rewrite_base_size;    /* AOF size on latest startup or rewrite. */\n    off_t aof_current_size;         /* AOF current size. */\n    int aof_rewrite_scheduled;      /* Rewrite once BGSAVE terminates. */\n    pid_t aof_child_pid;            /* PID if rewriting process */\n    dlist *aof_rewrite_buf_blocks;   /* Hold changes during an AOF rewrite. */\n    sds aof_buf;      /* AOF buffer, written before entering the event loop */\n    int aof_fd;       /* File descriptor of currently selected AOF file */\n    int aof_selected_db; /* Currently selected DB in AOF */\n    time_t aof_flush_postponed_start; /* UNIX time of postponed AOF flush */\n    time_t aof_last_fsync;            /* UNIX time of last fsync() */\n    time_t aof_rewrite_time_last;   /* Time used by last AOF rewrite run. */\n    time_t aof_rewrite_time_start;  /* Current AOF rewrite start time. */\n    int aof_lastbgrewrite_status;   /* VR_OK or VR_ERROR */\n    unsigned long aof_delayed_fsync;  /* delayed AOF fsync() counter */\n    int aof_rewrite_incremental_fsync;/* fsync incrementally while rewriting? */\n    int aof_last_write_status;      /* VR_OK or VR_ERROR */\n    int aof_last_write_errno;       /* Valid if aof_last_write_status is ERR */\n    int aof_load_truncated;         /* Don't stop on unexpected AOF EOF. */\n    /* AOF pipes used to communicate between parent and child during rewrite. */\n    int aof_pipe_write_data_to_child;\n    int aof_pipe_read_data_from_parent;\n    int aof_pipe_write_ack_to_parent;\n    int aof_pipe_read_ack_from_child;\n    int aof_pipe_write_ack_to_child;\n    int aof_pipe_read_ack_from_parent;\n    int aof_stop_sending_diff;     /* If true stop sending accumulated diffs\n                                      to child process. */\n    sds aof_child_diff;             /* AOF diff accumulator child side. */\n\n    /* RDB persistence */\n    long long dirty;                /* Changes to DB from the last save */\n    long long dirty_before_bgsave;  /* Used to restore dirty on failed BGSAVE */\n    pid_t rdb_child_pid;            /* PID of RDB saving child */\n    struct saveparam *saveparams;   /* Save points array for RDB */\n    int saveparamslen;              /* Number of saving points */\n    char *rdb_filename;             /* Name of RDB file */\n    int rdb_compression;            /* Use compression in RDB? */\n    int rdb_checksum;               /* Use RDB checksum? */\n    time_t lastsave;                /* Unix time of last successful save */\n    time_t lastbgsave_try;          /* Unix time of last attempted bgsave */\n    time_t rdb_save_time_last;      /* Time used by last RDB save run. */\n    time_t rdb_save_time_start;     /* Current RDB save start time. */\n    int rdb_child_type;             /* Type of save by active child. */\n    int lastbgsave_status;          /* VR_OK or VR_ERROR */\n    int stop_writes_on_bgsave_err;  /* Don't allow writes if can't BGSAVE */\n    int rdb_pipe_write_result_to_parent; /* RDB pipes used to return the state */\n    int rdb_pipe_read_result_from_child; /* of each slave in diskless SYNC. */\n\n    /* Scripting */\n    //lua_State *lua; /* The Lua interpreter. We use just one for all clients */\n    struct client *lua_client;   /* The \"fake client\" to query Redis from Lua */\n    struct client *lua_caller;   /* The client running EVAL right now, or NULL */\n    dict *lua_scripts;         /* A dictionary of SHA1 -> Lua scripts */\n    mstime_t lua_time_limit;  /* Script timeout in milliseconds */\n    mstime_t lua_time_start;  /* Start time of script, milliseconds time */\n    int lua_write_dirty;  /* True if a write command was called during the\n                             execution of the current script. */\n    int lua_random_dirty; /* True if a random command was called during the\n                             execution of the current script. */\n    int lua_replicate_commands; /* True if we are doing single commands repl. */\n    int lua_multi_emitted;/* True if we already proagated MULTI. */\n    int lua_repl;         /* Script replication flags for redis.set_repl(). */\n    int lua_timedout;     /* True if we reached the time limit for script\n                             execution. */\n    int lua_kill;         /* Kill the script if true. */\n    int lua_always_replicate_commands; /* Default replication type. */\n\n    /* Blocked clients */\n    dlist *ready_keys;        /* List of readyList structures for BLPOP & co */\n\n    /* Propagation of commands in AOF / replication */\n    redisOpArray also_propagate;    /* Additional command to propagate. */\n\n    /* Pubsub */\n    dict *pubsub_channels;  /* Map channels to list of subscribed clients */\n    dlist *pubsub_patterns;  /* A list of pubsub_patterns */\n    int notify_keyspace_events; /* Events to propagate via Pub/Sub. This is an\n                                   xor of NOTIFY_... flags. */\n\n    /* Fast pointers to often looked up command */\n    struct redisCommand *delCommand, *multiCommand, *lpushCommand, *lpopCommand,\n                        *rpopCommand, *sremCommand, *execCommand;\n\n    /* System hardware info */\n    size_t system_memory_size;  /* Total memory in system as reported by OS */\n};\n\n/* ZSETs use a specialized version of Skiplists */\ntypedef struct zskiplistNode {\n    robj *obj;\n    double score;\n    struct zskiplistNode *backward;\n    struct zskiplistLevel {\n        struct zskiplistNode *forward;\n        unsigned int span;\n    } level[];\n} zskiplistNode;\n\ntypedef struct zskiplist {\n    struct zskiplistNode *header, *tail;\n    unsigned long length;\n    int level;\n} zskiplist;\n\ntypedef struct zset {\n    dict *dict;\n    zskiplist *zsl;\n} zset;\n\n/* Structure to hold list iteration abstraction. */\ntypedef struct {\n    robj *subject;\n    unsigned char encoding;\n    unsigned char direction; /* Iteration direction */\n    quicklistIter *iter;\n} listTypeIterator;\n\n/* Structure for an entry while iterating over a list. */\ntypedef struct {\n    listTypeIterator *li;\n    quicklistEntry entry; /* Entry in quicklist */\n} listTypeEntry;\n\n/* Structure to hold set iteration abstraction. */\ntypedef struct {\n    robj *subject;\n    int encoding;\n    int ii; /* intset iterator */\n    dictIterator *di;\n} setTypeIterator;\n\n/* Structure to hold hash iteration abstraction. Note that iteration over\n * hashes involves both fields and values. Because it is possible that\n * not both are required, store pointers in the iterator to avoid\n * unnecessary memory allocation for fields/values. */\ntypedef struct {\n    robj *subject;\n    int encoding;\n\n    unsigned char *fptr, *vptr;\n\n    dictIterator *di;\n    dictEntry *de;\n} hashTypeIterator;\n\nstruct sharedObjectsStruct {\n    robj *crlf, *ok, *err, *emptybulk, *czero, *cone, *cnegone, *pong, *space,\n    *colon, *nullbulk, *nullmultibulk, *queued,\n    *emptymultibulk, *wrongtypeerr, *nokeyerr, *syntaxerr, *sameobjecterr,\n    *outofrangeerr, *noscripterr, *loadingerr, *slowscripterr, *bgsaveerr,\n    *masterdownerr, *roslaveerr, *execaborterr, *noautherr, *noadminerr, *noreplicaserr,\n    *busykeyerr, *oomerr, *plus, *messagebulk, *pmessagebulk, *subscribebulk,\n    *unsubscribebulk, *psubscribebulk, *punsubscribebulk, *del, *rpop, *lpop,\n    *lpush, *emptyscan, *minstring, *maxstring,\n    *select[PROTO_SHARED_SELECT_CMDS],\n    *integers[OBJ_SHARED_INTEGERS],\n    *mbulkhdr[OBJ_SHARED_BULKHDR_LEN], /* \"*<value>\\r\\n\" */\n    *bulkhdr[OBJ_SHARED_BULKHDR_LEN],  /* \"$<value>\\r\\n\" */\n    *outofcomplexitylimit,\n    *sentinel;  /* NULL pointer */\n};\n\n\nextern struct vr_server server;\nextern struct sharedObjectsStruct shared;;\nextern dictType hashDictType;\nextern dictType setDictType;\nextern dictType zsetDictType;\n\n#define serverPanic(_e) _log(__FILE__, __LINE__, LOG_EMERG, 1, \"assert faild: %s\", #_e)\n#define serverAssertWithInfo(_c,_o,_e) ((_e)?(void)0 : (_log(__FILE__, __LINE__, LOG_EMERG, 1, \"assert faild: %s\", #_e)))\n\nunsigned int dictStrHash(const void *key);\nunsigned int dictStrCaseHash(const void *key);\nunsigned int dictSdsHash(const void *key);\nunsigned int dictSdsCaseHash(const void *key);\nint dictStrKeyCompare(void *privdata, const void *key1, const void *key2);\nint dictStrKeyCaseCompare(void *privdata, const void *key1, const void *key2);\nint dictSdsKeyCompare(void *privdata, const void *key1, const void *key2);\nint dictSdsKeyCaseCompare(void *privdata, const void *key1, const void *key2);\nvoid *dictSdsKeyDupFromStr(void *privdata, const void *key);\nvoid dictSdsDestructor(void *privdata, void *val);\nvoid dictObjectDestructor(void *privdata, void *val);\nint dictEncObjKeyCompare(void *privdata, const void *key1, const void *key2);\nunsigned int dictEncObjHash(const void *key);\nunsigned int dictObjHash(const void *key);\nint dictObjKeyCompare(void *privdata, const void *key1, const void *key2);\nvoid dictListDestructor(void *privdata, void *val);\n\nint init_server(struct instance *nci);\n\nunsigned int getLRUClock(void);\n\nint freeMemoryIfNeeded(vr_eventloop *vel);\nvoid pingCommand(struct client *c);\nint time_independent_strcmp(char *a, char *b);\nvoid authCommand(struct client *c) ;\nvoid adminCommand(struct client *c) ;\n\nint htNeedsResize(dict *dict);\n\nsds genVireInfoString(vr_eventloop *vel, char *section);\nvoid infoCommand(client *c);\nvoid echoCommand(client *c);\nvoid timeCommand(client *c);\n\nint adjustOpenFilesLimit(int maxclients);\n\n#endif\n"
  },
  {
    "path": "src/vr_signal.c",
    "content": "#include <stdlib.h>\n#include <signal.h>\n\n#include <vr_core.h>\n#include <vr_signal.h>\n\nstatic struct signal signals[] = {\n    { SIGUSR1, \"SIGUSR1\", 0,                 signal_handler },\n    { SIGUSR2, \"SIGUSR2\", 0,                 signal_handler },\n    { SIGTTIN, \"SIGTTIN\", 0,                 signal_handler },\n    { SIGTTOU, \"SIGTTOU\", 0,                 signal_handler },\n    { SIGHUP,  \"SIGHUP\",  0,                 signal_handler },\n    { SIGINT,  \"SIGINT\",  0,                 signal_handler },\n    { SIGSEGV, \"SIGSEGV\", (int)SA_RESETHAND, signal_handler },\n    { SIGPIPE, \"SIGPIPE\", 0,                 SIG_IGN },\n    { 0,        NULL,     0,                 NULL }\n};\n\nrstatus_t\nsignal_init(void)\n{\n    struct signal *sig;\n\n    for (sig = signals; sig->signo != 0; sig++) {\n        rstatus_t status;\n        struct sigaction sa;\n\n        memset(&sa, 0, sizeof(sa));\n        sa.sa_handler = sig->handler;\n        sa.sa_flags = sig->flags;\n        sigemptyset(&sa.sa_mask);\n\n        status = sigaction(sig->signo, &sa, NULL);\n        if (status < 0) {\n            log_error(\"sigaction(%s) failed: %s\", sig->signame,\n                      strerror(errno));\n            return VR_ERROR;\n        }\n    }\n\n    return VR_OK;\n}\n\nvoid\nsignal_deinit(void)\n{\n}\n\nvoid\nsignal_handler(int signo)\n{\n    struct signal *sig;\n    void (*action)(void);\n    char *actionstr;\n    bool done;\n\n    for (sig = signals; sig->signo != 0; sig++) {\n        if (sig->signo == signo) {\n            break;\n        }\n    }\n    ASSERT(sig->signo != 0);\n\n    actionstr = \"\";\n    action = NULL;\n    done = false;\n\n    switch (signo) {\n    case SIGUSR1:\n        break;\n\n    case SIGUSR2:\n        break;\n\n    case SIGTTIN:\n        actionstr = \", up logging level\";\n        action = log_level_up;\n        break;\n\n    case SIGTTOU:\n        actionstr = \", down logging level\";\n        action = log_level_down;\n        break;\n\n    case SIGHUP:\n        actionstr = \", reopening log file\";\n        action = log_reopen;\n        break;\n\n    case SIGINT:\n        done = true;\n        actionstr = \", exiting\";\n        break;\n\n    case SIGSEGV:\n        log_stacktrace();\n        actionstr = \", core dumping\";\n        raise(SIGSEGV);\n        break;\n\n    default:\n        NOT_REACHED();\n    }\n\n    log_safe(\"signal %d (%s) received%s\", signo, sig->signame, actionstr);\n\n    if (action != NULL) {\n        action();\n    }\n\n    if (done) {\n        exit(1);\n    }\n}\n"
  },
  {
    "path": "src/vr_signal.h",
    "content": "#ifndef _VR_SIGNAL_H_\n#define _VR_SIGNAL_H_\n\nstruct signal {\n    int  signo;\n    char *signame;\n    int  flags;\n    void (*handler)(int signo);\n};\n\nrstatus_t signal_init(void);\nvoid signal_deinit(void);\nvoid signal_handler(int signo);\n\n#endif\n"
  },
  {
    "path": "src/vr_slowlog.c",
    "content": "#include <vr_core.h>\n\nstatic pthread_rwlock_t rwlocker;\nstatic dlist *slowlog;                  /* SLOWLOG list of commands */\nstatic long long slowlog_entry_id;     /* SLOWLOG current entry ID */\n\n/* Create a new slowlog entry.\n * Incrementing the ref count of all the objects retained is up to\n * this function. */\nslowlogEntry *slowlogCreateEntry(robj **argv, int argc, long long duration) {\n    slowlogEntry *se = dalloc(sizeof(*se));\n    int j, slargc = argc;\n\n    if (slargc > SLOWLOG_ENTRY_MAX_ARGC) slargc = SLOWLOG_ENTRY_MAX_ARGC;\n    se->argc = slargc;\n    se->argv = dalloc(sizeof(robj*)*slargc);\n    for (j = 0; j < slargc; j++) {\n        /* Logging too many arguments is a useless memory waste, so we stop\n         * at SLOWLOG_ENTRY_MAX_ARGC, but use the last argument to specify\n         * how many remaining arguments there were in the original command. */\n        if (slargc != argc && j == slargc-1) {\n            se->argv[j] = createObject(OBJ_STRING,\n                sdscatprintf(sdsempty(),\"... (%d more arguments)\",\n                argc-slargc+1));\n        } else {\n            /* Trim too long strings as well... */\n            if (argv[j]->type == OBJ_STRING &&\n                sdsEncodedObject(argv[j]) &&\n                sdslen(argv[j]->ptr) > SLOWLOG_ENTRY_MAX_STRING)\n            {\n                sds s = sdsnewlen(argv[j]->ptr, SLOWLOG_ENTRY_MAX_STRING);\n\n                s = sdscatprintf(s,\"... (%lu more bytes)\",\n                    (unsigned long)\n                    sdslen(argv[j]->ptr) - SLOWLOG_ENTRY_MAX_STRING);\n                se->argv[j] = createObject(OBJ_STRING,s);\n            } else {\n                se->argv[j] = dupStringObjectUnconstant(argv[j]);\n            }\n        }\n    }\n    se->time = time(NULL);\n    se->duration = duration;\n    return se;\n}\n\n/* Free a slow log entry. The argument is void so that the prototype of this\n * function matches the one of the 'free' method of adlist.c.\n *\n * This function will take care to release all the retained object. */\nvoid slowlogFreeEntry(void *septr) {\n    slowlogEntry *se = septr;\n    int j;\n\n    for (j = 0; j < se->argc; j++)\n        freeObject(se->argv[j]);\n    dfree(se->argv);\n    dfree(se);\n}\n\n/* Initialize the slow log. This function should be called a single time\n * at server startup. */\nvoid slowlogInit(void) {\n    pthread_rwlock_init(&rwlocker,NULL);\n    slowlog = dlistCreate();\n    slowlog_entry_id = 0;\n    dlistSetFreeMethod(slowlog,slowlogFreeEntry);\n}\n\n/* Push a new entry into the slow log.\n * This function will make sure to trim the slow log accordingly to the\n * configured max length. */\nvoid slowlogPushEntryIfNeeded(vr_eventloop *vel, robj **argv, int argc, long long duration) {\n    long long slowlog_log_slower_than;\n    int slowlog_max_len;\n    \n    slowlog_log_slower_than = vel->cc.slowlog_log_slower_than;\n    if (slowlog_log_slower_than < 0) return; /* Slowlog disabled */\n    if (duration >= slowlog_log_slower_than) {\n        slowlogEntry *se = slowlogCreateEntry(argv,argc,duration);\n        pthread_rwlock_wrlock(&rwlocker);\n        se->id = slowlog_entry_id++;\n        dlistAddNodeHead(slowlog,se);\n        pthread_rwlock_unlock(&rwlocker);\n    }\n\n    conf_server_get(CONFIG_SOPN_SLOWLOGML,&slowlog_max_len);\n    /* Remove old entries if needed. */\n    pthread_rwlock_wrlock(&rwlocker);\n    while (dlistLength(slowlog) > slowlog_max_len)\n        dlistDelNode(slowlog,dlistLast(slowlog));\n    pthread_rwlock_unlock(&rwlocker);\n}\n\n/* Remove all the entries from the current slow log. */\nvoid slowlogReset(void) {\n    pthread_rwlock_wrlock(&rwlocker);\n    while (dlistLength(slowlog) > 0)\n        dlistDelNode(slowlog,dlistLast(slowlog));\n    pthread_rwlock_unlock(&rwlocker);\n}\n\n/* The SLOWLOG command. Implements all the subcommands needed to handle the\n * Redis slow log. */\nvoid slowlogCommand(client *c) {\n    if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,\"reset\")) {\n        slowlogReset();\n        addReply(c,shared.ok);\n    } else if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,\"len\")) {\n        unsigned long len;\n        pthread_rwlock_rdlock(&rwlocker);\n        len = dlistLength(slowlog);\n        pthread_rwlock_unlock(&rwlocker);\n        addReplyLongLong(c,len);\n    } else if ((c->argc == 2 || c->argc == 3) &&\n               !strcasecmp(c->argv[1]->ptr,\"get\"))\n    {\n        long count = 10, sent = 0;\n        dlistIter li;\n        void *totentries;\n        dlistNode *ln;\n        slowlogEntry *se;\n\n        if (c->argc == 3 &&\n            getLongFromObjectOrReply(c,c->argv[2],&count,NULL) != VR_OK)\n            return;\n\n        pthread_rwlock_rdlock(&rwlocker);\n        dlistRewind(slowlog,&li);\n        totentries = addDeferredMultiBulkLength(c);\n        while(count-- && (ln = dlistNext(&li))) {\n            int j;\n\n            se = ln->value;\n            addReplyMultiBulkLen(c,4);\n            addReplyLongLong(c,se->id);\n            addReplyLongLong(c,se->time);\n            addReplyLongLong(c,se->duration);\n            addReplyMultiBulkLen(c,se->argc);\n            for (j = 0; j < se->argc; j++)\n                addReplyBulk(c,se->argv[j]);\n            sent++;\n        }\n        pthread_rwlock_unlock(&rwlocker);\n        setDeferredMultiBulkLength(c,totentries,sent);\n    } else {\n        addReplyError(c,\n            \"Unknown SLOWLOG subcommand or wrong # of args. Try GET, RESET, LEN.\");\n    }\n}\n\n"
  },
  {
    "path": "src/vr_slowlog.h",
    "content": "#ifndef _VR_SLOWLOG_H_\n#define _VR_SLOWLOG_H_\n\n#define SLOWLOG_ENTRY_MAX_ARGC 32\n#define SLOWLOG_ENTRY_MAX_STRING 128\n\n/* This structure defines an entry inside the slow log list */\ntypedef struct slowlogEntry {\n    robj **argv;\n    int argc;\n    long long id;       /* Unique entry identifier. */\n    long long duration; /* Time spent by the query, in nanoseconds. */\n    time_t time;        /* Unix time at which the query was executed. */\n} slowlogEntry;\n\n/* Exported API */\nvoid slowlogInit(void);\nvoid slowlogPushEntryIfNeeded(vr_eventloop *vel, robj **argv, int argc, long long duration);\n\n/* Exported commands */\nvoid slowlogCommand(client *c);\n\n#endif\n"
  },
  {
    "path": "src/vr_stats.c",
    "content": "#include <vr_core.h>\n\nint\nvr_stats_init(vr_stats *stats)\n{\n    rstatus_t ret;\n\n    if (stats == NULL) {\n        return VR_ERROR;\n    }\n\n    stats->starttime = 0;\n    stats->numcommands = 0;\n    stats->numconnections = 0;\n    stats->expiredkeys = 0;\n    stats->evictedkeys = 0;\n    stats->keyspace_hits = 0;\n    stats->keyspace_misses = 0;\n    stats->rejected_conn = 0;\n    stats->sync_full = 0;\n    stats->sync_partial_ok = 0;\n    stats->sync_partial_err = 0;\n    stats->net_input_bytes = 0;\n    stats->net_output_bytes = 0;\n    stats->peak_memory = 0;\n    \n#if !defined(STATS_ATOMIC_FIRST) || (!defined(__ATOMIC_RELAXED) && !defined(HAVE_ATOMIC))\n    ret = pthread_spin_init(&stats->statslock, 0);\n    if (ret != 0) {\n        return VR_ERROR;\n    }\n#endif\n\n    stats->starttime = time(NULL);\n\n    return VR_OK;\n}\n\nvoid\nvr_stats_deinit(vr_stats *stats)\n{\n    if (stats == NULL) {\n        return;\n    }\n\n    stats->starttime = 0;\n    stats->numcommands = 0;\n    stats->numconnections = 0;\n    stats->expiredkeys = 0;\n    stats->evictedkeys = 0;\n    stats->keyspace_hits = 0;\n    stats->keyspace_misses = 0;\n    stats->rejected_conn = 0;\n    stats->sync_full = 0;\n    stats->sync_partial_ok = 0;\n    stats->sync_partial_err = 0;\n    stats->net_input_bytes = 0;\n    stats->net_output_bytes = 0;\n    \n#if !defined(STATS_ATOMIC_FIRST) || (!defined(__ATOMIC_RELAXED) && !defined(HAVE_ATOMIC))\n    pthread_spin_destroy(&stats->statslock);\n#endif\n}\n\n/* Add a sample to the operations per second array of samples. */\nvoid trackInstantaneousMetric(vr_stats *stats, int metric, long long current_reading) {\n    long long t = vr_msec_now() - stats->inst_metric[metric].last_sample_time;\n    long long ops = current_reading -\n                    stats->inst_metric[metric].last_sample_count;\n    long long ops_sec;\n\n    ops_sec = t > 0 ? (ops*1000/t) : 0;\n    \n    update_stats_set(stats,inst_metric[metric].samples[stats->inst_metric[metric].idx],ops_sec);\n    stats->inst_metric[metric].idx++;\n    stats->inst_metric[metric].idx %= STATS_METRIC_SAMPLES;\n    stats->inst_metric[metric].last_sample_time = vr_msec_now();\n    stats->inst_metric[metric].last_sample_count = current_reading;\n}\n\n/* Return the mean of all the samples. */\nlong long getInstantaneousMetric(vr_stats *stats, int metric) {\n    int j;\n    long long sum = 0;\n\n    for (j = 0; j < STATS_METRIC_SAMPLES; j++) {\n        long long value;\n        update_stats_get(stats, inst_metric[metric].samples[j], &value);\n        sum += value;\n    }\n    return sum / STATS_METRIC_SAMPLES;\n}\n"
  },
  {
    "path": "src/vr_stats.h",
    "content": "#ifndef _VR_STATS_H_\n#define _VR_STATS_H_\n\n#if 1\n#define STATS_ATOMIC_FIRST 1\n#endif\n\n/* Instantaneous metrics tracking. */\n#define STATS_METRIC_SAMPLES 16     /* Number of samples per metric. */\n#define STATS_METRIC_COMMAND 0      /* Number of commands executed. */\n#define STATS_METRIC_NET_INPUT 1    /* Bytes read to network .*/\n#define STATS_METRIC_NET_OUTPUT 2   /* Bytes written to network. */\n#define STATS_METRIC_COUNT 3\n\ntypedef struct vr_stats {\n    /* Fields used only for stats */\n    time_t starttime;          /* Server start time */\n    long long numcommands;     /* Number of processed commands */\n    long long numconnections;  /* Number of connections received */\n    long long expiredkeys;     /* Number of expired keys */\n    long long evictedkeys;     /* Number of evicted keys (maxmemory) */\n    long long keyspace_hits;   /* Number of successful lookups of keys */\n    long long keyspace_misses; /* Number of failed lookups of keys */\n    long long rejected_conn;   /* Clients rejected because of maxclients */\n    long long sync_full;       /* Number of full resyncs with slaves. */\n    long long sync_partial_ok; /* Number of accepted PSYNC requests. */\n    long long sync_partial_err;/* Number of unaccepted PSYNC requests. */\n    long long net_input_bytes; /* Bytes read from network. */\n    long long net_output_bytes; /* Bytes written to network. */\n    size_t    peak_memory;     /* Max used memory record */\n    \n    /* The following two are used to track instantaneous metrics, like\n     * number of operations per second, network traffic. */\n    struct {\n        long long last_sample_time; /* Timestamp of last sample in ms */\n        long long last_sample_count;/* Count in last sample */\n        long long samples[STATS_METRIC_SAMPLES];\n        int idx;\n    } inst_metric[STATS_METRIC_COUNT];\n\n#if !defined(STATS_ATOMIC_FIRST) || (!defined(__ATOMIC_RELAXED) && !defined(HAVE_ATOMIC))\n    pthread_spinlock_t statslock;\n#endif\n}vr_stats;\n\n/* GCC version >= 4.7 */\n#if defined(__ATOMIC_RELAXED) && defined(STATS_ATOMIC_FIRST)\n#define update_stats_add(_stats, _field, _n) __atomic_add_fetch(&(_stats)->_field, (_n), __ATOMIC_RELAXED)\n#define update_stats_sub(_stats, _field, _n) __atomic_sub_fetch(&(_stats)->_field, (_n), __ATOMIC_RELAXED)\n#define update_stats_set(_stats, _field, _n) __atomic_store_n(&(_stats)->_field, (_n), __ATOMIC_RELAXED)\n#define update_stats_get(_stats, _field, _v) do {           \\\n    __atomic_load(&(_stats)->_field, _v, __ATOMIC_RELAXED); \\\n} while(0)\n\n#define STATS_LOCK_TYPE \"__ATOMIC_RELAXED\"\n/* GCC version >= 4.1 */\n#elif defined(HAVE_ATOMIC) && defined(STATS_ATOMIC_FIRST)\n#define update_stats_add(_stats, _field, _n) __sync_add_and_fetch(&(_stats)->_field, (_n))\n#define update_stats_sub(_stats, _field, _n) __sync_sub_and_fetch(&(_stats)->_field, (_n))\n#define update_stats_set(_stats, _field, _n) __sync_lock_test_and_set(&(_stats)->_field, (_n))\n#define update_stats_get(_stats, _field, _v) do {           \\\n    (*_v) = __sync_add_and_fetch(&(_stats)->_field, 0);     \\\n} while(0)\n\n#define STATS_LOCK_TYPE \"HAVE_ATOMIC\"\n#else\n#define update_stats_add(_stats, _field, _n) do {   \\\n    pthread_spin_lock(&(_stats)->statslock);        \\\n    (_stats)->_field += (_n);                       \\\n    pthread_spin_unlock(&(_stats)->statslock);      \\\n} while(0)\n\n#define update_stats_sub(_stats, _field, _n) do {   \\\n    pthread_spin_lock(&(_stats)->statslock);        \\\n    (_stats)->_field -= (_n);                       \\\n    pthread_spin_unlock(&(_stats)->statslock);      \\\n} while(0)\n\n#define update_stats_set(_stats, _field, _n) do {   \\\n    pthread_spin_lock(&(_stats)->statslock);        \\\n    (_stats)->_field = (_n);                        \\\n    pthread_spin_unlock(&(_stats)->statslock);      \\\n} while(0)\n\n#define update_stats_get(_stats, _field, _v) do {   \\\n    pthread_spin_lock(&(_stats)->statslock);        \\\n    (*_v) = (_stats)->_field;                       \\\n    pthread_spin_unlock(&(_stats)->statslock);      \\\n} while(0)\n\n#define STATS_LOCK_TYPE \"pthread_spin_lock\"\n#endif\n\nint vr_stats_init(vr_stats *stats);\nvoid vr_stats_deinit(vr_stats *stats);\n\nvoid trackInstantaneousMetric(vr_stats *stats, int metric, long long current_reading);\nlong long getInstantaneousMetric(vr_stats *stats, int metric);\n\n#endif\n"
  },
  {
    "path": "src/vr_t_hash.c",
    "content": "#include <math.h>\n\n#include <vr_core.h>\n\n/*-----------------------------------------------------------------------------\n * Hash type API\n *----------------------------------------------------------------------------*/\n\n/* Check the length of a number of objects to see if we need to convert a\n * ziplist to a real hash. Note that we only check string encoded objects\n * as their string length can be queried in constant time. */\nvoid hashTypeTryConversion(robj *o, robj **argv, int start, int end) {\n    int i;\n\n    if (o->encoding != OBJ_ENCODING_ZIPLIST) return;\n\n    for (i = start; i <= end; i++) {\n        if (sdsEncodedObject(argv[i]) &&\n            sdslen(argv[i]->ptr) > server.hash_max_ziplist_value)\n        {\n            hashTypeConvert(o, OBJ_ENCODING_HT);\n            break;\n        }\n    }\n}\n\n/* Encode given objects in-place when the hash uses a dict. */\nvoid hashTypeTryObjectEncoding(robj *subject, robj **o1, robj **o2) {\n    if (subject->encoding == OBJ_ENCODING_HT) {\n        if (o1) *o1 = tryObjectEncoding(*o1);\n        if (o2) *o2 = tryObjectEncoding(*o2);\n    }\n}\n\n/* Get the value from a ziplist encoded hash, identified by field.\n * Returns -1 when the field cannot be found. */\nint hashTypeGetFromZiplist(robj *o, robj *field,\n                           unsigned char **vstr,\n                           unsigned int *vlen,\n                           long long *vll)\n{\n    unsigned char *zl, *fptr = NULL, *vptr = NULL;\n    int ret;\n    robj *field_new;\n\n    ASSERT(o->encoding == OBJ_ENCODING_ZIPLIST);\n\n    field_new = getDecodedObject(field);\n\n    zl = o->ptr;\n    fptr = ziplistIndex(zl, ZIPLIST_HEAD);\n    if (fptr != NULL) {\n        fptr = ziplistFind(fptr, field_new->ptr, sdslen(field_new->ptr), 1);\n        if (fptr != NULL) {\n            /* Grab pointer to the value (fptr points to the field) */\n            vptr = ziplistNext(zl, fptr);\n            ASSERT(vptr != NULL);\n        }\n    }\n\n    if (field_new != field) freeObject(field_new);\n\n    if (vptr != NULL) {\n        ret = ziplistGet(vptr, vstr, vlen, vll);\n        ASSERT(ret);\n        return 0;\n    }\n\n    return -1;\n}\n\n/* Get the value from a hash table encoded hash, identified by field.\n * Returns -1 when the field cannot be found. */\nint hashTypeGetFromHashTable(robj *o, robj *field, robj **value) {\n    dictEntry *de;\n\n    ASSERT(o->encoding == OBJ_ENCODING_HT);\n\n    de = dictFind(o->ptr, field);\n    if (de == NULL) return -1;\n    *value = dictGetVal(de);\n    return 0;\n}\n\n/* Higher level function of hashTypeGet*() that always returns a Redis\n * object (either new or with refcount incremented), so that the caller\n * can retain a reference or call decrRefCount after the usage.\n *\n * The lower level function can prevent copy on write so it is\n * the preferred way of doing read operations. */\nrobj *hashTypeGetObject(robj *o, robj *field) {\n    robj *value = NULL;\n\n    if (o->encoding == OBJ_ENCODING_ZIPLIST) {\n        unsigned char *vstr = NULL;\n        unsigned int vlen = UINT_MAX;\n        long long vll = LLONG_MAX;\n\n        if (hashTypeGetFromZiplist(o, field, &vstr, &vlen, &vll) == 0) {\n            if (vstr) {\n                value = createStringObject((char*)vstr, vlen);\n            } else {\n                value = createStringObjectFromLongLong(vll);\n            }\n        }\n    } else if (o->encoding == OBJ_ENCODING_HT) {\n        robj *aux;\n\n        if (hashTypeGetFromHashTable(o, field, &aux) == 0) {\n            value = aux;\n        }\n    } else {\n        serverPanic(\"Unknown hash encoding\");\n    }\n    return value;\n}\n\n/* Higher level function using hashTypeGet*() to return the length of the\n * object associated with the requested field, or 0 if the field does not\n * exist. */\nsize_t hashTypeGetValueLength(robj *o, robj *field) {\n    size_t len = 0;\n    if (o->encoding == OBJ_ENCODING_ZIPLIST) {\n        unsigned char *vstr = NULL;\n        unsigned int vlen = UINT_MAX;\n        long long vll = LLONG_MAX;\n\n        if (hashTypeGetFromZiplist(o, field, &vstr, &vlen, &vll) == 0)\n            len = vstr ? vlen : sdigits10(vll);\n    } else if (o->encoding == OBJ_ENCODING_HT) {\n        robj *aux;\n\n        if (hashTypeGetFromHashTable(o, field, &aux) == 0)\n            len = stringObjectLen(aux);\n    } else {\n        serverPanic(\"Unknown hash encoding\");\n    }\n    return len;\n}\n\n/* Test if the specified field exists in the given hash. Returns 1 if the field\n * exists, and 0 when it doesn't. */\nint hashTypeExists(robj *o, robj *field) {\n    if (o->encoding == OBJ_ENCODING_ZIPLIST) {\n        unsigned char *vstr = NULL;\n        unsigned int vlen = UINT_MAX;\n        long long vll = LLONG_MAX;\n\n        if (hashTypeGetFromZiplist(o, field, &vstr, &vlen, &vll) == 0) return 1;\n    } else if (o->encoding == OBJ_ENCODING_HT) {\n        robj *aux;\n\n        if (hashTypeGetFromHashTable(o, field, &aux) == 0) return 1;\n    } else {\n        serverPanic(\"Unknown hash encoding\");\n    }\n    return 0;\n}\n\n/* Add an element, discard the old if the key already exists.\n * Return 0 on insert and 1 on update.\n * This function will take care of dump the fields and value \n * objects when insert into the hash table.\n * Filed and value objects just borrow to hashTypeSet(). */\nint hashTypeSet(robj *o, robj *field, robj *value) {\n    int update = 0;\n    robj *field_new, *value_new;\n\n    if (o->encoding == OBJ_ENCODING_ZIPLIST) {\n        unsigned char *zl, *fptr, *vptr;\n\n        field_new = getDecodedObject(field);\n        value_new = getDecodedObject(value);\n\n        zl = o->ptr;\n        fptr = ziplistIndex(zl, ZIPLIST_HEAD);\n        if (fptr != NULL) {\n            fptr = ziplistFind(fptr, field_new->ptr, sdslen(field_new->ptr), 1);\n            if (fptr != NULL) {\n                /* Grab pointer to the value (fptr points to the field) */\n                vptr = ziplistNext(zl, fptr);\n                ASSERT(vptr != NULL);\n                update = 1;\n\n                /* Delete value */\n                zl = ziplistDelete(zl, &vptr);\n\n                /* Insert new value */\n                zl = ziplistInsert(zl, vptr, value_new->ptr, sdslen(value_new->ptr));\n            }\n        }\n\n        if (!update) {\n            /* Push new field/value pair onto the tail of the ziplist */\n            zl = ziplistPush(zl, field_new->ptr, sdslen(field_new->ptr), ZIPLIST_TAIL);\n            zl = ziplistPush(zl, value_new->ptr, sdslen(value_new->ptr), ZIPLIST_TAIL);\n        }\n        o->ptr = zl;\n        if (field_new != field) freeObject(field_new);\n        if (value_new != value) freeObject(value_new);\n\n        /* Check if the ziplist needs to be converted to a hash table */\n        if (hashTypeLength(o) > server.hash_max_ziplist_entries)\n            hashTypeConvert(o, OBJ_ENCODING_HT);\n    } else if (o->encoding == OBJ_ENCODING_HT) {\n        field_new = dupStringObjectUnconstant(field);\n        value_new = dupStringObjectUnconstant(value);\n        if (dictReplace(o->ptr, field_new, value_new)) { /* Insert */\n            /* Do nothing */\n        } else { /* Update */\n            update = 1;\n            freeObject(field_new);\n        }\n    } else {\n        serverPanic(\"Unknown hash encoding\");\n    }\n    return update;\n}\n\n/* Delete an element from a hash.\n * Return 1 on deleted and 0 on not found. */\nint hashTypeDelete(robj *o, robj *field) {\n    int deleted = 0;\n\n    if (o->encoding == OBJ_ENCODING_ZIPLIST) {\n        unsigned char *zl, *fptr;\n        robj *field_new;\n\n        field_new = getDecodedObject(field);\n\n        zl = o->ptr;\n        fptr = ziplistIndex(zl, ZIPLIST_HEAD);\n        if (fptr != NULL) {\n            fptr = ziplistFind(fptr, field_new->ptr, sdslen(field_new->ptr), 1);\n            if (fptr != NULL) {\n                zl = ziplistDelete(zl,&fptr);\n                zl = ziplistDelete(zl,&fptr);\n                o->ptr = zl;\n                deleted = 1;\n            }\n        }\n\n        if (field_new != field) freeObject(field_new);\n    } else if (o->encoding == OBJ_ENCODING_HT) {\n        if (dictDelete((dict*)o->ptr, field) == VR_OK) {\n            deleted = 1;\n\n            /* Always check if the dictionary needs a resize after a delete. */\n            if (htNeedsResize(o->ptr)) dictResize(o->ptr);\n        }\n\n    } else {\n        serverPanic(\"Unknown hash encoding\");\n    }\n\n    return deleted;\n}\n\n/* Return the number of elements in a hash. */\nunsigned long hashTypeLength(robj *o) {\n    unsigned long length = ULONG_MAX;\n\n    if (o->encoding == OBJ_ENCODING_ZIPLIST) {\n        length = ziplistLen(o->ptr) / 2;\n    } else if (o->encoding == OBJ_ENCODING_HT) {\n        length = dictSize((dict*)o->ptr);\n    } else {\n        serverPanic(\"Unknown hash encoding\");\n    }\n\n    return length;\n}\n\nhashTypeIterator *hashTypeInitIterator(robj *subject) {\n    hashTypeIterator *hi = dalloc(sizeof(hashTypeIterator));\n    hi->subject = subject;\n    hi->encoding = subject->encoding;\n\n    if (hi->encoding == OBJ_ENCODING_ZIPLIST) {\n        hi->fptr = NULL;\n        hi->vptr = NULL;\n    } else if (hi->encoding == OBJ_ENCODING_HT) {\n        hi->di = dictGetIterator(subject->ptr);\n    } else {\n        serverPanic(\"Unknown hash encoding\");\n    }\n\n    return hi;\n}\n\nvoid hashTypeReleaseIterator(hashTypeIterator *hi) {\n    if (hi->encoding == OBJ_ENCODING_HT) {\n        dictReleaseIterator(hi->di);\n    }\n\n    dfree(hi);\n}\n\n/* Move to the next entry in the hash. Return VR_OK when the next entry\n * could be found and VR_ERROR when the iterator reaches the end. */\nint hashTypeNext(hashTypeIterator *hi) {\n    if (hi->encoding == OBJ_ENCODING_ZIPLIST) {\n        unsigned char *zl;\n        unsigned char *fptr, *vptr;\n\n        zl = hi->subject->ptr;\n        fptr = hi->fptr;\n        vptr = hi->vptr;\n\n        if (fptr == NULL) {\n            /* Initialize cursor */\n            ASSERT(vptr == NULL);\n            fptr = ziplistIndex(zl, 0);\n        } else {\n            /* Advance cursor */\n            ASSERT(vptr != NULL);\n            fptr = ziplistNext(zl, vptr);\n        }\n        if (fptr == NULL) return VR_ERROR;\n\n        /* Grab pointer to the value (fptr points to the field) */\n        vptr = ziplistNext(zl, fptr);\n        ASSERT(vptr != NULL);\n\n        /* fptr, vptr now point to the first or next pair */\n        hi->fptr = fptr;\n        hi->vptr = vptr;\n    } else if (hi->encoding == OBJ_ENCODING_HT) {\n        if ((hi->de = dictNext(hi->di)) == NULL) return VR_ERROR;\n    } else {\n        serverPanic(\"Unknown hash encoding\");\n    }\n    return VR_OK;\n}\n\n/* Get the field or value at iterator cursor, for an iterator on a hash value\n * encoded as a ziplist. Prototype is similar to `hashTypeGetFromZiplist`. */\nvoid hashTypeCurrentFromZiplist(hashTypeIterator *hi, int what,\n                                unsigned char **vstr,\n                                unsigned int *vlen,\n                                long long *vll)\n{\n    int ret;\n\n    ASSERT(hi->encoding == OBJ_ENCODING_ZIPLIST);\n\n    if (what & OBJ_HASH_KEY) {\n        ret = ziplistGet(hi->fptr, vstr, vlen, vll);\n        ASSERT(ret);\n    } else {\n        ret = ziplistGet(hi->vptr, vstr, vlen, vll);\n        ASSERT(ret);\n    }\n}\n\n/* Get the field or value at iterator cursor, for an iterator on a hash value\n * encoded as a ziplist. Prototype is similar to `hashTypeGetFromHashTable`. */\nvoid hashTypeCurrentFromHashTable(hashTypeIterator *hi, int what, robj **dst) {\n    ASSERT(hi->encoding == OBJ_ENCODING_HT);\n\n    if (what & OBJ_HASH_KEY) {\n        *dst = dictGetKey(hi->de);\n    } else {\n        *dst = dictGetVal(hi->de);\n    }\n}\n\n/* A non copy-on-write friendly but higher level version of hashTypeCurrent*()\n * that returns an object with incremented refcount (or a new object). It is up\n * to the caller to decrRefCount() the object if no reference is retained. */\nrobj *hashTypeCurrentObject(hashTypeIterator *hi, int what) {\n    robj *dst;\n\n    if (hi->encoding == OBJ_ENCODING_ZIPLIST) {\n        unsigned char *vstr = NULL;\n        unsigned int vlen = UINT_MAX;\n        long long vll = LLONG_MAX;\n\n        hashTypeCurrentFromZiplist(hi, what, &vstr, &vlen, &vll);\n        if (vstr) {\n            dst = createStringObject((char*)vstr, vlen);\n        } else {\n            dst = createStringObjectFromLongLong(vll);\n        }\n    } else if (hi->encoding == OBJ_ENCODING_HT) {\n        hashTypeCurrentFromHashTable(hi, what, &dst);\n    } else {\n        serverPanic(\"Unknown hash encoding\");\n    }\n    return dst;\n}\n\nrobj *hashTypeLookupWriteOrCreate(client *c, robj *key, int *expired) {\n    robj *o = lookupKeyWrite(c->db,key,expired);\n    if (o == NULL) {\n        o = createHashObject();\n        dbAdd(c->db,key,o);\n    } else {\n        if (o->type != OBJ_HASH) {\n            addReply(c,shared.wrongtypeerr);\n            return NULL;\n        }\n    }\n    return o;\n}\n\nvoid hashTypeConvertZiplist(robj *o, int enc) {\n    ASSERT(o->encoding == OBJ_ENCODING_ZIPLIST);\n\n    if (enc == OBJ_ENCODING_ZIPLIST) {\n        /* Nothing to do... */\n\n    } else if (enc == OBJ_ENCODING_HT) {\n        hashTypeIterator *hi;\n        dict *d;\n        int ret;\n\n        hi = hashTypeInitIterator(o);\n        d = dictCreate(&hashDictType, NULL);\n\n        while (hashTypeNext(hi) != VR_ERROR) {\n            robj *field, *value;\n\n            field = hashTypeCurrentObject(hi, OBJ_HASH_KEY);\n            field = tryObjectEncoding(field);\n            value = hashTypeCurrentObject(hi, OBJ_HASH_VALUE);\n            value = tryObjectEncoding(value);\n            ret = dictAdd(d, field, value);\n            if (ret != DICT_OK) {\n                //serverLogHexDump(LL_WARNING,\"ziplist with dup elements dump\",\n                //    o->ptr,ziplistBlobLen(o->ptr));\n                ASSERT(ret == DICT_OK);\n            }\n        }\n\n        hashTypeReleaseIterator(hi);\n        dfree(o->ptr);\n\n        o->encoding = OBJ_ENCODING_HT;\n        o->ptr = d;\n\n    } else {\n        serverPanic(\"Unknown hash encoding\");\n    }\n}\n\nvoid hashTypeConvert(robj *o, int enc) {\n    if (o->encoding == OBJ_ENCODING_ZIPLIST) {\n        hashTypeConvertZiplist(o, enc);\n    } else if (o->encoding == OBJ_ENCODING_HT) {\n        serverPanic(\"Not implemented\");\n    } else {\n        serverPanic(\"Unknown hash encoding\");\n    }\n}\n\n/*-----------------------------------------------------------------------------\n * Hash type commands\n *----------------------------------------------------------------------------*/\n\nvoid hsetCommand(client *c) {\n    int update;\n    robj *o;\n    int expired = 0;\n\n    fetchInternalDbByKey(c, c->argv[1]);\n    lockDbWrite(c->db);\n    if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1],&expired)) == NULL) {\n        unlockDb(c->db);\n        if (expired) update_stats_add(c->vel->stats,expiredkeys,1);\n        return;\n    }\n    hashTypeTryConversion(o,c->argv,2,3);\n    hashTypeTryObjectEncoding(o,&c->argv[2], &c->argv[3]);\n    update = hashTypeSet(o,c->argv[2],c->argv[3]);\n    addReply(c, update ? shared.czero : shared.cone);\n    signalModifiedKey(c->db,c->argv[1]);\n    notifyKeyspaceEvent(NOTIFY_HASH,\"hset\",c->argv[1],c->db->id);\n    c->vel->dirty++;\n    unlockDb(c->db);\n    if (expired) update_stats_add(c->vel->stats,expiredkeys,1);\n}\n\nvoid hsetnxCommand(client *c) {\n    robj *o;\n    int expired = 0;\n\n    fetchInternalDbByKey(c, c->argv[1]);\n    lockDbWrite(c->db);\n    if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1],&expired)) == NULL) {\n        unlockDb(c->db);\n        if (expired) update_stats_add(c->vel->stats,expiredkeys,1);\n        return;\n    }\n    hashTypeTryConversion(o,c->argv,2,3);\n\n    if (hashTypeExists(o, c->argv[2])) {\n        addReply(c, shared.czero);\n    } else {\n        hashTypeTryObjectEncoding(o,&c->argv[2], &c->argv[3]);\n        hashTypeSet(o,c->argv[2],c->argv[3]);\n        addReply(c, shared.cone);\n        signalModifiedKey(c->db,c->argv[1]);\n        notifyKeyspaceEvent(NOTIFY_HASH,\"hset\",c->argv[1],c->db->id);\n        server.dirty++;\n    }\n\n    unlockDb(c->db);\n    if (expired) update_stats_add(c->vel->stats,expiredkeys,1);\n}\n\nvoid hmsetCommand(client *c) {\n    int i;\n    robj *o;\n    int expired = 0;\n\n    if ((c->argc % 2) == 1) {\n        addReplyError(c,\"wrong number of arguments for HMSET\");\n        return;\n    }\n\n    fetchInternalDbByKey(c, c->argv[1]);\n    lockDbWrite(c->db);\n    if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1],&expired)) == NULL) {\n        unlockDb(c->db);\n        if (expired) update_stats_add(c->vel->stats,expiredkeys,1);\n        return;\n    }\n    hashTypeTryConversion(o,c->argv,2,c->argc-1);\n    for (i = 2; i < c->argc; i += 2) {\n        hashTypeTryObjectEncoding(o,&c->argv[i], &c->argv[i+1]);\n        hashTypeSet(o,c->argv[i],c->argv[i+1]);\n    }\n    addReply(c, shared.ok);\n    signalModifiedKey(c->db,c->argv[1]);\n    notifyKeyspaceEvent(NOTIFY_HASH,\"hset\",c->argv[1],c->db->id);\n    c->vel->dirty++;\n    \n    unlockDb(c->db);\n    if (expired) update_stats_add(c->vel->stats,expiredkeys,1);\n}\n\nvoid hincrbyCommand(client *c) {\n    long long value, incr, oldvalue;\n    robj *o, *current, *new;\n    int expired = 0;\n\n    if (getLongLongFromObjectOrReply(c,c->argv[3],&incr,NULL) != VR_OK) return;\n\n    fetchInternalDbByKey(c, c->argv[1]);\n    lockDbWrite(c->db);\n    if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1],&expired)) == NULL) goto end;\n    if ((current = hashTypeGetObject(o,c->argv[2])) != NULL) {\n        if (getLongLongFromObjectOrReply(c,current,&value,\n            \"hash value is not an integer\") != VR_OK) {\n            if (o->encoding == OBJ_ENCODING_ZIPLIST) freeObject(current);\n            goto end;\n        }\n        if (o->encoding == OBJ_ENCODING_ZIPLIST) freeObject(current);\n    } else {\n        value = 0;\n    }\n\n    oldvalue = value;\n    if ((incr < 0 && oldvalue < 0 && incr < (LLONG_MIN-oldvalue)) ||\n        (incr > 0 && oldvalue > 0 && incr > (LLONG_MAX-oldvalue))) {\n        addReplyError(c,\"increment or decrement would overflow\");\n        goto end;\n    }\n    value += incr;\n    new = createStringObjectFromLongLong(value);\n    hashTypeTryObjectEncoding(o,&c->argv[2],NULL);\n    hashTypeSet(o,c->argv[2],new);\n    freeObject(new);\n    addReplyLongLong(c,value);\n    signalModifiedKey(c->db,c->argv[1]);\n    notifyKeyspaceEvent(NOTIFY_HASH,\"hincrby\",c->argv[1],c->db->id);\n    c->vel->dirty++;\n\nend:\n    unlockDb(c->db);\n    if (expired) update_stats_add(c->vel->stats, expiredkeys, 1);\n}\n\nvoid hincrbyfloatCommand(client *c) {\n    double long value, incr;\n    robj *o, *current, *new, *aux;\n    int expired = 0;\n\n    if (getLongDoubleFromObjectOrReply(c,c->argv[3],&incr,NULL) != VR_OK) return;\n\n    fetchInternalDbByKey(c, c->argv[1]);\n    lockDbWrite(c->db);\n    if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1],&expired)) == NULL) {\n        unlockDb(c->db);\n        if (expired) update_stats_add(c->vel->stats, expiredkeys, 1);\n        return;\n    }\n    if ((current = hashTypeGetObject(o,c->argv[2])) != NULL) {\n        if (getLongDoubleFromObjectOrReply(c,current,&value,\n            \"hash value is not a valid float\") != VR_OK) {\n            if (o->encoding == OBJ_ENCODING_ZIPLIST) freeObject(current);\n            unlockDb(c->db);\n            if (expired) update_stats_add(c->vel->stats, expiredkeys, 1);\n            return;\n        }\n        if (o->encoding == OBJ_ENCODING_ZIPLIST) freeObject(current);\n    } else {\n        value = 0;\n    }\n\n    value += incr;\n    new = createStringObjectFromLongDouble(value,1);\n    hashTypeTryObjectEncoding(o,&c->argv[2],NULL);\n    hashTypeSet(o,c->argv[2],new);\n    addReplyBulk(c,new);\n    signalModifiedKey(c->db,c->argv[1]);\n    notifyKeyspaceEvent(NOTIFY_HASH,\"hincrbyfloat\",c->argv[1],c->db->id);\n    c->vel->dirty++;\n\n    unlockDb(c->db);\n    if (expired) update_stats_add(c->vel->stats, expiredkeys, 1);\n\n    /* Always replicate HINCRBYFLOAT as an HSET command with the final value\n     * in order to make sure that differences in float pricision or formatting\n     * will not create differences in replicas or after an AOF restart. */\n    aux = createStringObject(\"HSET\",4);\n    rewriteClientCommandArgument(c,0,aux);\n    rewriteClientCommandArgument(c,3,new);\n}\n\nstatic void addHashFieldToReply(client *c, robj *o, robj *field) {\n    int ret;\n\n    if (o == NULL) {\n        addReply(c, shared.nullbulk);\n        return;\n    }\n\n    if (o->encoding == OBJ_ENCODING_ZIPLIST) {\n        unsigned char *vstr = NULL;\n        unsigned int vlen = UINT_MAX;\n        long long vll = LLONG_MAX;\n\n        ret = hashTypeGetFromZiplist(o, field, &vstr, &vlen, &vll);\n        if (ret < 0) {\n            addReply(c, shared.nullbulk);\n        } else {\n            if (vstr) {\n                addReplyBulkCBuffer(c, vstr, vlen);\n            } else {\n                addReplyBulkLongLong(c, vll);\n            }\n        }\n\n    } else if (o->encoding == OBJ_ENCODING_HT) {\n        robj *value;\n\n        ret = hashTypeGetFromHashTable(o, field, &value);\n        if (ret < 0) {\n            addReply(c, shared.nullbulk);\n        } else {\n            addReplyBulk(c, value);\n        }\n\n    } else {\n        serverPanic(\"Unknown hash encoding\");\n    }\n}\n\nvoid hgetCommand(client *c) {\n    robj *o;\n\n    fetchInternalDbByKey(c, c->argv[1]);\n    lockDbRead(c->db);\n    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL) {\n        unlockDb(c->db);\n        update_stats_add(c->vel->stats, keyspace_misses, 1);\n        return;\n    } else if (checkType(c,o,OBJ_HASH)) {\n        unlockDb(c->db);\n        update_stats_add(c->vel->stats, keyspace_hits, 1);\n        return;\n    }\n    \n    addHashFieldToReply(c, o, c->argv[2]);\n    unlockDb(c->db);\n    update_stats_add(c->vel->stats, keyspace_hits, 1);\n}\n\nvoid hmgetCommand(client *c) {\n    robj *o;\n    int i;\n\n    fetchInternalDbByKey(c, c->argv[1]);\n    lockDbRead(c->db);\n    /* Don't abort when the key cannot be found. Non-existing keys are empty\n     * hashes, where HMGET should respond with a series of null bulks. */\n    o = lookupKeyRead(c->db, c->argv[1]);\n    if (o != NULL && o->type != OBJ_HASH) {\n        unlockDb(c->db);\n        update_stats_add(c->vel->stats, keyspace_hits, 1);\n        addReply(c, shared.wrongtypeerr);\n        return;\n    }\n\n    addReplyMultiBulkLen(c, c->argc-2);\n    for (i = 2; i < c->argc; i++) {\n        addHashFieldToReply(c, o, c->argv[i]);\n    }\n\n    if (o == NULL) {\n        unlockDb(c->db);\n        update_stats_add(c->vel->stats, keyspace_misses, 1);\n        return;\n    }\n\n    unlockDb(c->db);\n    update_stats_add(c->vel->stats, keyspace_hits, 1);\n}\n\nvoid hdelCommand(client *c) {\n    robj *o;\n    int j, deleted = 0, keyremoved = 0;\n    int expired = 0;\n\n    fetchInternalDbByKey(c, c->argv[1]);\n    lockDbWrite(c->db);\n    if ((o = lookupKeyWriteOrReply(c,c->argv[1],shared.czero,&expired)) == NULL ||\n        checkType(c,o,OBJ_HASH)) {\n        unlockDb(c->db);\n        if (expired) update_stats_add(c->vel->stats,expiredkeys,1);\n        return;\n    }\n\n    for (j = 2; j < c->argc; j++) {\n        if (hashTypeDelete(o,c->argv[j])) {\n            deleted++;\n            if (hashTypeLength(o) == 0) {\n                dbDelete(c->db,c->argv[1]);\n                keyremoved = 1;\n                break;\n            }\n        }\n    }\n\n    unlockDb(c->db);\n    if (expired) update_stats_add(c->vel->stats,expiredkeys,1);\n    \n    if (deleted) {\n        signalModifiedKey(c->db,c->argv[1]);\n        notifyKeyspaceEvent(NOTIFY_HASH,\"hdel\",c->argv[1],c->db->id);\n        if (keyremoved)\n            notifyKeyspaceEvent(NOTIFY_GENERIC,\"del\",c->argv[1],\n                                c->db->id);\n        server.dirty += deleted;\n    }\n    addReplyLongLong(c,deleted);\n}\n\nvoid hlenCommand(client *c) {\n    robj *o;\n\n    fetchInternalDbByKey(c, c->argv[1]);\n    lockDbRead(c->db);\n    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL) {\n        unlockDb(c->db);\n        update_stats_add(c->vel->stats, keyspace_misses, 1);\n        return;\n    } else if (checkType(c,o,OBJ_HASH)) {\n        unlockDb(c->db);\n        update_stats_add(c->vel->stats, keyspace_hits, 1);\n        return;\n    }\n    \n    addReplyLongLong(c,hashTypeLength(o));\n    unlockDb(c->db);\n    update_stats_add(c->vel->stats, keyspace_hits, 1);\n}\n\nvoid hstrlenCommand(client *c) {\n    robj *o;\n\n    fetchInternalDbByKey(c, c->argv[1]);\n    lockDbRead(c->db);\n    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL) {\n        unlockDb(c->db);\n        update_stats_add(c->vel->stats, keyspace_misses, 1);\n        return;\n    } else if (checkType(c,o,OBJ_HASH)) {\n        unlockDb(c->db);\n        update_stats_add(c->vel->stats, keyspace_hits, 1);\n        return;\n    }\n    \n    addReplyLongLong(c,hashTypeGetValueLength(o,c->argv[2]));\n\n    unlockDb(c->db);\n    update_stats_add(c->vel->stats, keyspace_hits, 1);\n}\n\nstatic void addHashIteratorCursorToReply(client *c, hashTypeIterator *hi, int what) {\n    if (hi->encoding == OBJ_ENCODING_ZIPLIST) {\n        unsigned char *vstr = NULL;\n        unsigned int vlen = UINT_MAX;\n        long long vll = LLONG_MAX;\n\n        hashTypeCurrentFromZiplist(hi, what, &vstr, &vlen, &vll);\n        if (vstr) {\n            addReplyBulkCBuffer(c, vstr, vlen);\n        } else {\n            addReplyBulkLongLong(c, vll);\n        }\n\n    } else if (hi->encoding == OBJ_ENCODING_HT) {\n        robj *value;\n\n        hashTypeCurrentFromHashTable(hi, what, &value);\n        addReplyBulk(c, value);\n\n    } else {\n        serverPanic(\"Unknown hash encoding\");\n    }\n}\n\nvoid genericHgetallCommand(client *c, int flags) {\n    robj *o;\n    hashTypeIterator *hi;\n    int multiplier = 0;\n    int length, count = 0;\n\n    fetchInternalDbByKey(c, c->argv[1]);\n    lockDbRead(c->db);\n    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptymultibulk)) == NULL) {\n        unlockDb(c->db);\n        update_stats_add(c->vel->stats, keyspace_misses, 1);\n        return;\n    } else if (checkType(c,o,OBJ_HASH)) {\n        unlockDb(c->db);\n        update_stats_add(c->vel->stats, keyspace_hits, 1);\n        return;\n    }\n    if (flags & OBJ_HASH_KEY) multiplier++;\n    if (flags & OBJ_HASH_VALUE) multiplier++;\n\n    length = hashTypeLength(o) * multiplier;\n    addReplyMultiBulkLen(c, length);\n\n    hi = hashTypeInitIterator(o);\n    while (hashTypeNext(hi) != VR_ERROR) {\n        if (flags & OBJ_HASH_KEY) {\n            addHashIteratorCursorToReply(c, hi, OBJ_HASH_KEY);\n            count++;\n        }\n        if (flags & OBJ_HASH_VALUE) {\n            addHashIteratorCursorToReply(c, hi, OBJ_HASH_VALUE);\n            count++;\n        }\n    }\n\n    hashTypeReleaseIterator(hi);\n    ASSERT(count == length);\n    \n    unlockDb(c->db);\n    update_stats_add(c->vel->stats, keyspace_hits, 1);\n}\n\nvoid hkeysCommand(client *c) {\n    genericHgetallCommand(c,OBJ_HASH_KEY);\n}\n\nvoid hvalsCommand(client *c) {\n    genericHgetallCommand(c,OBJ_HASH_VALUE);\n}\n\nvoid hgetallCommand(client *c) {\n    genericHgetallCommand(c,OBJ_HASH_KEY|OBJ_HASH_VALUE);\n}\n\nvoid hexistsCommand(client *c) {\n    robj *o;\n\n    fetchInternalDbByKey(c, c->argv[1]);\n    lockDbRead(c->db);\n    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL) {\n        unlockDb(c->db);\n        update_stats_add(c->vel->stats, keyspace_misses, 1);\n        return;\n    } else if (checkType(c,o,OBJ_HASH)) {\n        unlockDb(c->db);\n        update_stats_add(c->vel->stats, keyspace_hits, 1);\n        return;\n    }\n    \n    addReply(c, hashTypeExists(o,c->argv[2]) ? shared.cone : shared.czero);\n    unlockDb(c->db);\n    update_stats_add(c->vel->stats, keyspace_hits, 1);\n}\n\nvoid hscanCommand(client *c) {\n    scanGenericCommand(c,SCAN_TYPE_HASH);\n}\n"
  },
  {
    "path": "src/vr_t_hash.h",
    "content": "#ifndef _VR_T_HASH_H_\n#define _VR_T_HASH_H_\n\nvoid hashTypeTryConversion(robj *o, robj **argv, int start, int end);\nvoid hashTypeTryObjectEncoding(robj *subject, robj **o1, robj **o2);\nint hashTypeGetFromZiplist(robj *o, robj *field, unsigned char **vstr, unsigned int *vlen, long long *vll);\nint hashTypeGetFromHashTable(robj *o, robj *field, robj **value);\nrobj *hashTypeGetObject(robj *o, robj *field);\nsize_t hashTypeGetValueLength(robj *o, robj *field);\nint hashTypeExists(robj *o, robj *field);\nint hashTypeSet(robj *o, robj *field, robj *value);\nint hashTypeDelete(robj *o, robj *field);\nunsigned long hashTypeLength(robj *o);\nhashTypeIterator *hashTypeInitIterator(robj *subject);\nvoid hashTypeReleaseIterator(hashTypeIterator *hi);\nint hashTypeNext(hashTypeIterator *hi);\nvoid hashTypeCurrentFromZiplist(hashTypeIterator *hi, int what, unsigned char **vstr, unsigned int *vlen, long long *vll);\nvoid hashTypeCurrentFromHashTable(hashTypeIterator *hi, int what, robj **dst);\nrobj *hashTypeCurrentObject(hashTypeIterator *hi, int what);\nrobj *hashTypeLookupWriteOrCreate(client *c, robj *key, int *expired);\nvoid hashTypeConvertZiplist(robj *o, int enc);\nvoid hashTypeConvert(robj *o, int enc);\nvoid hsetCommand(client *c);\nvoid hsetnxCommand(client *c);\nvoid hmsetCommand(client *c);\nvoid hincrbyCommand(client *c);\nvoid hincrbyfloatCommand(client *c);\nvoid hgetCommand(client *c);\nvoid hmgetCommand(client *c);\nvoid hdelCommand(client *c);\nvoid hlenCommand(client *c);\nvoid hstrlenCommand(client *c);\nvoid genericHgetallCommand(client *c, int flags);\nvoid hkeysCommand(client *c);\nvoid hvalsCommand(client *c);\nvoid hgetallCommand(client *c);\nvoid hexistsCommand(client *c);\nvoid hscanCommand(client *c);\n\n#endif\n"
  },
  {
    "path": "src/vr_t_list.c",
    "content": "#include <vr_core.h>\n\n/*-----------------------------------------------------------------------------\n * List API\n *----------------------------------------------------------------------------*/\n\n/* The function pushes an element to the specified list object 'subject',\n * at head or tail position as specified by 'where'.\n *\n * There is no need for the caller to increment the refcount of 'value' as\n * the function takes care of it if needed. */\nvoid listTypePush(robj *subject, robj *value, int where) {\n    if (subject->encoding == OBJ_ENCODING_QUICKLIST) {\n        robj *value_new;\n        int pos = (where == LIST_HEAD) ? QUICKLIST_HEAD : QUICKLIST_TAIL;\n        value_new = getDecodedObject(value);\n        size_t len = sdslen(value_new->ptr);\n        quicklistPush(subject->ptr, value_new->ptr, len, pos);\n        if (value_new != value) freeObject(value_new);\n    } else {\n        serverPanic(\"Unknown list encoding\");\n    }\n}\n\nvoid *listPopSaver(unsigned char *data, unsigned int sz) {\n    return createStringObject((char*)data,sz);\n}\n\nrobj *listTypePop(robj *subject, int where) {\n    long long vlong;\n    robj *value = NULL;\n\n    int ql_where = where == LIST_HEAD ? QUICKLIST_HEAD : QUICKLIST_TAIL;\n    if (subject->encoding == OBJ_ENCODING_QUICKLIST) {\n        if (quicklistPopCustom(subject->ptr, ql_where, (unsigned char **)&value,\n                               NULL, &vlong, listPopSaver)) {\n            if (!value)\n                value = createStringObjectFromLongLong(vlong);\n        }\n    } else {\n        serverPanic(\"Unknown list encoding\");\n    }\n    return value;\n}\n\nunsigned long listTypeLength(robj *subject) {\n    if (subject->encoding == OBJ_ENCODING_QUICKLIST) {\n        return quicklistCount(subject->ptr);\n    } else {\n        serverPanic(\"Unknown list encoding\");\n    }\n}\n\n/* Initialize an iterator at the specified index. */\nlistTypeIterator *listTypeInitIterator(robj *subject, long index,\n                                       unsigned char direction) {\n    listTypeIterator *li = dalloc(sizeof(listTypeIterator));\n    li->subject = subject;\n    li->encoding = subject->encoding;\n    li->direction = direction;\n    li->iter = NULL;\n    /* LIST_HEAD means start at TAIL and move *towards* head.\n     * LIST_TAIL means start at HEAD and move *towards tail. */\n    int iter_direction =\n        direction == LIST_HEAD ? AL_START_TAIL : AL_START_HEAD;\n    if (li->encoding == OBJ_ENCODING_QUICKLIST) {\n        li->iter = quicklistGetIteratorAtIdx(li->subject->ptr,\n                                             iter_direction, index);\n    } else {\n        serverPanic(\"Unknown list encoding\");\n    }\n    return li;\n}\n\n/* Clean up the iterator. */\nvoid listTypeReleaseIterator(listTypeIterator *li) {\n    dfree(li->iter);\n    dfree(li);\n}\n\n/* Stores pointer to current the entry in the provided entry structure\n * and advances the position of the iterator. Returns 1 when the current\n * entry is in fact an entry, 0 otherwise. */\nint listTypeNext(listTypeIterator *li, listTypeEntry *entry) {\n    /* Protect from converting when iterating */\n    ASSERT(li->subject->encoding == li->encoding);\n\n    entry->li = li;\n    if (li->encoding == OBJ_ENCODING_QUICKLIST) {\n        return quicklistNext(li->iter, &entry->entry);\n    } else {\n        serverPanic(\"Unknown list encoding\");\n    }\n    return 0;\n}\n\n/* Return entry or NULL at the current position of the iterator. */\nrobj *listTypeGet(listTypeEntry *entry) {\n    robj *value = NULL;\n    if (entry->li->encoding == OBJ_ENCODING_QUICKLIST) {\n        if (entry->entry.value) {\n            value = createStringObject((char *)entry->entry.value,\n                                       entry->entry.sz);\n        } else {\n            value = createStringObjectFromLongLong(entry->entry.longval);\n        }\n    } else {\n        serverPanic(\"Unknown list encoding\");\n    }\n    return value;\n}\n\nvoid listTypeInsert(listTypeEntry *entry, robj *value, int where) {\n    if (entry->li->encoding == OBJ_ENCODING_QUICKLIST) {\n        value = getDecodedObject(value);\n        sds str = value->ptr;\n        size_t len = sdslen(str);\n        if (where == LIST_TAIL) {\n            quicklistInsertAfter((quicklist *)entry->entry.quicklist,\n                                 &entry->entry, str, len);\n        } else if (where == LIST_HEAD) {\n            quicklistInsertBefore((quicklist *)entry->entry.quicklist,\n                                  &entry->entry, str, len);\n        }\n        decrRefCount(value);\n    } else {\n        serverPanic(\"Unknown list encoding\");\n    }\n}\n\n/* Compare the given object with the entry at the current position. */\nint listTypeEqual(listTypeEntry *entry, robj *o) {\n    if (entry->li->encoding == OBJ_ENCODING_QUICKLIST) {\n        serverAssertWithInfo(NULL,o,sdsEncodedObject(o));\n        return quicklistCompare(entry->entry.zi,o->ptr,sdslen(o->ptr));\n    } else {\n        serverPanic(\"Unknown list encoding\");\n    }\n}\n\n/* Delete the element pointed to. */\nvoid listTypeDelete(listTypeIterator *iter, listTypeEntry *entry) {\n    if (entry->li->encoding == OBJ_ENCODING_QUICKLIST) {\n        quicklistDelEntry(iter->iter, &entry->entry);\n    } else {\n        serverPanic(\"Unknown list encoding\");\n    }\n}\n\n/* Create a quicklist from a single ziplist */\nvoid listTypeConvert(robj *subject, int enc) {\n    serverAssertWithInfo(NULL,subject,subject->type==OBJ_LIST);\n    serverAssertWithInfo(NULL,subject,subject->encoding==OBJ_ENCODING_ZIPLIST);\n\n    if (enc == OBJ_ENCODING_QUICKLIST) {\n        size_t zlen = server.list_max_ziplist_size;\n        int depth = server.list_compress_depth;\n        subject->ptr = quicklistCreateFromZiplist(zlen, depth, subject->ptr);\n        subject->encoding = OBJ_ENCODING_QUICKLIST;\n    } else {\n        serverPanic(\"Unsupported list conversion\");\n    }\n}\n\n/*-----------------------------------------------------------------------------\n * List Commands\n *----------------------------------------------------------------------------*/\n\nvoid pushGenericCommand(client *c, int where) {\n    int j, waiting = 0, pushed = 0;\n    robj *lobj;\n    int expired = 0;\n\n    fetchInternalDbByKey(c, c->argv[1]);\n    lockDbWrite(c->db);\n    lobj = lookupKeyWrite(c->db,c->argv[1],&expired);\n    if (lobj && lobj->type != OBJ_LIST) {\n        unlockDb(c->db);\n        if (expired) update_stats_add(c->vel->stats,expiredkeys,1);\n        addReply(c,shared.wrongtypeerr);\n        return;\n    }\n\n    for (j = 2; j < c->argc; j++) {\n        c->argv[j] = tryObjectEncoding(c->argv[j]);\n        if (!lobj) {\n            lobj = createQuicklistObject();\n            quicklistSetOptions(lobj->ptr, server.list_max_ziplist_size,\n                                server.list_compress_depth);\n            dbAdd(c->db,c->argv[1],lobj);\n        }\n        listTypePush(lobj,c->argv[j],where);\n        pushed++;\n    }\n    addReplyLongLong(c, waiting + (lobj ? listTypeLength(lobj) : 0));\n    if (pushed) {\n        char *event = (where == LIST_HEAD) ? \"lpush\" : \"rpush\";\n\n        signalModifiedKey(c->db,c->argv[1]);\n        notifyKeyspaceEvent(NOTIFY_LIST,event,c->argv[1],c->db->id);\n    }\n    c->vel->dirty += pushed;\n\n    unlockDb(c->db);\n    if (expired) update_stats_add(c->vel->stats,expiredkeys,1);\n}\n\nvoid lpushCommand(client *c) {\n    pushGenericCommand(c,LIST_HEAD);\n}\n\nvoid rpushCommand(client *c) {\n    pushGenericCommand(c,LIST_TAIL);\n}\n\nvoid pushxGenericCommand(client *c, robj *refval, robj *val, int where) {\n    robj *subject;\n    listTypeIterator *iter;\n    listTypeEntry entry;\n    int inserted = 0;\n\n    if ((subject = lookupKeyWriteOrReply(c,c->argv[1],shared.czero,NULL)) == NULL ||\n        checkType(c,subject,OBJ_LIST)) return;\n\n    if (refval != NULL) {\n        /* Seek refval from head to tail */\n        iter = listTypeInitIterator(subject,0,LIST_TAIL);\n        while (listTypeNext(iter,&entry)) {\n            if (listTypeEqual(&entry,refval)) {\n                listTypeInsert(&entry,val,where);\n                inserted = 1;\n                break;\n            }\n        }\n        listTypeReleaseIterator(iter);\n\n        if (inserted) {\n            signalModifiedKey(c->db,c->argv[1]);\n            notifyKeyspaceEvent(NOTIFY_LIST,\"linsert\",\n                                c->argv[1],c->db->id);\n            server.dirty++;\n        } else {\n            /* Notify client of a failed insert */\n            addReply(c,shared.cnegone);\n            return;\n        }\n    } else {\n        char *event = (where == LIST_HEAD) ? \"lpush\" : \"rpush\";\n\n        listTypePush(subject,val,where);\n        signalModifiedKey(c->db,c->argv[1]);\n        notifyKeyspaceEvent(NOTIFY_LIST,event,c->argv[1],c->db->id);\n        server.dirty++;\n    }\n\n    addReplyLongLong(c,listTypeLength(subject));\n}\n\nvoid lpushxCommand(client *c) {\n    c->argv[2] = tryObjectEncoding(c->argv[2]);\n    pushxGenericCommand(c,NULL,c->argv[2],LIST_HEAD);\n}\n\nvoid rpushxCommand(client *c) {\n    c->argv[2] = tryObjectEncoding(c->argv[2]);\n    pushxGenericCommand(c,NULL,c->argv[2],LIST_TAIL);\n}\n\nvoid linsertCommand(client *c) {\n    c->argv[4] = tryObjectEncoding(c->argv[4]);\n    if (strcasecmp(c->argv[2]->ptr,\"after\") == 0) {\n        pushxGenericCommand(c,c->argv[3],c->argv[4],LIST_TAIL);\n    } else if (strcasecmp(c->argv[2]->ptr,\"before\") == 0) {\n        pushxGenericCommand(c,c->argv[3],c->argv[4],LIST_HEAD);\n    } else {\n        addReply(c,shared.syntaxerr);\n    }\n}\n\nvoid llenCommand(client *c) {\n    robj *o;\n    \n    fetchInternalDbByKey(c, c->argv[1]);\n    lockDbRead(c->db);\n    o = lookupKeyReadOrReply(c,c->argv[1],shared.czero);\n    if (o == NULL) {\n        unlockDb(c->db);\n        update_stats_add(c->vel->stats, keyspace_misses, 1);\n        return;\n    } else if(checkType(c,o,OBJ_LIST)) {\n        unlockDb(c->db);\n        update_stats_add(c->vel->stats, keyspace_hits, 1);\n        return;\n    }\n\n    addReplyLongLong(c,listTypeLength(o));\n    unlockDb(c->db);\n    update_stats_add(c->vel->stats, keyspace_hits, 1);\n}\n\nvoid lindexCommand(client *c) {\n    robj *o;\n    long index;\n    robj *value = NULL;\n\n    if ((getLongFromObjectOrReply(c, c->argv[2], &index, NULL) != VR_OK))\n        return;\n\n    fetchInternalDbByKey(c,c->argv[1]);\n    lockDbRead(c->db);\n    o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk);\n    if (o == NULL) {\n        unlockDb(c->db);\n        update_stats_add(c->vel->stats, keyspace_misses, 1);\n        return;\n    } else if(checkType(c,o,OBJ_LIST)) {\n        unlockDb(c->db);\n        update_stats_add(c->vel->stats, keyspace_hits, 1);\n        return;\n    }\n    if (o->encoding == OBJ_ENCODING_QUICKLIST) {\n        quicklistEntry entry;\n        if (quicklistIndex(o->ptr, index, &entry)) {\n            if (entry.value) {\n                value = createStringObject((char*)entry.value,entry.sz);\n            } else {\n                value = createStringObjectFromLongLong(entry.longval);\n            }\n            addReplyBulk(c,value);\n            freeObject(value);\n        } else {\n            addReply(c,shared.nullbulk);\n        }\n    } else {\n        serverPanic(\"Unknown list encoding\");\n    }\n    unlockDb(c->db);\n    update_stats_add(c->vel->stats, keyspace_hits, 1);\n}\n\nvoid lsetCommand(client *c) {\n    robj *o;\n    long index;\n    robj *value = c->argv[3];\n    int expired = 0;\n\n    if ((getLongFromObjectOrReply(c, c->argv[2], &index, NULL) != VR_OK))\n        return;\n\n    fetchInternalDbByKey(c, c->argv[1]);\n    lockDbWrite(c->db);\n    o = lookupKeyWriteOrReply(c,c->argv[1],shared.nokeyerr,&expired);\n    if (o == NULL || checkType(c,o,OBJ_LIST)) {\n        unlockDb(c->db);\n        if (expired) update_stats_add(c->vel->stats,expiredkeys,1);\n        return;\n    }\n    if (o->encoding == OBJ_ENCODING_QUICKLIST) {\n        quicklist *ql = o->ptr;\n        int replaced = quicklistReplaceAtIndex(ql, index,\n                                               value->ptr, sdslen(value->ptr));\n        if (!replaced) {\n            addReply(c,shared.outofrangeerr);\n        } else {\n            addReply(c,shared.ok);\n            signalModifiedKey(c->db,c->argv[1]);\n            notifyKeyspaceEvent(NOTIFY_LIST,\"lset\",c->argv[1],c->db->id);\n            c->vel->dirty++;\n        }\n    } else {\n        serverPanic(\"Unknown list encoding\");\n    }\n\n    unlockDb(c->db);\n    if (expired) update_stats_add(c->vel->stats,expiredkeys,1);\n}\n\nvoid popGenericCommand(client *c, int where) {\n    robj *o, *value;\n    int expired = 0;\n\n    fetchInternalDbByKey(c, c->argv[1]);\n    lockDbWrite(c->db);\n    o = lookupKeyWriteOrReply(c,c->argv[1],shared.nullbulk,&expired);\n    if (o == NULL || checkType(c,o,OBJ_LIST)) {\n        unlockDb(c->db);\n        if (expired) update_stats_add(c->vel->stats,expiredkeys,1);\n        return;\n    }\n\n    value = listTypePop(o,where);\n    if (value == NULL) {\n        addReply(c,shared.nullbulk);\n    } else {\n        char *event = (where == LIST_HEAD) ? \"lpop\" : \"rpop\";\n\n        addReplyBulk(c,value);\n        freeObject(value);\n        notifyKeyspaceEvent(NOTIFY_LIST,event,c->argv[1],c->db->id);\n        if (listTypeLength(o) == 0) {\n            notifyKeyspaceEvent(NOTIFY_GENERIC,\"del\",\n                                c->argv[1],c->db->id);\n            dbDelete(c->db,c->argv[1]);\n        }\n        signalModifiedKey(c->db,c->argv[1]);\n        c->vel->dirty++;\n    }\n    unlockDb(c->db);\n    if (expired) update_stats_add(c->vel->stats,expiredkeys,1);\n}\n\nvoid lpopCommand(client *c) {\n    popGenericCommand(c,LIST_HEAD);\n}\n\nvoid rpopCommand(client *c) {\n    popGenericCommand(c,LIST_TAIL);\n}\n\nvoid lrangeCommand(client *c) {\n    robj *o;\n    long start, end, llen, rangelen;\n\n    if ((getLongFromObjectOrReply(c, c->argv[2], &start, NULL) != VR_OK) ||\n        (getLongFromObjectOrReply(c, c->argv[3], &end, NULL) != VR_OK)) return;\n\n    fetchInternalDbByKey(c, c->argv[1]);\n    lockDbRead(c->db);\n    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptymultibulk)) == NULL) {\n        unlockDb(c->db);\n        update_stats_add(c->vel->stats, keyspace_misses, 1);\n        return;\n    } else if (checkType(c,o,OBJ_LIST)) {\n        unlockDb(c->db);\n        update_stats_add(c->vel->stats, keyspace_hits, 1);\n        return;\n    }\n\n    llen = listTypeLength(o);\n\n    /* convert negative indexes */\n    if (start < 0) start = llen+start;\n    if (end < 0) end = llen+end;\n    if (start < 0) start = 0;\n\n    /* Invariant: start >= 0, so this test will be true when end < 0.\n     * The range is empty when start > end or start >= length. */\n    if (start > end || start >= llen) {\n        unlockDb(c->db);\n        update_stats_add(c->vel->stats, keyspace_hits, 1);\n        addReply(c,shared.emptymultibulk);\n        return;\n    }\n    if (end >= llen) end = llen-1;\n    rangelen = (end-start)+1;\n\n    /* Return the result in form of a multi-bulk reply */\n    addReplyMultiBulkLen(c,rangelen);\n    if (o->encoding == OBJ_ENCODING_QUICKLIST) {\n        listTypeIterator *iter = listTypeInitIterator(o, start, LIST_TAIL);\n\n        while(rangelen--) {\n            listTypeEntry entry;\n            listTypeNext(iter, &entry);\n            quicklistEntry *qe = &entry.entry;\n            if (qe->value) {\n                addReplyBulkCBuffer(c,qe->value,qe->sz);\n            } else {\n                addReplyBulkLongLong(c,qe->longval);\n            }\n        }\n        listTypeReleaseIterator(iter);\n    } else {\n        serverPanic(\"List encoding is not QUICKLIST!\");\n    }\n\n    unlockDb(c->db);\n    update_stats_add(c->vel->stats, keyspace_hits, 1);\n}\n\nvoid ltrimCommand(client *c) {\n    robj *o;\n    long start, end, llen, ltrim, rtrim;\n    int expired = 0;\n\n    if ((getLongFromObjectOrReply(c, c->argv[2], &start, NULL) != VR_OK) ||\n        (getLongFromObjectOrReply(c, c->argv[3], &end, NULL) != VR_OK)) return;\n\n    fetchInternalDbByKey(c, c->argv[1]);\n    lockDbWrite(c->db);\n    if ((o = lookupKeyWriteOrReply(c,c->argv[1],shared.ok,&expired)) == NULL ||\n        checkType(c,o,OBJ_LIST)) {\n        unlockDb(c->db);\n        if (expired) update_stats_add(c->vel->stats, expiredkeys, 1);\n        return;\n    }\n    llen = listTypeLength(o);\n\n    /* convert negative indexes */\n    if (start < 0) start = llen+start;\n    if (end < 0) end = llen+end;\n    if (start < 0) start = 0;\n\n    /* Invariant: start >= 0, so this test will be true when end < 0.\n     * The range is empty when start > end or start >= length. */\n    if (start > end || start >= llen) {\n        /* Out of range start or start > end result in empty list */\n        ltrim = llen;\n        rtrim = 0;\n    } else {\n        if (end >= llen) end = llen-1;\n        ltrim = start;\n        rtrim = llen-end-1;\n    }\n\n    /* Remove list elements to perform the trim */\n    if (o->encoding == OBJ_ENCODING_QUICKLIST) {\n        quicklistDelRange(o->ptr,0,ltrim);\n        quicklistDelRange(o->ptr,-rtrim,rtrim);\n    } else {\n        serverPanic(\"Unknown list encoding\");\n    }\n\n    notifyKeyspaceEvent(NOTIFY_LIST,\"ltrim\",c->argv[1],c->db->id);\n    if (listTypeLength(o) == 0) {\n        dbDelete(c->db,c->argv[1]);\n        notifyKeyspaceEvent(NOTIFY_GENERIC,\"del\",c->argv[1],c->db->id);\n    }\n    signalModifiedKey(c->db,c->argv[1]);\n    c->vel->dirty++;\n    addReply(c,shared.ok);\n    unlockDb(c->db);\n    if (expired) update_stats_add(c->vel->stats, expiredkeys, 1);\n}\n\nvoid lremCommand(client *c) {\n    robj *subject, *obj;\n    obj = c->argv[3];\n    long toremove;\n    long removed = 0;\n    int expired = 0;\n\n    if ((getLongFromObjectOrReply(c, c->argv[2], &toremove, NULL) != VR_OK))\n        return;\n\n    fetchInternalDbByKey(c, c->argv[1]);\n    lockDbWrite(c->db);\n    subject = lookupKeyWriteOrReply(c,c->argv[1],shared.czero,&expired);\n    if (subject == NULL || checkType(c,subject,OBJ_LIST)) {\n        unlockDb(c->db);\n        if (expired) update_stats_add(c->vel->stats, expiredkeys, 1);\n        return;\n    }\n    listTypeIterator *li;\n    if (toremove < 0) {\n        toremove = -toremove;\n        li = listTypeInitIterator(subject,-1,LIST_HEAD);\n    } else {\n        li = listTypeInitIterator(subject,0,LIST_TAIL);\n    }\n\n    listTypeEntry entry;\n    while (listTypeNext(li,&entry)) {\n        if (listTypeEqual(&entry,obj)) {\n            listTypeDelete(li, &entry);\n            c->vel->dirty++;\n            removed++;\n            if (toremove && removed == toremove) break;\n        }\n    }\n    listTypeReleaseIterator(li);\n\n    if (removed) {\n        signalModifiedKey(c->db,c->argv[1]);\n        notifyKeyspaceEvent(NOTIFY_GENERIC,\"lrem\",c->argv[1],c->db->id);\n    }\n\n    if (listTypeLength(subject) == 0) {\n        dbDelete(c->db,c->argv[1]);\n        notifyKeyspaceEvent(NOTIFY_GENERIC,\"del\",c->argv[1],c->db->id);\n    }\n\n    addReplyLongLong(c,removed);\n    unlockDb(c->db);\n    if (expired) update_stats_add(c->vel->stats, expiredkeys, 1);\n}\n\n/* This is the semantic of this command:\n *  RPOPLPUSH srclist dstlist:\n *    IF LLEN(srclist) > 0\n *      element = RPOP srclist\n *      LPUSH dstlist element\n *      RETURN element\n *    ELSE\n *      RETURN nil\n *    END\n *  END\n *\n * The idea is to be able to get an element from a list in a reliable way\n * since the element is not just returned but pushed against another list\n * as well. This command was originally proposed by Ezra Zygmuntowicz.\n */\n\nvoid rpoplpushHandlePush(client *c, robj *dstkey, robj *dstobj, robj *value) {\n    /* Create the list if the key does not exist */\n    if (!dstobj) {\n        dstobj = createQuicklistObject();\n        quicklistSetOptions(dstobj->ptr, server.list_max_ziplist_size,\n                            server.list_compress_depth);\n        dbAdd(c->db,dstkey,dstobj);\n    }\n    signalModifiedKey(c->db,dstkey);\n    listTypePush(dstobj,value,LIST_HEAD);\n    notifyKeyspaceEvent(NOTIFY_LIST,\"lpush\",dstkey,c->db->id);\n    /* Always send the pushed value to the client. */\n    addReplyBulk(c,value);\n}\n\nvoid rpoplpushCommand(client *c) {\n    robj *sobj, *value;\n    if ((sobj = lookupKeyWriteOrReply(c,c->argv[1],shared.nullbulk,NULL)) == NULL ||\n        checkType(c,sobj,OBJ_LIST)) return;\n\n    if (listTypeLength(sobj) == 0) {\n        /* This may only happen after loading very old RDB files. Recent\n         * versions of Redis delete keys of empty lists. */\n        addReply(c,shared.nullbulk);\n    } else {\n        robj *dobj = lookupKeyWrite(c->db,c->argv[2],NULL);\n        robj *touchedkey = c->argv[1];\n\n        if (dobj && checkType(c,dobj,OBJ_LIST)) return;\n        value = listTypePop(sobj,LIST_TAIL);\n        /* We saved touched key, and protect it, since rpoplpushHandlePush\n         * may change the client command argument vector (it does not\n         * currently). */\n        incrRefCount(touchedkey);\n        rpoplpushHandlePush(c,c->argv[2],dobj,value);\n\n        /* listTypePop returns an object with its refcount incremented */\n        decrRefCount(value);\n\n        /* Delete the source list when it is empty */\n        notifyKeyspaceEvent(NOTIFY_LIST,\"rpop\",touchedkey,c->db->id);\n        if (listTypeLength(sobj) == 0) {\n            dbDelete(c->db,touchedkey);\n            notifyKeyspaceEvent(NOTIFY_GENERIC,\"del\",\n                                touchedkey,c->db->id);\n        }\n        signalModifiedKey(c->db,touchedkey);\n        decrRefCount(touchedkey);\n        server.dirty++;\n    }\n}\n\n/*-----------------------------------------------------------------------------\n * Blocking POP operations\n *----------------------------------------------------------------------------*/\n\n/* This is how the current blocking POP works, we use BLPOP as example:\n * - If the user calls BLPOP and the key exists and contains a non empty list\n *   then LPOP is called instead. So BLPOP is semantically the same as LPOP\n *   if blocking is not required.\n * - If instead BLPOP is called and the key does not exists or the list is\n *   empty we need to block. In order to do so we remove the notification for\n *   new data to read in the client socket (so that we'll not serve new\n *   requests if the blocking request is not served). Also we put the client\n *   in a dictionary (db->blocking_keys) mapping keys to a list of clients\n *   blocking for this keys.\n * - If a PUSH operation against a key with blocked clients waiting is\n *   performed, we mark this key as \"ready\", and after the current command,\n *   MULTI/EXEC block, or script, is executed, we serve all the clients waiting\n *   for this list, from the one that blocked first, to the last, accordingly\n *   to the number of elements we have in the ready list.\n */\n\n/* Set a client in blocking mode for the specified key, with the specified\n * timeout */\nvoid blockForKeys(client *c, robj **keys, int numkeys, mstime_t timeout, robj *target) {\n    dictEntry *de;\n    dlist *l;\n    int j;\n\n    c->bpop.timeout = timeout;\n    c->bpop.target = target;\n\n    if (target != NULL) incrRefCount(target);\n\n    for (j = 0; j < numkeys; j++) {\n        /* If the key already exists in the dict ignore it. */\n        if (dictAdd(c->bpop.keys,keys[j],NULL) != DICT_OK) continue;\n        incrRefCount(keys[j]);\n\n        /* And in the other \"side\", to map keys -> clients */\n        de = dictFind(c->db->blocking_keys,keys[j]);\n        if (de == NULL) {\n            int retval;\n\n            /* For every key we take a list of clients blocked for it */\n            l = dlistCreate();\n            retval = dictAdd(c->db->blocking_keys,keys[j],l);\n            incrRefCount(keys[j]);\n            serverAssertWithInfo(c,keys[j],retval == DICT_OK);\n        } else {\n            l = dictGetVal(de);\n        }\n        dlistAddNodeTail(l,c);\n    }\n    blockClient(c,BLOCKED_LIST);\n}\n\n/* Unblock a client that's waiting in a blocking operation such as BLPOP.\n * You should never call this function directly, but unblockClient() instead. */\nvoid unblockClientWaitingData(client *c) {\n    dictEntry *de;\n    dictIterator *di;\n    dlist *l;\n\n    serverAssertWithInfo(c,NULL,dictSize(c->bpop.keys) != 0);\n    di = dictGetIterator(c->bpop.keys);\n    /* The client may wait for multiple keys, so unblock it for every key. */\n    while((de = dictNext(di)) != NULL) {\n        robj *key = dictGetKey(de);\n\n        /* Remove this client from the list of clients waiting for this key. */\n        l = dictFetchValue(c->db->blocking_keys,key);\n        serverAssertWithInfo(c,key,l != NULL);\n        dlistDelNode(l,dlistSearchKey(l,c));\n        /* If the list is empty we need to remove it to avoid wasting memory */\n        if (dlistLength(l) == 0)\n            dictDelete(c->db->blocking_keys,key);\n    }\n    dictReleaseIterator(di);\n\n    /* Cleanup the client structure */\n    dictEmpty(c->bpop.keys,NULL);\n    if (c->bpop.target) {\n        decrRefCount(c->bpop.target);\n        c->bpop.target = NULL;\n    }\n}\n\n/* If the specified key has clients blocked waiting for list pushes, this\n * function will put the key reference into the server.ready_keys list.\n * Note that db->ready_keys is a hash table that allows us to avoid putting\n * the same key again and again in the list in case of multiple pushes\n * made by a script or in the context of MULTI/EXEC.\n *\n * The list will be finally processed by handleClientsBlockedOnLists() */\nvoid signalListAsReady(redisDb *db, robj *key) {\n    int ret;\n    readyList *rl;\n\n    /* No clients blocking for this key? No need to queue it. */\n    if (dictFind(db->blocking_keys,key) == NULL) return;\n\n    /* Key was already signaled? No need to queue it again. */\n    if (dictFind(db->ready_keys,key) != NULL) return;\n\n    /* Ok, we need to queue this key into server.ready_keys. */\n    rl = dalloc(sizeof(*rl));\n    rl->key = key;\n    rl->db = db;\n    incrRefCount(key);\n    dlistAddNodeTail(server.ready_keys,rl);\n\n    /* We also add the key in the db->ready_keys dictionary in order\n     * to avoid adding it multiple times into a list with a simple O(1)\n     * check. */\n    incrRefCount(key);\n    ret = dictAdd(db->ready_keys,key,NULL);\n    ASSERT(ret == DICT_OK);\n}\n\n/* This is a helper function for handleClientsBlockedOnLists(). It's work\n * is to serve a specific client (receiver) that is blocked on 'key'\n * in the context of the specified 'db', doing the following:\n *\n * 1) Provide the client with the 'value' element.\n * 2) If the dstkey is not NULL (we are serving a BRPOPLPUSH) also push the\n *    'value' element on the destination list (the LPUSH side of the command).\n * 3) Propagate the resulting BRPOP, BLPOP and additional LPUSH if any into\n *    the AOF and replication channel.\n *\n * The argument 'where' is LIST_TAIL or LIST_HEAD, and indicates if the\n * 'value' element was popped fron the head (BLPOP) or tail (BRPOP) so that\n * we can propagate the command properly.\n *\n * The function returns VR_OK if we are able to serve the client, otherwise\n * VR_ERROR is returned to signal the caller that the list POP operation\n * should be undone as the client was not served: This only happens for\n * BRPOPLPUSH that fails to push the value to the destination key as it is\n * of the wrong type. */\nint serveClientBlockedOnList(client *receiver, robj *key, robj *dstkey, redisDb *db, robj *value, int where)\n{\n    robj *argv[3];\n\n    if (dstkey == NULL) {\n        /* Propagate the [LR]POP operation. */\n        argv[0] = (where == LIST_HEAD) ? shared.lpop :\n                                          shared.rpop;\n        argv[1] = key;\n        propagate((where == LIST_HEAD) ?\n            server.lpopCommand : server.rpopCommand,\n            db->id,argv,2,PROPAGATE_AOF|PROPAGATE_REPL);\n\n        /* BRPOP/BLPOP */\n        addReplyMultiBulkLen(receiver,2);\n        addReplyBulk(receiver,key);\n        addReplyBulk(receiver,value);\n    } else {\n        /* BRPOPLPUSH */\n        robj *dstobj =\n            lookupKeyWrite(receiver->db,dstkey,NULL);\n        if (!(dstobj &&\n             checkType(receiver,dstobj,OBJ_LIST)))\n        {\n            /* Propagate the RPOP operation. */\n            argv[0] = shared.rpop;\n            argv[1] = key;\n            propagate(server.rpopCommand,\n                db->id,argv,2,\n                PROPAGATE_AOF|\n                PROPAGATE_REPL);\n            rpoplpushHandlePush(receiver,dstkey,dstobj,\n                value);\n            /* Propagate the LPUSH operation. */\n            argv[0] = shared.lpush;\n            argv[1] = dstkey;\n            argv[2] = value;\n            propagate(server.lpushCommand,\n                db->id,argv,3,\n                PROPAGATE_AOF|\n                PROPAGATE_REPL);\n        } else {\n            /* BRPOPLPUSH failed because of wrong\n             * destination type. */\n            return VR_ERROR;\n        }\n    }\n    return VR_OK;\n}\n\n/* This function should be called by Redis every time a single command,\n * a MULTI/EXEC block, or a Lua script, terminated its execution after\n * being called by a client.\n *\n * All the keys with at least one client blocked that received at least\n * one new element via some PUSH operation are accumulated into\n * the server.ready_keys list. This function will run the list and will\n * serve clients accordingly. Note that the function will iterate again and\n * again as a result of serving BRPOPLPUSH we can have new blocking clients\n * to serve because of the PUSH side of BRPOPLPUSH. */\nvoid handleClientsBlockedOnLists(void) {\n    while(dlistLength(server.ready_keys) != 0) {\n        dlist *l;\n\n        /* Point server.ready_keys to a fresh list and save the current one\n         * locally. This way as we run the old list we are free to call\n         * signalListAsReady() that may push new elements in server.ready_keys\n         * when handling clients blocked into BRPOPLPUSH. */\n        l = server.ready_keys;\n        server.ready_keys = dlistCreate();\n\n        while(dlistLength(l) != 0) {\n            dlistNode *ln = dlistFirst(l);\n            readyList *rl = ln->value;\n\n            /* First of all remove this key from db->ready_keys so that\n             * we can safely call signalListAsReady() against this key. */\n            dictDelete(rl->db->ready_keys,rl->key);\n\n            /* If the key exists and it's a list, serve blocked clients\n             * with data. */\n            robj *o = lookupKeyWrite(rl->db,rl->key,NULL);\n            if (o != NULL && o->type == OBJ_LIST) {\n                dictEntry *de;\n\n                /* We serve clients in the same order they blocked for\n                 * this key, from the first blocked to the last. */\n                de = dictFind(rl->db->blocking_keys,rl->key);\n                if (de) {\n                    dlist *clients = dictGetVal(de);\n                    int numclients = dlistLength(clients);\n\n                    while(numclients--) {\n                        dlistNode *clientnode = dlistFirst(clients);\n                        client *receiver = clientnode->value;\n                        robj *dstkey = receiver->bpop.target;\n                        int where = (receiver->lastcmd &&\n                                     receiver->lastcmd->proc == blpopCommand) ?\n                                    LIST_HEAD : LIST_TAIL;\n                        robj *value = listTypePop(o,where);\n\n                        if (value) {\n                            /* Protect receiver->bpop.target, that will be\n                             * freed by the next unblockClient()\n                             * call. */\n                            if (dstkey) incrRefCount(dstkey);\n                            unblockClient(receiver);\n\n                            if (serveClientBlockedOnList(receiver,\n                                rl->key,dstkey,rl->db,value,\n                                where) == VR_ERROR)\n                            {\n                                /* If we failed serving the client we need\n                                 * to also undo the POP operation. */\n                                    listTypePush(o,value,where);\n                            }\n\n                            if (dstkey) decrRefCount(dstkey);\n                            decrRefCount(value);\n                        } else {\n                            break;\n                        }\n                    }\n                }\n\n                if (listTypeLength(o) == 0) {\n                    dbDelete(rl->db,rl->key);\n                }\n                /* We don't call signalModifiedKey() as it was already called\n                 * when an element was pushed on the list. */\n            }\n\n            /* Free this item. */\n            decrRefCount(rl->key);\n            dfree(rl);\n            dlistDelNode(l,ln);\n        }\n        dlistRelease(l); /* We have the new list on place at this point. */\n    }\n}\n\n/* Blocking RPOP/LPOP */\nvoid blockingPopGenericCommand(client *c, int where) {\n    robj *o;\n    mstime_t timeout;\n    int j;\n\n    if (getTimeoutFromObjectOrReply(c,c->argv[c->argc-1],&timeout,UNIT_SECONDS)\n        != VR_OK) return;\n\n    for (j = 1; j < c->argc-1; j++) {\n        o = lookupKeyWrite(c->db,c->argv[j],NULL);\n        if (o != NULL) {\n            if (o->type != OBJ_LIST) {\n                addReply(c,shared.wrongtypeerr);\n                return;\n            } else {\n                if (listTypeLength(o) != 0) {\n                    /* Non empty list, this is like a non normal [LR]POP. */\n                    char *event = (where == LIST_HEAD) ? \"lpop\" : \"rpop\";\n                    robj *value = listTypePop(o,where);\n                    ASSERT(value != NULL);\n\n                    addReplyMultiBulkLen(c,2);\n                    addReplyBulk(c,c->argv[j]);\n                    addReplyBulk(c,value);\n                    decrRefCount(value);\n                    notifyKeyspaceEvent(NOTIFY_LIST,event,\n                                        c->argv[j],c->db->id);\n                    if (listTypeLength(o) == 0) {\n                        dbDelete(c->db,c->argv[j]);\n                        notifyKeyspaceEvent(NOTIFY_GENERIC,\"del\",\n                                            c->argv[j],c->db->id);\n                    }\n                    signalModifiedKey(c->db,c->argv[j]);\n                    server.dirty++;\n\n                    /* Replicate it as an [LR]POP instead of B[LR]POP. */\n                    rewriteClientCommandVector(c,2,\n                        (where == LIST_HEAD) ? shared.lpop : shared.rpop,\n                        c->argv[j]);\n                    return;\n                }\n            }\n        }\n    }\n\n    /* If we are inside a MULTI/EXEC and the list is empty the only thing\n     * we can do is treating it as a timeout (even with timeout 0). */\n    if (c->flags & CLIENT_MULTI) {\n        addReply(c,shared.nullmultibulk);\n        return;\n    }\n\n    /* If the list is empty or the key does not exists we must block */\n    blockForKeys(c, c->argv + 1, c->argc - 2, timeout, NULL);\n}\n\nvoid blpopCommand(client *c) {\n    blockingPopGenericCommand(c,LIST_HEAD);\n}\n\nvoid brpopCommand(client *c) {\n    blockingPopGenericCommand(c,LIST_TAIL);\n}\n\nvoid brpoplpushCommand(client *c) {\n    mstime_t timeout;\n\n    if (getTimeoutFromObjectOrReply(c,c->argv[3],&timeout,UNIT_SECONDS)\n        != VR_OK) return;\n\n    robj *key = lookupKeyWrite(c->db, c->argv[1],NULL);\n\n    if (key == NULL) {\n        if (c->flags & CLIENT_MULTI) {\n            /* Blocking against an empty list in a multi state\n             * returns immediately. */\n            addReply(c, shared.nullbulk);\n        } else {\n            /* The list is empty and the client blocks. */\n            blockForKeys(c, c->argv + 1, 1, timeout, c->argv[2]);\n        }\n    } else {\n        if (key->type != OBJ_LIST) {\n            addReply(c, shared.wrongtypeerr);\n        } else {\n            /* The list exists and has elements, so\n             * the regular rpoplpushCommand is executed. */\n            serverAssertWithInfo(c,key,listTypeLength(key) > 0);\n            rpoplpushCommand(c);\n        }\n    }\n}\n"
  },
  {
    "path": "src/vr_t_list.h",
    "content": "#ifndef _VR_T_LIST_H_\n#define _VR_T_LIST_H_\n\nvoid listTypePush(robj *subject, robj *value, int where);\nvoid *listPopSaver(unsigned char *data, unsigned int sz);\nrobj *listTypePop(robj *subject, int where);\nunsigned long listTypeLength(robj *subject);\nlistTypeIterator *listTypeInitIterator(robj *subject, long index, unsigned char direction);\nvoid listTypeReleaseIterator(listTypeIterator *li);\nint listTypeNext(listTypeIterator *li, listTypeEntry *entry);\nrobj *listTypeGet(listTypeEntry *entry);\nvoid listTypeInsert(listTypeEntry *entry, robj *value, int where);\nint listTypeEqual(listTypeEntry *entry, robj *o);\nvoid listTypeDelete(listTypeIterator *iter, listTypeEntry *entry);\nvoid listTypeConvert(robj *subject, int enc);\nvoid pushGenericCommand(client *c, int where);\nvoid lpushCommand(client *c);\nvoid rpushCommand(client *c);\nvoid pushxGenericCommand(client *c, robj *refval, robj *val, int where);\nvoid lpushxCommand(client *c);\nvoid rpushxCommand(client *c);\nvoid linsertCommand(client *c);\nvoid llenCommand(client *c);\nvoid lindexCommand(client *c);\nvoid lsetCommand(client *c);\nvoid popGenericCommand(client *c, int where);\nvoid lpopCommand(client *c);\nvoid rpopCommand(client *c);\nvoid lrangeCommand(client *c);\nvoid ltrimCommand(client *c);\nvoid lremCommand(client *c);\nvoid rpoplpushHandlePush(client *c, robj *dstkey, robj *dstobj, robj *value);\nvoid rpoplpushCommand(client *c);\nvoid blockForKeys(client *c, robj **keys, int numkeys, mstime_t timeout, robj *target);\nvoid unblockClientWaitingData(client *c);\nvoid signalListAsReady(redisDb *db, robj *key);\nint serveClientBlockedOnList(client *receiver, robj *key, robj *dstkey, redisDb *db, robj *value, int where);\nvoid handleClientsBlockedOnLists(void);\nvoid blockingPopGenericCommand(client *c, int where);\nvoid blpopCommand(client *c);\nvoid brpopCommand(client *c);\nvoid brpoplpushCommand(client *c);\n\n#endif\n"
  },
  {
    "path": "src/vr_t_set.c",
    "content": "#include <vr_core.h>\n\n/*-----------------------------------------------------------------------------\n * Set Commands\n *----------------------------------------------------------------------------*/\n\nvoid sunionDiffGenericCommand(client *c, robj **setkeys, int setnum,\n                              robj *dstkey, int op);\n\n/* Factory method to return a set that *can* hold \"value\". When the object has\n * an integer-encodable value, an intset will be returned. Otherwise a regular\n * hash table. */\nrobj *setTypeCreate(robj *value) {\n    if (isObjectRepresentableAsLongLong(value,NULL) == VR_OK)\n        return createIntsetObject();\n    return createSetObject();\n}\n\n/* Add the specified value into a set. The function takes care of incrementing\n * the reference count of the object if needed in order to retain a copy.\n *\n * If the value was already member of the set, nothing is done and 0 is\n * returned, otherwise the new element is added and 1 is returned. */\nint setTypeAdd(robj *subject, robj *value) {\n    long long llval;\n    robj *obj;\n    if (subject->encoding == OBJ_ENCODING_HT) {\n        obj = dupStringObjectUnconstant(value);\n        if (dictAdd(subject->ptr,obj,NULL) == DICT_OK) {\n            return 1;\n        } else {\n            freeObject(obj);\n        }\n    } else if (subject->encoding == OBJ_ENCODING_INTSET) {\n        if (isObjectRepresentableAsLongLong(value,&llval) == VR_OK) {\n            uint8_t success = 0;\n            subject->ptr = intsetAdd(subject->ptr,llval,&success);\n            if (success) {\n                /* Convert to regular set when the intset contains\n                 * too many entries. */\n                if (intsetLen(subject->ptr) > server.set_max_intset_entries)\n                    setTypeConvert(subject,OBJ_ENCODING_HT);\n                return 1;\n            }\n        } else {\n            /* Failed to get integer from object, convert to regular set. */\n            setTypeConvert(subject,OBJ_ENCODING_HT);\n            obj = dupStringObjectUnconstant(value);\n            /* The set *was* an intset and this value is not integer\n             * encodable, so dictAdd should always work. */\n            serverAssertWithInfo(NULL,obj,\n                dictAdd(subject->ptr,obj,NULL) == DICT_OK);\n            return 1;\n        }\n    } else {\n        serverPanic(\"Unknown set encoding\");\n    }\n    return 0;\n}\n\nint setTypeRemove(robj *setobj, robj *value) {\n    long long llval;\n    if (setobj->encoding == OBJ_ENCODING_HT) {\n        if (dictDelete(setobj->ptr,value) == DICT_OK) {\n            if (htNeedsResize(setobj->ptr)) dictResize(setobj->ptr);\n            return 1;\n        }\n    } else if (setobj->encoding == OBJ_ENCODING_INTSET) {\n        if (isObjectRepresentableAsLongLong(value,&llval) == VR_OK) {\n            int success;\n            setobj->ptr = intsetRemove(setobj->ptr,llval,&success);\n            if (success) return 1;\n        }\n    } else {\n        serverPanic(\"Unknown set encoding\");\n    }\n    return 0;\n}\n\nint setTypeIsMember(robj *subject, robj *value) {\n    long long llval;\n    if (subject->encoding == OBJ_ENCODING_HT) {\n        return dictFind((dict*)subject->ptr,value) != NULL;\n    } else if (subject->encoding == OBJ_ENCODING_INTSET) {\n        if (isObjectRepresentableAsLongLong(value,&llval) == VR_OK) {\n            return intsetFind((intset*)subject->ptr,llval);\n        }\n    } else {\n        serverPanic(\"Unknown set encoding\");\n    }\n    return 0;\n}\n\nsetTypeIterator *setTypeInitIterator(robj *subject) {\n    setTypeIterator *si = dalloc(sizeof(setTypeIterator));\n    si->subject = subject;\n    si->encoding = subject->encoding;\n    if (si->encoding == OBJ_ENCODING_HT) {\n        si->di = dictGetIterator(subject->ptr);\n    } else if (si->encoding == OBJ_ENCODING_INTSET) {\n        si->ii = 0;\n    } else {\n        serverPanic(\"Unknown set encoding\");\n    }\n    return si;\n}\n\nvoid setTypeReleaseIterator(setTypeIterator *si) {\n    if (si->encoding == OBJ_ENCODING_HT)\n        dictReleaseIterator(si->di);\n    dfree(si);\n}\n\n/* Move to the next entry in the set. Returns the object at the current\n * position.\n *\n * Since set elements can be internally be stored as redis objects or\n * simple arrays of integers, setTypeNext returns the encoding of the\n * set object you are iterating, and will populate the appropriate pointer\n * (objele) or (llele) accordingly.\n *\n * Note that both the objele and llele pointers should be passed and cannot\n * be NULL since the function will try to defensively populate the non\n * used field with values which are easy to trap if misused.\n *\n * When there are no longer elements -1 is returned.\n * Returned objects ref count is not incremented, so this function is\n * copy on write friendly. */\nint setTypeNext(setTypeIterator *si, robj **objele, int64_t *llele) {\n    if (si->encoding == OBJ_ENCODING_HT) {\n        dictEntry *de = dictNext(si->di);\n        if (de == NULL) return -1;\n        *objele = dictGetKey(de);\n        *llele = -123456789; /* Not needed. Defensive. */\n    } else if (si->encoding == OBJ_ENCODING_INTSET) {\n        if (!intsetGet(si->subject->ptr,si->ii++,llele))\n            return -1;\n        *objele = NULL; /* Not needed. Defensive. */\n    } else {\n        serverPanic(\"Wrong set encoding in setTypeNext\");\n    }\n    return si->encoding;\n}\n\n/* The not copy on write friendly version but easy to use version\n * of setTypeNext() is setTypeNextObject(), returning new objects\n * or the returned objects. So if you don't\n * retain a pointer to this object you should call freeObject() against \n * it if  si->encoding == OBJ_ENCODING_INTSET.\n *\n * This function is the way to go for write operations where COW is not\n * an issue as the result will be anyway of incrementing the ref count. */\nrobj *setTypeNextObject(setTypeIterator *si) {\n    int64_t intele;\n    robj *objele;\n    int encoding;\n\n    encoding = setTypeNext(si,&objele,&intele);\n    switch(encoding) {\n        case -1:    return NULL;\n        case OBJ_ENCODING_INTSET:\n            return createStringObjectFromLongLong(intele);\n        case OBJ_ENCODING_HT:\n            return objele;\n        default:\n            serverPanic(\"Unsupported encoding\");\n    }\n    return NULL; /* just to suppress warnings */\n}\n\n/* Return random element from a non empty set.\n * The returned element can be a int64_t value if the set is encoded\n * as an \"intset\" blob of integers, or a redis object if the set\n * is a regular set.\n *\n * The caller provides both pointers to be populated with the right\n * object. The return value of the function is the object->encoding\n * field of the object and is used by the caller to check if the\n * int64_t pointer or the redis object pointer was populated.\n *\n * Note that both the objele and llele pointers should be passed and cannot\n * be NULL since the function will try to defensively populate the non\n * used field with values which are easy to trap if misused.\n *\n * When an object is returned (the set was a real set) the ref count\n * of the object is not incremented so this function can be considered\n * copy on write friendly. */\nint setTypeRandomElement(robj *setobj, robj **objele, int64_t *llele) {\n    if (setobj->encoding == OBJ_ENCODING_HT) {\n        dictEntry *de = dictGetRandomKey(setobj->ptr);\n        *objele = dictGetKey(de);\n        *llele = -123456789; /* Not needed. Defensive. */\n    } else if (setobj->encoding == OBJ_ENCODING_INTSET) {\n        *llele = intsetRandom(setobj->ptr);\n        *objele = NULL; /* Not needed. Defensive. */\n    } else {\n        serverPanic(\"Unknown set encoding\");\n    }\n    return setobj->encoding;\n}\n\nunsigned long setTypeSize(robj *subject) {\n    if (subject->encoding == OBJ_ENCODING_HT) {\n        return dictSize((dict*)subject->ptr);\n    } else if (subject->encoding == OBJ_ENCODING_INTSET) {\n        return intsetLen((intset*)subject->ptr);\n    } else {\n        serverPanic(\"Unknown set encoding\");\n    }\n}\n\n/* Convert the set to specified encoding. The resulting dict (when converting\n * to a hash table) is presized to hold the number of elements in the original\n * set. */\nvoid setTypeConvert(robj *setobj, int enc) {\n    setTypeIterator *si;\n    serverAssertWithInfo(NULL,setobj,setobj->type == OBJ_SET &&\n                             setobj->encoding == OBJ_ENCODING_INTSET);\n\n    if (enc == OBJ_ENCODING_HT) {\n        int64_t intele;\n        dict *d = dictCreate(&setDictType,NULL);\n        robj *element;\n\n        /* Presize the dict to avoid rehashing */\n        dictExpand(d,intsetLen(setobj->ptr));\n\n        /* To add the elements we extract integers and create redis objects */\n        si = setTypeInitIterator(setobj);\n        while (setTypeNext(si,&element,&intele) != -1) {\n            element = createStringObjectFromLongLong(intele);\n            serverAssertWithInfo(NULL,element,\n                dictAdd(d,element,NULL) == DICT_OK);\n        }\n        setTypeReleaseIterator(si);\n\n        setobj->encoding = OBJ_ENCODING_HT;\n        dfree(setobj->ptr);\n        setobj->ptr = d;\n    } else {\n        serverPanic(\"Unsupported set conversion\");\n    }\n}\n\nvoid saddCommand(client *c) {\n    robj *set;\n    int j, added = 0;\n    int expired = 0;\n\n    fetchInternalDbByKey(c, c->argv[1]);\n    lockDbWrite(c->db);\n    set = lookupKeyWrite(c->db,c->argv[1],&expired);\n    if (set == NULL) {\n        set = setTypeCreate(c->argv[2]);\n        dbAdd(c->db,c->argv[1],set);\n    } else {\n        if (set->type != OBJ_SET) {\n            unlockDb(c->db);\n            if (expired) update_stats_add(c->vel->stats,expiredkeys,1);\n            addReply(c,shared.wrongtypeerr);\n            return;\n        }\n    }\n\n    for (j = 2; j < c->argc; j++) {\n        c->argv[j] = tryObjectEncoding(c->argv[j]);\n        if (setTypeAdd(set,c->argv[j])) added++;\n    }\n    if (added) {\n        signalModifiedKey(c->db,c->argv[1]);\n        notifyKeyspaceEvent(NOTIFY_SET,\"sadd\",c->argv[1],c->db->id);\n    }\n    c->vel->dirty += added;\n    addReplyLongLong(c,added);\n    unlockDb(c->db);\n    if (expired) update_stats_add(c->vel->stats,expiredkeys,1);\n}\n\nvoid sremCommand(client *c) {\n    robj *set;\n    int j, deleted = 0, keyremoved = 0;\n    int expired = 0;\n    \n    fetchInternalDbByKey(c, c->argv[1]);\n    lockDbWrite(c->db);\n    if ((set = lookupKeyWriteOrReply(c,c->argv[1],shared.czero,&expired)) == NULL ||\n        checkType(c,set,OBJ_SET)) {\n        unlockDb(c->db);\n        if (expired) update_stats_add(c->vel->stats,expiredkeys,1);\n        return;\n    }\n\n    for (j = 2; j < c->argc; j++) {\n        if (setTypeRemove(set,c->argv[j])) {\n            deleted++;\n            if (setTypeSize(set) == 0) {\n                dbDelete(c->db,c->argv[1]);\n                keyremoved = 1;\n                break;\n            }\n        }\n    }\n    if (deleted) {\n        signalModifiedKey(c->db,c->argv[1]);\n        notifyKeyspaceEvent(NOTIFY_SET,\"srem\",c->argv[1],c->db->id);\n        if (keyremoved)\n            notifyKeyspaceEvent(NOTIFY_GENERIC,\"del\",c->argv[1],\n                                c->db->id);\n        c->vel->dirty += deleted;\n    }\n    addReplyLongLong(c,deleted);\n    unlockDb(c->db);\n    if (expired) update_stats_add(c->vel->stats,expiredkeys,1);\n}\n\nvoid smoveCommand(client *c) {\n    robj *srcset, *dstset, *ele;\n    srcset = lookupKeyWrite(c->db,c->argv[1],NULL);\n    dstset = lookupKeyWrite(c->db,c->argv[2],NULL);\n    ele = c->argv[3] = tryObjectEncoding(c->argv[3]);\n\n    /* If the source key does not exist return 0 */\n    if (srcset == NULL) {\n        addReply(c,shared.czero);\n        return;\n    }\n\n    /* If the source key has the wrong type, or the destination key\n     * is set and has the wrong type, return with an error. */\n    if (checkType(c,srcset,OBJ_SET) ||\n        (dstset && checkType(c,dstset,OBJ_SET))) return;\n\n    /* If srcset and dstset are equal, SMOVE is a no-op */\n    if (srcset == dstset) {\n        addReply(c,setTypeIsMember(srcset,ele) ? shared.cone : shared.czero);\n        return;\n    }\n\n    /* If the element cannot be removed from the src set, return 0. */\n    if (!setTypeRemove(srcset,ele)) {\n        addReply(c,shared.czero);\n        return;\n    }\n    notifyKeyspaceEvent(NOTIFY_SET,\"srem\",c->argv[1],c->db->id);\n\n    /* Remove the src set from the database when empty */\n    if (setTypeSize(srcset) == 0) {\n        dbDelete(c->db,c->argv[1]);\n        notifyKeyspaceEvent(NOTIFY_GENERIC,\"del\",c->argv[1],c->db->id);\n    }\n    signalModifiedKey(c->db,c->argv[1]);\n    signalModifiedKey(c->db,c->argv[2]);\n    server.dirty++;\n\n    /* Create the destination set when it doesn't exist */\n    if (!dstset) {\n        dstset = setTypeCreate(ele);\n        dbAdd(c->db,c->argv[2],dstset);\n    }\n\n    /* An extra key has changed when ele was successfully added to dstset */\n    if (setTypeAdd(dstset,ele)) {\n        server.dirty++;\n        notifyKeyspaceEvent(NOTIFY_SET,\"sadd\",c->argv[2],c->db->id);\n    }\n    addReply(c,shared.cone);\n}\n\nvoid sismemberCommand(client *c) {\n    robj *set;\n\n    fetchInternalDbByKey(c, c->argv[1]);\n    lockDbRead(c->db);\n    if ((set = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL) {\n        unlockDb(c->db);\n        update_stats_add(c->vel->stats, keyspace_misses, 1);\n        return;\n    } else if (checkType(c,set,OBJ_SET)) {\n        unlockDb(c->db);\n        update_stats_add(c->vel->stats, keyspace_hits, 1);\n        return;\n    }\n    \n    c->argv[2] = tryObjectEncoding(c->argv[2]);\n    if (setTypeIsMember(set,c->argv[2]))\n        addReply(c,shared.cone);\n    else\n        addReply(c,shared.czero);\n\n    unlockDb(c->db);\n    update_stats_add(c->vel->stats, keyspace_hits, 1);\n}\n\nvoid scardCommand(client *c) {\n    robj *o;\n\n    fetchInternalDbByKey(c, c->argv[1]);\n    lockDbRead(c->db);\n    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL) {\n        unlockDb(c->db);\n        update_stats_add(c->vel->stats, keyspace_misses, 1);\n        return;\n    } else if (checkType(c,o,OBJ_SET)) {\n        unlockDb(c->db);\n        update_stats_add(c->vel->stats, keyspace_hits, 1);\n        return;\n    }\n    \n    addReplyLongLong(c,setTypeSize(o));\n    unlockDb(c->db);\n    update_stats_add(c->vel->stats, keyspace_hits, 1);\n}\n\nvoid smembersGenericCommand(client *c, robj *set)\n{\n    setTypeIterator *si;\n    robj *eleobj;\n    int64_t intobj;\n    int encoding;\n    \n    addReplyMultiBulkLen(c, setTypeSize(set));\n    si = setTypeInitIterator(set);\n    while ((encoding = setTypeNext(si,&eleobj,&intobj)) != -1) {\n        if (encoding == OBJ_ENCODING_HT) {\n            addReplyBulk(c, eleobj);\n        } else if (encoding == OBJ_ENCODING_INTSET) {\n            addReplyBulkLongLong(c, intobj);\n        }\n    }\n    setTypeReleaseIterator(si);\n}\n\n/* Handle the \"SPOP key <count>\" variant. The normal version of the\n * command is handled by the spopCommand() function itself. */\n\n/* How many times bigger should be the set compared to the remaining size\n * for us to use the \"create new set\" strategy? Read later in the\n * implementation for more info. */\n#define SPOP_MOVE_STRATEGY_MUL 5\n\nvoid spopWithCountCommand(client *c) {\n    long l;\n    unsigned long count, size;\n    robj *set;\n    int expired = 0;\n\n    /* Get the count argument */\n    if (getLongFromObjectOrReply(c,c->argv[2],&l,NULL) != VR_OK) return;\n    if (l >= 0) {\n        count = (unsigned) l;\n    } else {\n        addReply(c,shared.outofrangeerr);\n        return;\n    }\n\n    fetchInternalDbByKey(c, c->argv[1]);\n    lockDbWrite(c->db);\n    /* Make sure a key with the name inputted exists, and that it's type is\n     * indeed a set. Otherwise, return nil */\n    if ((set = lookupKeyWriteOrReply(c,c->argv[1],shared.emptymultibulk,&expired))\n        == NULL || checkType(c,set,OBJ_SET)) {\n        unlockDb(c->db);\n        if (expired) update_stats_add(c->vel->stats,expiredkeys,1);\n        return;\n    }\n\n    /* If count is zero, serve an empty multibulk ASAP to avoid special\n     * cases later. */\n    if (count == 0) {\n        unlockDb(c->db);\n        if (expired) update_stats_add(c->vel->stats,expiredkeys,1);\n        addReply(c,shared.emptymultibulk);\n        return;\n    }\n\n    size = setTypeSize(set);\n\n    /* Generate an SPOP keyspace notification */\n    notifyKeyspaceEvent(NOTIFY_SET,\"spop\",c->argv[1],c->db->id);\n    c->vel->dirty += count;\n\n    /* CASE 1:\n     * The number of requested elements is greater than or equal to\n     * the number of elements inside the set: simply return the whole set. */\n    if (count >= size) {\n        robj *aux;\n        \n        /* We just return the entire set */\n        smembersGenericCommand(c, set);\n\n        /* Delete the set as it is now empty */\n        dbDelete(c->db,c->argv[1]);\n        notifyKeyspaceEvent(NOTIFY_GENERIC,\"del\",c->argv[1],c->db->id);\n\n        /* Propagate this command as an DEL operation */\n        aux = dupStringObjectUnconstant(c->argv[1]);\n        rewriteClientCommandVector(c,2,shared.del,aux);\n        signalModifiedKey(c->db,c->argv[1]);\n        c->vel->dirty++;\n        unlockDb(c->db);\n        if (expired) update_stats_add(c->vel->stats,expiredkeys,1);\n        return;\n    }\n\n    /* Case 2 and 3 require to replicate SPOP as a set of SERM commands.\n     * Prepare our replication argument vector. Also send the array length\n     * which is common to both the code paths. */\n    robj *propargv[3];\n    propargv[0] = createStringObject(\"SREM\",4);\n    propargv[1] = c->argv[1];\n    addReplyMultiBulkLen(c,count);\n\n    /* Common iteration vars. */\n    robj *objele;\n    int encoding;\n    int64_t llele;\n    unsigned long remaining = size-count; /* Elements left after SPOP. */\n\n    /* If we are here, the number of requested elements is less than the\n     * number of elements inside the set. Also we are sure that count < size.\n     * Use two different strategies.\n     *\n     * CASE 2: The number of elements to return is small compared to the\n     * set size. We can just extract random elements and return them to\n     * the set. */\n    if (remaining*SPOP_MOVE_STRATEGY_MUL > count) {\n        while(count--) {\n            encoding = setTypeRandomElement(set,&objele,&llele);\n            if (encoding == OBJ_ENCODING_INTSET) {\n                objele = createStringObjectFromLongLong(llele);\n            } else {\n                objele = dupStringObjectUnconstant(objele);\n            }\n\n            /* Return the element to the client and remove from the set. */\n            addReplyBulk(c,objele);\n            setTypeRemove(set,objele);\n\n            /* Replicate/AOF this command as an SREM operation */\n            propargv[2] = objele;\n            alsoPropagate(server.sremCommand,c->db->id,propargv,3,\n                PROPAGATE_AOF|PROPAGATE_REPL);\n            freeObject(objele);\n        }\n    } else {\n    /* CASE 3: The number of elements to return is very big, approaching\n     * the size of the set itself. After some time extracting random elements\n     * from such a set becomes computationally expensive, so we use\n     * a different strategy, we extract random elements that we don't\n     * want to return (the elements that will remain part of the set),\n     * creating a new set as we do this (that will be stored as the original\n     * set). Then we return the elements left in the original set and\n     * release it. */\n        robj *newset = NULL;\n\n        /* Create a new set with just the remaining elements. */\n        while(remaining--) {\n            encoding = setTypeRandomElement(set,&objele,&llele);\n            if (encoding == OBJ_ENCODING_INTSET)\n                objele = createStringObjectFromLongLong(llele);\n            \n            if (!newset) newset = setTypeCreate(objele);\n            setTypeAdd(newset,objele);\n            setTypeRemove(set,objele);\n            if (encoding == OBJ_ENCODING_INTSET)\n                freeObject(objele);\n        }\n\n        /* Tranfer the old set to the client. */\n        setTypeIterator *si;\n        si = setTypeInitIterator(set);\n        while((encoding = setTypeNext(si,&objele,&llele)) != -1) {\n            if (encoding == OBJ_ENCODING_INTSET)\n                objele = createStringObjectFromLongLong(llele);\n            addReplyBulk(c,objele);\n\n            /* Replicate/AOF this command as an SREM operation */\n            propargv[2] = objele;\n            alsoPropagate(server.sremCommand,c->db->id,propargv,3,\n                PROPAGATE_AOF|PROPAGATE_REPL);\n            if (encoding == OBJ_ENCODING_INTSET)\n                freeObject(objele);\n        }\n        setTypeReleaseIterator(si);\n\n        /* Assign the new set as the key value. */\n        dbOverwrite(c->db,c->argv[1],newset);\n    }\n\n    /* Don't propagate the command itself even if we incremented the\n     * dirty counter. We don't want to propagate an SPOP command since\n     * we propagated the command as a set of SREMs operations using\n     * the alsoPropagate() API. */\n    freeObject(propargv[0]);\n    preventCommandPropagation(c);\n    unlockDb(c->db);\n    if (expired) update_stats_add(c->vel->stats,expiredkeys,1);\n}\n\nvoid spopCommand(client *c) {\n    robj *set, *ele, *aux1, *aux2;\n    int64_t llele;\n    int encoding;\n    int expired = 0;\n\n    if (c->argc == 3) {\n        spopWithCountCommand(c);\n        return;\n    } else if (c->argc > 3) {\n        addReply(c,shared.syntaxerr);\n        return;\n    }\n\n    fetchInternalDbByKey(c, c->argv[1]);\n    lockDbWrite(c->db);\n    /* Make sure a key with the name inputted exists, and that it's type is\n     * indeed a set */\n    if ((set = lookupKeyWriteOrReply(c,c->argv[1],shared.nullbulk,&expired)) == NULL ||\n        checkType(c,set,OBJ_SET)) {\n        unlockDb(c->db);\n        if (expired) update_stats_add(c->vel->stats,expiredkeys,1);\n        return;\n    }\n    \n    /* Get a random element from the set */\n    encoding = setTypeRandomElement(set,&ele,&llele);\n\n    /* Remove the element from the set */\n    if (encoding == OBJ_ENCODING_INTSET) {\n        ele = createStringObjectFromLongLong(llele);\n        set->ptr = intsetRemove(set->ptr,llele,NULL);\n    } else {\n        ele = dupStringObjectUnconstant(ele);\n        setTypeRemove(set,ele);\n    }\n\n    notifyKeyspaceEvent(NOTIFY_SET,\"spop\",c->argv[1],c->db->id);\n\n    /* Replicate/AOF this command as an SREM operation */\n    aux1 = createStringObject(\"SREM\",4);\n    aux2 = dupStringObjectUnconstant(c->argv[1]);\n    rewriteClientCommandVector(c,3,aux1,aux2,ele);\n\n    /* Add the element to the reply */\n    addReplyBulk(c,ele);\n\n    /* Delete the set if it's empty */\n    if (setTypeSize(set) == 0) {\n        dbDelete(c->db,c->argv[1]);\n        notifyKeyspaceEvent(NOTIFY_GENERIC,\"del\",c->argv[1],c->db->id);\n    }\n\n    /* Set has been modified */\n    signalModifiedKey(c->db,c->argv[1]);\n    c->vel->dirty++;\n    unlockDb(c->db);\n    if (expired) update_stats_add(c->vel->stats,expiredkeys,1);\n}\n\n/* handle the \"SRANDMEMBER key <count>\" variant. The normal version of the\n * command is handled by the srandmemberCommand() function itself. */\n\n/* How many times bigger should be the set compared to the requested size\n * for us to don't use the \"remove elements\" strategy? Read later in the\n * implementation for more info. */\n#define SRANDMEMBER_SUB_STRATEGY_MUL 3\n\nvoid srandmemberWithCountCommand(client *c) {\n    long l;\n    unsigned long count, size;\n    int uniq = 1;\n    robj *set, *ele;\n    int64_t llele;\n    int encoding;\n\n    dict *d;\n\n    if (getLongFromObjectOrReply(c,c->argv[2],&l,NULL) != VR_OK) return;\n    if (l >= 0) {\n        count = (unsigned) l;\n    } else {\n        /* A negative count means: return the same elements multiple times\n         * (i.e. don't remove the extracted element after every extraction). */\n        count = -l;\n        uniq = 0;\n    }\n\n    if ((set = lookupKeyReadOrReply(c,c->argv[1],shared.emptymultibulk))\n        == NULL || checkType(c,set,OBJ_SET)) return;\n    size = setTypeSize(set);\n\n    /* If count is zero, serve it ASAP to avoid special cases later. */\n    if (count == 0) {\n        addReply(c,shared.emptymultibulk);\n        return;\n    }\n\n    /* CASE 1: The count was negative, so the extraction method is just:\n     * \"return N random elements\" sampling the whole set every time.\n     * This case is trivial and can be served without auxiliary data\n     * structures. */\n    if (!uniq) {\n        addReplyMultiBulkLen(c,count);\n        while(count--) {\n            encoding = setTypeRandomElement(set,&ele,&llele);\n            if (encoding == OBJ_ENCODING_INTSET) {\n                addReplyBulkLongLong(c,llele);\n            } else {\n                addReplyBulk(c,ele);\n            }\n        }\n        return;\n    }\n\n    /* CASE 2:\n     * The number of requested elements is greater than the number of\n     * elements inside the set: simply return the whole set. */\n    if (count >= size) {\n        //sunionDiffGenericCommand(c,c->argv+1,1,NULL,SET_OP_UNION);\n        smembersGenericCommand(c, set);\n        return;\n    }\n\n    /* For CASE 3 and CASE 4 we need an auxiliary dictionary. */\n    d = dictCreate(&setDictType,NULL);\n\n    /* CASE 3:\n     * The number of elements inside the set is not greater than\n     * SRANDMEMBER_SUB_STRATEGY_MUL times the number of requested elements.\n     * In this case we create a set from scratch with all the elements, and\n     * subtract random elements to reach the requested number of elements.\n     *\n     * This is done because if the number of requsted elements is just\n     * a bit less than the number of elements in the set, the natural approach\n     * used into CASE 3 is highly inefficient. */\n    if (count*SRANDMEMBER_SUB_STRATEGY_MUL > size) {\n        setTypeIterator *si;\n\n        /* Add all the elements into the temporary dictionary. */\n        si = setTypeInitIterator(set);\n        while((encoding = setTypeNext(si,&ele,&llele)) != -1) {\n            int retval = DICT_ERR;\n\n            if (encoding == OBJ_ENCODING_INTSET) {\n                retval = dictAdd(d,createStringObjectFromLongLong(llele),NULL);\n            } else {\n                retval = dictAdd(d,dupStringObject(ele),NULL);\n            }\n            ASSERT(retval == DICT_OK);\n        }\n        setTypeReleaseIterator(si);\n        ASSERT(dictSize(d) == size);\n\n        /* Remove random elements to reach the right count. */\n        while(size > count) {\n            dictEntry *de;\n\n            de = dictGetRandomKey(d);\n            dictDelete(d,dictGetKey(de));\n            size--;\n        }\n    }\n\n    /* CASE 4: We have a big set compared to the requested number of elements.\n     * In this case we can simply get random elements from the set and add\n     * to the temporary set, trying to eventually get enough unique elements\n     * to reach the specified count. */\n    else {\n        unsigned long added = 0;\n\n        while(added < count) {\n            encoding = setTypeRandomElement(set,&ele,&llele);\n            if (encoding == OBJ_ENCODING_INTSET) {\n                ele = createStringObjectFromLongLong(llele);\n            } else {\n                ele = dupStringObject(ele);\n            }\n            /* Try to add the object to the dictionary. If it already exists\n             * free it, otherwise increment the number of objects we have\n             * in the result dictionary. */\n            if (dictAdd(d,ele,NULL) == DICT_OK)\n                added++;\n            else\n                decrRefCount(ele);\n        }\n    }\n\n    /* CASE 3 & 4: send the result to the user. */\n    {\n        dictIterator *di;\n        dictEntry *de;\n\n        addReplyMultiBulkLen(c,count);\n        di = dictGetIterator(d);\n        while((de = dictNext(di)) != NULL)\n            addReplyBulk(c,dictGetKey(de));\n        dictReleaseIterator(di);\n        dictRelease(d);\n    }\n}\n\nvoid srandmemberCommand(client *c) {\n    robj *set, *ele;\n    int64_t llele;\n    int encoding;\n\n    if (c->argc == 3) {\n        srandmemberWithCountCommand(c);\n        return;\n    } else if (c->argc > 3) {\n        addReply(c,shared.syntaxerr);\n        return;\n    }\n\n    if ((set = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL ||\n        checkType(c,set,OBJ_SET)) return;\n\n    encoding = setTypeRandomElement(set,&ele,&llele);\n    if (encoding == OBJ_ENCODING_INTSET) {\n        addReplyBulkLongLong(c,llele);\n    } else {\n        addReplyBulk(c,ele);\n    }\n}\n\nvoid smembersCommand(client *c) {\n    robj *set;\n\n    fetchInternalDbByKey(c, c->argv[1]);\n    lockDbRead(c->db);\n    set = lookupKeyReadOrReply(c,c->argv[1],shared.emptymultibulk);\n    if (set == NULL) {\n        unlockDb(c->db);\n        update_stats_add(c->vel->stats, keyspace_misses, 1);\n        return;\n    } else if(checkType(c,set,OBJ_SET)) {\n        unlockDb(c->db);\n        update_stats_add(c->vel->stats, keyspace_hits, 1);\n        return;\n    }\n\n    smembersGenericCommand(c, set);\n    unlockDb(c->db);\n    update_stats_add(c->vel->stats, keyspace_hits, 1);\n}\n\nint qsortCompareSetsByCardinality(const void *s1, const void *s2) {\n    return setTypeSize(*(robj**)s1)-setTypeSize(*(robj**)s2);\n}\n\n/* This is used by SDIFF and in this case we can receive NULL that should\n * be handled as empty sets. */\nint qsortCompareSetsByRevCardinality(const void *s1, const void *s2) {\n    robj *o1 = *(robj**)s1, *o2 = *(robj**)s2;\n\n    return  (o2 ? setTypeSize(o2) : 0) - (o1 ? setTypeSize(o1) : 0);\n}\n\nvoid sinterGenericCommand(client *c, robj **setkeys,\n                          unsigned long setnum, robj *dstkey) {\n    setTypeIterator *si;\n    robj *eleobj, *dstset = NULL;\n    int64_t intobj;\n    unsigned long j, cardinality = 0;\n    int encoding;\n    robj *setobj, *min_len_set;\n    unsigned long min_len = -1;\n    unsigned long min_len_idx = 0;\n\n    for (j = 0; j < setnum; j++) {\n        fetchInternalDbByKey(c,setkeys[j]);\n        lockDbRead(c->db);\n        setobj = lookupKeyRead(c->db,setkeys[j]);\n        if (!setobj) {\n            unlockDb(c->db);\n            update_stats_add(c->vel->stats,keyspace_misses,1);\n            if (dstkey) {\n                fetchInternalDbByKey(c,dstkey);\n                lockDbWrite(c->db);\n                if (dbDelete(c->db,dstkey)) {\n                    signalModifiedKey(c->db,dstkey);\n                    c->vel->dirty++;\n                }\n                unlockDb(c->db);\n                addReply(c,shared.czero);\n            } else {\n                addReply(c,shared.emptymultibulk);\n            }\n            return;\n        }\n        if (checkType(c,setobj,OBJ_SET)) {\n            unlockDb(c->db);\n            update_stats_add(c->vel->stats,keyspace_hits,1);\n            return;\n        }\n\n        if (min_len == -1 || setTypeSize(setobj) < min_len) {\n            min_len = setTypeSize(setobj);\n            min_len_idx = j;\n        }\n\n        unlockDb(c->db);\n        update_stats_add(c->vel->stats,keyspace_hits,1);\n    }\n\n    min_len_set = createIntsetObject();\n    fetchInternalDbByKey(c,setkeys[min_len_idx]);\n    lockDbRead(c->db);\n    setobj = lookupKeyRead(c->db,setkeys[min_len_idx]);\n    if (!setobj) {\n        unlockDb(c->db);\n        freeObject(min_len_set);\n        goto done;\n    }\n    if (checkType(c,setobj,OBJ_SET)) {\n        unlockDb(c->db);\n        freeObject(min_len_set);\n        return;\n    }\n    si = setTypeInitIterator(setobj);\n    while((eleobj = setTypeNextObject(si)) != NULL) {\n        setTypeAdd(min_len_set,eleobj);\n        if (si->encoding == OBJ_ENCODING_INTSET) \n            freeObject(eleobj); /* free this object for intset type */\n    }\n    setTypeReleaseIterator(si);\n    unlockDb(c->db);\n\n    dstset = createIntsetObject();\n\n    /* Iterate all the elements of the first (smallest) set, and test\n     * the element against all the other sets, if at least one set does\n     * not include the element it is discarded */\n    si = setTypeInitIterator(min_len_set);\n    while((encoding = setTypeNext(si,&eleobj,&intobj)) != -1) {\n        for (j = 0; j < setnum; j++) {\n            if (j == min_len_idx) continue;\n            fetchInternalDbByKey(c,setkeys[j]);\n            lockDbRead(c->db);\n            setobj = lookupKeyRead(c->db,setkeys[j]);\n            if (!setobj) {\n                unlockDb(c->db);\n                freeObject(min_len_set);\n                if (dstset) {\n                    freeObject(dstset);\n                    dstset = NULL;\n                }\n                setTypeReleaseIterator(si);\n                goto done;\n            }\n            if (checkType(c,setobj,OBJ_SET)) {\n                unlockDb(c->db);\n                freeObject(min_len_set);\n                if (dstset) {\n                    freeObject(dstset);\n                    dstset = NULL;\n                }\n                setTypeReleaseIterator(si);\n                return;\n            }\n            \n            if (encoding == OBJ_ENCODING_INTSET) {\n                /* intset with intset is simple... and fast */\n                if (setobj->encoding == OBJ_ENCODING_INTSET &&\n                    !intsetFind((intset*)setobj->ptr,intobj))\n                {\n                    unlockDb(c->db);\n                    break;\n                /* in order to compare an integer with an object we\n                 * have to use the generic function, creating an object\n                 * for this */\n                } else if (setobj->encoding == OBJ_ENCODING_HT) {\n                    eleobj = createStringObjectFromLongLong(intobj);\n                    if (!setTypeIsMember(setobj,eleobj)) {\n                        unlockDb(c->db);\n                        freeObject(eleobj);\n                        break;\n                    }\n                    freeObject(eleobj);\n                }\n            } else if (encoding == OBJ_ENCODING_HT) {\n                /* Optimization... if the source object is integer\n                 * encoded AND the target set is an intset, we can get\n                 * a much faster path. */\n                if (eleobj->encoding == OBJ_ENCODING_INT &&\n                    setobj->encoding == OBJ_ENCODING_INTSET &&\n                    !intsetFind((intset*)setobj->ptr,(long)eleobj->ptr))\n                {\n                    unlockDb(c->db);\n                    break;\n                /* else... object to object check is easy as we use the\n                 * type agnostic API here. */\n                } else if (!setTypeIsMember(setobj,eleobj)) {\n                    unlockDb(c->db);\n                    break;\n                }\n            }\n            unlockDb(c->db);\n        }\n\n        /* Only take action when all sets contain the member */\n        if (j == setnum) {\n            if (encoding == OBJ_ENCODING_INTSET) {\n                eleobj = createStringObjectFromLongLong(intobj);\n                setTypeAdd(dstset,eleobj);\n                freeObject(eleobj);\n            } else {\n                setTypeAdd(dstset,eleobj);\n            }\n            cardinality ++;\n        }\n    }\n    setTypeReleaseIterator(si);\n    freeObject(min_len_set);\n\ndone:\n    if (dstkey) {\n        fetchInternalDbByKey(c,dstkey);\n        lockDbWrite(c->db);\n        /* Store the resulting set into the target, if the intersection\n            * is not an empty set. */\n        int deleted = dbDelete(c->db,dstkey);\n        if (dstset && setTypeSize(dstset) > 0) {\n            dbAdd(c->db,dstkey,dstset);\n            addReplyLongLong(c,setTypeSize(dstset));\n            notifyKeyspaceEvent(NOTIFY_SET,\"sinterstore\",\n                dstkey,c->db->id);\n        } else {\n            if (dstset) {\n                freeObject(dstset);\n                dstset = NULL;\n            }\n            addReply(c,shared.czero);\n            if (deleted)\n                notifyKeyspaceEvent(NOTIFY_GENERIC,\"del\",\n                    dstkey,c->db->id);\n        }\n        signalModifiedKey(c->db,dstkey);\n        unlockDb(c->db);\n        c->vel->dirty++;\n    } else {\n        addReplyMultiBulkLen(c,cardinality);\n        if (dstset && setTypeSize(dstset) > 0) {\n            si = setTypeInitIterator(dstset);\n            while((eleobj = setTypeNextObject(si)) != NULL) {\n                addReplyBulk(c,eleobj);\n                if (si->encoding == OBJ_ENCODING_INTSET) \n                    freeObject(eleobj); /* free this object for intset type */\n            }\n            setTypeReleaseIterator(si);\n        }\n        if (dstset) freeObject(dstset);\n    }\n}\n\nvoid sinterCommand(client *c) {\n    sinterGenericCommand(c,c->argv+1,c->argc-1,NULL);\n}\n\nvoid sinterstoreCommand(client *c) {\n    sinterGenericCommand(c,c->argv+2,c->argc-2,c->argv[1]);\n}\n\n#define SET_OP_UNION 0\n#define SET_OP_DIFF 1\n#define SET_OP_INTER 2\n\nvoid sunionDiffGenericCommand(client *c, robj **setkeys, int setnum,\n                              robj *dstkey, int op) {\n    setTypeIterator *si;\n    robj *ele, *dstset = NULL;\n    int j, cardinality = 0;\n    int diff_algo = 1;\n    robj *setobj;\n    long long algo_one_work = 0, algo_two_work = 0;\n    long long first_length = 0;\n\n    for (j = 0; j < setnum; j++) {\n        fetchInternalDbByKey(c,setkeys[j]);\n        lockDbRead(c->db);\n        setobj = lookupKeyRead(c->db,setkeys[j]);\n        if (!setobj) {\n            unlockDb(c->db);\n            update_stats_add(c->vel->stats,keyspace_misses,1);\n            continue;\n        }\n        if (checkType(c,setobj,OBJ_SET)) {\n            unlockDb(c->db);\n            update_stats_add(c->vel->stats,keyspace_hits,1);\n            return;\n        }\n        \n        if (op == SET_OP_DIFF) {\n            if (j == 0) first_length = setTypeSize(setobj);\n            algo_one_work += first_length;\n            algo_two_work += setTypeSize(setobj);\n        }\n\n        unlockDb(c->db);\n        update_stats_add(c->vel->stats,keyspace_hits,1);\n    }\n\n    /* Select what DIFF algorithm to use.\n     *\n     * Algorithm 1 is O(N*M) where N is the size of the element first set\n     * and M the total number of sets.\n     *\n     * Algorithm 2 is O(N) where N is the total number of elements in all\n     * the sets.\n     *\n     * We compute what is the best bet with the current input here. */\n    if (op == SET_OP_DIFF) {\n        /* Algorithm 1 has better constant times and performs less operations\n         * if there are elements in common. Give it some advantage. */\n        algo_one_work /= 2;\n        diff_algo = (algo_one_work <= algo_two_work) ? 1 : 2;\n    }\n\n    /* We need a temp set object to store our union. If the dstkey\n     * is not NULL (that is, we are inside an SUNIONSTORE operation) then\n     * this set object will be the resulting object to set into the target key*/\n    dstset = createIntsetObject();\n\n    if (op == SET_OP_UNION) {\n        /* Union is trivial, just add every element of every set to the\n         * temporary set. */\n        for (j = 0; j < setnum; j++) {\n            fetchInternalDbByKey(c,setkeys[j]);\n            lockDbRead(c->db);\n            setobj = lookupKeyRead(c->db,setkeys[j]);\n\n            if (!setobj) {\n                unlockDb(c->db);\n                continue;\n            }\n            if (checkType(c,setobj,OBJ_SET)) {\n                unlockDb(c->db);\n                freeObject(dstset);\n                return;\n            }\n\n            si = setTypeInitIterator(setobj);\n            while((ele = setTypeNextObject(si)) != NULL) {\n                if (setTypeAdd(dstset,ele)) cardinality++;\n                if (si->encoding == OBJ_ENCODING_INTSET)\n                    freeObject(ele); /* free this object for intset type */\n            }\n            setTypeReleaseIterator(si);\n            unlockDb(c->db);\n        }\n    } else if (op == SET_OP_DIFF && diff_algo == 1) {\n        /* DIFF Algorithm 1:\n         *\n         * We perform the diff by iterating all the elements of the first set,\n         * and only adding it to the target set if the element does not exist\n         * into all the other sets.\n         *\n         * This way we perform at max N*M operations, where N is the size of\n         * the first set, and M the number of sets. */\n        robj *first_set;\n        \n        first_set = createIntsetObject();\n        fetchInternalDbByKey(c,setkeys[0]);\n        lockDbRead(c->db);\n        setobj = lookupKeyRead(c->db,setkeys[0]);\n        if (!setobj) {\n            unlockDb(c->db);\n            freeObject(first_set);\n            goto done;\n        }\n        if (checkType(c,setobj,OBJ_SET)) {\n            unlockDb(c->db);\n            freeObject(dstset);\n            freeObject(first_set);\n            return;\n        }\n        si = setTypeInitIterator(setobj);\n        while((ele = setTypeNextObject(si)) != NULL) {\n            setTypeAdd(first_set,ele);\n            if (si->encoding == OBJ_ENCODING_INTSET) \n                freeObject(ele); /* free this object for intset type */\n        }\n        setTypeReleaseIterator(si);\n        unlockDb(c->db);\n\n        si = setTypeInitIterator(first_set);\n        while((ele = setTypeNextObject(si)) != NULL) {\n            for (j = 1; j < setnum; j++) {\n                fetchInternalDbByKey(c,setkeys[j]);\n                lockDbRead(c->db);\n                setobj = lookupKeyRead(c->db,setkeys[j]);\n                if (!setobj) {\n                    unlockDb(c->db);\n                    continue;\n                }\n                if (checkType(c,setobj,OBJ_SET)) {\n                    unlockDb(c->db);\n                    freeObject(dstset);\n                    freeObject(first_set);\n                    if (si->encoding == OBJ_ENCODING_INTSET) \n                        freeObject(ele); /* free this object for intset type */\n                    setTypeReleaseIterator(si);\n                    return;\n                }\n                if (setTypeIsMember(setobj,ele)) {\n                    unlockDb(c->db);\n                    break;\n                }\n                unlockDb(c->db);\n            }\n\n            if (j == setnum) {\n                /* There is no other set with this element. Add it. */\n                setTypeAdd(dstset,ele);\n                cardinality++;\n            }\n\n            if (si->encoding == OBJ_ENCODING_INTSET) \n                freeObject(ele); /* free this object for intset type */\n        }\n        setTypeReleaseIterator(si);\n        freeObject(first_set);\n    } else if (op == SET_OP_DIFF && diff_algo == 2) {\n        /* DIFF Algorithm 2:\n            *\n            * Add all the elements of the first set to the auxiliary set.\n            * Then remove all the elements of all the next sets from it.\n            *\n            * This is O(N) where N is the sum of all the elements in every\n            * set. */\n        for (j = 0; j < setnum; j++) {\n            fetchInternalDbByKey(c,setkeys[0]);\n            lockDbRead(c->db);\n            setobj = lookupKeyRead(c->db,setkeys[0]);\n            if (!setobj) {\n                if (j == 0) {\n                    unlockDb(c->db);\n                    goto done;\n                }\n                \n                unlockDb(c->db);\n                continue; /* non existing keys are like empty sets */\n            }\n            if (checkType(c,setobj,OBJ_SET)) {\n                unlockDb(c->db);\n                freeObject(dstset);\n                return;\n            }\n\n            si = setTypeInitIterator(setobj);\n            while((ele = setTypeNextObject(si)) != NULL) {\n                if (j == 0) {\n                    if (setTypeAdd(dstset,ele)) cardinality++;\n                } else {\n                    if (setTypeRemove(dstset,ele)) cardinality--;\n                }\n                if (si->encoding == OBJ_ENCODING_INTSET) \n                    freeObject(ele); /* free this object for intset type */\n            }\n            setTypeReleaseIterator(si);\n\n            /* Exit if result set is empty as any additional removal\n                    * of elements will have no effect. */\n            if (cardinality == 0) {\n                unlockDb(c->db);\n                break;\n            }\n            unlockDb(c->db);\n        }\n    }\n\ndone:\n    /* Output the content of the resulting set, if not in STORE mode */\n    if (!dstkey) {\n        addReplyMultiBulkLen(c,cardinality);\n        si = setTypeInitIterator(dstset);\n        while((ele = setTypeNextObject(si)) != NULL) {\n            addReplyBulk(c,ele);\n            if (si->encoding == OBJ_ENCODING_INTSET) \n                freeObject(ele); /* free this object for intset type */\n        }\n        setTypeReleaseIterator(si);\n        freeObject(dstset);\n    } else {\n        fetchInternalDbByKey(c,dstkey);\n        lockDbWrite(c->db);\n        /* If we have a target key where to store the resulting set\n         * create this key with the result set inside */\n        int deleted = dbDelete(c->db,dstkey);\n        if (setTypeSize(dstset) > 0) {\n            dbAdd(c->db,dstkey,dstset);\n            addReplyLongLong(c,setTypeSize(dstset));\n            notifyKeyspaceEvent(NOTIFY_SET,\n                op == SET_OP_UNION ? \"sunionstore\" : \"sdiffstore\",\n                dstkey,c->db->id);\n        } else {\n            freeObject(dstset);\n            addReply(c,shared.czero);\n            if (deleted)\n                notifyKeyspaceEvent(NOTIFY_GENERIC,\"del\",\n                    dstkey,c->db->id);\n        }\n        unlockDb(c->db);\n        signalModifiedKey(c->db,dstkey);\n        c->vel->dirty++;\n    }\n}\n\nvoid sunionCommand(client *c) {\n    sunionDiffGenericCommand(c,c->argv+1,c->argc-1,NULL,SET_OP_UNION);\n}\n\nvoid sunionstoreCommand(client *c) {\n    sunionDiffGenericCommand(c,c->argv+2,c->argc-2,c->argv[1],SET_OP_UNION);\n}\n\nvoid sdiffCommand(client *c) {\n    sunionDiffGenericCommand(c,c->argv+1,c->argc-1,NULL,SET_OP_DIFF);\n}\n\nvoid sdiffstoreCommand(client *c) {\n    sunionDiffGenericCommand(c,c->argv+2,c->argc-2,c->argv[1],SET_OP_DIFF);\n}\n\nvoid sscanCommand(client *c) {\n    scanGenericCommand(c,SCAN_TYPE_SET);\n}\n"
  },
  {
    "path": "src/vr_t_set.h",
    "content": "#ifndef _VR_T_SET_H_\n#define _VR_T_SET_H_\n\nrobj *setTypeCreate(robj *value);\nint setTypeAdd(robj *subject, robj *value);\nint setTypeRemove(robj *setobj, robj *value);\nint setTypeIsMember(robj *subject, robj *value);\nsetTypeIterator *setTypeInitIterator(robj *subject);\nvoid setTypeReleaseIterator(setTypeIterator *si);\nint setTypeNext(setTypeIterator *si, robj **objele, int64_t *llele);\nrobj *setTypeNextObject(setTypeIterator *si);\nint setTypeRandomElement(robj *setobj, robj **objele, int64_t *llele);\n\nunsigned long setTypeSize(robj *subject);\nvoid setTypeConvert(robj *setobj, int enc);\nvoid saddCommand(client *c);\nvoid sremCommand(client *c);\nvoid smoveCommand(client *c);\nvoid sismemberCommand(client *c);\nvoid scardCommand(client *c);\nvoid spopWithCountCommand(client *c);\nvoid spopCommand(client *c);\nvoid srandmemberWithCountCommand(client *c);\nvoid srandmemberCommand(client *c);\nvoid smembersCommand(client *c);\nint qsortCompareSetsByCardinality(const void *s1, const void *s2);\nint qsortCompareSetsByRevCardinality(const void *s1, const void *s2);\nvoid sinterGenericCommand(client *c, robj **setkeys, unsigned long setnum, robj *dstkey);\nvoid sinterCommand(client *c);\nvoid sinterstoreCommand(client *c);\nvoid sunionDiffGenericCommand(client *c, robj **setkeys, int setnum, robj *dstkey, int op);\nvoid sunionCommand(client *c);\nvoid sunionstoreCommand(client *c);\nvoid sdiffCommand(client *c);\nvoid sdiffstoreCommand(client *c);\nvoid sscanCommand(client *c);\n\n#endif\n"
  },
  {
    "path": "src/vr_t_string.c",
    "content": "#include <vr_core.h>\n\n/*-----------------------------------------------------------------------------\n * String Commands\n *----------------------------------------------------------------------------*/\n\nstatic int checkStringLength(client *c, long long size) {\n    if (size > 512*1024*1024) {\n        addReplyError(c,\"string exceeds maximum allowed size (512MB)\");\n        return VR_ERROR;\n    }\n    return VR_OK;\n}\n\n/* The setGenericCommand() function implements the SET operation with different\n * options and variants. This function is called in order to implement the\n * following commands: SET, SETEX, PSETEX, SETNX.\n *\n * 'flags' changes the behavior of the command (NX or XX, see belove).\n *\n * 'expire' represents an expire to set in form of a Redis object as passed\n * by the user. It is interpreted according to the specified 'unit'.\n *\n * 'ok_reply' and 'abort_reply' is what the function will reply to the client\n * if the operation is performed, or when it is not because of NX or\n * XX flags.\n *\n * If ok_reply is NULL \"+OK\" is used.\n * If abort_reply is NULL, \"$-1\" is used. */\n\n#define OBJ_SET_NO_FLAGS 0\n#define OBJ_SET_NX (1<<0)     /* Set if key not exists. */\n#define OBJ_SET_XX (1<<1)     /* Set if key exists. */\n#define OBJ_SET_EX (1<<2)     /* Set if time in seconds is given */\n#define OBJ_SET_PX (1<<3)     /* Set if time in ms in given */\n\nvoid setGenericCommand(client *c, int flags, robj *key, robj *val, robj *expire, int unit, robj *ok_reply, robj *abort_reply) {\n    long long milliseconds = 0; /* initialized to avoid any harmness warning */\n    int expired = 0;\n    int exist;\n\n    if (expire) {\n        if (getLongLongFromObjectOrReply(c, expire, &milliseconds, NULL) != VR_OK)\n            return;\n        if (milliseconds <= 0) {\n            addReplyErrorFormat(c,\"invalid expire time in %s\",c->cmd->name);\n            return;\n        }\n        if (unit == UNIT_SECONDS) milliseconds *= 1000;\n    }\n\n    fetchInternalDbByKey(c,key);\n    lockDbWrite(c->db);\n    if (lookupKeyWrite(c->db,key,&expired) == NULL)\n        exist = 0;\n    else\n        exist = 1;\n\n    if ((flags & OBJ_SET_NX && exist) ||\n        (flags & OBJ_SET_XX && !exist))\n    {\n        unlockDb(c->db);\n        if (expired) update_stats_add(c->vel->stats, expiredkeys, 1);\n        addReply(c, abort_reply ? abort_reply : shared.nullbulk);\n        return;\n    }\n\n    setKey(c->db,key,dupStringObjectUnconstant(val),NULL);\n    c->vel->dirty++;\n    if (expire) setExpire(c->db,key,vr_msec_now()+milliseconds);\n    notifyKeyspaceEvent(NOTIFY_STRING,\"set\",key,c->db->id);\n    if (expire) notifyKeyspaceEvent(NOTIFY_GENERIC,\n        \"expire\",key,c->db->id);\n    addReply(c, ok_reply ? ok_reply : shared.ok);\n    unlockDb(c->db);\n    if (expired) update_stats_add(c->vel->stats,expiredkeys,1);\n}\n\n/* SET key value [NX] [XX] [EX <seconds>] [PX <milliseconds>] */\nvoid setCommand(client *c) {\n    int j;\n    robj *expire = NULL;\n    int unit = UNIT_SECONDS;\n    int flags = OBJ_SET_NO_FLAGS;\n\n    for (j = 3; j < c->argc; j++) {\n        char *a = c->argv[j]->ptr;\n        robj *next = (j == c->argc-1) ? NULL : c->argv[j+1];\n\n        if ((a[0] == 'n' || a[0] == 'N') &&\n            (a[1] == 'x' || a[1] == 'X') && a[2] == '\\0' &&\n            !(flags & OBJ_SET_XX))\n        {\n            flags |= OBJ_SET_NX;\n        } else if ((a[0] == 'x' || a[0] == 'X') &&\n                   (a[1] == 'x' || a[1] == 'X') && a[2] == '\\0' &&\n                   !(flags & OBJ_SET_NX))\n        {\n            flags |= OBJ_SET_XX;\n        } else if ((a[0] == 'e' || a[0] == 'E') &&\n                   (a[1] == 'x' || a[1] == 'X') && a[2] == '\\0' &&\n                   !(flags & OBJ_SET_PX) && next)\n        {\n            flags |= OBJ_SET_EX;\n            unit = UNIT_SECONDS;\n            expire = next;\n            j++;\n        } else if ((a[0] == 'p' || a[0] == 'P') &&\n                   (a[1] == 'x' || a[1] == 'X') && a[2] == '\\0' &&\n                   !(flags & OBJ_SET_EX) && next)\n        {\n            flags |= OBJ_SET_PX;\n            unit = UNIT_MILLISECONDS;\n            expire = next;\n            j++;\n        } else {\n            addReply(c,shared.syntaxerr);\n            return;\n        }\n    }\n\n    c->argv[2] = tryObjectEncoding(c->argv[2]);\n    setGenericCommand(c,flags,c->argv[1],c->argv[2],expire,unit,NULL,NULL);\n}\n\nvoid setnxCommand(client *c) {\n    c->argv[2] = tryObjectEncoding(c->argv[2]);\n    setGenericCommand(c,OBJ_SET_NX,c->argv[1],c->argv[2],NULL,0,shared.cone,shared.czero);\n}\n\nvoid setexCommand(client *c) {\n    c->argv[3] = tryObjectEncoding(c->argv[3]);\n    setGenericCommand(c,OBJ_SET_NO_FLAGS,c->argv[1],c->argv[3],c->argv[2],UNIT_SECONDS,NULL,NULL);\n}\n\nvoid psetexCommand(client *c) {\n    c->argv[3] = tryObjectEncoding(c->argv[3]);\n    setGenericCommand(c,OBJ_SET_NO_FLAGS,c->argv[1],c->argv[3],c->argv[2],UNIT_MILLISECONDS,NULL,NULL);\n}\n\nint getGenericCommand(client *c) {\n    robj *o;\n    \n    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL) {\n        return VR_OK;\n    }\n    \n    if (o->type != OBJ_STRING) {\n        addReply(c,shared.wrongtypeerr);\n        return VR_ERROR;\n    } else {\n        addReplyBulk(c,o);\n        return VR_OK;\n    }\n}\n\nvoid getCommand(client *c) {\n    robj *o;\n\n    fetchInternalDbByKey(c,c->argv[1]);\n    lockDbRead(c->db);\n    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL) {\n        unlockDb(c->db);\n        update_stats_add(c->vel->stats, keyspace_misses, 1);\n        return;\n    }\n\n    if (o->type != OBJ_STRING) {\n        addReply(c,shared.wrongtypeerr);\n    } else {\n        addReplyBulk(c,o);\n    }\n    \n    unlockDb(c->db);\n    update_stats_add(c->vel->stats, keyspace_hits, 1);\n}\n\nvoid getsetCommand(client *c) {\n    robj *key, *val;\n    int expired = 0;\n    int exist;\n\n    key = c->argv[1];\n    c->argv[2] = tryObjectEncoding(c->argv[2]);\n    \n    fetchInternalDbByKey(c,key);\n    lockDbWrite(c->db);\n    val = lookupKeyWriteOrReply(c,key,shared.nullbulk,&expired);\n    if (val == NULL) {\n        exist = 0;\n        dbAdd(c->db,key,dupStringObjectUnconstant(c->argv[2]));\n    } else {    \n        exist = 1;\n        if (val->type != OBJ_STRING) {\n            addReply(c,shared.wrongtypeerr);\n            goto end;\n        }\n\n        addReplyBulk(c,val);\n        dbOverwrite(c->db,key,dupStringObjectUnconstant(c->argv[2]));\n        removeExpire(c->db,key);\n    }\n\n    notifyKeyspaceEvent(NOTIFY_STRING,\"set\",c->argv[1],c->db->id);\n    c->vel->dirty++;\n    \nend:\n    unlockDb(c->db);\n    if (expired) update_stats_add(c->vel->stats, expiredkeys, 1);\n    if (exist)\n        update_stats_add(c->vel->stats, keyspace_hits, 1);\n    else\n        update_stats_add(c->vel->stats, keyspace_misses, 1);\n}\n\nvoid setrangeCommand(client *c) {\n    robj *o;\n    long offset;\n    sds value = c->argv[3]->ptr;\n    int expired = 0;\n\n    if (getLongFromObjectOrReply(c,c->argv[2],&offset,NULL) != VR_OK)\n        return;\n\n    if (offset < 0) {\n        addReplyError(c,\"offset is out of range\");\n        return;\n    }\n\n    fetchInternalDbByKey(c, c->argv[1]);\n    lockDbWrite(c->db);\n    o = lookupKeyWrite(c->db,c->argv[1],&expired);\n    if (o == NULL) {\n        /* Return 0 when setting nothing on a non-existing string */\n        if (sdslen(value) == 0) {\n            unlockDb(c->db);\n            if (expired) update_stats_add(c->vel->stats,expiredkeys,1);\n            addReply(c,shared.czero);\n            return;\n        }\n\n        /* Return when the resulting string exceeds allowed size */\n        if (checkStringLength(c,offset+sdslen(value)) != VR_OK) {\n            unlockDb(c->db);\n            if (expired) update_stats_add(c->vel->stats,expiredkeys,1);\n            return;\n        }\n\n        o = createObject(OBJ_STRING,sdsnewlen(NULL, offset+sdslen(value)));\n        dbAdd(c->db,c->argv[1],o);\n    } else {\n        size_t olen;\n\n        /* Key exists, check type */\n        if (checkType(c,o,OBJ_STRING)) {\n            unlockDb(c->db);\n            if (expired) update_stats_add(c->vel->stats,expiredkeys,1);\n            return;\n        }\n\n        /* Return existing string length when setting nothing */\n        olen = stringObjectLen(o);\n        if (sdslen(value) == 0) {\n            unlockDb(c->db);\n            if (expired) update_stats_add(c->vel->stats,expiredkeys,1);\n            addReplyLongLong(c,olen);\n            return;\n        }\n\n        /* Return when the resulting string exceeds allowed size */\n        if (checkStringLength(c,offset+sdslen(value)) != VR_OK) {\n            unlockDb(c->db);\n            if (expired) update_stats_add(c->vel->stats,expiredkeys,1);\n            return;\n        }\n\n        /* Create a copy when the object is shared or encoded. */\n        o = dbUnshareStringValue(c->db,c->argv[1],o);\n    }\n\n    if (sdslen(value) > 0) {\n        o->ptr = sdsgrowzero(o->ptr,offset+sdslen(value));\n        memcpy((char*)o->ptr+offset,value,sdslen(value));\n        signalModifiedKey(c->db,c->argv[1]);\n        notifyKeyspaceEvent(NOTIFY_STRING,\n            \"setrange\",c->argv[1],c->db->id);\n        c->vel->dirty++;\n    }\n    addReplyLongLong(c,sdslen(o->ptr));\n    unlockDb(c->db);\n    if (expired) update_stats_add(c->vel->stats,expiredkeys,1);\n}\n\nvoid getrangeCommand(client *c) {\n    robj *o;\n    long long start, end;\n    char *str, llbuf[32];\n    size_t strlen;\n\n    if (getLongLongFromObjectOrReply(c,c->argv[2],&start,NULL) != VR_OK)\n        return;\n    if (getLongLongFromObjectOrReply(c,c->argv[3],&end,NULL) != VR_OK)\n        return;\n\n    fetchInternalDbByKey(c, c->argv[1]);\n    lockDbRead(c->db);\n    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptybulk)) == NULL) {\n        unlockDb(c->db);\n        update_stats_add(c->vel->stats, keyspace_misses, 1);\n        return;\n    } else if (checkType(c,o,OBJ_STRING)) {\n        unlockDb(c->db);\n        update_stats_add(c->vel->stats, keyspace_hits, 1);\n        return;\n    }\n\n    if (o->encoding == OBJ_ENCODING_INT) {\n        str = llbuf;\n        strlen = ll2string(llbuf,sizeof(llbuf),(long)o->ptr);\n    } else {\n        str = o->ptr;\n        strlen = sdslen(str);\n    }\n\n    /* Convert negative indexes */\n    if (start < 0) start = strlen+start;\n    if (end < 0) end = strlen+end;\n    if (start < 0) start = 0;\n    if (end < 0) end = 0;\n    if ((unsigned long long)end >= strlen) end = strlen-1;\n\n    /* Precondition: end >= 0 && end < strlen, so the only condition where\n     * nothing can be returned is: start > end. */\n    if (start > end || strlen == 0) {\n        addReply(c,shared.emptybulk);\n    } else {\n        addReplyBulkCBuffer(c,(char*)str+start,end-start+1);\n    }\n    unlockDb(c->db);\n    update_stats_add(c->vel->stats, keyspace_hits, 1);\n}\n\nvoid mgetCommand(client *c) {\n    int j;\n\n    addReplyMultiBulkLen(c,c->argc-1);\n    for (j = 1; j < c->argc; j++) {\n        fetchInternalDbByKey(c,c->argv[j]);\n        lockDbRead(c->db);\n        robj *o = lookupKeyRead(c->db,c->argv[j]);\n        if (o == NULL) {\n            unlockDb(c->db);\n            update_stats_add(c->vel->stats, keyspace_misses, 1);\n            addReply(c,shared.nullbulk);\n        } else {\n            if (o->type != OBJ_STRING) {\n                addReply(c,shared.nullbulk);\n            } else {\n                addReplyBulk(c,o);\n            }\n            unlockDb(c->db);\n            update_stats_add(c->vel->stats, keyspace_hits, 1);\n        }\n    }\n}\n\nvoid msetGenericCommand(client *c, int nx) {\n    int j, busykeys = 0;\n\n    if ((c->argc % 2) == 0) {\n        addReplyError(c,\"wrong number of arguments for MSET\");\n        return;\n    }\n    /* Handle the NX flag. The MSETNX semantic is to return zero and don't\n     * set nothing at all if at least one already key exists. */\n    if (nx) {\n        for (j = 1; j < c->argc; j += 2) {\n            if (lookupKeyWrite(c->db,c->argv[j],NULL) != NULL) {\n                busykeys++;\n            }\n        }\n        if (busykeys) {\n            addReply(c, shared.czero);\n            return;\n        }\n    }\n\n    for (j = 1; j < c->argc; j += 2) {\n        c->argv[j+1] = tryObjectEncoding(c->argv[j+1]);\n        setKey(c->db,c->argv[j],c->argv[j+1],NULL);\n        notifyKeyspaceEvent(NOTIFY_STRING,\"set\",c->argv[j],c->db->id);\n    }\n    server.dirty += (c->argc-1)/2;\n    addReply(c, nx ? shared.cone : shared.ok);\n}\n\nvoid msetCommand(client *c) {\n    int j;\n    int expired = 0, expired_total = 0;\n\n    if ((c->argc % 2) == 0) {\n        addReplyError(c,\"wrong number of arguments for MSET\");\n        return;\n    }\n\n    for (j = 1; j < c->argc; j += 2) {\n        c->argv[j+1] = tryObjectEncoding(c->argv[j+1]);\n        fetchInternalDbByKey(c,c->argv[j]);\n        lockDbWrite(c->db);\n        setKey(c->db,c->argv[j],dupStringObjectUnconstant(c->argv[j+1]),&expired);\n        unlockDb(c->db);\n        if (expired) expired_total ++;\n        notifyKeyspaceEvent(NOTIFY_STRING,\"set\",c->argv[j],c->db->id);\n    }\n\n    if (expired_total) update_stats_add(c->vel->stats,expiredkeys,expired_total);\n    c->vel->dirty += (c->argc-1)/2;\n    addReply(c, shared.ok);\n}\n\nvoid msetnxCommand(client *c) {\n    msetGenericCommand(c,1);\n}\n\nvoid incrDecrCommand(client *c, long long incr) {\n    long long value, oldvalue;\n    robj *o, *new;\n    int expired = 0;\n\n    fetchInternalDbByKey(c, c->argv[1]);\n    lockDbWrite(c->db);\n    o = lookupKeyWrite(c->db,c->argv[1],&expired);\n    if (o != NULL && checkType(c,o,OBJ_STRING)) goto end;\n    if (getLongLongFromObjectOrReply(c,o,&value,NULL) != VR_OK) goto end;\n\n    oldvalue = value;\n    if ((incr < 0 && oldvalue < 0 && incr < (LLONG_MIN-oldvalue)) ||\n        (incr > 0 && oldvalue > 0 && incr > (LLONG_MAX-oldvalue))) {\n        addReplyError(c,\"increment or decrement would overflow\");\n        goto end;\n    }\n    value += incr;\n\n    if (o && o->refcount == 1 && o->encoding == OBJ_ENCODING_INT &&\n        (value < 0 || value >= OBJ_SHARED_INTEGERS) &&\n        value >= LONG_MIN && value <= LONG_MAX)\n    {\n        new = o;\n        o->ptr = (void*)((long)value);\n    } else {\n        new = createStringObjectFromLongLong(value);\n        if (o) {\n            dbOverwrite(c->db,c->argv[1],new);\n        } else {\n            dbAdd(c->db,c->argv[1],new);\n        }\n    }\n    signalModifiedKey(c->db,c->argv[1]);\n    notifyKeyspaceEvent(NOTIFY_STRING,\"incrby\",c->argv[1],c->db->id);\n    c->vel->dirty++;\n    addReply(c,shared.colon);\n    addReply(c,new);\n    addReply(c,shared.crlf);\n    \nend:\n    unlockDb(c->db);\n    if (expired) update_stats_add(c->vel->stats, expiredkeys, 1);\n}\n\nvoid incrCommand(client *c) {\n    incrDecrCommand(c,1);\n}\n\nvoid decrCommand(client *c) {\n    incrDecrCommand(c,-1);\n}\n\nvoid incrbyCommand(client *c) {\n    long long incr;\n\n    if (getLongLongFromObjectOrReply(c, c->argv[2], &incr, NULL) != VR_OK) return;\n    incrDecrCommand(c,incr);\n}\n\nvoid decrbyCommand(client *c) {\n    long long incr;\n\n    if (getLongLongFromObjectOrReply(c, c->argv[2], &incr, NULL) != VR_OK) return;\n    incrDecrCommand(c,-incr);\n}\n\nvoid incrbyfloatCommand(client *c) {\n    long double incr, value;\n    robj *o, *new, *aux;\n    int expired = 0;\n\n    fetchInternalDbByKey(c, c->argv[1]);\n    lockDbWrite(c->db);\n    o = lookupKeyWrite(c->db,c->argv[1],&expired);\n    if (o != NULL && checkType(c,o,OBJ_STRING)) goto end;\n    if (getLongDoubleFromObjectOrReply(c,o,&value,NULL) != VR_OK ||\n        getLongDoubleFromObjectOrReply(c,c->argv[2],&incr,NULL) != VR_OK)\n        goto end;\n\n    value += incr;\n    if (isnan(value) || isinf(value)) {\n        addReplyError(c,\"increment would produce NaN or Infinity\");\n        goto end;\n    }\n    new = createStringObjectFromLongDouble(value,1);\n    if (o)\n        dbOverwrite(c->db,c->argv[1],new);\n    else\n        dbAdd(c->db,c->argv[1],new);\n    signalModifiedKey(c->db,c->argv[1]);\n    notifyKeyspaceEvent(NOTIFY_STRING,\"incrbyfloat\",c->argv[1],c->db->id);\n    c->vel->dirty++;\n    addReplyBulk(c,new);\n\n    /* Always replicate INCRBYFLOAT as a SET command with the final value\n     * in order to make sure that differences in float precision or formatting\n     * will not create differences in replicas or after an AOF restart. */\n    aux = createStringObject(\"SET\",3);\n    rewriteClientCommandArgument(c,0,aux);\n    aux = dupStringObjectUnconstant(new);\n    rewriteClientCommandArgument(c,2,aux);\n\nend:\n    unlockDb(c->db);\n    if (expired) update_stats_add(c->vel->stats,expiredkeys,1);\n}\n\nvoid appendCommand(client *c) {\n    size_t totlen;\n    robj *o, *append;\n    int expired = 0;\n\n    fetchInternalDbByKey(c, c->argv[1]);\n    lockDbWrite(c->db);\n    o = lookupKeyWrite(c->db,c->argv[1],&expired);\n    if (o == NULL) {\n        /* Create the key */\n        c->argv[2] = tryObjectEncoding(c->argv[2]);\n        dbAdd(c->db,c->argv[1],dupStringObjectUnconstant(c->argv[2]));\n        totlen = stringObjectLen(c->argv[2]);\n    } else {    \n        /* Key exists, check type */\n        if (checkType(c,o,OBJ_STRING))\n            goto end;\n\n        /* \"append\" is an argument, so always an sds */\n        append = c->argv[2];\n        totlen = stringObjectLen(o)+sdslen(append->ptr);\n        if (checkStringLength(c,totlen) != VR_OK)\n            goto end;\n\n        /* Append the value */\n        o = dbUnshareStringValue(c->db,c->argv[1],o);\n        o->ptr = sdscatlen(o->ptr,append->ptr,sdslen(append->ptr));\n        totlen = sdslen(o->ptr);\n    }\n    signalModifiedKey(c->db,c->argv[1]);\n    notifyKeyspaceEvent(NOTIFY_STRING,\"append\",c->argv[1],c->db->id);\n    c->vel->dirty++;\n    addReplyLongLong(c,totlen);\n\nend:\n    unlockDb(c->db);\n    if (expired) update_stats_add(c->vel->stats,expiredkeys,1);\n}\n\nvoid strlenCommand(client *c) {\n    robj *o;\n    \n    fetchInternalDbByKey(c, c->argv[1]);\n    lockDbRead(c->db);\n    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL) {\n        unlockDb(c->db);\n        update_stats_add(c->vel->stats, keyspace_misses, 1);\n        return;\n    } else if(checkType(c,o,OBJ_STRING)) {\n        unlockDb(c->db);\n        update_stats_add(c->vel->stats, keyspace_hits, 1);\n        return;\n    }\n    \n    addReplyLongLong(c,stringObjectLen(o));\n    unlockDb(c->db);\n    update_stats_add(c->vel->stats, keyspace_hits, 1);\n}\n"
  },
  {
    "path": "src/vr_t_string.h",
    "content": "#ifndef _VR_T_STRING_H_\n#define _VR_T_STRING_H_\n\nvoid setGenericCommand(client *c, int flags, robj *key, robj *val, robj *expire, int unit, robj *ok_reply, robj *abort_reply);\nvoid setCommand(client *c);\nvoid setnxCommand(client *c);\nvoid setexCommand(client *c);\nvoid psetexCommand(client *c);\nint getGenericCommand(client *c);\nvoid getCommand(client *c);\nvoid getsetCommand(client *c);\nvoid setrangeCommand(client *c);\nvoid getrangeCommand(client *c);\nvoid mgetCommand(client *c);\nvoid msetGenericCommand(client *c, int nx);\nvoid msetCommand(client *c);\nvoid msetnxCommand(client *c);\nvoid incrDecrCommand(client *c, long long incr);\nvoid incrCommand(client *c);\nvoid decrCommand(client *c);\nvoid incrbyCommand(client *c);\nvoid decrbyCommand(client *c);\nvoid incrbyfloatCommand(client *c);\nvoid appendCommand(client *c);\nvoid strlenCommand(client *c);\n\n#endif\n"
  },
  {
    "path": "src/vr_t_zset.c",
    "content": "#include <vr_core.h>\n\nstatic int zslLexValueGteMin(robj *value, zlexrangespec *spec);\nstatic int zslLexValueLteMax(robj *value, zlexrangespec *spec);\n\nzskiplistNode *zslCreateNode(int level, double score, robj *obj) {\n    zskiplistNode *zn = dalloc(sizeof(*zn)+level*sizeof(struct zskiplistLevel));\n    zn->score = score;\n    zn->obj = obj;\n    return zn;\n}\n\nzskiplist *zslCreate(void) {\n    int j;\n    zskiplist *zsl;\n\n    zsl = dalloc(sizeof(*zsl));\n    zsl->level = 1;\n    zsl->length = 0;\n    zsl->header = zslCreateNode(ZSKIPLIST_MAXLEVEL,0,NULL);\n    for (j = 0; j < ZSKIPLIST_MAXLEVEL; j++) {\n        zsl->header->level[j].forward = NULL;\n        zsl->header->level[j].span = 0;\n    }\n    zsl->header->backward = NULL;\n    zsl->tail = NULL;\n    return zsl;\n}\n\nvoid zslFreeNode(zskiplistNode *node) {\n    /* node->obj is stored in the dict of the zset object, so we just free the node. */\n    dfree(node);\n}\n\nvoid zslFree(zskiplist *zsl) {\n    zskiplistNode *node = zsl->header->level[0].forward, *next;\n\n    dfree(zsl->header);\n    while(node) {\n        next = node->level[0].forward;\n        zslFreeNode(node);\n        node = next;\n    }\n    dfree(zsl);\n}\n\n/* Returns a random level for the new skiplist node we are going to create.\n * The return value of this function is between 1 and ZSKIPLIST_MAXLEVEL\n * (both inclusive), with a powerlaw-alike distribution where higher\n * levels are less likely to be returned. */\nint zslRandomLevel(void) {\n    int level = 1;\n    while ((random()&0xFFFF) < (ZSKIPLIST_P * 0xFFFF))\n        level += 1;\n    return (level<ZSKIPLIST_MAXLEVEL) ? level : ZSKIPLIST_MAXLEVEL;\n}\n\nzskiplistNode *zslInsert(zskiplist *zsl, double score, robj *obj) {\n    zskiplistNode *update[ZSKIPLIST_MAXLEVEL], *x;\n    unsigned int rank[ZSKIPLIST_MAXLEVEL];\n    int i, level;\n\n    ASSERT(!isnan(score));\n    x = zsl->header;\n    for (i = zsl->level-1; i >= 0; i--) {\n        /* store rank that is crossed to reach the insert position */\n        rank[i] = i == (zsl->level-1) ? 0 : rank[i+1];\n        while (x->level[i].forward &&\n            (x->level[i].forward->score < score ||\n                (x->level[i].forward->score == score &&\n                compareStringObjects(x->level[i].forward->obj,obj) < 0))) {\n            rank[i] += x->level[i].span;\n            x = x->level[i].forward;\n        }\n        update[i] = x;\n    }\n    /* we assume the key is not already inside, since we allow duplicated\n     * scores, and the re-insertion of score and redis object should never\n     * happen since the caller of zslInsert() should test in the hash table\n     * if the element is already inside or not. */\n    level = zslRandomLevel();\n    if (level > zsl->level) {\n        for (i = zsl->level; i < level; i++) {\n            rank[i] = 0;\n            update[i] = zsl->header;\n            update[i]->level[i].span = zsl->length;\n        }\n        zsl->level = level;\n    }\n    x = zslCreateNode(level,score,obj);\n    for (i = 0; i < level; i++) {\n        x->level[i].forward = update[i]->level[i].forward;\n        update[i]->level[i].forward = x;\n\n        /* update span covered by update[i] as x is inserted here */\n        x->level[i].span = update[i]->level[i].span - (rank[0] - rank[i]);\n        update[i]->level[i].span = (rank[0] - rank[i]) + 1;\n    }\n\n    /* increment span for untouched levels */\n    for (i = level; i < zsl->level; i++) {\n        update[i]->level[i].span++;\n    }\n\n    x->backward = (update[0] == zsl->header) ? NULL : update[0];\n    if (x->level[0].forward)\n        x->level[0].forward->backward = x;\n    else\n        zsl->tail = x;\n    zsl->length++;\n    return x;\n}\n\n/* Internal function used by zslDelete, zslDeleteByScore and zslDeleteByRank */\nvoid zslDeleteNode(zskiplist *zsl, zskiplistNode *x, zskiplistNode **update) {\n    int i;\n    for (i = 0; i < zsl->level; i++) {\n        if (update[i]->level[i].forward == x) {\n            update[i]->level[i].span += x->level[i].span - 1;\n            update[i]->level[i].forward = x->level[i].forward;\n        } else {\n            update[i]->level[i].span -= 1;\n        }\n    }\n    if (x->level[0].forward) {\n        x->level[0].forward->backward = x->backward;\n    } else {\n        zsl->tail = x->backward;\n    }\n    while(zsl->level > 1 && zsl->header->level[zsl->level-1].forward == NULL)\n        zsl->level--;\n    zsl->length--;\n}\n\n/* Delete an element with matching score/object from the skiplist. */\nint zslDelete(zskiplist *zsl, double score, robj *obj) {\n    zskiplistNode *update[ZSKIPLIST_MAXLEVEL], *x;\n    int i;\n\n    x = zsl->header;\n    for (i = zsl->level-1; i >= 0; i--) {\n        while (x->level[i].forward &&\n            (x->level[i].forward->score < score ||\n                (x->level[i].forward->score == score &&\n                compareStringObjects(x->level[i].forward->obj,obj) < 0)))\n            x = x->level[i].forward;\n        update[i] = x;\n    }\n    /* We may have multiple elements with the same score, what we need\n     * is to find the element with both the right score and object. */\n    x = x->level[0].forward;\n    if (x && score == x->score && equalStringObjects(x->obj,obj)) {\n        zslDeleteNode(zsl, x, update);\n        zslFreeNode(x);\n        return 1;\n    }\n    return 0; /* not found */\n}\n\nstatic int zslValueGteMin(double value, zrangespec *spec) {\n    return spec->minex ? (value > spec->min) : (value >= spec->min);\n}\n\nint zslValueLteMax(double value, zrangespec *spec) {\n    return spec->maxex ? (value < spec->max) : (value <= spec->max);\n}\n\n/* Returns if there is a part of the zset is in range. */\nint zslIsInRange(zskiplist *zsl, zrangespec *range) {\n    zskiplistNode *x;\n\n    /* Test for ranges that will always be empty. */\n    if (range->min > range->max ||\n            (range->min == range->max && (range->minex || range->maxex)))\n        return 0;\n    x = zsl->tail;\n    if (x == NULL || !zslValueGteMin(x->score,range))\n        return 0;\n    x = zsl->header->level[0].forward;\n    if (x == NULL || !zslValueLteMax(x->score,range))\n        return 0;\n    return 1;\n}\n\n/* Find the first node that is contained in the specified range.\n * Returns NULL when no element is contained in the range. */\nzskiplistNode *zslFirstInRange(zskiplist *zsl, zrangespec *range) {\n    zskiplistNode *x;\n    int i;\n\n    /* If everything is out of range, return early. */\n    if (!zslIsInRange(zsl,range)) return NULL;\n\n    x = zsl->header;\n    for (i = zsl->level-1; i >= 0; i--) {\n        /* Go forward while *OUT* of range. */\n        while (x->level[i].forward &&\n            !zslValueGteMin(x->level[i].forward->score,range))\n                x = x->level[i].forward;\n    }\n\n    /* This is an inner range, so the next node cannot be NULL. */\n    x = x->level[0].forward;\n    ASSERT(x != NULL);\n\n    /* Check if score <= max. */\n    if (!zslValueLteMax(x->score,range)) return NULL;\n    return x;\n}\n\n/* Find the last node that is contained in the specified range.\n * Returns NULL when no element is contained in the range. */\nzskiplistNode *zslLastInRange(zskiplist *zsl, zrangespec *range) {\n    zskiplistNode *x;\n    int i;\n\n    /* If everything is out of range, return early. */\n    if (!zslIsInRange(zsl,range)) return NULL;\n\n    x = zsl->header;\n    for (i = zsl->level-1; i >= 0; i--) {\n        /* Go forward while *IN* range. */\n        while (x->level[i].forward &&\n            zslValueLteMax(x->level[i].forward->score,range))\n                x = x->level[i].forward;\n    }\n\n    /* This is an inner range, so this node cannot be NULL. */\n    ASSERT(x != NULL);\n\n    /* Check if score >= min. */\n    if (!zslValueGteMin(x->score,range)) return NULL;\n    return x;\n}\n\n/* Delete all the elements with score between min and max from the skiplist.\n * Min and max are inclusive, so a score >= min || score <= max is deleted.\n * Note that this function takes the reference to the hash table view of the\n * sorted set, in order to remove the elements from the hash table too. */\nunsigned long zslDeleteRangeByScore(zskiplist *zsl, zrangespec *range, dict *dict) {\n    zskiplistNode *update[ZSKIPLIST_MAXLEVEL], *x;\n    unsigned long removed = 0;\n    int i;\n\n    x = zsl->header;\n    for (i = zsl->level-1; i >= 0; i--) {\n        while (x->level[i].forward && (range->minex ?\n            x->level[i].forward->score <= range->min :\n            x->level[i].forward->score < range->min))\n                x = x->level[i].forward;\n        update[i] = x;\n    }\n\n    /* Current node is the last with score < or <= min. */\n    x = x->level[0].forward;\n\n    /* Delete nodes while in range. */\n    while (x &&\n           (range->maxex ? x->score < range->max : x->score <= range->max))\n    {\n        zskiplistNode *next = x->level[0].forward;\n        zslDeleteNode(zsl,x,update);\n        dictDelete(dict,x->obj);\n        zslFreeNode(x);\n        removed++;\n        x = next;\n    }\n    return removed;\n}\n\nunsigned long zslDeleteRangeByLex(zskiplist *zsl, zlexrangespec *range, dict *dict) {\n    zskiplistNode *update[ZSKIPLIST_MAXLEVEL], *x;\n    unsigned long removed = 0;\n    int i;\n\n\n    x = zsl->header;\n    for (i = zsl->level-1; i >= 0; i--) {\n        while (x->level[i].forward &&\n            !zslLexValueGteMin(x->level[i].forward->obj,range))\n                x = x->level[i].forward;\n        update[i] = x;\n    }\n\n    /* Current node is the last with score < or <= min. */\n    x = x->level[0].forward;\n\n    /* Delete nodes while in range. */\n    while (x && zslLexValueLteMax(x->obj,range)) {\n        zskiplistNode *next = x->level[0].forward;\n        zslDeleteNode(zsl,x,update);\n        dictDelete(dict,x->obj);\n        zslFreeNode(x);\n        removed++;\n        x = next;\n    }\n    return removed;\n}\n\n/* Delete all the elements with rank between start and end from the skiplist.\n * Start and end are inclusive. Note that start and end need to be 1-based */\nunsigned long zslDeleteRangeByRank(zskiplist *zsl, unsigned int start, unsigned int end, dict *dict) {\n    zskiplistNode *update[ZSKIPLIST_MAXLEVEL], *x;\n    unsigned long traversed = 0, removed = 0;\n    int i;\n\n    x = zsl->header;\n    for (i = zsl->level-1; i >= 0; i--) {\n        while (x->level[i].forward && (traversed + x->level[i].span) < start) {\n            traversed += x->level[i].span;\n            x = x->level[i].forward;\n        }\n        update[i] = x;\n    }\n\n    traversed++;\n    x = x->level[0].forward;\n    while (x && traversed <= end) {\n        zskiplistNode *next = x->level[0].forward;\n        zslDeleteNode(zsl,x,update);\n        dictDelete(dict,x->obj);\n        zslFreeNode(x);\n        removed++;\n        traversed++;\n        x = next;\n    }\n    return removed;\n}\n\n/* Find the rank for an element by both score and key.\n * Returns 0 when the element cannot be found, rank otherwise.\n * Note that the rank is 1-based due to the span of zsl->header to the\n * first element. */\nunsigned long zslGetRank(zskiplist *zsl, double score, robj *o) {\n    zskiplistNode *x;\n    unsigned long rank = 0;\n    int i;\n\n    x = zsl->header;\n    for (i = zsl->level-1; i >= 0; i--) {\n        while (x->level[i].forward &&\n            (x->level[i].forward->score < score ||\n                (x->level[i].forward->score == score &&\n                compareStringObjects(x->level[i].forward->obj,o) <= 0))) {\n            rank += x->level[i].span;\n            x = x->level[i].forward;\n        }\n\n        /* x might be equal to zsl->header, so test if obj is non-NULL */\n        if (x->obj && equalStringObjects(x->obj,o)) {\n            return rank;\n        }\n    }\n    return 0;\n}\n\n/* Finds an element by its rank. The rank argument needs to be 1-based. */\nzskiplistNode* zslGetElementByRank(zskiplist *zsl, unsigned long rank) {\n    zskiplistNode *x;\n    unsigned long traversed = 0;\n    int i;\n\n    x = zsl->header;\n    for (i = zsl->level-1; i >= 0; i--) {\n        while (x->level[i].forward && (traversed + x->level[i].span) <= rank)\n        {\n            traversed += x->level[i].span;\n            x = x->level[i].forward;\n        }\n        if (traversed == rank) {\n            return x;\n        }\n    }\n    return NULL;\n}\n\n/* Populate the rangespec according to the objects min and max. */\nstatic int zslParseRange(robj *min, robj *max, zrangespec *spec) {\n    char *eptr;\n    spec->minex = spec->maxex = 0;\n\n    /* Parse the min-max interval. If one of the values is prefixed\n     * by the \"(\" character, it's considered \"open\". For instance\n     * ZRANGEBYSCORE zset (1.5 (2.5 will match min < x < max\n     * ZRANGEBYSCORE zset 1.5 2.5 will instead match min <= x <= max */\n    if (min->encoding == OBJ_ENCODING_INT) {\n        spec->min = (long)min->ptr;\n    } else {\n        if (((char*)min->ptr)[0] == '(') {\n            spec->min = strtod((char*)min->ptr+1,&eptr);\n            if (eptr[0] != '\\0' || isnan(spec->min)) return VR_ERROR;\n            spec->minex = 1;\n        } else {\n            spec->min = strtod((char*)min->ptr,&eptr);\n            if (eptr[0] != '\\0' || isnan(spec->min)) return VR_ERROR;\n        }\n    }\n    if (max->encoding == OBJ_ENCODING_INT) {\n        spec->max = (long)max->ptr;\n    } else {\n        if (((char*)max->ptr)[0] == '(') {\n            spec->max = strtod((char*)max->ptr+1,&eptr);\n            if (eptr[0] != '\\0' || isnan(spec->max)) return VR_ERROR;\n            spec->maxex = 1;\n        } else {\n            spec->max = strtod((char*)max->ptr,&eptr);\n            if (eptr[0] != '\\0' || isnan(spec->max)) return VR_ERROR;\n        }\n    }\n\n    return VR_OK;\n}\n\n/* ------------------------ Lexicographic ranges ---------------------------- */\n\n/* Parse max or min argument of ZRANGEBYLEX.\n  * (foo means foo (open interval)\n  * [foo means foo (closed interval)\n  * - means the min string possible\n  * + means the max string possible\n  *\n  * If the string is valid the *dest pointer is set to the redis object\n  * that will be used for the comparision, and ex will be set to 0 or 1\n  * respectively if the item is exclusive or inclusive. VR_OK will be\n  * returned.\n  *\n  * If the string is not a valid range VR_ERROR is returned, and the value\n  * of *dest and *ex is undefined. */\nint zslParseLexRangeItem(robj *item, robj **dest, int *ex) {\n    char *c = item->ptr;\n\n    switch(c[0]) {\n    case '+':\n        if (c[1] != '\\0') return VR_ERROR;\n        *ex = 0;\n        *dest = dupStringObjectUnconstant(shared.maxstring);\n        return VR_OK;\n    case '-':\n        if (c[1] != '\\0') return VR_ERROR;\n        *ex = 0;\n        *dest = dupStringObjectUnconstant(shared.minstring);\n        return VR_OK;\n    case '(':\n        *ex = 1;\n        *dest = createStringObject(c+1,sdslen(c)-1);\n        return VR_OK;\n    case '[':\n        *ex = 0;\n        *dest = createStringObject(c+1,sdslen(c)-1);\n        return VR_OK;\n    default:\n        return VR_ERROR;\n    }\n}\n\n/* Populate the rangespec according to the objects min and max.\n *\n * Return VR_OK on success. On error VR_ERROR is returned.\n * When OK is returned the structure must be freed with zslFreeLexRange(),\n * otherwise no release is needed. */\nstatic int zslParseLexRange(robj *min, robj *max, zlexrangespec *spec) {\n    /* The range can't be valid if objects are integer encoded.\n     * Every item must start with ( or [. */\n    if (min->encoding == OBJ_ENCODING_INT ||\n        max->encoding == OBJ_ENCODING_INT) return VR_ERROR;\n\n    spec->min = spec->max = NULL;\n    if (zslParseLexRangeItem(min, &spec->min, &spec->minex) == VR_ERROR ||\n        zslParseLexRangeItem(max, &spec->max, &spec->maxex) == VR_ERROR) {\n        if (spec->min) freeObject(spec->min);\n        if (spec->max) freeObject(spec->max);\n        return VR_ERROR;\n    } else {\n        return VR_OK;\n    }\n}\n\n/* Free a lex range structure, must be called only after zelParseLexRange()\n * populated the structure with success (VR_OK returned). */\nvoid zslFreeLexRange(zlexrangespec *spec) {\n    freeObject(spec->min);\n    freeObject(spec->max);\n}\n\n/* This is just a wrapper to compareStringObjects() that is able to\n * handle shared.minstring and shared.maxstring as the equivalent of\n * -inf and +inf for strings */\nint compareStringObjectsForLexRange(robj *a, robj *b) {\n    if (a == b) return 0; /* This makes sure that we handle inf,inf and\n                             -inf,-inf ASAP. One special case less. */\n    if (a == shared.minstring || b == shared.maxstring) return -1;\n    if (a == shared.maxstring || b == shared.minstring) return 1;\n    return compareStringObjects(a,b);\n}\n\nstatic int zslLexValueGteMin(robj *value, zlexrangespec *spec) {\n    return spec->minex ?\n        (compareStringObjectsForLexRange(value,spec->min) > 0) :\n        (compareStringObjectsForLexRange(value,spec->min) >= 0);\n}\n\nstatic int zslLexValueLteMax(robj *value, zlexrangespec *spec) {\n    return spec->maxex ?\n        (compareStringObjectsForLexRange(value,spec->max) < 0) :\n        (compareStringObjectsForLexRange(value,spec->max) <= 0);\n}\n\n/* Returns if there is a part of the zset is in the lex range. */\nint zslIsInLexRange(zskiplist *zsl, zlexrangespec *range) {\n    zskiplistNode *x;\n\n    /* Test for ranges that will always be empty. */\n    if (compareStringObjectsForLexRange(range->min,range->max) > 1 ||\n            (compareStringObjects(range->min,range->max) == 0 &&\n            (range->minex || range->maxex)))\n        return 0;\n    x = zsl->tail;\n    if (x == NULL || !zslLexValueGteMin(x->obj,range))\n        return 0;\n    x = zsl->header->level[0].forward;\n    if (x == NULL || !zslLexValueLteMax(x->obj,range))\n        return 0;\n    return 1;\n}\n\n/* Find the first node that is contained in the specified lex range.\n * Returns NULL when no element is contained in the range. */\nzskiplistNode *zslFirstInLexRange(zskiplist *zsl, zlexrangespec *range) {\n    zskiplistNode *x;\n    int i;\n\n    /* If everything is out of range, return early. */\n    if (!zslIsInLexRange(zsl,range)) return NULL;\n\n    x = zsl->header;\n    for (i = zsl->level-1; i >= 0; i--) {\n        /* Go forward while *OUT* of range. */\n        while (x->level[i].forward &&\n            !zslLexValueGteMin(x->level[i].forward->obj,range))\n                x = x->level[i].forward;\n    }\n\n    /* This is an inner range, so the next node cannot be NULL. */\n    x = x->level[0].forward;\n    ASSERT(x != NULL);\n\n    /* Check if score <= max. */\n    if (!zslLexValueLteMax(x->obj,range)) return NULL;\n    return x;\n}\n\n/* Find the last node that is contained in the specified range.\n * Returns NULL when no element is contained in the range. */\nzskiplistNode *zslLastInLexRange(zskiplist *zsl, zlexrangespec *range) {\n    zskiplistNode *x;\n    int i;\n\n    /* If everything is out of range, return early. */\n    if (!zslIsInLexRange(zsl,range)) return NULL;\n\n    x = zsl->header;\n    for (i = zsl->level-1; i >= 0; i--) {\n        /* Go forward while *IN* range. */\n        while (x->level[i].forward &&\n            zslLexValueLteMax(x->level[i].forward->obj,range))\n                x = x->level[i].forward;\n    }\n\n    /* This is an inner range, so this node cannot be NULL. */\n    ASSERT(x != NULL);\n\n    /* Check if score >= min. */\n    if (!zslLexValueGteMin(x->obj,range)) return NULL;\n    return x;\n}\n\n/*-----------------------------------------------------------------------------\n * Ziplist-backed sorted set API\n *----------------------------------------------------------------------------*/\n\ndouble zzlGetScore(unsigned char *sptr) {\n    int ret;\n    unsigned char *vstr;\n    unsigned int vlen;\n    long long vlong;\n    char buf[128];\n    double score;\n\n    ASSERT(sptr != NULL);\n    \n    ret = (int)ziplistGet(sptr,&vstr,&vlen,&vlong);\n    ASSERT(ret > 0);\n\n    if (vstr) {\n        memcpy(buf,vstr,vlen);\n        buf[vlen] = '\\0';\n        score = strtod(buf,NULL);\n    } else {\n        score = vlong;\n    }\n\n    return score;\n}\n\n/* Return a ziplist element as a Redis string object.\n * This simple abstraction can be used to simplifies some code at the\n * cost of some performance. */\nrobj *ziplistGetObject(unsigned char *sptr) {\n    int ret;\n    unsigned char *vstr;\n    unsigned int vlen;\n    long long vlong;\n\n    ASSERT(sptr != NULL);\n\n    ret = (int)ziplistGet(sptr,&vstr,&vlen,&vlong);\n    ASSERT(ret > 0);\n\n    if (vstr) {\n        return createStringObject((char*)vstr,vlen);\n    } else {\n        return createStringObjectFromLongLong(vlong);\n    }\n}\n\n/* Compare element in sorted set with given element. */\nint zzlCompareElements(unsigned char *eptr, unsigned char *cstr, unsigned int clen) {\n    int ret;\n    unsigned char *vstr;\n    unsigned int vlen;\n    long long vlong;\n    unsigned char vbuf[32];\n    int minlen, cmp;\n\n    ret = (int)ziplistGet(eptr,&vstr,&vlen,&vlong);\n    ASSERT(ret > 0);\n    if (vstr == NULL) {\n        /* Store string representation of long long in buf. */\n        vlen = ll2string((char*)vbuf,sizeof(vbuf),vlong);\n        vstr = vbuf;\n    }\n\n    minlen = (vlen < clen) ? vlen : clen;\n    cmp = memcmp(vstr,cstr,minlen);\n    if (cmp == 0) return vlen-clen;\n    return cmp;\n}\n\nunsigned int zzlLength(unsigned char *zl) {\n    return ziplistLen(zl)/2;\n}\n\n/* Move to next entry based on the values in eptr and sptr. Both are set to\n * NULL when there is no next entry. */\nvoid zzlNext(unsigned char *zl, unsigned char **eptr, unsigned char **sptr) {\n    unsigned char *_eptr, *_sptr;\n    ASSERT(*eptr != NULL && *sptr != NULL);\n\n    _eptr = ziplistNext(zl,*sptr);\n    if (_eptr != NULL) {\n        _sptr = ziplistNext(zl,_eptr);\n        ASSERT(_sptr != NULL);\n    } else {\n        /* No next entry. */\n        _sptr = NULL;\n    }\n\n    *eptr = _eptr;\n    *sptr = _sptr;\n}\n\n/* Move to the previous entry based on the values in eptr and sptr. Both are\n * set to NULL when there is no next entry. */\nvoid zzlPrev(unsigned char *zl, unsigned char **eptr, unsigned char **sptr) {\n    unsigned char *_eptr, *_sptr;\n    ASSERT(*eptr != NULL && *sptr != NULL);\n\n    _sptr = ziplistPrev(zl,*eptr);\n    if (_sptr != NULL) {\n        _eptr = ziplistPrev(zl,_sptr);\n        ASSERT(_eptr != NULL);\n    } else {\n        /* No previous entry. */\n        _eptr = NULL;\n    }\n\n    *eptr = _eptr;\n    *sptr = _sptr;\n}\n\n/* Returns if there is a part of the zset is in range. Should only be used\n * internally by zzlFirstInRange and zzlLastInRange. */\nint zzlIsInRange(unsigned char *zl, zrangespec *range) {\n    unsigned char *p;\n    double score;\n\n    /* Test for ranges that will always be empty. */\n    if (range->min > range->max ||\n            (range->min == range->max && (range->minex || range->maxex)))\n        return 0;\n\n    p = ziplistIndex(zl,-1); /* Last score. */\n    if (p == NULL) return 0; /* Empty sorted set */\n    score = zzlGetScore(p);\n    if (!zslValueGteMin(score,range))\n        return 0;\n\n    p = ziplistIndex(zl,1); /* First score. */\n    ASSERT(p != NULL);\n    score = zzlGetScore(p);\n    if (!zslValueLteMax(score,range))\n        return 0;\n\n    return 1;\n}\n\n/* Find pointer to the first element contained in the specified range.\n * Returns NULL when no element is contained in the range. */\nunsigned char *zzlFirstInRange(unsigned char *zl, zrangespec *range) {\n    unsigned char *eptr = ziplistIndex(zl,0), *sptr;\n    double score;\n\n    /* If everything is out of range, return early. */\n    if (!zzlIsInRange(zl,range)) return NULL;\n\n    while (eptr != NULL) {\n        sptr = ziplistNext(zl,eptr);\n        ASSERT(sptr != NULL);\n\n        score = zzlGetScore(sptr);\n        if (zslValueGteMin(score,range)) {\n            /* Check if score <= max. */\n            if (zslValueLteMax(score,range))\n                return eptr;\n            return NULL;\n        }\n\n        /* Move to next element. */\n        eptr = ziplistNext(zl,sptr);\n    }\n\n    return NULL;\n}\n\n/* Find pointer to the last element contained in the specified range.\n * Returns NULL when no element is contained in the range. */\nunsigned char *zzlLastInRange(unsigned char *zl, zrangespec *range) {\n    unsigned char *eptr = ziplistIndex(zl,-2), *sptr;\n    double score;\n\n    /* If everything is out of range, return early. */\n    if (!zzlIsInRange(zl,range)) return NULL;\n\n    while (eptr != NULL) {\n        sptr = ziplistNext(zl,eptr);\n        ASSERT(sptr != NULL);\n\n        score = zzlGetScore(sptr);\n        if (zslValueLteMax(score,range)) {\n            /* Check if score >= min. */\n            if (zslValueGteMin(score,range))\n                return eptr;\n            return NULL;\n        }\n\n        /* Move to previous element by moving to the score of previous element.\n         * When this returns NULL, we know there also is no element. */\n        sptr = ziplistPrev(zl,eptr);\n        if (sptr != NULL) {\n            eptr = ziplistPrev(zl,sptr);\n            ASSERT(eptr != NULL);\n        } else {\n            eptr = NULL;\n        }\n    }\n\n    return NULL;\n}\n\nstatic int zzlLexValueGteMin(unsigned char *p, zlexrangespec *spec) {\n    robj *value = ziplistGetObject(p);\n    int res = zslLexValueGteMin(value,spec);\n    freeObject(value);\n    return res;\n}\n\nstatic int zzlLexValueLteMax(unsigned char *p, zlexrangespec *spec) {\n    robj *value = ziplistGetObject(p);\n    int res = zslLexValueLteMax(value,spec);\n    freeObject(value);\n    return res;\n}\n\n/* Returns if there is a part of the zset is in range. Should only be used\n * internally by zzlFirstInRange and zzlLastInRange. */\nint zzlIsInLexRange(unsigned char *zl, zlexrangespec *range) {\n    unsigned char *p;\n\n    /* Test for ranges that will always be empty. */\n    if (compareStringObjectsForLexRange(range->min,range->max) > 1 ||\n            (compareStringObjects(range->min,range->max) == 0 &&\n            (range->minex || range->maxex)))\n        return 0;\n\n    p = ziplistIndex(zl,-2); /* Last element. */\n    if (p == NULL) return 0;\n    if (!zzlLexValueGteMin(p,range))\n        return 0;\n\n    p = ziplistIndex(zl,0); /* First element. */\n    ASSERT(p != NULL);\n    if (!zzlLexValueLteMax(p,range))\n        return 0;\n\n    return 1;\n}\n\n/* Find pointer to the first element contained in the specified lex range.\n * Returns NULL when no element is contained in the range. */\nunsigned char *zzlFirstInLexRange(unsigned char *zl, zlexrangespec *range) {\n    unsigned char *eptr = ziplistIndex(zl,0), *sptr;\n\n    /* If everything is out of range, return early. */\n    if (!zzlIsInLexRange(zl,range)) return NULL;\n\n    while (eptr != NULL) {\n        if (zzlLexValueGteMin(eptr,range)) {\n            /* Check if score <= max. */\n            if (zzlLexValueLteMax(eptr,range))\n                return eptr;\n            return NULL;\n        }\n\n        /* Move to next element. */\n        sptr = ziplistNext(zl,eptr); /* This element score. Skip it. */\n        ASSERT(sptr != NULL);\n        eptr = ziplistNext(zl,sptr); /* Next element. */\n    }\n\n    return NULL;\n}\n\n/* Find pointer to the last element contained in the specified lex range.\n * Returns NULL when no element is contained in the range. */\nunsigned char *zzlLastInLexRange(unsigned char *zl, zlexrangespec *range) {\n    unsigned char *eptr = ziplistIndex(zl,-2), *sptr;\n\n    /* If everything is out of range, return early. */\n    if (!zzlIsInLexRange(zl,range)) return NULL;\n\n    while (eptr != NULL) {\n        if (zzlLexValueLteMax(eptr,range)) {\n            /* Check if score >= min. */\n            if (zzlLexValueGteMin(eptr,range))\n                return eptr;\n            return NULL;\n        }\n\n        /* Move to previous element by moving to the score of previous element.\n         * When this returns NULL, we know there also is no element. */\n        sptr = ziplistPrev(zl,eptr);\n        if (sptr != NULL) {\n            eptr = ziplistPrev(zl,sptr);\n            ASSERT(eptr != NULL);\n        } else {\n            eptr = NULL;\n        }\n    }\n\n    return NULL;\n}\n\nunsigned char *zzlFind(unsigned char *zl, robj *ele, double *score) {\n    unsigned char *eptr = ziplistIndex(zl,0), *sptr;\n    robj *ele_new;\n\n    ele_new = getDecodedObject(ele);\n    while (eptr != NULL) {\n        sptr = ziplistNext(zl,eptr);\n        serverAssertWithInfo(NULL,ele_new,sptr != NULL);\n\n        if (ziplistCompare(eptr,ele_new->ptr,sdslen(ele_new->ptr))) {\n            /* Matching element, pull out score. */\n            if (score != NULL) *score = zzlGetScore(sptr);\n            if (ele_new!= ele) freeObject(ele_new);\n            return eptr;\n        }\n\n        /* Move to next element. */\n        eptr = ziplistNext(zl,sptr);\n    }\n\n    if (ele_new!= ele) freeObject(ele_new);\n    return NULL;\n}\n\n/* Delete (element,score) pair from ziplist. Use local copy of eptr because we\n * don't want to modify the one given as argument. */\nunsigned char *zzlDelete(unsigned char *zl, unsigned char *eptr) {\n    unsigned char *p = eptr;\n\n    /* TODO: add function to ziplist API to delete N elements from offset. */\n    zl = ziplistDelete(zl,&p);\n    zl = ziplistDelete(zl,&p);\n    return zl;\n}\n\nunsigned char *zzlInsertAt(unsigned char *zl, unsigned char *eptr, robj *ele, double score) {\n    unsigned char *sptr;\n    char scorebuf[128];\n    int scorelen;\n    size_t offset;\n\n    serverAssertWithInfo(NULL,ele,sdsEncodedObject(ele));\n    scorelen = d2string(scorebuf,sizeof(scorebuf),score);\n    if (eptr == NULL) {\n        zl = ziplistPush(zl,ele->ptr,sdslen(ele->ptr),ZIPLIST_TAIL);\n        zl = ziplistPush(zl,(unsigned char*)scorebuf,scorelen,ZIPLIST_TAIL);\n    } else {\n        /* Keep offset relative to zl, as it might be re-allocated. */\n        offset = eptr-zl;\n        zl = ziplistInsert(zl,eptr,ele->ptr,sdslen(ele->ptr));\n        eptr = zl+offset;\n\n        /* Insert score after the element. */\n        serverAssertWithInfo(NULL,ele,(sptr = ziplistNext(zl,eptr)) != NULL);\n        zl = ziplistInsert(zl,sptr,(unsigned char*)scorebuf,scorelen);\n    }\n\n    return zl;\n}\n\n/* Insert (element,score) pair in ziplist. This function assumes the element is\n * not yet present in the list. */\nunsigned char *zzlInsert(unsigned char *zl, robj *ele, double score) {\n    unsigned char *eptr = ziplistIndex(zl,0), *sptr;\n    double s;\n    robj *ele_new;\n\n    ele_new = getDecodedObject(ele);\n    while (eptr != NULL) {\n        sptr = ziplistNext(zl,eptr);\n        serverAssertWithInfo(NULL,ele_new,sptr != NULL);\n        s = zzlGetScore(sptr);\n\n        if (s > score) {\n            /* First element with score larger than score for element to be\n             * inserted. This means we should take its spot in the list to\n             * maintain ordering. */\n            zl = zzlInsertAt(zl,eptr,ele_new,score);\n            break;\n        } else if (s == score) {\n            /* Ensure lexicographical ordering for elements. */\n            if (zzlCompareElements(eptr,ele_new->ptr,sdslen(ele_new->ptr)) > 0) {\n                zl = zzlInsertAt(zl,eptr,ele_new,score);\n                break;\n            }\n        }\n\n        /* Move to next element. */\n        eptr = ziplistNext(zl,sptr);\n    }\n\n    /* Push on tail of list when it was not yet inserted. */\n    if (eptr == NULL)\n        zl = zzlInsertAt(zl,NULL,ele_new,score);\n\n    if (ele_new != ele) freeObject(ele_new);\n    return zl;\n}\n\nunsigned char *zzlDeleteRangeByScore(unsigned char *zl, zrangespec *range, unsigned long *deleted) {\n    unsigned char *eptr, *sptr;\n    double score;\n    unsigned long num = 0;\n\n    if (deleted != NULL) *deleted = 0;\n\n    eptr = zzlFirstInRange(zl,range);\n    if (eptr == NULL) return zl;\n\n    /* When the tail of the ziplist is deleted, eptr will point to the sentinel\n     * byte and ziplistNext will return NULL. */\n    while ((sptr = ziplistNext(zl,eptr)) != NULL) {\n        score = zzlGetScore(sptr);\n        if (zslValueLteMax(score,range)) {\n            /* Delete both the element and the score. */\n            zl = ziplistDelete(zl,&eptr);\n            zl = ziplistDelete(zl,&eptr);\n            num++;\n        } else {\n            /* No longer in range. */\n            break;\n        }\n    }\n\n    if (deleted != NULL) *deleted = num;\n    return zl;\n}\n\nunsigned char *zzlDeleteRangeByLex(unsigned char *zl, zlexrangespec *range, unsigned long *deleted) {\n    unsigned char *eptr, *sptr;\n    unsigned long num = 0;\n\n    if (deleted != NULL) *deleted = 0;\n\n    eptr = zzlFirstInLexRange(zl,range);\n    if (eptr == NULL) return zl;\n\n    /* When the tail of the ziplist is deleted, eptr will point to the sentinel\n     * byte and ziplistNext will return NULL. */\n    while ((sptr = ziplistNext(zl,eptr)) != NULL) {\n        if (zzlLexValueLteMax(eptr,range)) {\n            /* Delete both the element and the score. */\n            zl = ziplistDelete(zl,&eptr);\n            zl = ziplistDelete(zl,&eptr);\n            num++;\n        } else {\n            /* No longer in range. */\n            break;\n        }\n    }\n\n    if (deleted != NULL) *deleted = num;\n    return zl;\n}\n\n/* Delete all the elements with rank between start and end from the skiplist.\n * Start and end are inclusive. Note that start and end need to be 1-based */\nunsigned char *zzlDeleteRangeByRank(unsigned char *zl, unsigned int start, unsigned int end, unsigned long *deleted) {\n    unsigned int num = (end-start)+1;\n    if (deleted) *deleted = num;\n    zl = ziplistDeleteRange(zl,2*(start-1),2*num);\n    return zl;\n}\n\n/*-----------------------------------------------------------------------------\n * Common sorted set API\n *----------------------------------------------------------------------------*/\n\nunsigned int zsetLength(robj *zobj) {\n    int length = -1;\n    if (zobj->encoding == OBJ_ENCODING_ZIPLIST) {\n        length = zzlLength(zobj->ptr);\n    } else if (zobj->encoding == OBJ_ENCODING_SKIPLIST) {\n        length = ((zset*)zobj->ptr)->zsl->length;\n    } else {\n        serverPanic(\"Unknown sorted set encoding\");\n    }\n    return length;\n}\n\nvoid zsetConvert(robj *zobj, int encoding) {\n    zset *zs;\n    zskiplistNode *node, *next;\n    robj *ele;\n    double score;\n\n    if (zobj->encoding == encoding) return;\n    if (zobj->encoding == OBJ_ENCODING_ZIPLIST) {\n        unsigned char *zl = zobj->ptr;\n        unsigned char *eptr, *sptr;\n        unsigned char *vstr;\n        unsigned int vlen;\n        long long vlong;\n\n        if (encoding != OBJ_ENCODING_SKIPLIST)\n            serverPanic(\"Unknown target encoding\");\n\n        zs = dalloc(sizeof(*zs));\n        zs->dict = dictCreate(&zsetDictType,NULL);\n        zs->zsl = zslCreate();\n\n        eptr = ziplistIndex(zl,0);\n        serverAssertWithInfo(NULL,zobj,eptr != NULL);\n        sptr = ziplistNext(zl,eptr);\n        serverAssertWithInfo(NULL,zobj,sptr != NULL);\n\n        while (eptr != NULL) {\n            score = zzlGetScore(sptr);\n            serverAssertWithInfo(NULL,zobj,ziplistGet(eptr,&vstr,&vlen,&vlong));\n            if (vstr == NULL)\n                ele = createStringObjectFromLongLong(vlong);\n            else\n                ele = createStringObject((char*)vstr,vlen);\n\n            /* Has incremented refcount since it was just created. */\n            node = zslInsert(zs->zsl,score,ele);\n            serverAssertWithInfo(NULL,zobj,dictAdd(zs->dict,ele,&node->score) == DICT_OK);\n            zzlNext(zl,&eptr,&sptr);\n        }\n\n        dfree(zobj->ptr);\n        zobj->ptr = zs;\n        zobj->encoding = OBJ_ENCODING_SKIPLIST;\n    } else if (zobj->encoding == OBJ_ENCODING_SKIPLIST) {\n        unsigned char *zl = ziplistNew();\n\n        if (encoding != OBJ_ENCODING_ZIPLIST)\n            serverPanic(\"Unknown target encoding\");\n\n        /* Approach similar to zslFree(), since we want to free the skiplist at\n         * the same time as creating the ziplist. */\n        zs = zobj->ptr;\n        node = zs->zsl->header->level[0].forward;\n        dfree(zs->zsl->header);\n        dfree(zs->zsl);\n\n        while (node) {\n            ele = getDecodedObject(node->obj);\n            zl = zzlInsertAt(zl,NULL,ele,node->score);\n            if (ele != node->obj) freeObject(ele);\n            next = node->level[0].forward;\n            zslFreeNode(node);\n            node = next;\n        }\n\n        dictRelease(zs->dict);\n        dfree(zs);\n        zobj->ptr = zl;\n        zobj->encoding = OBJ_ENCODING_ZIPLIST;\n    } else {\n        serverPanic(\"Unknown sorted set encoding\");\n    }\n}\n\n/* Convert the sorted set object into a ziplist if it is not already a ziplist\n * and if the number of elements and the maximum element size is within the\n * expected ranges. */\nvoid zsetConvertToZiplistIfNeeded(robj *zobj, size_t maxelelen) {\n    if (zobj->encoding == OBJ_ENCODING_ZIPLIST) return;\n    zset *zset = zobj->ptr;\n\n    if (zset->zsl->length <= server.zset_max_ziplist_entries &&\n        maxelelen <= server.zset_max_ziplist_value)\n            zsetConvert(zobj,OBJ_ENCODING_ZIPLIST);\n}\n\n/* Return (by reference) the score of the specified member of the sorted set\n * storing it into *score. If the element does not exist VR_ERROR is returned\n * otherwise VR_OK is returned and *score is correctly populated.\n * If 'zobj' or 'member' is NULL, VR_ERROR is returned. */\nint zsetScore(robj *zobj, robj *member, double *score) {\n    if (!zobj || !member) return VR_ERROR;\n\n    if (zobj->encoding == OBJ_ENCODING_ZIPLIST) {\n        if (zzlFind(zobj->ptr, member, score) == NULL) return VR_ERROR;\n    } else if (zobj->encoding == OBJ_ENCODING_SKIPLIST) {\n        zset *zs = zobj->ptr;\n        dictEntry *de = dictFind(zs->dict, member);\n        if (de == NULL) return VR_ERROR;\n        *score = *(double*)dictGetVal(de);\n    } else {\n        serverPanic(\"Unknown sorted set encoding\");\n    }\n    return VR_OK;\n}\n\n/*-----------------------------------------------------------------------------\n * Sorted set commands\n *----------------------------------------------------------------------------*/\n\n/* This generic command implements both ZADD and ZINCRBY. */\n#define ZADD_NONE 0\n#define ZADD_INCR (1<<0)    /* Increment the score instead of setting it. */\n#define ZADD_NX (1<<1)      /* Don't touch elements not already existing. */\n#define ZADD_XX (1<<2)      /* Only touch elements already exisitng. */\n#define ZADD_CH (1<<3)      /* Return num of elements added or updated. */\n\nvoid zaddGenericCommand(client *c, int flags) {\n    static char *nanerr = \"resulting score is not a number (NaN)\";\n    robj *key = c->argv[1];\n    robj *ele;\n    robj *zobj;\n    robj *curobj;\n    double score = 0, *scores = NULL, curscore = 0.0;\n    int j, elements;\n    int scoreidx = 0;\n    /* The following vars are used in order to track what the command actually\n     * did during the execution, to reply to the client and to trigger the\n     * notification of keyspace change. */\n    int added = 0;      /* Number of new elements added. */\n    int updated = 0;    /* Number of elements with updated score. */\n    int processed = 0;  /* Number of elements processed, may remain zero with\n                           options like XX. */\n    int expired = 0;\n\n    /* Parse options. At the end 'scoreidx' is set to the argument position\n     * of the score of the first score-element pair. */\n    scoreidx = 2;\n    while(scoreidx < c->argc) {\n        char *opt = c->argv[scoreidx]->ptr;\n        if (!strcasecmp(opt,\"nx\")) flags |= ZADD_NX;\n        else if (!strcasecmp(opt,\"xx\")) flags |= ZADD_XX;\n        else if (!strcasecmp(opt,\"ch\")) flags |= ZADD_CH;\n        else if (!strcasecmp(opt,\"incr\")) flags |= ZADD_INCR;\n        else break;\n        scoreidx++;\n    }\n\n    /* Turn options into simple to check vars. */\n    int incr = (flags & ZADD_INCR) != 0;\n    int nx = (flags & ZADD_NX) != 0;\n    int xx = (flags & ZADD_XX) != 0;\n    int ch = (flags & ZADD_CH) != 0;\n\n    /* After the options, we expect to have an even number of args, since\n     * we expect any number of score-element pairs. */\n    elements = c->argc-scoreidx;\n    if (elements % 2) {\n        addReply(c,shared.syntaxerr);\n        return;\n    }\n    elements /= 2; /* Now this holds the number of score-element pairs. */\n\n    /* Check for incompatible options. */\n    if (nx && xx) {\n        addReplyError(c,\n            \"XX and NX options at the same time are not compatible\");\n        return;\n    }\n\n    if (incr && elements > 1) {\n        addReplyError(c,\n            \"INCR option supports a single increment-element pair\");\n        return;\n    }\n\n    /* Start parsing all the scores, we need to emit any syntax error\n     * before executing additions to the sorted set, as the command should\n     * either execute fully or nothing at all. */\n    scores = dalloc(sizeof(double)*elements);\n    for (j = 0; j < elements; j++) {\n        if (getDoubleFromObjectOrReply(c,c->argv[scoreidx+j*2],&scores[j],NULL)\n            != VR_OK) goto cleanup;\n    }\n\r   \n    fetchInternalDbByKey(c, key);\n    lockDbWrite(c->db);\n    /* Lookup the key and create the sorted set if does not exist. */\n    zobj = lookupKeyWrite(c->db,key,&expired);\n    if (zobj == NULL) {\n        if (xx) {\n            unlockDb(c->db);\n            if (expired) update_stats_add(c->vel->stats,expiredkeys,1);\n            goto reply_to_client; /* No key + XX option: nothing to do. */\n        }\n        if (server.zset_max_ziplist_entries == 0 ||\n            server.zset_max_ziplist_value < sdslen(c->argv[scoreidx+1]->ptr))\n        {\n            zobj = createZsetObject();\n        } else {\n            zobj = createZsetZiplistObject();\n        }\n        dbAdd(c->db,key,zobj);\n    } else {\n        if (zobj->type != OBJ_ZSET) {\n            unlockDb(c->db);\n            if (expired) update_stats_add(c->vel->stats,expiredkeys,1);\n            addReply(c,shared.wrongtypeerr);\n            goto cleanup;\n        }\n    }\n\n    for (j = 0; j < elements; j++) {\n        score = scores[j];\n\n        if (zobj->encoding == OBJ_ENCODING_ZIPLIST) {\n            unsigned char *eptr;\n\n            /* Prefer non-encoded element when dealing with ziplists. */\n            ele = c->argv[scoreidx+1+j*2];\n            if ((eptr = zzlFind(zobj->ptr,ele,&curscore)) != NULL) {\n                if (nx) continue;\n                if (incr) {\n                    score += curscore;\n                    if (isnan(score)) {\n                        unlockDb(c->db);\n                        if (expired) update_stats_add(c->vel->stats,expiredkeys,1);\n                        addReplyError(c,nanerr);\n                        goto cleanup;\n                    }\n                }\n\n                /* Remove and re-insert when score changed. */\n                if (score != curscore) {\n                    zobj->ptr = zzlDelete(zobj->ptr,eptr);\n                    zobj->ptr = zzlInsert(zobj->ptr,ele,score);\n                    c->vel->dirty++;\n                    updated++;\n                }\n                processed++;\n            } else if (!xx) {\n                /* Optimize: check if the element is too large or the list\n                 * becomes too long *before* executing zzlInsert. */\n                zobj->ptr = zzlInsert(zobj->ptr,ele,score);\n                if (zzlLength(zobj->ptr) > server.zset_max_ziplist_entries)\n                    zsetConvert(zobj,OBJ_ENCODING_SKIPLIST);\n                if (sdslen(ele->ptr) > server.zset_max_ziplist_value)\n                    zsetConvert(zobj,OBJ_ENCODING_SKIPLIST);\n                c->vel->dirty++;\n                added++;\n                processed++;\n            }\n        } else if (zobj->encoding == OBJ_ENCODING_SKIPLIST) {\n            zset *zs = zobj->ptr;\n            zskiplistNode *znode;\n            dictEntry *de;\n\n            ele = c->argv[scoreidx+1+j*2] =\n                tryObjectEncoding(c->argv[scoreidx+1+j*2]);\n            de = dictFind(zs->dict,ele);\n            if (de != NULL) {\n                if (nx) continue;\n                curobj = dictGetKey(de);\n                curscore = *(double*)dictGetVal(de);\n\n                if (incr) {\n                    score += curscore;\n                    if (isnan(score)) {\n                        unlockDb(c->db);\n                        if (expired) update_stats_add(c->vel->stats,expiredkeys,1);\n                        addReplyError(c,nanerr);\n                        /* Don't need to check if the sorted set is empty\n                         * because we know it has at least one element. */\n                        goto cleanup;\n                    }\n                }\n\n                /* Remove and re-insert when score changed. We can safely\n                 * delete the key object from the skiplist, since the\n                 * dictionary still has a reference to it. */\n                if (score != curscore) {\n                    serverAssertWithInfo(c,curobj,zslDelete(zs->zsl,curscore,curobj));\n                    znode = zslInsert(zs->zsl,score,curobj);\n                    dictGetVal(de) = &znode->score; /* Update score ptr. */\n                    c->vel->dirty++;\n                    updated++;\n                }\n                processed++;\n            } else if (!xx) {\n                ele = dupStringObjectUnconstant(ele);\n                znode = zslInsert(zs->zsl,score,ele);\n                serverAssertWithInfo(c,NULL,dictAdd(zs->dict,ele,&znode->score) == DICT_OK);\n                c->vel->dirty++;\n                added++;\n                processed++;\n            }\n        } else {\n            serverPanic(\"Unknown sorted set encoding\");\n        }\n    }\n\n    unlockDb(c->db);\n    if (expired) update_stats_add(c->vel->stats,expiredkeys,1);\n\nreply_to_client:\n    if (incr) { /* ZINCRBY or INCR option. */\n        if (processed)\n            addReplyDouble(c,score);\n        else\n            addReply(c,shared.nullbulk);\n    } else { /* ZADD. */\n        addReplyLongLong(c,ch ? added+updated : added);\n    }\n\ncleanup:\n    dfree(scores);\n    if (added || updated) {\n        signalModifiedKey(c->db,key);\n        notifyKeyspaceEvent(NOTIFY_ZSET,\n            incr ? \"zincr\" : \"zadd\", key, c->db->id);\n    }\n}\n\nvoid zaddCommand(client *c) {\n    zaddGenericCommand(c,ZADD_NONE);\n}\n\nvoid zincrbyCommand(client *c) {\n    zaddGenericCommand(c,ZADD_INCR);\n}\n\nvoid zremCommand(client *c) {\n    robj *key = c->argv[1];\n    robj *zobj;\n    int deleted = 0, keyremoved = 0, j;\n    int expired = 0;\n\n    fetchInternalDbByKey(c, key);\n    lockDbWrite(c->db);\n    if ((zobj = lookupKeyWriteOrReply(c,key,shared.czero,&expired)) == NULL ||\n        checkType(c,zobj,OBJ_ZSET)) {\n        unlockDb(c->db);\n        if (expired) update_stats_add(c->vel->stats,expiredkeys,1);\n        return;\n    }\n\n    if (zobj->encoding == OBJ_ENCODING_ZIPLIST) {\n        unsigned char *eptr;\n\n        for (j = 2; j < c->argc; j++) {\n            if ((eptr = zzlFind(zobj->ptr,c->argv[j],NULL)) != NULL) {\n                deleted++;\n                zobj->ptr = zzlDelete(zobj->ptr,eptr);\n                if (zzlLength(zobj->ptr) == 0) {\n                    dbDelete(c->db,key);\n                    keyremoved = 1;\n                    break;\n                }\n            }\n        }\n    } else if (zobj->encoding == OBJ_ENCODING_SKIPLIST) {\n        zset *zs = zobj->ptr;\n        dictEntry *de;\n        double score;\n\n        for (j = 2; j < c->argc; j++) {\n            de = dictFind(zs->dict,c->argv[j]);\n            if (de != NULL) {\n                deleted++;\n\n                /* Delete from the skiplist */\n                score = *(double*)dictGetVal(de);\n                serverAssertWithInfo(c,c->argv[j],zslDelete(zs->zsl,score,c->argv[j]));\n\n                /* Delete from the hash table */\n                dictDelete(zs->dict,c->argv[j]);\n                if (htNeedsResize(zs->dict)) dictResize(zs->dict);\n                if (dictSize(zs->dict) == 0) {\n                    dbDelete(c->db,key);\n                    keyremoved = 1;\n                    break;\n                }\n            }\n        }\n    } else {\n        serverPanic(\"Unknown sorted set encoding\");\n    }\n\n    if (deleted) {\n        notifyKeyspaceEvent(NOTIFY_ZSET,\"zrem\",key,c->db->id);\n        if (keyremoved)\n            notifyKeyspaceEvent(NOTIFY_GENERIC,\"del\",key,c->db->id);\n        signalModifiedKey(c->db,key);\n        c->vel->dirty += deleted;\n    }\n    addReplyLongLong(c,deleted);\n    unlockDb(c->db);\n    if (expired) update_stats_add(c->vel->stats,expiredkeys,1);\n}\n\n/* Implements ZREMRANGEBYRANK, ZREMRANGEBYSCORE, ZREMRANGEBYLEX commands. */\n#define ZRANGE_RANK 0\n#define ZRANGE_SCORE 1\n#define ZRANGE_LEX 2\nvoid zremrangeGenericCommand(client *c, int rangetype) {\n    robj *key = c->argv[1];\n    robj *zobj;\n    int keyremoved = 0;\n    unsigned long deleted = 0;\n    zrangespec range;\n    zlexrangespec lexrange;\n    long start, end, llen;\n    int expired = 0;\n\n    /* Step 1: Parse the range. */\n    if (rangetype == ZRANGE_RANK) {\n        if ((getLongFromObjectOrReply(c,c->argv[2],&start,NULL) != VR_OK) ||\n            (getLongFromObjectOrReply(c,c->argv[3],&end,NULL) != VR_OK))\n            return;\n    } else if (rangetype == ZRANGE_SCORE) {\n        if (zslParseRange(c->argv[2],c->argv[3],&range) != VR_OK) {\n            addReplyError(c,\"min or max is not a float\");\n            return;\n        }\n    } else if (rangetype == ZRANGE_LEX) {\n        if (zslParseLexRange(c->argv[2],c->argv[3],&lexrange) != VR_OK) {\n            addReplyError(c,\"min or max not valid string range item\");\n            return;\n        }\n    }\n\n    fetchInternalDbByKey(c, key);\n    lockDbWrite(c->db);\n    /* Step 2: Lookup & range sanity checks if needed. */\n    if ((zobj = lookupKeyWriteOrReply(c,key,shared.czero,&expired)) == NULL ||\n        checkType(c,zobj,OBJ_ZSET)) goto cleanup;\n\n    if (rangetype == ZRANGE_RANK) {\n        /* Sanitize indexes. */\n        llen = zsetLength(zobj);\n        if (start < 0) start = llen+start;\n        if (end < 0) end = llen+end;\n        if (start < 0) start = 0;\n\n        /* Invariant: start >= 0, so this test will be true when end < 0.\n         * The range is empty when start > end or start >= length. */\n        if (start > end || start >= llen) {\n            addReply(c,shared.czero);\n            goto cleanup;\n        }\n        if (end >= llen) end = llen-1;\n    }\n\n    /* Step 3: Perform the range deletion operation. */\n    if (zobj->encoding == OBJ_ENCODING_ZIPLIST) {\n        switch(rangetype) {\n        case ZRANGE_RANK:\n            zobj->ptr = zzlDeleteRangeByRank(zobj->ptr,start+1,end+1,&deleted);\n            break;\n        case ZRANGE_SCORE:\n            zobj->ptr = zzlDeleteRangeByScore(zobj->ptr,&range,&deleted);\n            break;\n        case ZRANGE_LEX:\n            zobj->ptr = zzlDeleteRangeByLex(zobj->ptr,&lexrange,&deleted);\n            break;\n        }\n        if (zzlLength(zobj->ptr) == 0) {\n            dbDelete(c->db,key);\n            keyremoved = 1;\n        }\n    } else if (zobj->encoding == OBJ_ENCODING_SKIPLIST) {\n        zset *zs = zobj->ptr;\n        switch(rangetype) {\n        case ZRANGE_RANK:\n            deleted = zslDeleteRangeByRank(zs->zsl,start+1,end+1,zs->dict);\n            break;\n        case ZRANGE_SCORE:\n            deleted = zslDeleteRangeByScore(zs->zsl,&range,zs->dict);\n            break;\n        case ZRANGE_LEX:\n            deleted = zslDeleteRangeByLex(zs->zsl,&lexrange,zs->dict);\n            break;\n        }\n        if (htNeedsResize(zs->dict)) dictResize(zs->dict);\n        if (dictSize(zs->dict) == 0) {\n            dbDelete(c->db,key);\n            keyremoved = 1;\n        }\n    } else {\n        serverPanic(\"Unknown sorted set encoding\");\n    }\n\n    /* Step 4: Notifications and reply. */\n    if (deleted) {\n        char *event[3] = {\"zremrangebyrank\",\"zremrangebyscore\",\"zremrangebylex\"};\n        signalModifiedKey(c->db,key);\n        notifyKeyspaceEvent(NOTIFY_ZSET,event[rangetype],key,c->db->id);\n        if (keyremoved)\n            notifyKeyspaceEvent(NOTIFY_GENERIC,\"del\",key,c->db->id);\n    }\n    c->vel->dirty += deleted;\n    addReplyLongLong(c,deleted);\n\ncleanup:\n    if (rangetype == ZRANGE_LEX) zslFreeLexRange(&lexrange);\n    unlockDb(c->db);\n    if (expired) update_stats_add(c->vel->stats,expiredkeys,1);\n}\n\nvoid zremrangebyrankCommand(client *c) {\n    zremrangeGenericCommand(c,ZRANGE_RANK);\n}\n\nvoid zremrangebyscoreCommand(client *c) {\n    zremrangeGenericCommand(c,ZRANGE_SCORE);\n}\n\nvoid zremrangebylexCommand(client *c) {\n    zremrangeGenericCommand(c,ZRANGE_LEX);\n}\n\n/* Use dirty flags for pointers that need to be cleaned up in the next\n * iteration over the zsetopval. The dirty flag for the long long value is\n * special, since long long values don't need cleanup. Instead, it means that\n * we already checked that \"ell\" holds a long long, or tried to convert another\n * representation into a long long value. When this was successful,\n * OPVAL_VALID_LL is set as well. */\n#define OPVAL_DIRTY_ROBJ 1\n#define OPVAL_DIRTY_LL 2\n#define OPVAL_VALID_LL 4\n\ntypedef union _iterset iterset;\ntypedef union _iterzset iterzset;\n\nvoid zuiInitIterator(zsetopsrc *op) {\n    if (op->subject == NULL)\n        return;\n\n    if (op->type == OBJ_SET) {\n        iterset *it = &op->iter.set;\n        if (op->encoding == OBJ_ENCODING_INTSET) {\n            it->is.is = op->subject->ptr;\n            it->is.ii = 0;\n        } else if (op->encoding == OBJ_ENCODING_HT) {\n            it->ht.dict = op->subject->ptr;\n            it->ht.di = dictGetIterator(op->subject->ptr);\n            it->ht.de = dictNext(it->ht.di);\n        } else {\n            serverPanic(\"Unknown set encoding\");\n        }\n    } else if (op->type == OBJ_ZSET) {\n        iterzset *it = &op->iter.zset;\n        if (op->encoding == OBJ_ENCODING_ZIPLIST) {\n            it->zl.zl = op->subject->ptr;\n            it->zl.eptr = ziplistIndex(it->zl.zl,0);\n            if (it->zl.eptr != NULL) {\n                it->zl.sptr = ziplistNext(it->zl.zl,it->zl.eptr);\n                ASSERT(it->zl.sptr != NULL);\n            }\n        } else if (op->encoding == OBJ_ENCODING_SKIPLIST) {\n            it->sl.zs = op->subject->ptr;\n            it->sl.node = it->sl.zs->zsl->header->level[0].forward;\n        } else {\n            serverPanic(\"Unknown sorted set encoding\");\n        }\n    } else {\n        serverPanic(\"Unsupported type\");\n    }\n}\n\nvoid zuiClearIterator(zsetopsrc *op) {\n    if (op->subject == NULL)\n        return;\n\n    if (op->type == OBJ_SET) {\n        iterset *it = &op->iter.set;\n        if (op->encoding == OBJ_ENCODING_INTSET) {\n            UNUSED(it); /* skip */\n        } else if (op->encoding == OBJ_ENCODING_HT) {\n            dictReleaseIterator(it->ht.di);\n        } else {\n            serverPanic(\"Unknown set encoding\");\n        }\n    } else if (op->type == OBJ_ZSET) {\n        iterzset *it = &op->iter.zset;\n        if (op->encoding == OBJ_ENCODING_ZIPLIST) {\n            UNUSED(it); /* skip */\n        } else if (op->encoding == OBJ_ENCODING_SKIPLIST) {\n            UNUSED(it); /* skip */\n        } else {\n            serverPanic(\"Unknown sorted set encoding\");\n        }\n    } else {\n        serverPanic(\"Unsupported type\");\n    }\n}\n\nint zuiLength(zsetopsrc *op) {\n    if (op->subject == NULL)\n        return 0;\n\n    if (op->type == OBJ_SET) {\n        if (op->encoding == OBJ_ENCODING_INTSET) {\n            return intsetLen(op->subject->ptr);\n        } else if (op->encoding == OBJ_ENCODING_HT) {\n            dict *ht = op->subject->ptr;\n            return dictSize(ht);\n        } else {\n            serverPanic(\"Unknown set encoding\");\n        }\n    } else if (op->type == OBJ_ZSET) {\n        if (op->encoding == OBJ_ENCODING_ZIPLIST) {\n            return zzlLength(op->subject->ptr);\n        } else if (op->encoding == OBJ_ENCODING_SKIPLIST) {\n            zset *zs = op->subject->ptr;\n            return zs->zsl->length;\n        } else {\n            serverPanic(\"Unknown sorted set encoding\");\n        }\n    } else {\n        serverPanic(\"Unsupported type\");\n    }\n}\n\n/* Check if the current value is valid. If so, store it in the passed structure\n * and move to the next element. If not valid, this means we have reached the\n * end of the structure and can abort. */\nint zuiNext(zsetopsrc *op, zsetopval *val) {\n    int ret;\n    \n    if (op->subject == NULL)\n        return 0;\n\n    if (val->flags & OPVAL_DIRTY_ROBJ)\n        decrRefCount(val->ele);\n\n    memset(val,0,sizeof(zsetopval));\n\n    if (op->type == OBJ_SET) {\n        iterset *it = &op->iter.set;\n        if (op->encoding == OBJ_ENCODING_INTSET) {\n            int64_t ell;\n\n            if (!intsetGet(it->is.is,it->is.ii,&ell))\n                return 0;\n            val->ell = ell;\n            val->score = 1.0;\n\n            /* Move to next element. */\n            it->is.ii++;\n        } else if (op->encoding == OBJ_ENCODING_HT) {\n            if (it->ht.de == NULL)\n                return 0;\n            val->ele = dictGetKey(it->ht.de);\n            val->score = 1.0;\n\n            /* Move to next element. */\n            it->ht.de = dictNext(it->ht.di);\n        } else {\n            serverPanic(\"Unknown set encoding\");\n        }\n    } else if (op->type == OBJ_ZSET) {\n        iterzset *it = &op->iter.zset;\n        if (op->encoding == OBJ_ENCODING_ZIPLIST) {\n            /* No need to check both, but better be explicit. */\n            if (it->zl.eptr == NULL || it->zl.sptr == NULL)\n                return 0;\n            ret = (int) ziplistGet(it->zl.eptr,&val->estr,&val->elen,&val->ell);\n            ASSERT(ret > 0);\n            val->score = zzlGetScore(it->zl.sptr);\n\n            /* Move to next element. */\n            zzlNext(it->zl.zl,&it->zl.eptr,&it->zl.sptr);\n        } else if (op->encoding == OBJ_ENCODING_SKIPLIST) {\n            if (it->sl.node == NULL)\n                return 0;\n            val->ele = it->sl.node->obj;\n            val->score = it->sl.node->score;\n\n            /* Move to next element. */\n            it->sl.node = it->sl.node->level[0].forward;\n        } else {\n            serverPanic(\"Unknown sorted set encoding\");\n        }\n    } else {\n        serverPanic(\"Unsupported type\");\n    }\n    return 1;\n}\n\nint zuiLongLongFromValue(zsetopval *val) {\n    if (!(val->flags & OPVAL_DIRTY_LL)) {\n        val->flags |= OPVAL_DIRTY_LL;\n\n        if (val->ele != NULL) {\n            if (val->ele->encoding == OBJ_ENCODING_INT) {\n                val->ell = (long)val->ele->ptr;\n                val->flags |= OPVAL_VALID_LL;\n            } else if (sdsEncodedObject(val->ele)) {\n                if (string2ll(val->ele->ptr,sdslen(val->ele->ptr),&val->ell))\n                    val->flags |= OPVAL_VALID_LL;\n            } else {\n                serverPanic(\"Unsupported element encoding\");\n            }\n        } else if (val->estr != NULL) {\n            if (string2ll((char*)val->estr,val->elen,&val->ell))\n                val->flags |= OPVAL_VALID_LL;\n        } else {\n            /* The long long was already set, flag as valid. */\n            val->flags |= OPVAL_VALID_LL;\n        }\n    }\n    return val->flags & OPVAL_VALID_LL;\n}\n\nrobj *zuiObjectFromValue(zsetopval *val) {\n    if (val->ele == NULL) {\n        if (val->estr != NULL) {\n            val->ele = createStringObject((char*)val->estr,val->elen);\n        } else {\n            val->ele = createStringObjectFromLongLong(val->ell);\n        }\n        val->flags |= OPVAL_DIRTY_ROBJ;\n    }\n    return val->ele;\n}\n\nint zuiBufferFromValue(zsetopval *val) {\n    if (val->estr == NULL) {\n        if (val->ele != NULL) {\n            if (val->ele->encoding == OBJ_ENCODING_INT) {\n                val->elen = ll2string((char*)val->_buf,sizeof(val->_buf),(long)val->ele->ptr);\n                val->estr = val->_buf;\n            } else if (sdsEncodedObject(val->ele)) {\n                val->elen = sdslen(val->ele->ptr);\n                val->estr = val->ele->ptr;\n            } else {\n                serverPanic(\"Unsupported element encoding\");\n            }\n        } else {\n            val->elen = ll2string((char*)val->_buf,sizeof(val->_buf),val->ell);\n            val->estr = val->_buf;\n        }\n    }\n    return 1;\n}\n\n/* Find value pointed to by val in the source pointer to by op. When found,\n * return 1 and store its score in target. Return 0 otherwise. */\nint zuiFind(zsetopsrc *op, zsetopval *val, double *score) {\n    if (op->subject == NULL)\n        return 0;\n\n    if (op->type == OBJ_SET) {\n        if (op->encoding == OBJ_ENCODING_INTSET) {\n            if (zuiLongLongFromValue(val) &&\n                intsetFind(op->subject->ptr,val->ell))\n            {\n                *score = 1.0;\n                return 1;\n            } else {\n                return 0;\n            }\n        } else if (op->encoding == OBJ_ENCODING_HT) {\n            dict *ht = op->subject->ptr;\n            zuiObjectFromValue(val);\n            if (dictFind(ht,val->ele) != NULL) {\n                *score = 1.0;\n                return 1;\n            } else {\n                return 0;\n            }\n        } else {\n            serverPanic(\"Unknown set encoding\");\n        }\n    } else if (op->type == OBJ_ZSET) {\n        zuiObjectFromValue(val);\n\n        if (op->encoding == OBJ_ENCODING_ZIPLIST) {\n            if (zzlFind(op->subject->ptr,val->ele,score) != NULL) {\n                /* Score is already set by zzlFind. */\n                return 1;\n            } else {\n                return 0;\n            }\n        } else if (op->encoding == OBJ_ENCODING_SKIPLIST) {\n            zset *zs = op->subject->ptr;\n            dictEntry *de;\n            if ((de = dictFind(zs->dict,val->ele)) != NULL) {\n                *score = *(double*)dictGetVal(de);\n                return 1;\n            } else {\n                return 0;\n            }\n        } else {\n            serverPanic(\"Unknown sorted set encoding\");\n        }\n    } else {\n        serverPanic(\"Unsupported type\");\n    }\n}\n\nint zuiCompareByCardinality(const void *s1, const void *s2) {\n    return zuiLength((zsetopsrc*)s1) - zuiLength((zsetopsrc*)s2);\n}\n\n#define REDIS_AGGR_SUM 1\n#define REDIS_AGGR_MIN 2\n#define REDIS_AGGR_MAX 3\n#define zunionInterDictValue(_e) (dictGetVal(_e) == NULL ? 1.0 : *(double*)dictGetVal(_e))\n\ninline static void zunionInterAggregate(double *target, double val, int aggregate) {\n    if (aggregate == REDIS_AGGR_SUM) {\n        *target = *target + val;\n        /* The result of adding two doubles is NaN when one variable\n         * is +inf and the other is -inf. When these numbers are added,\n         * we maintain the convention of the result being 0.0. */\n        if (isnan(*target)) *target = 0.0;\n    } else if (aggregate == REDIS_AGGR_MIN) {\n        *target = val < *target ? val : *target;\n    } else if (aggregate == REDIS_AGGR_MAX) {\n        *target = val > *target ? val : *target;\n    } else {\n        /* safety net */\n        serverPanic(\"Unknown ZUNION/INTER aggregate type\");\n    }\n}\n\n#define SET_OP_UNION 0\n#define SET_OP_DIFF 1\n#define SET_OP_INTER 2\n\nvoid zunionInterGenericCommand(client *c, robj *dstkey, int op) {\n    int i, j;\n    long setnum;\n    int aggregate = REDIS_AGGR_SUM;\n    zsetopsrc *src;\n    zsetopval zval;\n    robj *tmp;\n    unsigned int maxelelen = 0;\n    robj *dstobj;\n    zset *dstzset;\n    zskiplistNode *znode;\n    int touched = 0;\n\n    /* expect setnum input keys to be given */\n    if ((getLongFromObjectOrReply(c, c->argv[2], &setnum, NULL) != VR_OK))\n        return;\n\n    if (setnum < 1) {\n        addReplyError(c,\n            \"at least 1 input key is needed for ZUNIONSTORE/ZINTERSTORE\");\n        return;\n    }\n\n    /* test if the expected number of keys would overflow */\n    if (setnum > c->argc-3) {\n        addReply(c,shared.syntaxerr);\n        return;\n    }\n\n    /* read keys to be used for input */\n    src = dcalloc(setnum, sizeof(zsetopsrc));\n    for (i = 0, j = 3; i < setnum; i++, j++) {\n        robj *obj = lookupKeyWrite(c->db,c->argv[j],NULL);\n        if (obj != NULL) {\n            if (obj->type != OBJ_ZSET && obj->type != OBJ_SET) {\n                dfree(src);\n                addReply(c,shared.wrongtypeerr);\n                return;\n            }\n\n            src[i].subject = obj;\n            src[i].type = obj->type;\n            src[i].encoding = obj->encoding;\n        } else {\n            src[i].subject = NULL;\n        }\n\n        /* Default all weights to 1. */\n        src[i].weight = 1.0;\n    }\n\n    /* parse optional extra arguments */\n    if (j < c->argc) {\n        int remaining = c->argc - j;\n\n        while (remaining) {\n            if (remaining >= (setnum + 1) && !strcasecmp(c->argv[j]->ptr,\"weights\")) {\n                j++; remaining--;\n                for (i = 0; i < setnum; i++, j++, remaining--) {\n                    if (getDoubleFromObjectOrReply(c,c->argv[j],&src[i].weight,\n                            \"weight value is not a float\") != VR_OK)\n                    {\n                        dfree(src);\n                        return;\n                    }\n                }\n            } else if (remaining >= 2 && !strcasecmp(c->argv[j]->ptr,\"aggregate\")) {\n                j++; remaining--;\n                if (!strcasecmp(c->argv[j]->ptr,\"sum\")) {\n                    aggregate = REDIS_AGGR_SUM;\n                } else if (!strcasecmp(c->argv[j]->ptr,\"min\")) {\n                    aggregate = REDIS_AGGR_MIN;\n                } else if (!strcasecmp(c->argv[j]->ptr,\"max\")) {\n                    aggregate = REDIS_AGGR_MAX;\n                } else {\n                    dfree(src);\n                    addReply(c,shared.syntaxerr);\n                    return;\n                }\n                j++; remaining--;\n            } else {\n                dfree(src);\n                addReply(c,shared.syntaxerr);\n                return;\n            }\n        }\n    }\n\n    /* sort sets from the smallest to largest, this will improve our\n     * algorithm's performance */\n    qsort(src,setnum,sizeof(zsetopsrc),zuiCompareByCardinality);\n\n    dstobj = createZsetObject();\n    dstzset = dstobj->ptr;\n    memset(&zval, 0, sizeof(zval));\n\n    if (op == SET_OP_INTER) {\n        /* Skip everything if the smallest input is empty. */\n        if (zuiLength(&src[0]) > 0) {\n            /* Precondition: as src[0] is non-empty and the inputs are ordered\n             * by size, all src[i > 0] are non-empty too. */\n            zuiInitIterator(&src[0]);\n            while (zuiNext(&src[0],&zval)) {\n                double score, value;\n\n                score = src[0].weight * zval.score;\n                if (isnan(score)) score = 0;\n\n                for (j = 1; j < setnum; j++) {\n                    /* It is not safe to access the zset we are\n                     * iterating, so explicitly check for equal object. */\n                    if (src[j].subject == src[0].subject) {\n                        value = zval.score*src[j].weight;\n                        zunionInterAggregate(&score,value,aggregate);\n                    } else if (zuiFind(&src[j],&zval,&value)) {\n                        value *= src[j].weight;\n                        zunionInterAggregate(&score,value,aggregate);\n                    } else {\n                        break;\n                    }\n                }\n\n                /* Only continue when present in every input. */\n                if (j == setnum) {\n                    tmp = zuiObjectFromValue(&zval);\n                    znode = zslInsert(dstzset->zsl,score,tmp);\n                    incrRefCount(tmp); /* added to skiplist */\n                    dictAdd(dstzset->dict,tmp,&znode->score);\n                    incrRefCount(tmp); /* added to dictionary */\n\n                    if (sdsEncodedObject(tmp)) {\n                        if (sdslen(tmp->ptr) > maxelelen)\n                            maxelelen = sdslen(tmp->ptr);\n                    }\n                }\n            }\n            zuiClearIterator(&src[0]);\n        }\n    } else if (op == SET_OP_UNION) {\n        dict *accumulator = dictCreate(&setDictType,NULL);\n        dictIterator *di;\n        dictEntry *de;\n        double score;\n\n        if (setnum) {\n            /* Our union is at least as large as the largest set.\n             * Resize the dictionary ASAP to avoid useless rehashing. */\n            dictExpand(accumulator,zuiLength(&src[setnum-1]));\n        }\n\n        /* Step 1: Create a dictionary of elements -> aggregated-scores\n         * by iterating one sorted set after the other. */\n        for (i = 0; i < setnum; i++) {\n            if (zuiLength(&src[i]) == 0) continue;\n\n            zuiInitIterator(&src[i]);\n            while (zuiNext(&src[i],&zval)) {\n                /* Initialize value */\n                score = src[i].weight * zval.score;\n                if (isnan(score)) score = 0;\n\n                /* Search for this element in the accumulating dictionary. */\n                de = dictFind(accumulator,zuiObjectFromValue(&zval));\n                /* If we don't have it, we need to create a new entry. */\n                if (de == NULL) {\n                    tmp = zuiObjectFromValue(&zval);\n                    /* Remember the longest single element encountered,\n                     * to understand if it's possible to convert to ziplist\n                     * at the end. */\n                    if (sdsEncodedObject(tmp)) {\n                        if (sdslen(tmp->ptr) > maxelelen)\n                            maxelelen = sdslen(tmp->ptr);\n                    }\n                    /* Add the element with its initial score. */\n                    de = dictAddRaw(accumulator,tmp);\n                    incrRefCount(tmp);\n                    dictSetDoubleVal(de,score);\n                } else {\n                    /* Update the score with the score of the new instance\n                     * of the element found in the current sorted set.\n                     *\n                     * Here we access directly the dictEntry double\n                     * value inside the union as it is a big speedup\n                     * compared to using the getDouble/setDouble API. */\n                    zunionInterAggregate(&de->v.d,score,aggregate);\n                }\n            }\n            zuiClearIterator(&src[i]);\n        }\n\n        /* Step 2: convert the dictionary into the final sorted set. */\n        di = dictGetIterator(accumulator);\n\n        /* We now are aware of the final size of the resulting sorted set,\n         * let's resize the dictionary embedded inside the sorted set to the\n         * right size, in order to save rehashing time. */\n        dictExpand(dstzset->dict,dictSize(accumulator));\n\n        while((de = dictNext(di)) != NULL) {\n            robj *ele = dictGetKey(de);\n            score = dictGetDoubleVal(de);\n            znode = zslInsert(dstzset->zsl,score,ele);\n            incrRefCount(ele); /* added to skiplist */\n            dictAdd(dstzset->dict,ele,&znode->score);\n            incrRefCount(ele); /* added to dictionary */\n        }\n        dictReleaseIterator(di);\n\n        /* We can free the accumulator dictionary now. */\n        dictRelease(accumulator);\n    } else {\n        serverPanic(\"Unknown operator\");\n    }\n\n    if (dbDelete(c->db,dstkey)) {\n        signalModifiedKey(c->db,dstkey);\n        touched = 1;\n        server.dirty++;\n    }\n    if (dstzset->zsl->length) {\n        zsetConvertToZiplistIfNeeded(dstobj,maxelelen);\n        dbAdd(c->db,dstkey,dstobj);\n        addReplyLongLong(c,zsetLength(dstobj));\n        if (!touched) signalModifiedKey(c->db,dstkey);\n        notifyKeyspaceEvent(NOTIFY_ZSET,\n            (op == SET_OP_UNION) ? \"zunionstore\" : \"zinterstore\",\n            dstkey,c->db->id);\n        server.dirty++;\n    } else {\n        decrRefCount(dstobj);\n        addReply(c,shared.czero);\n        if (touched)\n            notifyKeyspaceEvent(NOTIFY_GENERIC,\"del\",dstkey,c->db->id);\n    }\n    dfree(src);\n}\n\nvoid zunionstoreCommand(client *c) {\n    zunionInterGenericCommand(c,c->argv[1], SET_OP_UNION);\n}\n\nvoid zinterstoreCommand(client *c) {\n    zunionInterGenericCommand(c,c->argv[1], SET_OP_INTER);\n}\n\nvoid zrangeGenericCommand(client *c, int reverse) {\n    robj *key = c->argv[1];\n    robj *zobj;\n    int withscores = 0;\n    long start;\n    long end;\n    int llen;\n    int rangelen;\n\n    if ((getLongFromObjectOrReply(c, c->argv[2], &start, NULL) != VR_OK) ||\n        (getLongFromObjectOrReply(c, c->argv[3], &end, NULL) != VR_OK)) return;\n\n    if (c->argc == 5 && !strcasecmp(c->argv[4]->ptr,\"withscores\")) {\n        withscores = 1;\n    } else if (c->argc >= 5) {\n        addReply(c,shared.syntaxerr);\n        return;\n    }\n\n    fetchInternalDbByKey(c, key);\n    lockDbRead(c->db);\n    if ((zobj = lookupKeyReadOrReply(c,key,shared.emptymultibulk)) == NULL) {\n        unlockDb(c->db);\n        update_stats_add(c->vel->stats, keyspace_misses, 1);\n        return;\n    } else if (checkType(c,zobj,OBJ_ZSET)) {\n        unlockDb(c->db);\n        update_stats_add(c->vel->stats, keyspace_hits, 1);\n        return;\n    }\n\n    /* Sanitize indexes. */\n    llen = zsetLength(zobj);\n    if (start < 0) start = llen+start;\n    if (end < 0) end = llen+end;\n    if (start < 0) start = 0;\n\n    /* Invariant: start >= 0, so this test will be true when end < 0.\n     * The range is empty when start > end or start >= length. */\n    if (start > end || start >= llen) {\n        unlockDb(c->db);\n        update_stats_add(c->vel->stats, keyspace_hits, 1);\n        addReply(c,shared.emptymultibulk);\n        return;\n    }\n    if (end >= llen) end = llen-1;\n    rangelen = (end-start)+1;\n\n    /* Return the result in form of a multi-bulk reply */\n    addReplyMultiBulkLen(c, withscores ? (rangelen*2) : rangelen);\n\n    if (zobj->encoding == OBJ_ENCODING_ZIPLIST) {\n        unsigned char *zl = zobj->ptr;\n        unsigned char *eptr, *sptr;\n        unsigned char *vstr;\n        unsigned int vlen;\n        long long vlong;\n\n        if (reverse)\n            eptr = ziplistIndex(zl,-2-(2*start));\n        else\n            eptr = ziplistIndex(zl,2*start);\n\n        serverAssertWithInfo(c,zobj,eptr != NULL);\n        sptr = ziplistNext(zl,eptr);\n\n        while (rangelen--) {\n            serverAssertWithInfo(c,zobj,eptr != NULL && sptr != NULL);\n            serverAssertWithInfo(c,zobj,ziplistGet(eptr,&vstr,&vlen,&vlong));\n            if (vstr == NULL)\n                addReplyBulkLongLong(c,vlong);\n            else\n                addReplyBulkCBuffer(c,vstr,vlen);\n\n            if (withscores)\n                addReplyDouble(c,zzlGetScore(sptr));\n\n            if (reverse)\n                zzlPrev(zl,&eptr,&sptr);\n            else\n                zzlNext(zl,&eptr,&sptr);\n        }\n\n    } else if (zobj->encoding == OBJ_ENCODING_SKIPLIST) {\n        zset *zs = zobj->ptr;\n        zskiplist *zsl = zs->zsl;\n        zskiplistNode *ln;\n        robj *ele;\n\n        /* Check if starting point is trivial, before doing log(N) lookup. */\n        if (reverse) {\n            ln = zsl->tail;\n            if (start > 0)\n                ln = zslGetElementByRank(zsl,llen-start);\n        } else {\n            ln = zsl->header->level[0].forward;\n            if (start > 0)\n                ln = zslGetElementByRank(zsl,start+1);\n        }\n\n        while(rangelen--) {\n            serverAssertWithInfo(c,zobj,ln != NULL);\n            ele = ln->obj;\n            addReplyBulk(c,ele);\n            if (withscores)\n                addReplyDouble(c,ln->score);\n            ln = reverse ? ln->backward : ln->level[0].forward;\n        }\n    } else {\n        serverPanic(\"Unknown sorted set encoding\");\n    }\n\n    unlockDb(c->db);\n    update_stats_add(c->vel->stats, keyspace_hits, 1);\n}\n\nvoid zrangeCommand(client *c) {\n    zrangeGenericCommand(c,0);\n}\n\nvoid zrevrangeCommand(client *c) {\n    zrangeGenericCommand(c,1);\n}\n\n/* This command implements ZRANGEBYSCORE, ZREVRANGEBYSCORE. */\nvoid genericZrangebyscoreCommand(client *c, int reverse) {\n    zrangespec range;\n    robj *key = c->argv[1];\n    robj *zobj;\n    long offset = 0, limit = -1;\n    int withscores = 0;\n    unsigned long rangelen = 0;\n    void *replylen = NULL;\n    int minidx, maxidx;\n\n    /* Parse the range arguments. */\n    if (reverse) {\n        /* Range is given as [max,min] */\n        maxidx = 2; minidx = 3;\n    } else {\n        /* Range is given as [min,max] */\n        minidx = 2; maxidx = 3;\n    }\n\n    if (zslParseRange(c->argv[minidx],c->argv[maxidx],&range) != VR_OK) {\n        addReplyError(c,\"min or max is not a float\");\n        return;\n    }\n\n    /* Parse optional extra arguments. Note that ZCOUNT will exactly have\n     * 4 arguments, so we'll never enter the following code path. */\n    if (c->argc > 4) {\n        int remaining = c->argc - 4;\n        int pos = 4;\n\n        while (remaining) {\n            if (remaining >= 1 && !strcasecmp(c->argv[pos]->ptr,\"withscores\")) {\n                pos++; remaining--;\n                withscores = 1;\n            } else if (remaining >= 3 && !strcasecmp(c->argv[pos]->ptr,\"limit\")) {\n                if ((getLongFromObjectOrReply(c, c->argv[pos+1], &offset, NULL) != VR_OK) ||\n                    (getLongFromObjectOrReply(c, c->argv[pos+2], &limit, NULL) != VR_OK)) return;\n                pos += 3; remaining -= 3;\n            } else {\n                addReply(c,shared.syntaxerr);\n                return;\n            }\n        }\n    }\n\n    fetchInternalDbByKey(c, key);\n    lockDbRead(c->db);\n    /* Ok, lookup the key and get the range */\n    if ((zobj = lookupKeyReadOrReply(c,key,shared.emptymultibulk)) == NULL) {\n        unlockDb(c->db);\n        update_stats_add(c->vel->stats, keyspace_misses, 1);\n        return;\n    } else if (checkType(c,zobj,OBJ_ZSET)) {\n        unlockDb(c->db);\n        update_stats_add(c->vel->stats, keyspace_hits, 1);\n        return;\n    }\n\n    if (zobj->encoding == OBJ_ENCODING_ZIPLIST) {\n        unsigned char *zl = zobj->ptr;\n        unsigned char *eptr, *sptr;\n        unsigned char *vstr;\n        unsigned int vlen;\n        long long vlong;\n        double score;\n\n        /* If reversed, get the last node in range as starting point. */\n        if (reverse) {\n            eptr = zzlLastInRange(zl,&range);\n        } else {\n            eptr = zzlFirstInRange(zl,&range);\n        }\n\n        /* No \"first\" element in the specified interval. */\n        if (eptr == NULL) {\n            unlockDb(c->db);\n            update_stats_add(c->vel->stats, keyspace_hits, 1);\n            addReply(c, shared.emptymultibulk);\n            return;\n        }\n\n        /* Get score pointer for the first element. */\n        serverAssertWithInfo(c,zobj,eptr != NULL);\n        sptr = ziplistNext(zl,eptr);\n\n        /* We don't know in advance how many matching elements there are in the\n         * list, so we push this object that will represent the multi-bulk\n         * length in the output buffer, and will \"fix\" it later */\n        replylen = addDeferredMultiBulkLength(c);\n\n        /* If there is an offset, just traverse the number of elements without\n         * checking the score because that is done in the next loop. */\n        while (eptr && offset--) {\n            if (reverse) {\n                zzlPrev(zl,&eptr,&sptr);\n            } else {\n                zzlNext(zl,&eptr,&sptr);\n            }\n        }\n\n        while (eptr && limit--) {\n            score = zzlGetScore(sptr);\n\n            /* Abort when the node is no longer in range. */\n            if (reverse) {\n                if (!zslValueGteMin(score,&range)) break;\n            } else {\n                if (!zslValueLteMax(score,&range)) break;\n            }\n\n            /* We know the element exists, so ziplistGet should always succeed */\n            serverAssertWithInfo(c,zobj,ziplistGet(eptr,&vstr,&vlen,&vlong));\n\n            rangelen++;\n            if (vstr == NULL) {\n                addReplyBulkLongLong(c,vlong);\n            } else {\n                addReplyBulkCBuffer(c,vstr,vlen);\n            }\n\n            if (withscores) {\n                addReplyDouble(c,score);\n            }\n\n            /* Move to next node */\n            if (reverse) {\n                zzlPrev(zl,&eptr,&sptr);\n            } else {\n                zzlNext(zl,&eptr,&sptr);\n            }\n        }\n    } else if (zobj->encoding == OBJ_ENCODING_SKIPLIST) {\n        zset *zs = zobj->ptr;\n        zskiplist *zsl = zs->zsl;\n        zskiplistNode *ln;\n\n        /* If reversed, get the last node in range as starting point. */\n        if (reverse) {\n            ln = zslLastInRange(zsl,&range);\n        } else {\n            ln = zslFirstInRange(zsl,&range);\n        }\n\n        /* No \"first\" element in the specified interval. */\n        if (ln == NULL) {\n            unlockDb(c->db);\n            update_stats_add(c->vel->stats, keyspace_hits, 1);\n            addReply(c, shared.emptymultibulk);\n            return;\n        }\n\n        /* We don't know in advance how many matching elements there are in the\n         * list, so we push this object that will represent the multi-bulk\n         * length in the output buffer, and will \"fix\" it later */\n        replylen = addDeferredMultiBulkLength(c);\n\n        /* If there is an offset, just traverse the number of elements without\n         * checking the score because that is done in the next loop. */\n        while (ln && offset--) {\n            if (reverse) {\n                ln = ln->backward;\n            } else {\n                ln = ln->level[0].forward;\n            }\n        }\n\n        while (ln && limit--) {\n            /* Abort when the node is no longer in range. */\n            if (reverse) {\n                if (!zslValueGteMin(ln->score,&range)) break;\n            } else {\n                if (!zslValueLteMax(ln->score,&range)) break;\n            }\n\n            rangelen++;\n            addReplyBulk(c,ln->obj);\n\n            if (withscores) {\n                addReplyDouble(c,ln->score);\n            }\n\n            /* Move to next node */\n            if (reverse) {\n                ln = ln->backward;\n            } else {\n                ln = ln->level[0].forward;\n            }\n        }\n    } else {\n        serverPanic(\"Unknown sorted set encoding\");\n    }\n\n    if (withscores) {\n        rangelen *= 2;\n    }\n\n    setDeferredMultiBulkLength(c, replylen, rangelen);\n    unlockDb(c->db);\n    update_stats_add(c->vel->stats, keyspace_hits, 1);\n}\n\nvoid zrangebyscoreCommand(client *c) {\n    genericZrangebyscoreCommand(c,0);\n}\n\nvoid zrevrangebyscoreCommand(client *c) {\n    genericZrangebyscoreCommand(c,1);\n}\n\nvoid zcountCommand(client *c) {\n    robj *key = c->argv[1];\n    robj *zobj;\n    zrangespec range;\n    int count = 0;\n\n    /* Parse the range arguments */\n    if (zslParseRange(c->argv[2],c->argv[3],&range) != VR_OK) {\n        addReplyError(c,\"min or max is not a float\");\n        return;\n    }\n\n    fetchInternalDbByKey(c, key);\n    lockDbRead(c->db);\n    /* Lookup the sorted set */\n    if ((zobj = lookupKeyReadOrReply(c, key, shared.czero)) == NULL) {\n        unlockDb(c->db);\n        update_stats_add(c->vel->stats, keyspace_misses, 1);\n        return;\n    } else if (checkType(c, zobj, OBJ_ZSET)) {\n        unlockDb(c->db);\n        update_stats_add(c->vel->stats, keyspace_hits, 1);\n        return;\n    }\n\n    if (zobj->encoding == OBJ_ENCODING_ZIPLIST) {\n        unsigned char *zl = zobj->ptr;\n        unsigned char *eptr, *sptr;\n        double score;\n\n        /* Use the first element in range as the starting point */\n        eptr = zzlFirstInRange(zl,&range);\n\n        /* No \"first\" element */\n        if (eptr == NULL) {\n            unlockDb(c->db);\n            update_stats_add(c->vel->stats, keyspace_hits, 1);\n            addReply(c, shared.czero);\n            return;\n        }\n\n        /* First element is in range */\n        sptr = ziplistNext(zl,eptr);\n        score = zzlGetScore(sptr);\n        serverAssertWithInfo(c,zobj,zslValueLteMax(score,&range));\n\n        /* Iterate over elements in range */\n        while (eptr) {\n            score = zzlGetScore(sptr);\n\n            /* Abort when the node is no longer in range. */\n            if (!zslValueLteMax(score,&range)) {\n                break;\n            } else {\n                count++;\n                zzlNext(zl,&eptr,&sptr);\n            }\n        }\n    } else if (zobj->encoding == OBJ_ENCODING_SKIPLIST) {\n        zset *zs = zobj->ptr;\n        zskiplist *zsl = zs->zsl;\n        zskiplistNode *zn;\n        unsigned long rank;\n\n        /* Find first element in range */\n        zn = zslFirstInRange(zsl, &range);\n\n        /* Use rank of first element, if any, to determine preliminary count */\n        if (zn != NULL) {\n            rank = zslGetRank(zsl, zn->score, zn->obj);\n            count = (zsl->length - (rank - 1));\n\n            /* Find last element in range */\n            zn = zslLastInRange(zsl, &range);\n\n            /* Use rank of last element, if any, to determine the actual count */\n            if (zn != NULL) {\n                rank = zslGetRank(zsl, zn->score, zn->obj);\n                count -= (zsl->length - rank);\n            }\n        }\n    } else {\n        serverPanic(\"Unknown sorted set encoding\");\n    }\n\n    addReplyLongLong(c, count);\n    unlockDb(c->db);\n    update_stats_add(c->vel->stats, keyspace_hits, 1);\n}\n\nvoid zlexcountCommand(client *c) {\n    robj *key = c->argv[1];\n    robj *zobj;\n    zlexrangespec range;\n    int count = 0;\n\n    /* Parse the range arguments */\n    if (zslParseLexRange(c->argv[2],c->argv[3],&range) != VR_OK) {\n        addReplyError(c,\"min or max not valid string range item\");\n        return;\n    }\n\n    /* Lookup the sorted set */\n    if ((zobj = lookupKeyReadOrReply(c, key, shared.czero)) == NULL ||\n        checkType(c, zobj, OBJ_ZSET))\n    {\n        zslFreeLexRange(&range);\n        return;\n    }\n\n    if (zobj->encoding == OBJ_ENCODING_ZIPLIST) {\n        unsigned char *zl = zobj->ptr;\n        unsigned char *eptr, *sptr;\n\n        /* Use the first element in range as the starting point */\n        eptr = zzlFirstInLexRange(zl,&range);\n\n        /* No \"first\" element */\n        if (eptr == NULL) {\n            zslFreeLexRange(&range);\n            addReply(c, shared.czero);\n            return;\n        }\n\n        /* First element is in range */\n        sptr = ziplistNext(zl,eptr);\n        serverAssertWithInfo(c,zobj,zzlLexValueLteMax(eptr,&range));\n\n        /* Iterate over elements in range */\n        while (eptr) {\n            /* Abort when the node is no longer in range. */\n            if (!zzlLexValueLteMax(eptr,&range)) {\n                break;\n            } else {\n                count++;\n                zzlNext(zl,&eptr,&sptr);\n            }\n        }\n    } else if (zobj->encoding == OBJ_ENCODING_SKIPLIST) {\n        zset *zs = zobj->ptr;\n        zskiplist *zsl = zs->zsl;\n        zskiplistNode *zn;\n        unsigned long rank;\n\n        /* Find first element in range */\n        zn = zslFirstInLexRange(zsl, &range);\n\n        /* Use rank of first element, if any, to determine preliminary count */\n        if (zn != NULL) {\n            rank = zslGetRank(zsl, zn->score, zn->obj);\n            count = (zsl->length - (rank - 1));\n\n            /* Find last element in range */\n            zn = zslLastInLexRange(zsl, &range);\n\n            /* Use rank of last element, if any, to determine the actual count */\n            if (zn != NULL) {\n                rank = zslGetRank(zsl, zn->score, zn->obj);\n                count -= (zsl->length - rank);\n            }\n        }\n    } else {\n        serverPanic(\"Unknown sorted set encoding\");\n    }\n\n    zslFreeLexRange(&range);\n    addReplyLongLong(c, count);\n}\n\n/* This command implements ZRANGEBYLEX, ZREVRANGEBYLEX. */\nvoid genericZrangebylexCommand(client *c, int reverse) {\n    zlexrangespec range;\n    robj *key = c->argv[1];\n    robj *zobj;\n    long offset = 0, limit = -1;\n    unsigned long rangelen = 0;\n    void *replylen = NULL;\n    int minidx, maxidx;\n\n    /* Parse the range arguments. */\n    if (reverse) {\n        /* Range is given as [max,min] */\n        maxidx = 2; minidx = 3;\n    } else {\n        /* Range is given as [min,max] */\n        minidx = 2; maxidx = 3;\n    }\n\n    if (zslParseLexRange(c->argv[minidx],c->argv[maxidx],&range) != VR_OK) {\n        addReplyError(c,\"min or max not valid string range item\");\n        return;\n    }\n\n    /* Parse optional extra arguments. Note that ZCOUNT will exactly have\n     * 4 arguments, so we'll never enter the following code path. */\n    if (c->argc > 4) {\n        int remaining = c->argc - 4;\n        int pos = 4;\n\n        while (remaining) {\n            if (remaining >= 3 && !strcasecmp(c->argv[pos]->ptr,\"limit\")) {\n                if ((getLongFromObjectOrReply(c, c->argv[pos+1], &offset, NULL) != VR_OK) ||\n                    (getLongFromObjectOrReply(c, c->argv[pos+2], &limit, NULL) != VR_OK)) return;\n                pos += 3; remaining -= 3;\n            } else {\n                zslFreeLexRange(&range);\n                addReply(c,shared.syntaxerr);\n                return;\n            }\n        }\n    }\n\n    /* Ok, lookup the key and get the range */\n    if ((zobj = lookupKeyReadOrReply(c,key,shared.emptymultibulk)) == NULL ||\n        checkType(c,zobj,OBJ_ZSET))\n    {\n        zslFreeLexRange(&range);\n        return;\n    }\n\n    if (zobj->encoding == OBJ_ENCODING_ZIPLIST) {\n        unsigned char *zl = zobj->ptr;\n        unsigned char *eptr, *sptr;\n        unsigned char *vstr;\n        unsigned int vlen;\n        long long vlong;\n\n        /* If reversed, get the last node in range as starting point. */\n        if (reverse) {\n            eptr = zzlLastInLexRange(zl,&range);\n        } else {\n            eptr = zzlFirstInLexRange(zl,&range);\n        }\n\n        /* No \"first\" element in the specified interval. */\n        if (eptr == NULL) {\n            addReply(c, shared.emptymultibulk);\n            zslFreeLexRange(&range);\n            return;\n        }\n\n        /* Get score pointer for the first element. */\n        serverAssertWithInfo(c,zobj,eptr != NULL);\n        sptr = ziplistNext(zl,eptr);\n\n        /* We don't know in advance how many matching elements there are in the\n         * list, so we push this object that will represent the multi-bulk\n         * length in the output buffer, and will \"fix\" it later */\n        replylen = addDeferredMultiBulkLength(c);\n\n        /* If there is an offset, just traverse the number of elements without\n         * checking the score because that is done in the next loop. */\n        while (eptr && offset--) {\n            if (reverse) {\n                zzlPrev(zl,&eptr,&sptr);\n            } else {\n                zzlNext(zl,&eptr,&sptr);\n            }\n        }\n\n        while (eptr && limit--) {\n            /* Abort when the node is no longer in range. */\n            if (reverse) {\n                if (!zzlLexValueGteMin(eptr,&range)) break;\n            } else {\n                if (!zzlLexValueLteMax(eptr,&range)) break;\n            }\n\n            /* We know the element exists, so ziplistGet should always\n             * succeed. */\n            serverAssertWithInfo(c,zobj,ziplistGet(eptr,&vstr,&vlen,&vlong));\n\n            rangelen++;\n            if (vstr == NULL) {\n                addReplyBulkLongLong(c,vlong);\n            } else {\n                addReplyBulkCBuffer(c,vstr,vlen);\n            }\n\n            /* Move to next node */\n            if (reverse) {\n                zzlPrev(zl,&eptr,&sptr);\n            } else {\n                zzlNext(zl,&eptr,&sptr);\n            }\n        }\n    } else if (zobj->encoding == OBJ_ENCODING_SKIPLIST) {\n        zset *zs = zobj->ptr;\n        zskiplist *zsl = zs->zsl;\n        zskiplistNode *ln;\n\n        /* If reversed, get the last node in range as starting point. */\n        if (reverse) {\n            ln = zslLastInLexRange(zsl,&range);\n        } else {\n            ln = zslFirstInLexRange(zsl,&range);\n        }\n\n        /* No \"first\" element in the specified interval. */\n        if (ln == NULL) {\n            addReply(c, shared.emptymultibulk);\n            zslFreeLexRange(&range);\n            return;\n        }\n\n        /* We don't know in advance how many matching elements there are in the\n         * list, so we push this object that will represent the multi-bulk\n         * length in the output buffer, and will \"fix\" it later */\n        replylen = addDeferredMultiBulkLength(c);\n\n        /* If there is an offset, just traverse the number of elements without\n         * checking the score because that is done in the next loop. */\n        while (ln && offset--) {\n            if (reverse) {\n                ln = ln->backward;\n            } else {\n                ln = ln->level[0].forward;\n            }\n        }\n\n        while (ln && limit--) {\n            /* Abort when the node is no longer in range. */\n            if (reverse) {\n                if (!zslLexValueGteMin(ln->obj,&range)) break;\n            } else {\n                if (!zslLexValueLteMax(ln->obj,&range)) break;\n            }\n\n            rangelen++;\n            addReplyBulk(c,ln->obj);\n\n            /* Move to next node */\n            if (reverse) {\n                ln = ln->backward;\n            } else {\n                ln = ln->level[0].forward;\n            }\n        }\n    } else {\n        serverPanic(\"Unknown sorted set encoding\");\n    }\n\n    zslFreeLexRange(&range);\n    setDeferredMultiBulkLength(c, replylen, rangelen);\n}\n\nvoid zrangebylexCommand(client *c) {\n    genericZrangebylexCommand(c,0);\n}\n\nvoid zrevrangebylexCommand(client *c) {\n    genericZrangebylexCommand(c,1);\n}\n\nvoid zcardCommand(client *c) {\n    robj *key = c->argv[1];\n    robj *zobj;\n\n    fetchInternalDbByKey(c, key);\n    lockDbRead(c->db);\n    if ((zobj = lookupKeyReadOrReply(c,key,shared.czero)) == NULL) {\n        unlockDb(c->db);\n        update_stats_add(c->vel->stats, keyspace_misses, 1);\n        return;\n    } else if (checkType(c,zobj,OBJ_ZSET)) {\n        unlockDb(c->db);\n        update_stats_add(c->vel->stats, keyspace_hits, 1);\n        return;\n    }\n    \n    addReplyLongLong(c,zsetLength(zobj));\n\n    unlockDb(c->db);\n    update_stats_add(c->vel->stats, keyspace_hits, 1);\n}\n\nvoid zscoreCommand(client *c) {\n    robj *key = c->argv[1];\n    robj *zobj;\n    double score;\n\n    fetchInternalDbByKey(c, key);\n    lockDbRead(c->db);\n    if ((zobj = lookupKeyReadOrReply(c,key,shared.nullbulk)) == NULL) {\n        unlockDb(c->db);\n        update_stats_add(c->vel->stats, keyspace_misses, 1);\n        return;\n    } else if (checkType(c,zobj,OBJ_ZSET)) {\n        unlockDb(c->db);\n        update_stats_add(c->vel->stats, keyspace_hits, 1);\n        return;\n    }\n\n    if (zsetScore(zobj,c->argv[2],&score) == VR_ERROR) {\n        addReply(c,shared.nullbulk);\n    } else {\n        addReplyDouble(c,score);\n    }\n\n    unlockDb(c->db);\n    update_stats_add(c->vel->stats, keyspace_hits, 1);\n}\n\nvoid zrankGenericCommand(client *c, int reverse) {\n    robj *key = c->argv[1];\n    robj *ele = c->argv[2];\n    robj *zobj;\n    unsigned long llen;\n    unsigned long rank;\n\n    fetchInternalDbByKey(c, key);\n    lockDbRead(c->db);\n    if ((zobj = lookupKeyReadOrReply(c,key,shared.nullbulk)) == NULL) {\n        unlockDb(c->db);\n        update_stats_add(c->vel->stats, keyspace_misses, 1);\n        return;\n    } else if (checkType(c,zobj,OBJ_ZSET)) {\n        unlockDb(c->db);\n        update_stats_add(c->vel->stats, keyspace_hits, 1);\n        return;\n    }\n    llen = zsetLength(zobj);\n\n    serverAssertWithInfo(c,ele,sdsEncodedObject(ele));\n\n    if (zobj->encoding == OBJ_ENCODING_ZIPLIST) {\n        unsigned char *zl = zobj->ptr;\n        unsigned char *eptr, *sptr;\n\n        eptr = ziplistIndex(zl,0);\n        serverAssertWithInfo(c,zobj,eptr != NULL);\n        sptr = ziplistNext(zl,eptr);\n        serverAssertWithInfo(c,zobj,sptr != NULL);\n\n        rank = 1;\n        while(eptr != NULL) {\n            if (ziplistCompare(eptr,ele->ptr,sdslen(ele->ptr)))\n                break;\n            rank++;\n            zzlNext(zl,&eptr,&sptr);\n        }\n\n        if (eptr != NULL) {\n            if (reverse)\n                addReplyLongLong(c,llen-rank);\n            else\n                addReplyLongLong(c,rank-1);\n        } else {\n            addReply(c,shared.nullbulk);\n        }\n    } else if (zobj->encoding == OBJ_ENCODING_SKIPLIST) {\n        zset *zs = zobj->ptr;\n        zskiplist *zsl = zs->zsl;\n        dictEntry *de;\n        double score;\n\n        ele = c->argv[2];\n        de = dictFind(zs->dict,ele);\n        if (de != NULL) {\n            score = *(double*)dictGetVal(de);\n            rank = zslGetRank(zsl,score,ele);\n            serverAssertWithInfo(c,ele,rank); /* Existing elements always have a rank. */\n            if (reverse)\n                addReplyLongLong(c,llen-rank);\n            else\n                addReplyLongLong(c,rank-1);\n        } else {\n            addReply(c,shared.nullbulk);\n        }\n    } else {\n        serverPanic(\"Unknown sorted set encoding\");\n    }\n\n    unlockDb(c->db);\n    update_stats_add(c->vel->stats, keyspace_hits, 1);\n}\n\nvoid zrankCommand(client *c) {\n    zrankGenericCommand(c, 0);\n}\n\nvoid zrevrankCommand(client *c) {\n    zrankGenericCommand(c, 1);\n}\n\nvoid zscanCommand(client *c) {\n    scanGenericCommand(c,SCAN_TYPE_ZSET);\n}\n"
  },
  {
    "path": "src/vr_t_zset.h",
    "content": "#ifndef _VR_T_ZSET_H_\n#define _VR_T_ZSET_H_\n\n/* Struct to hold a inclusive/exclusive range spec by score comparison. */\ntypedef struct {\n    double min, max;\n    int minex, maxex; /* are min or max exclusive? */\n} zrangespec;\n\n/* Struct to hold an inclusive/exclusive range spec by lexicographic comparison. */\ntypedef struct {\n    robj *min, *max;  /* May be set to shared.(minstring|maxstring) */\n    int minex, maxex; /* are min or max exclusive? */\n} zlexrangespec;\n\ntypedef struct {\n    robj *subject;\n    int type; /* Set, sorted set */\n    int encoding;\n    double weight;\n\n    union {\n        /* Set iterators. */\n        union _iterset {\n            struct {\n                intset *is;\n                int ii;\n            } is;\n            struct {\n                dict *dict;\n                dictIterator *di;\n                dictEntry *de;\n            } ht;\n        } set;\n\n        /* Sorted set iterators. */\n        union _iterzset {\n            struct {\n                unsigned char *zl;\n                unsigned char *eptr, *sptr;\n            } zl;\n            struct {\n                zset *zs;\n                zskiplistNode *node;\n            } sl;\n        } zset;\n    } iter;\n} zsetopsrc;\n\n/* Store value retrieved from the iterator. */\ntypedef struct {\n    int flags;\n    unsigned char _buf[32]; /* Private buffer. */\n    robj *ele;\n    unsigned char *estr;\n    unsigned int elen;\n    long long ell;\n    double score;\n} zsetopval;\n\nzskiplistNode *zslCreateNode(int level, double score, robj *obj);\nzskiplist *zslCreate(void);\nvoid zslFreeNode(zskiplistNode *node);\nvoid zslFree(zskiplist *zsl);\nint zslRandomLevel(void);\nzskiplistNode *zslInsert(zskiplist *zsl, double score, robj *obj);\nvoid zslDeleteNode(zskiplist *zsl, zskiplistNode *x, zskiplistNode **update);\nint zslDelete(zskiplist *zsl, double score, robj *obj);\nint zslValueLteMax(double value, zrangespec *spec);\nint zslIsInRange(zskiplist *zsl, zrangespec *range);\nzskiplistNode *zslFirstInRange(zskiplist *zsl, zrangespec *range);\nzskiplistNode *zslLastInRange(zskiplist *zsl, zrangespec *range);\nunsigned long zslDeleteRangeByScore(zskiplist *zsl, zrangespec *range, dict *dict);\nunsigned long zslDeleteRangeByLex(zskiplist *zsl, zlexrangespec *range, dict *dict);\nunsigned long zslDeleteRangeByRank(zskiplist *zsl, unsigned int start, unsigned int end, dict *dict);\nunsigned long zslGetRank(zskiplist *zsl, double score, robj *o);\nzskiplistNode* zslGetElementByRank(zskiplist *zsl, unsigned long rank);\nint zslParseLexRangeItem(robj *item, robj **dest, int *ex);\nvoid zslFreeLexRange(zlexrangespec *spec);\nint compareStringObjectsForLexRange(robj *a, robj *b);\nint zslIsInLexRange(zskiplist *zsl, zlexrangespec *range);\nzskiplistNode *zslFirstInLexRange(zskiplist *zsl, zlexrangespec *range);\nzskiplistNode *zslLastInLexRange(zskiplist *zsl, zlexrangespec *range);\ndouble zzlGetScore(unsigned char *sptr);\nrobj *ziplistGetObject(unsigned char *sptr);\nint zzlCompareElements(unsigned char *eptr, unsigned char *cstr, unsigned int clen);\nunsigned int zzlLength(unsigned char *zl);\nvoid zzlNext(unsigned char *zl, unsigned char **eptr, unsigned char **sptr);\nvoid zzlPrev(unsigned char *zl, unsigned char **eptr, unsigned char **sptr);\nint zzlIsInRange(unsigned char *zl, zrangespec *range);\nunsigned char *zzlFirstInRange(unsigned char *zl, zrangespec *range);\nunsigned char *zzlLastInRange(unsigned char *zl, zrangespec *range);\nint zzlIsInLexRange(unsigned char *zl, zlexrangespec *range);\nunsigned char *zzlFirstInLexRange(unsigned char *zl, zlexrangespec *range);\n\nunsigned char *zzlLastInLexRange(unsigned char *zl, zlexrangespec *range);\nunsigned char *zzlFind(unsigned char *zl, robj *ele, double *score);\nunsigned char *zzlDelete(unsigned char *zl, unsigned char *eptr);\nunsigned char *zzlInsertAt(unsigned char *zl, unsigned char *eptr, robj *ele, double score);\nunsigned char *zzlInsert(unsigned char *zl, robj *ele, double score);\nunsigned char *zzlDeleteRangeByScore(unsigned char *zl, zrangespec *range, unsigned long *deleted);\nunsigned char *zzlDeleteRangeByLex(unsigned char *zl, zlexrangespec *range, unsigned long *deleted);\nunsigned char *zzlDeleteRangeByRank(unsigned char *zl, unsigned int start, unsigned int end, unsigned long *deleted);\nunsigned int zsetLength(robj *zobj);\nvoid zsetConvert(robj *zobj, int encoding);\nvoid zsetConvertToZiplistIfNeeded(robj *zobj, size_t maxelelen);\nint zsetScore(robj *zobj, robj *member, double *score);\n\nvoid zaddGenericCommand(client *c, int flags);\nvoid zaddCommand(client *c);\nvoid zincrbyCommand(client *c);\nvoid zremCommand(client *c);\nvoid zremrangeGenericCommand(client *c, int rangetype);\nvoid zremrangebyrankCommand(client *c);\nvoid zremrangebyscoreCommand(client *c);\nvoid zremrangebylexCommand(client *c) ;\n\nvoid zuiInitIterator(zsetopsrc *op);\nvoid zuiClearIterator(zsetopsrc *op);\nint zuiLength(zsetopsrc *op);\n\n/* Check if the current value is valid. If so, store it in the passed structure\n * and move to the next element. If not valid, this means we have reached the\n * end of the structure and can abort. */\nint zuiNext(zsetopsrc *op, zsetopval *val);\nint zuiLongLongFromValue(zsetopval *val);\nrobj *zuiObjectFromValue(zsetopval *val);\nint zuiBufferFromValue(zsetopval *val);\n\n/* Find value pointed to by val in the source pointer to by op. When found,\n * return 1 and store its score in target. Return 0 otherwise. */\nint zuiFind(zsetopsrc *op, zsetopval *val, double *score);\nint zuiCompareByCardinality(const void *s1, const void *s2);\nvoid zunionInterGenericCommand(client *c, robj *dstkey, int op);\nvoid zunionstoreCommand(client *c);\nvoid zinterstoreCommand(client *c);\nvoid zrangeGenericCommand(client *c, int reverse);\nvoid zrangeCommand(client *c);\nvoid zrevrangeCommand(client *c);\n\n/* This command implements ZRANGEBYSCORE, ZREVRANGEBYSCORE. */\nvoid genericZrangebyscoreCommand(client *c, int reverse);\nvoid zrangebyscoreCommand(client *c);\nvoid zrevrangebyscoreCommand(client *c);\nvoid zcountCommand(client *c);\nvoid zlexcountCommand(client *c);\n\n/* This command implements ZRANGEBYLEX, ZREVRANGEBYLEX. */\nvoid genericZrangebylexCommand(client *c, int reverse);\nvoid zrangebylexCommand(client *c);\nvoid zrevrangebylexCommand(client *c);\nvoid zcardCommand(client *c);\nvoid zscoreCommand(client *c);\nvoid zrankGenericCommand(client *c, int reverse);\nvoid zrankCommand(client *c);\nvoid zrevrankCommand(client *c);\nvoid zscanCommand(client *c);\n\n#endif\n"
  },
  {
    "path": "src/vr_thread.c",
    "content": "#include <vr_core.h>\n\nint\nvr_thread_init(vr_thread *thread)\n{    \n    if (thread == NULL) {\n        return VR_ERROR;\n    }\n\n    thread->id = 0;\n    thread->thread_id = 0;\n    thread->fun_run = NULL;\n    thread->data = NULL;\n\n    return VR_OK;\n}\n\nvoid\nvr_thread_deinit(vr_thread *thread)\n{\n    if (thread == NULL) {\n        return;\n    }\n\n    thread->id = 0;\n    thread->thread_id = 0;\n    thread->fun_run = NULL;\n    thread->data = NULL;\n}\n\nstatic void *vr_thread_run(void *data)\n{\n    vr_thread *thread = data;\n    srand(vr_usec_now()^(int)pthread_self());\n    \n    thread->fun_run(thread->data);\n}\n\nint vr_thread_start(vr_thread *thread)\n{\n    pthread_attr_t attr;\n    pthread_attr_init(&attr);\n    \n    if (thread == NULL || thread->fun_run == NULL) {\n        return VR_ERROR;\n    }\n\n    pthread_create(&thread->thread_id, \n        &attr, vr_thread_run, thread);\n\n    return VR_OK;\n}\n"
  },
  {
    "path": "src/vr_thread.h",
    "content": "#ifndef _VR_THREAD_H_\n#define _VR_THREAD_H_\n\ntypedef void *(*vr_thread_func_t)(void *data);\n\ntypedef struct vr_thread {\n    int id;\n    pthread_t thread_id;\n\n    vr_thread_func_t fun_run;\n    void *data;\n}vr_thread;\n\nint vr_thread_init(vr_thread *thread);\nvoid vr_thread_deinit(vr_thread *thread);\nint vr_thread_start(vr_thread *thread);\n\n#endif\n"
  },
  {
    "path": "src/vr_util.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <stdarg.h>\n#include <string.h>\n#include <unistd.h>\n#include <fcntl.h>\n#include <netdb.h>\n\n#include <sys/time.h>\n#include <sys/types.h>\n#include <sys/socket.h>\n#include <sys/ioctl.h>\n\n#include <netinet/in.h>\n#include <netinet/tcp.h>\n\n#include <vr_core.h>\n\n#ifdef VR_HAVE_BACKTRACE\n# include <execinfo.h>\n#endif\n\nint\nvr_set_blocking(int sd)\n{\n    int flags;\n\n    flags = fcntl(sd, F_GETFL, 0);\n    if (flags < 0) {\n        return flags;\n    }\n\n    return fcntl(sd, F_SETFL, flags & ~O_NONBLOCK);\n}\n\nint\nvr_set_nonblocking(int sd)\n{\n    int flags;\n\n    flags = fcntl(sd, F_GETFL, 0);\n    if (flags < 0) {\n        return flags;\n    }\n\n    return fcntl(sd, F_SETFL, flags | O_NONBLOCK);\n}\n\nint\nvr_set_reuseaddr(int sd)\n{\n    int reuse;\n    socklen_t len;\n\n    reuse = 1;\n    len = sizeof(reuse);\n\n    return setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &reuse, len);\n}\n\n/*\n * Disable Nagle algorithm on TCP socket.\n *\n * This option helps to minimize transmit latency by disabling coalescing\n * of data to fill up a TCP segment inside the kernel. Sockets with this\n * option must use readv() or writev() to do data transfer in bulk and\n * hence avoid the overhead of small packets.\n */\nint\nvr_set_tcpnodelay(int sd)\n{\n    int nodelay;\n    socklen_t len;\n\n    nodelay = 1;\n    len = sizeof(nodelay);\n\n    return setsockopt(sd, IPPROTO_TCP, TCP_NODELAY, &nodelay, len);\n}\n\nint\nvr_set_linger(int sd, int timeout)\n{\n    struct linger linger;\n    socklen_t len;\n\n    linger.l_onoff = 1;\n    linger.l_linger = timeout;\n\n    len = sizeof(linger);\n\n    return setsockopt(sd, SOL_SOCKET, SO_LINGER, &linger, len);\n}\n\nint\nvr_set_sndbuf(int sd, int size)\n{\n    socklen_t len;\n\n    len = sizeof(size);\n\n    return setsockopt(sd, SOL_SOCKET, SO_SNDBUF, &size, len);\n}\n\nint\nvr_set_rcvbuf(int sd, int size)\n{\n    socklen_t len;\n\n    len = sizeof(size);\n\n    return setsockopt(sd, SOL_SOCKET, SO_RCVBUF, &size, len);\n}\n\nint\nvr_get_soerror(int sd)\n{\n    int status, err;\n    socklen_t len;\n\n    err = 0;\n    len = sizeof(err);\n\n    status = getsockopt(sd, SOL_SOCKET, SO_ERROR, &err, &len);\n    if (status == 0) {\n        errno = err;\n    }\n\n    return status;\n}\n\nint\nvr_get_sndbuf(int sd)\n{\n    int status, size;\n    socklen_t len;\n\n    size = 0;\n    len = sizeof(size);\n\n    status = getsockopt(sd, SOL_SOCKET, SO_SNDBUF, &size, &len);\n    if (status < 0) {\n        return status;\n    }\n\n    return size;\n}\n\nint\nvr_get_rcvbuf(int sd)\n{\n    int status, size;\n    socklen_t len;\n\n    size = 0;\n    len = sizeof(size);\n\n    status = getsockopt(sd, SOL_SOCKET, SO_RCVBUF, &size, &len);\n    if (status < 0) {\n        return status;\n    }\n\n    return size;\n}\n\nint\nvr_set_tcpkeepalive(int sd, int keepidle, int keepinterval, int keepcount)\n{\n    rstatus_t status;\n    int tcpkeepalive;\n    socklen_t len;\n\n    tcpkeepalive = 1;\n    len = sizeof(tcpkeepalive);\n\n    status = setsockopt(sd, SOL_SOCKET, SO_KEEPALIVE, &tcpkeepalive, len);\n    if (status < 0) {\n        log_error(\"setsockopt SO_KEEPALIVE call error(%s)\", strerror(errno));\n        return VR_ERROR;\n    }\n    \n#ifdef SOL_TCP\n    if (keepidle > 0) {\n        len = sizeof(keepidle);\n        status = setsockopt(sd, SOL_TCP, TCP_KEEPIDLE, &keepidle, len);\n        if (status < 0) {\n            log_error(\"setsockopt TCP_KEEPIDLE call error(%s)\", strerror(errno));\n            return VR_ERROR;\n        }\n    }\n\n    if (keepinterval > 0) {\n        len = sizeof(keepinterval);\n        status = setsockopt(sd, SOL_TCP, TCP_KEEPINTVL, &keepinterval, len);\n        if (status < 0) {\n            log_error(\"setsockopt TCP_KEEPINTVL call error(%s)\", strerror(errno));\n            return VR_ERROR;\n        }\n    }\n\n    if (keepcount > 0) {\n        len = sizeof(keepcount);\n        status = setsockopt(sd, SOL_TCP, TCP_KEEPCNT, &keepcount, len);\n        if (status < 0) {\n            log_error(\"setsockopt TCP_KEEPCNT call error(%s)\", strerror(errno));\n            return VR_ERROR;\n        }\n    }\n#endif\n\n    return VR_OK;\n}\n\nint\n_vr_atoi(char *line, size_t n)\n{\n    int value;\n\n    if (n == 0) {\n        return -1;\n    }\n\n    for (value = 0; n--; line++) {\n        if (*line < '0' || *line > '9') {\n            return -1;\n        }\n\n        value = value * 10 + (*line - '0');\n    }\n\n    if (value < 0) {\n        return -1;\n    }\n\n    return value;\n}\n\n/* Return the number of digits of 'v' when converted to string in radix 10.\n * See ll2string() for more information. */\nuint32_t digits10(uint64_t v) {\n    if (v < 10) return 1;\n    if (v < 100) return 2;\n    if (v < 1000) return 3;\n    if (v < 1000000000000UL) {\n        if (v < 100000000UL) {\n            if (v < 1000000) {\n                if (v < 10000) return 4;\n                return 5 + (v >= 100000);\n            }\n            return 7 + (v >= 10000000UL);\n        }\n        if (v < 10000000000UL) {\n            return 9 + (v >= 1000000000UL);\n        }\n        return 11 + (v >= 100000000000UL);\n    }\n    return 12 + digits10(v / 1000000000000UL);\n}\n\n/* Like digits10() but for signed values. */\nuint32_t sdigits10(int64_t v) {\n    if (v < 0) {\n        /* Abs value of LLONG_MIN requires special handling. */\n        uint64_t uv = (v != LLONG_MIN) ?\n                      (uint64_t)-v : ((uint64_t) LLONG_MAX)+1;\n        return digits10(uv)+1; /* +1 for the minus. */\n    } else {\n        return digits10(v);\n    }\n}\n\n/* Convert a long long into a string. Returns the number of\n * characters needed to represent the number.\n * If the buffer is not big enough to store the string, 0 is returned.\n *\n * Based on the following article (that apparently does not provide a\n * novel approach but only publicizes an already used technique):\n *\n * https://www.facebook.com/notes/facebook-engineering/three-optimization-tips-for-c/10151361643253920\n *\n * Modified in order to handle signed integers since the original code was\n * designed for unsigned integers. */\nint\nll2string(char* dst, size_t dstlen, long long svalue) {\n    static const char digits[201] =\n        \"0001020304050607080910111213141516171819\"\n        \"2021222324252627282930313233343536373839\"\n        \"4041424344454647484950515253545556575859\"\n        \"6061626364656667686970717273747576777879\"\n        \"8081828384858687888990919293949596979899\";\n    int negative;\n    unsigned long long value;\n\n    /* The main loop works with 64bit unsigned integers for simplicity, so\n     * we convert the number here and remember if it is negative. */\n    if (svalue < 0) {\n        if (svalue != LLONG_MIN) {\n            value = -svalue;\n        } else {\n            value = ((unsigned long long) LLONG_MAX)+1;\n        }\n        negative = 1;\n    } else {\n        value = svalue;\n        negative = 0;\n    }\n\n    /* Check length. */\n    uint32_t const length = digits10(value)+negative;\n    if (length >= dstlen) return 0;\n\n    /* Null term. */\n    uint32_t next = length;\n    dst[next] = '\\0';\n    next--;\n    while (value >= 100) {\n        int const i = (value % 100) * 2;\n        value /= 100;\n        dst[next] = digits[i + 1];\n        dst[next - 1] = digits[i];\n        next -= 2;\n    }\n\n    /* Handle last 1-2 digits. */\n    if (value < 10) {\n        dst[next] = '0' + (uint32_t) value;\n    } else {\n        int i = (uint32_t) value * 2;\n        dst[next] = digits[i + 1];\n        dst[next - 1] = digits[i];\n    }\n\n    /* Add sign. */\n    if (negative) dst[0] = '-';\n    return length;\n}\n\n/* Convert a string into a long long. Returns 1 if the string could be parsed\n * into a (non-overflowing) long long, 0 otherwise. The value will be set to\n * the parsed value when appropriate. */\nint\nstring2ll(const char *s, size_t slen, long long *value) {\n    const char *p = s;\n    size_t plen = 0;\n    int negative = 0;\n    unsigned long long v;\n\n    if (plen == slen)\n        return 0;\n\n    /* Special case: first and only digit is 0. */\n    if (slen == 1 && p[0] == '0') {\n        if (value != NULL) *value = 0;\n        return 1;\n    }\n\n    if (p[0] == '-') {\n        negative = 1;\n        p++; plen++;\n\n        /* Abort on only a negative sign. */\n        if (plen == slen)\n            return 0;\n    }\n\n    /* First digit should be 1-9, otherwise the string should just be 0. */\n    if (p[0] >= '1' && p[0] <= '9') {\n        v = p[0]-'0';\n        p++; plen++;\n    } else if (p[0] == '0' && slen == 1) {\n        *value = 0;\n        return 1;\n    } else {\n        return 0;\n    }\n\n    while (plen < slen && p[0] >= '0' && p[0] <= '9') {\n        if (v > (ULLONG_MAX / 10)) /* Overflow. */\n            return 0;\n        v *= 10;\n\n        if (v > (ULLONG_MAX - (p[0]-'0'))) /* Overflow. */\n            return 0;\n        v += p[0]-'0';\n\n        p++; plen++;\n    }\n\n    /* Return if not all bytes were used. */\n    if (plen < slen)\n        return 0;\n\n    if (negative) {\n        if (v > ((unsigned long long)(-(LLONG_MIN+1))+1)) /* Overflow. */\n            return 0;\n        if (value != NULL) *value = -v;\n    } else {\n        if (v > LLONG_MAX) /* Overflow. */\n            return 0;\n        if (value != NULL) *value = v;\n    }\n    return 1;\n}\n\n/* Convert a string into a long. Returns 1 if the string could be parsed into a\n * (non-overflowing) long, 0 otherwise. The value will be set to the parsed\n * value when appropriate. */\nint string2l(const char *s, size_t slen, long *lval) {\n    long long llval;\n\n    if (!string2ll(s,slen,&llval))\n        return 0;\n\n    if (llval < LONG_MIN || llval > LONG_MAX)\n        return 0;\n\n    *lval = (long)llval;\n    return 1;\n}\n\n/* Convert a double to a string representation. Returns the number of bytes\n * required. The representation should always be parsable by strtod(3). */\nint d2string(char *buf, size_t len, double value) {\n    if (isnan(value)) {\n        len = snprintf(buf,len,\"nan\");\n    } else if (isinf(value)) {\n        if (value < 0)\n            len = snprintf(buf,len,\"-inf\");\n        else\n            len = snprintf(buf,len,\"inf\");\n    } else if (value == 0) {\n        /* See: http://en.wikipedia.org/wiki/Signed_zero, \"Comparisons\". */\n        if (1.0/value < 0)\n            len = snprintf(buf,len,\"-0\");\n        else\n            len = snprintf(buf,len,\"0\");\n    } else {\n#if (DBL_MANT_DIG >= 52) && (LLONG_MAX == 0x7fffffffffffffffLL)\n        /* Check if the float is in a safe range to be casted into a\n         * long long. We are assuming that long long is 64 bit here.\n         * Also we are assuming that there are no implementations around where\n         * double has precision < 52 bit.\n         *\n         * Under this assumptions we test if a double is inside an interval\n         * where casting to long long is safe. Then using two castings we\n         * make sure the decimal part is zero. If all this is true we use\n         * integer printing function that is much faster. */\n        double min = -4503599627370495; /* (2^52)-1 */\n        double max = 4503599627370496; /* -(2^52) */\n        if (value > min && value < max && value == ((double)((long long)value)))\n            len = ll2string(buf,len,(long long)value);\n        else\n#endif\n            len = snprintf(buf,len,\"%.17g\",value);\n    }\n\n    return len;\n}\n\nbool\nvr_valid_port(int n)\n{\n    if (n < 1 || n > UINT16_MAX) {\n        return false;\n    }\n\n    return true;\n}\n\n/*\n * Send n bytes on a blocking descriptor\n */\nssize_t\n_vr_sendn(int sd, const void *vptr, size_t n)\n{\n    size_t nleft;\n    ssize_t\tnsend;\n    const char *ptr;\n\n    ptr = vptr;\n    nleft = n;\n    while (nleft > 0) {\n        nsend = send(sd, ptr, nleft, 0);\n        if (nsend < 0) {\n            if (errno == EINTR) {\n                continue;\n            }\n            return nsend;\n        }\n        if (nsend == 0) {\n            return -1;\n        }\n\n        nleft -= (size_t)nsend;\n        ptr += nsend;\n    }\n\n    return (ssize_t)n;\n}\n\n/*\n * Recv n bytes from a blocking descriptor\n */\nssize_t\n_vr_recvn(int sd, void *vptr, size_t n)\n{\n\tsize_t nleft;\n\tssize_t\tnrecv;\n\tchar *ptr;\n\n\tptr = vptr;\n\tnleft = n;\n\twhile (nleft > 0) {\n        nrecv = recv(sd, ptr, nleft, 0);\n        if (nrecv < 0) {\n            if (errno == EINTR) {\n                continue;\n            }\n            return nrecv;\n        }\n        if (nrecv == 0) {\n            break;\n        }\n\n        nleft -= (size_t)nrecv;\n        ptr += nrecv;\n    }\n\n    return (ssize_t)(n - nleft);\n}\n\n/*\n * Return the current time in microseconds since Epoch\n */\nint64_t\nvr_usec_now(void)\n{\n    struct timeval now;\n    int64_t usec;\n    int status;\n\n    status = gettimeofday(&now, NULL);\n    if (status < 0) {\n        log_error(\"gettimeofday failed: %s\", strerror(errno));\n        return -1;\n    }\n\n    usec = (int64_t)now.tv_sec * 1000000LL + (int64_t)now.tv_usec;\n\n    return usec;\n}\n\n/*\n * Return the current time in milliseconds since Epoch\n */\nint64_t\nvr_msec_now(void)\n{\n    return vr_usec_now() / 1000LL;\n}\n\nstatic int\nvr_resolve_inet(sds name, int port, struct sockinfo *si)\n{\n    int status;\n    struct addrinfo *ai, *cai; /* head and current addrinfo */\n    struct addrinfo hints;\n    char *node, service[VR_UINTMAX_MAXLEN];\n    bool found;\n\n    ASSERT(vr_valid_port(port));\n\n    memset(&hints, 0, sizeof(hints));\n    hints.ai_flags = AI_NUMERICSERV;\n    hints.ai_family = AF_UNSPEC;     /* AF_INET or AF_INET6 */\n    hints.ai_socktype = SOCK_STREAM;\n    hints.ai_protocol = 0;\n    hints.ai_addrlen = 0;\n    hints.ai_addr = NULL;\n    hints.ai_canonname = NULL;\n\n    if (name != NULL) {\n        node = (char *)name;\n    } else {\n        /*\n         * If AI_PASSIVE flag is specified in hints.ai_flags, and node is\n         * NULL, then the returned socket addresses will be suitable for\n         * bind(2)ing a socket that will accept(2) connections. The returned\n         * socket address will contain the wildcard IP address.\n         */\n        node = NULL;\n        hints.ai_flags |= AI_PASSIVE;\n    }\n\n    dsnprintf(service, VR_UINTMAX_MAXLEN, \"%d\", port);\n\n    /*\n     * getaddrinfo() returns zero on success or one of the error codes listed\n     * in gai_strerror(3) if an error occurs\n     */\n    status = getaddrinfo(node, service, &hints, &ai);\n    if (status != 0) {\n        log_error(\"address resolution of node '%s' service '%s' failed: %s\",\n                  node, service, gai_strerror(status));\n        return -1;\n    }\n\n    /*\n     * getaddrinfo() can return a linked list of more than one addrinfo,\n     * since we requested for both AF_INET and AF_INET6 addresses and the\n     * host itself can be multi-homed. Since we don't care whether we are\n     * using ipv4 or ipv6, we just use the first address from this collection\n     * in the order in which it was returned.\n     *\n     * The sorting function used within getaddrinfo() is defined in RFC 3484;\n     * the order can be tweaked for a particular system by editing\n     * /etc/gai.conf\n     */\n    for (cai = ai, found = false; cai != NULL; cai = cai->ai_next) {\n        si->family = cai->ai_family;\n        si->addrlen = cai->ai_addrlen;\n        vr_memcpy(&si->addr, cai->ai_addr, si->addrlen);\n        found = true;\n        break;\n    }\n\n    freeaddrinfo(ai);\n\n    return !found ? -1 : 0;\n}\n\nstatic int\nvr_resolve_unix(sds name, struct sockinfo *si)\n{\n    struct sockaddr_un *un;\n\n    if (sdslen(name) >= VR_UNIX_ADDRSTRLEN) {\n        return -1;\n    }\n\n    un = &si->addr.un;\n\n    un->sun_family = AF_UNIX;\n    vr_memcpy(un->sun_path, name, sdslen(name));\n    un->sun_path[sdslen(name)] = '\\0';\n\n    si->family = AF_UNIX;\n    si->addrlen = sizeof(*un);\n    /* si->addr is an alias of un */\n\n    return 0;\n}\n\n/*\n * Resolve a hostname and service by translating it to socket address and\n * return it in si\n *\n * This routine is reentrant\n */\nint\nvr_resolve(sds name, int port, struct sockinfo *si)\n{\n    if (name != NULL && name[0] == '/') {\n        return vr_resolve_unix(name, si);\n    }\n\n    return vr_resolve_inet(name, port, si);\n}\n\nstatic int vr_net_peer_to_string(int fd, char *ip, size_t ip_len, int *port) {\n    struct sockaddr_storage sa;\n    socklen_t salen = sizeof(sa);\n\n    if (getpeername(fd,(struct sockaddr*)&sa,&salen) == -1) goto error;\n    if (ip_len == 0) goto error;\n\n    if (sa.ss_family == AF_INET) {\n        struct sockaddr_in *s = (struct sockaddr_in *)&sa;\n        if (ip) inet_ntop(AF_INET,(void*)&(s->sin_addr),ip,ip_len);\n        if (port) *port = ntohs(s->sin_port);\n    } else if (sa.ss_family == AF_INET6) {\n        struct sockaddr_in6 *s = (struct sockaddr_in6 *)&sa;\n        if (ip) inet_ntop(AF_INET6,(void*)&(s->sin6_addr),ip,ip_len);\n        if (port) *port = ntohs(s->sin6_port);\n    } else if (sa.ss_family == AF_UNIX) {\n        if (ip) strncpy(ip,\"/unixsocket\",ip_len);\n        if (port) *port = 0;\n    } else {\n        goto error;\n    }\n    return 0;\n\nerror:\n    if (ip) {\n        if (ip_len >= 2) {\n            ip[0] = '?';\n            ip[1] = '\\0';\n        } else if (ip_len == 1) {\n            ip[0] = '\\0';\n        }\n    }\n    if (port) *port = 0;\n    return -1;\n}\n\n/* Format an IP,port pair into something easy to parse. If IP is IPv6\n * (matches for \":\"), the ip is surrounded by []. IP and port are just\n * separated by colons. This the standard to display addresses within Redis. */\nstatic int vr_net_format_addr(char *buf, size_t buf_len, char *ip, int port) {\n    return snprintf(buf,buf_len, strchr(ip,':') ?\n           \"[%s]:%d\" : \"%s:%d\", ip, port);\n}\n\n/* Like anetFormatAddr() but extract ip and port from the socket's peer. */\nint vr_net_format_peer(int fd, char *buf, size_t buf_len) {\n    char ip[VR_INET6_ADDRSTRLEN];\n    int port;\n\n    vr_net_peer_to_string(fd,ip,sizeof(ip),&port);\n    return vr_net_format_addr(buf, buf_len, ip, port);\n}\n\n/* Generate the Vire \"Run ID\", a SHA1-sized random number that identifies a\n * given execution of Vire, so that if you are talking with an instance\n * having run_id == A, and you reconnect and it has run_id == B, you can be\n * sure that it is either a different instance or it was restarted. */\nvoid\nget_random_hex_chars(char *p, unsigned int len) {\n    char *charset = \"0123456789abcdef\";\n    unsigned int j;\n\n    /* Global state. */\n    static int seed_initialized = 0;\n    static unsigned char seed[20]; /* The SHA1 seed, from /dev/urandom. */\n    static uint64_t counter = 0; /* The counter we hash with the seed. */\n\n    if (!seed_initialized) {\n        /* Initialize a seed and use SHA1 in counter mode, where we hash\n         * the same seed with a progressive counter. For the goals of this\n         * function we just need non-colliding strings, there are no\n         * cryptographic security needs. */\n        FILE *fp = fopen(\"/dev/urandom\",\"r\");\n        if (fp && fread(seed,sizeof(seed),1,fp) == 1)\n            seed_initialized = 1;\n        if (fp) fclose(fp);\n    }\n\n    if (seed_initialized) {\n        while(len) {\n            unsigned char digest[20];\n            SHA1_CTX ctx;\n            unsigned int copylen = len > 20 ? 20 : len;\n\n            SHA1Init(&ctx);\n            SHA1Update(&ctx, seed, sizeof(seed));\n            SHA1Update(&ctx, (unsigned char*)&counter,sizeof(counter));\n            SHA1Final(digest, &ctx);\n            counter++;\n\n            memcpy(p,digest,copylen);\n            /* Convert to hex digits. */\n            for (j = 0; j < copylen; j++) p[j] = charset[p[j] & 0x0F];\n            len -= copylen;\n            p += copylen;\n        }\n    } else {\n        /* If we can't read from /dev/urandom, do some reasonable effort\n         * in order to create some entropy, since this function is used to\n         * generate run_id and cluster instance IDs */\n        char *x = p;\n        unsigned int l = len;\n        struct timeval tv;\n        pid_t pid = getpid();\n\n        /* Use time and PID to fill the initial array. */\n        gettimeofday(&tv,NULL);\n        if (l >= sizeof(tv.tv_usec)) {\n            memcpy(x,&tv.tv_usec,sizeof(tv.tv_usec));\n            l -= sizeof(tv.tv_usec);\n            x += sizeof(tv.tv_usec);\n        }\n        if (l >= sizeof(tv.tv_sec)) {\n            memcpy(x,&tv.tv_sec,sizeof(tv.tv_sec));\n            l -= sizeof(tv.tv_sec);\n            x += sizeof(tv.tv_sec);\n        }\n        if (l >= sizeof(pid)) {\n            memcpy(x,&pid,sizeof(pid));\n            l -= sizeof(pid);\n            x += sizeof(pid);\n        }\n        /* Finally xor it with rand() output, that was already seeded with\n         * time() at startup, and convert to hex digits. */\n        for (j = 0; j < len; j++) {\n            p[j] ^= rand();\n            p[j] = charset[p[j] & 0x0F];\n        }\n    }\n}\n\n/* Glob-style pattern matching. */\nint stringmatchlen(const char *pattern, int patternLen,\n        const char *string, int stringLen, int nocase)\n{\n    while(patternLen) {\n        switch(pattern[0]) {\n        case '*':\n            while (pattern[1] == '*') {\n                pattern++;\n                patternLen--;\n            }\n            if (patternLen == 1)\n                return 1; /* match */\n            while(stringLen) {\n                if (stringmatchlen(pattern+1, patternLen-1,\n                            string, stringLen, nocase))\n                    return 1; /* match */\n                string++;\n                stringLen--;\n            }\n            return 0; /* no match */\n            break;\n        case '?':\n            if (stringLen == 0)\n                return 0; /* no match */\n            string++;\n            stringLen--;\n            break;\n        case '[':\n        {\n            int not, match;\n\n            pattern++;\n            patternLen--;\n            not = pattern[0] == '^';\n            if (not) {\n                pattern++;\n                patternLen--;\n            }\n            match = 0;\n            while(1) {\n                if (pattern[0] == '\\\\') {\n                    pattern++;\n                    patternLen--;\n                    if (pattern[0] == string[0])\n                        match = 1;\n                } else if (pattern[0] == ']') {\n                    break;\n                } else if (patternLen == 0) {\n                    pattern--;\n                    patternLen++;\n                    break;\n                } else if (pattern[1] == '-' && patternLen >= 3) {\n                    int start = pattern[0];\n                    int end = pattern[2];\n                    int c = string[0];\n                    if (start > end) {\n                        int t = start;\n                        start = end;\n                        end = t;\n                    }\n                    if (nocase) {\n                        start = tolower(start);\n                        end = tolower(end);\n                        c = tolower(c);\n                    }\n                    pattern += 2;\n                    patternLen -= 2;\n                    if (c >= start && c <= end)\n                        match = 1;\n                } else {\n                    if (!nocase) {\n                        if (pattern[0] == string[0])\n                            match = 1;\n                    } else {\n                        if (tolower((int)pattern[0]) == tolower((int)string[0]))\n                            match = 1;\n                    }\n                }\n                pattern++;\n                patternLen--;\n            }\n            if (not)\n                match = !match;\n            if (!match)\n                return 0; /* no match */\n            string++;\n            stringLen--;\n            break;\n        }\n        case '\\\\':\n            if (patternLen >= 2) {\n                pattern++;\n                patternLen--;\n            }\n            /* fall through */\n        default:\n            if (!nocase) {\n                if (pattern[0] != string[0])\n                    return 0; /* no match */\n            } else {\n                if (tolower((int)pattern[0]) != tolower((int)string[0]))\n                    return 0; /* no match */\n            }\n            string++;\n            stringLen--;\n            break;\n        }\n        pattern++;\n        patternLen--;\n        if (stringLen == 0) {\n            while(*pattern == '*') {\n                pattern++;\n                patternLen--;\n            }\n            break;\n        }\n    }\n    if (patternLen == 0 && stringLen == 0)\n        return 1;\n    return 0;\n}\n\nint stringmatch(const char *pattern, const char *string, int nocase) {\n    return stringmatchlen(pattern,strlen(pattern),string,strlen(string),nocase);\n}\n\n/* Toggle the 16 bit unsigned integer pointed by *p from little endian to\n * big endian */\nvoid memrev16(void *p) {\n    unsigned char *x = p, t;\n\n    t = x[0];\n    x[0] = x[1];\n    x[1] = t;\n}\n\n/* Toggle the 32 bit unsigned integer pointed by *p from little endian to\n * big endian */\nvoid memrev32(void *p) {\n    unsigned char *x = p, t;\n\n    t = x[0];\n    x[0] = x[3];\n    x[3] = t;\n    t = x[1];\n    x[1] = x[2];\n    x[2] = t;\n}\n\n/* Toggle the 64 bit unsigned integer pointed by *p from little endian to\n * big endian */\nvoid memrev64(void *p) {\n    unsigned char *x = p, t;\n\n    t = x[0];\n    x[0] = x[7];\n    x[7] = t;\n    t = x[1];\n    x[1] = x[6];\n    x[6] = t;\n    t = x[2];\n    x[2] = x[5];\n    x[5] = t;\n    t = x[3];\n    x[3] = x[4];\n    x[4] = t;\n}\n\nuint16_t intrev16(uint16_t v) {\n    memrev16(&v);\n    return v;\n}\n\nuint32_t intrev32(uint32_t v) {\n    memrev32(&v);\n    return v;\n}\n\nuint64_t intrev64(uint64_t v) {\n    memrev64(&v);\n    return v;\n}\n\n/* Convert a string representing an amount of memory into the number of\n * bytes, so for instance memtoll(\"1Gb\") will return 1073741824 that is\n * (1024*1024*1024).\n *\n * On parsing error, if *err is not NULL, it's set to 1, otherwise it's\n * set to 0. On error the function return value is 0, regardless of the\n * fact 'err' is NULL or not. */\nlong long memtoll(const char *p, int *err) {\n    const char *u;\n    char buf[128];\n    long mul; /* unit multiplier */\n    long long val;\n    unsigned int digits;\n\n    if (err) *err = 0;\n\n    /* Search the first non digit character. */\n    u = p;\n    if (*u == '-') u++;\n    while(*u && isdigit(*u)) u++;\n    if (*u == '\\0' || !strcasecmp(u,\"b\")) {\n        mul = 1;\n    } else if (!strcasecmp(u,\"k\")) {\n        mul = 1000;\n    } else if (!strcasecmp(u,\"kb\")) {\n        mul = 1024;\n    } else if (!strcasecmp(u,\"m\")) {\n        mul = 1000*1000;\n    } else if (!strcasecmp(u,\"mb\")) {\n        mul = 1024*1024;\n    } else if (!strcasecmp(u,\"g\")) {\n        mul = 1000L*1000*1000;\n    } else if (!strcasecmp(u,\"gb\")) {\n        mul = 1024L*1024*1024;\n    } else {\n        if (err) *err = 1;\n        return 0;\n    }\n\n    /* Copy the digits into a buffer, we'll use strtoll() to convert\n     * the digit (without the unit) into a number. */\n    digits = u-p;\n    if (digits >= sizeof(buf)) {\n        if (err) *err = 1;\n        return 0;\n    }\n    memcpy(buf,p,digits);\n    buf[digits] = '\\0';\n\n    char *endptr;\n    errno = 0;\n    val = strtoll(buf,&endptr,10);\n    if ((val == 0 && errno == EINVAL) || *endptr != '\\0') {\n        if (err) *err = 1;\n        return 0;\n    }\n    return val*mul;\n}\n\n/* Convert an amount of bytes into a human readable string in the form\n * of 100B, 2G, 100M, 4K, and so forth. */\nvoid bytesToHuman(char *s, unsigned long long n) {\n    double d;\n\n    if (n < 1024) {\n        /* Bytes */\n        sprintf(s,\"%lluB\",n);\n        return;\n    } else if (n < (1024*1024)) {\n        d = (double)n/(1024);\n        sprintf(s,\"%.2fK\",d);\n    } else if (n < (1024LL*1024*1024)) {\n        d = (double)n/(1024*1024);\n        sprintf(s,\"%.2fM\",d);\n    } else if (n < (1024LL*1024*1024*1024)) {\n        d = (double)n/(1024LL*1024*1024);\n        sprintf(s,\"%.2fG\",d);\n    } else if (n < (1024LL*1024*1024*1024*1024)) {\n        d = (double)n/(1024LL*1024*1024*1024);\n        sprintf(s,\"%.2fT\",d);\n    } else if (n < (1024LL*1024*1024*1024*1024*1024)) {\n        d = (double)n/(1024LL*1024*1024*1024*1024);\n        sprintf(s,\"%.2fP\",d);\n    } else {\n        /* Let's hope we never need this */\n        sprintf(s,\"%lluB\",n);\n    }\n}\n\n/* Given the filename, return the absolute path as an SDS string, or NULL\n * if it fails for some reason. Note that \"filename\" may be an absolute path\n * already, this will be detected and handled correctly.\n *\n * The function does not try to normalize everything, but only the obvious\n * case of one or more \"../\" appearning at the start of \"filename\"\n * relative path. */\nsds getAbsolutePath(char *filename) {\n    char cwd[1024];\n    sds abspath;\n    sds relpath = sdsnew(filename);\n\n    relpath = sdstrim(relpath,\" \\r\\n\\t\");\n    if (relpath[0] == '/') return relpath; /* Path is already absolute. */\n\n    /* If path is relative, join cwd and relative path. */\n    if (getcwd(cwd,sizeof(cwd)) == NULL) {\n        sdsfree(relpath);\n        return NULL;\n    }\n    abspath = sdsnew(cwd);\n    if (sdslen(abspath) && abspath[sdslen(abspath)-1] != '/')\n        abspath = sdscat(abspath,\"/\");\n\n    /* At this point we have the current path always ending with \"/\", and\n     * the trimmed relative path. Try to normalize the obvious case of\n     * trailing ../ elements at the start of the path.\n     *\n     * For every \"../\" we find in the filename, we remove it and also remove\n     * the last element of the cwd, unless the current cwd is \"/\". */\n    while (sdslen(relpath) >= 3 &&\n           relpath[0] == '.' && relpath[1] == '.' && relpath[2] == '/')\n    {\n        sdsrange(relpath,3,-1);\n        if (sdslen(abspath) > 1) {\n            char *p = abspath + sdslen(abspath)-2;\n            int trimlen = 1;\n\n            while(*p != '/') {\n                p--;\n                trimlen++;\n            }\n            sdsrange(abspath,0,-(trimlen+1));\n        }\n    }\n\n    /* Finally glue the two parts together. */\n    abspath = sdscatsds(abspath,relpath);\n    sdsfree(relpath);\n    return abspath;\n}\n"
  },
  {
    "path": "src/vr_util.h",
    "content": "#ifndef _VR_UTIL_H_\n#define _VR_UTIL_H_\n\n#include <stdarg.h>\n#include <stdint.h>\n#include <stdbool.h>\n\n#include <netinet/in.h>\n#include <sys/un.h>\n\n/* Double expansion needed for stringification of macro values. */\n#define __xstr(s) __str(s)\n#define __str(s) #s\n\n#define VR_INET4_ADDRSTRLEN (sizeof(\"255.255.255.255\") - 1)\n#define VR_INET6_ADDRSTRLEN \\\n    (sizeof(\"ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255\") - 1)\n#define VR_INET_ADDRSTRLEN  MAX(VR_INET4_ADDRSTRLEN, VR_INET6_ADDRSTRLEN)\n#define VR_UNIX_ADDRSTRLEN  \\\n    (sizeof(struct sockaddr_un) - offsetof(struct sockaddr_un, sun_path))\n    \n#define VR_INET_PEER_ID_LEN (VR_INET_ADDRSTRLEN+32) /* Must be enough for ip:port */\n\n#define VR_MAXHOSTNAMELEN   256\n\n/*\n * Length of 1 byte, 2 bytes, 4 bytes, 8 bytes and largest integral\n * type (uintmax_t) in ascii, including the null terminator '\\0'\n *\n * From stdint.h, we have:\n * # define UINT8_MAX\t(255)\n * # define UINT16_MAX\t(65535)\n * # define UINT32_MAX\t(4294967295U)\n * # define UINT64_MAX\t(__UINT64_C(18446744073709551615))\n */\n#define VR_UINT8_MAXLEN     (3 + 1)\n#define VR_UINT16_MAXLEN    (5 + 1)\n#define VR_UINT32_MAXLEN    (10 + 1)\n#define VR_UINT64_MAXLEN    (20 + 1)\n#define VR_UINTMAX_MAXLEN   VR_UINT64_MAXLEN\n\n#define LONG_STR_SIZE       21  /* Bytes needed for long -> str */\n\n/*\n * Make data 'd' or pointer 'p', n-byte aligned, where n is a power of 2\n * of 2.\n */\n#define VR_ALIGNMENT        sizeof(unsigned long) /* platform word */\n#define VR_ALIGN(d, n)      (((d) + (n - 1)) & ~(n - 1))\n#define VR_ALIGN_PTR(p, n)  \\\n    (void *) (((uintptr_t) (p) + ((uintptr_t) n - 1)) & ~((uintptr_t) n - 1))\n\n/*\n * Wrapper to workaround well known, safe, implicit type conversion when\n * invoking system calls.\n */\n#define vr_gethostname(_name, _len) \\\n    gethostname((char *)_name, (size_t)_len)\n\n#define vr_atoi(_line, _n)          \\\n    _vr_atoi((char *)_line, (size_t)_n)\n\nint vr_set_blocking(int sd);\nint vr_set_nonblocking(int sd);\nint vr_set_reuseaddr(int sd);\nint vr_set_tcpnodelay(int sd);\nint vr_set_linger(int sd, int timeout);\nint vr_set_sndbuf(int sd, int size);\nint vr_set_rcvbuf(int sd, int size);\nint vr_get_soerror(int sd);\nint vr_get_sndbuf(int sd);\nint vr_get_rcvbuf(int sd);\nint vr_set_tcpkeepalive(int sd, int keepidle, int keepinterval, int keepcount);\n\nint _vr_atoi(char *line, size_t n);\nuint32_t digits10(uint64_t v);\nuint32_t sdigits10(int64_t v);\nint ll2string(char* dst, size_t dstlen, long long svalue);\nint string2ll(const char *s, size_t slen, long long *value);\nint string2l(const char *s, size_t slen, long *lval);\nint d2string(char *buf, size_t len, double value);\n\nbool vr_valid_port(int n);\n\n/*\n * Wrappers to send or receive n byte message on a blocking\n * socket descriptor.\n */\n#define vr_sendn(_s, _b, _n)    \\\n    _vr_sendn(_s, _b, (size_t)(_n))\n\n#define vr_recvn(_s, _b, _n)    \\\n    _vr_recvn(_s, _b, (size_t)(_n))\n\n/*\n * Wrappers to read or write data to/from (multiple) buffers\n * to a file or socket descriptor.\n */\n#define vr_read(_d, _b, _n)     \\\n    read(_d, _b, (size_t)(_n))\n\n#define vr_readv(_d, _b, _n)    \\\n    readv(_d, _b, (int)(_n))\n\n#define vr_write(_d, _b, _n)    \\\n    write(_d, _b, (size_t)(_n))\n\n#define vr_writev(_d, _b, _n)   \\\n    writev(_d, _b, (int)(_n))\n\nssize_t _vr_sendn(int sd, const void *vptr, size_t n);\nssize_t _vr_recvn(int sd, void *vptr, size_t n);\n\nint64_t vr_usec_now(void);\nint64_t vr_msec_now(void);\n\n/*\n * Address resolution for internet (ipv4 and ipv6) and unix domain\n * socket address.\n */\n\nstruct sockinfo {\n    int       family;              /* socket address family */\n    socklen_t addrlen;             /* socket address length */\n    union {\n        struct sockaddr_in  in;    /* ipv4 socket address */\n        struct sockaddr_in6 in6;   /* ipv6 socket address */\n        struct sockaddr_un  un;    /* unix domain address */\n    } addr;\n};\n\nint vr_resolve(sds name, int port, struct sockinfo *si);\nint vr_net_format_peer(int fd, char *buf, size_t buf_len);\n\nvoid get_random_hex_chars(char *p, unsigned int len);\n\nint stringmatchlen(const char *pattern, int patternLen, const char *string, int stringLen, int nocase);\nint stringmatch(const char *pattern, const char *string, int nocase);\n\n/*\n * Wrapper around common routines for manipulating C character\n * strings\n */\n#define vr_memcpy(_d, _c, _n)           \\\n    memcpy(_d, _c, (size_t)(_n))\n\n#define vr_memmove(_d, _c, _n)          \\\n    memmove(_d, _c, (size_t)(_n))\n\n#define vr_memchr(_d, _c, _n)           \\\n    memchr(_d, _c, (size_t)(_n))\n\n#define vr_strlen(_s)                   \\\n    strlen((char *)(_s))\n\n#define vr_strncmp(_s1, _s2, _n)        \\\n    strncmp((char *)(_s1), (char *)(_s2), (size_t)(_n))\n\n#define vr_strchr(_p, _l, _c)           \\\n    _vr_strchr((uint8_t *)(_p), (uint8_t *)(_l), (uint8_t)(_c))\n\n#define vr_strrchr(_p, _s, _c)          \\\n    _vr_strrchr((uint8_t *)(_p),(uint8_t *)(_s), (uint8_t)(_c))\n\n#define vr_strndup(_s, _n)              \\\n    (uint8_t *)strndup((char *)(_s), (size_t)(_n));\n\nstatic inline uint8_t *\n_vr_strchr(uint8_t *p, uint8_t *last, uint8_t c)\n{\n    while (p < last) {\n        if (*p == c) {\n            return p;\n        }\n        p++;\n    }\n\n    return NULL;\n}\n\nstatic inline uint8_t *\n_vr_strrchr(uint8_t *p, uint8_t *start, uint8_t c)\n{\n    while (p >= start) {\n        if (*p == c) {\n            return p;\n        }\n        p--;\n    }\n\n    return NULL;\n}\n\nvoid memrev16(void *p);\nvoid memrev32(void *p);\nvoid memrev64(void *p);\nuint16_t intrev16(uint16_t v);\nuint32_t intrev32(uint32_t v);\nuint64_t intrev64(uint64_t v);\n\n/* variants of the function doing the actual convertion only if the target\n * host is big endian */\n#ifdef VR_LITTLE_ENDIAN\n#define memrev16ifbe(p)\n#define memrev32ifbe(p)\n#define memrev64ifbe(p)\n#define intrev16ifbe(v) (v)\n#define intrev32ifbe(v) (v)\n#define intrev64ifbe(v) (v)\n#else\n#define memrev16ifbe(p) memrev16(p)\n#define memrev32ifbe(p) memrev32(p)\n#define memrev64ifbe(p) memrev64(p)\n#define intrev16ifbe(v) intrev16(v)\n#define intrev32ifbe(v) intrev32(v)\n#define intrev64ifbe(v) intrev64(v)\n#endif\n\nlong long memtoll(const char *p, int *err);\nvoid bytesToHuman(char *s, unsigned long long n);\n\nsds getAbsolutePath(char *filename);\n\n#endif\n"
  },
  {
    "path": "src/vr_worker.c",
    "content": "#include <vr_core.h>\n\n/* Which thread we assigned a connection to most recently. */\nstatic int last_worker_thread = -1;\nstatic int num_worker_threads;\n\nstruct darray workers;\n\nstatic void *worker_thread_run(void *args);\n\n#define SU_PER_ALLOC 64\n\n/* Free list of swapunit structs */\nstatic struct connswapunit *csui_freelist;\nstatic pthread_mutex_t csui_freelist_lock;\n\n/*\n * Returns a fresh connection connswapunit queue item.\n */\nstruct connswapunit *\ncsui_new(void) {\n    struct connswapunit *item = NULL;\n    pthread_mutex_lock(&csui_freelist_lock);\n    if (csui_freelist) {\n        item = csui_freelist;\n        csui_freelist = item->next;\n    }\n    pthread_mutex_unlock(&csui_freelist_lock);\n\n    if (NULL == item) {\n        int i;\n\n        /* Allocate a bunch of items at once to reduce fragmentation */\n        item = dalloc(sizeof(struct connswapunit) * SU_PER_ALLOC);\n        if (NULL == item) {\n            return NULL;\n        }\n\n        /*\n         * Link together all the new items except the first one\n         * (which we'll return to the caller) for placement on\n         * the freelist.\n         */\n        for (i = 2; i < SU_PER_ALLOC; i++)\n            item[i - 1].next = &item[i];\n\n        pthread_mutex_lock(&csui_freelist_lock);\n        item[SU_PER_ALLOC - 1].next = csui_freelist;\n        csui_freelist = &item[1];\n        pthread_mutex_unlock(&csui_freelist_lock);\n    }\n\n    return item;\n}\n\n/*\n * Frees a connection connswapunit queue item (adds it to the freelist.)\n */\nvoid \ncsui_free(struct connswapunit *item) {\n    pthread_mutex_lock(&csui_freelist_lock);\n    item->next = csui_freelist;\n    csui_freelist = item;\n    pthread_mutex_unlock(&csui_freelist_lock);\n}\n\nvoid\ncsul_push(vr_worker *worker, struct connswapunit *su)\n{\n    pthread_mutex_lock(&worker->csullock);\n    dlistPush(worker->csul, su);\n    pthread_mutex_unlock(&worker->csullock);\n}\n\nstruct connswapunit *\ncsul_pop(vr_worker *worker)\n{\n    struct connswapunit *su = NULL;\n\n    pthread_mutex_lock(&worker->csullock);\n    su = dlistPop(worker->csul);\n    pthread_mutex_unlock(&worker->csullock);\n    \n    return su;\n}\n\nint\nvr_worker_init(vr_worker *worker)\n{\n    rstatus_t status;\n    int maxclients, threads_num;\n    int filelimit;\n    \n    if (worker == NULL) {\n        return VR_ERROR;\n    }\n\n    worker->id = 0;\n    worker->socketpairs[0] = -1;\n    worker->socketpairs[1] = -1;\n    worker->csul = NULL;\n    pthread_mutex_init(&worker->csullock, NULL);\n    worker->current_db = 0;\n    worker->timelimit_exit = 0;\n    worker->last_fast_cycle = 0;\n    worker->resize_db = 0;\n    worker->rehash_db = 0;\n\n    conf_server_get(CONFIG_SOPN_MAXCLIENTS,&maxclients);\n    filelimit = adjustOpenFilesLimit(maxclients);\n    if (filelimit <= 0) {\n        return VR_ERROR;\n    }\n    vr_eventloop_init(&worker->vel, filelimit);\n    worker->vel.thread.fun_run = worker_thread_run;\n    worker->vel.thread.data = worker;\n    worker->vel.cstable = commandStatsTableCreate();\n\n    status = socketpair(AF_LOCAL, SOCK_STREAM, 0, worker->socketpairs);\n    if (status < 0) {\n        log_error(\"create socketpairs failed: %s\", strerror(errno));\n        return VR_ERROR;\n    }\n    status = vr_set_nonblocking(worker->socketpairs[0]);\n    if (status < 0) {\n        log_error(\"set socketpairs[0] %d nonblocking failed: %s\", \n            worker->socketpairs[0], strerror(errno));\n        close(worker->socketpairs[0]);\n        worker->socketpairs[0] = -1;\n        close(worker->socketpairs[1]);\n        worker->socketpairs[1] = -1;\n        return VR_ERROR;\n    }\n    status = vr_set_nonblocking(worker->socketpairs[1]);\n    if (status < 0) {\n        log_error(\"set socketpairs[1] %d nonblocking failed: %s\", \n            worker->socketpairs[1], strerror(errno));\n        close(worker->socketpairs[0]);\n        worker->socketpairs[0] = -1;\n        close(worker->socketpairs[1]);\n        worker->socketpairs[1] = -1;\n        return VR_ERROR;\n    }\n\n    worker->csul = dlistCreate();\n    if (worker->csul == NULL) {\n        log_error(\"create list failed: out of memory\");\n        return VR_ENOMEM;\n    }\n    \n    return VR_OK;\n}\n\nvoid\nvr_worker_deinit(vr_worker *worker)\n{\n    if (worker == NULL) {\n        return;\n    }\n\n    vr_eventloop_deinit(&worker->vel);\n\n    if (worker->socketpairs[0] > 0){\n        close(worker->socketpairs[0]);\n        worker->socketpairs[0] = -1;\n    }\n    if (worker->socketpairs[1] > 0){\n        close(worker->socketpairs[1]);\n        worker->socketpairs[1] = -1;\n    }\n\n    if (worker->csul != NULL) {\n        dlistRelease(worker->csul);\n        worker->csul = NULL;\n    }\n}\n\nint\nworker_get_next_idx(int curidx)\n{\n    int idx = curidx + 1;\n    return idx>=num_worker_threads?0:idx;\n}\n\nvoid\ndispatch_conn_new(vr_listen *vlisten, int sd)\n{\n    struct connswapunit *su = csui_new();\n    char buf[1];\n    vr_worker *worker;\n\n    if (su == NULL) {\n        close(sd);\n        /* given that malloc failed this may also fail, but let's try */\n        log_error(\"Failed to allocate memory for connection swap object\\n\");\n        return ;\n    }\n    \n    int tid = (last_worker_thread + 1) % num_worker_threads;\n    worker = darray_get(&workers, (uint32_t)tid);\n\n    last_worker_thread = tid;\n\n    su->num = sd;\n    su->data = vlisten;\n\n    csul_push(worker, su);\n\n    buf[0] = 'c';\n    if (vr_write(worker->socketpairs[0], buf, 1) != 1) {\n        log_error(\"Notice the worker failed.\");\n    }\n    \n    update_curr_clients_add(1);\n}\n\nstatic void\nthread_event_process(aeEventLoop *el, int fd, void *privdata, int mask) {\n\n    rstatus_t status;\n    vr_worker *worker = privdata;\n    char buf[1];\n    int sd;\n    vr_listen *vlisten;\n    struct conn *conn;\n    struct connswapunit *csu;\n    client *c;\n\n    ASSERT(el == worker->vel.el);\n    ASSERT(fd == worker->socketpairs[1]);\n\n    if (vr_read(fd, buf, 1) != 1) {\n        log_warn(\"Can't read for worker(id:%d) socketpairs[1](%d)\", \n            worker->vel.thread.id, fd);\n        buf[0] = 'c';\n    }\n    \n    switch (buf[0]) {\n    case 'c':\n        csu = csul_pop(worker);\n        if (csu == NULL) {\n            return;\n        }\n        sd = csu->num;\n        vlisten = csu->data;\n        csui_free(csu);\n        conn = conn_get(worker->vel.cb);\n        if (conn == NULL) {\n            log_error(\"get conn for c %d failed: %s\", \n                sd, strerror(errno));\n            status = close(sd);\n            if (status < 0) {\n                log_error(\"close c %d failed, ignored: %s\", sd, strerror(errno));\n            }\n            return;\n        }\n        conn->sd = sd;\n    \n        status = vr_set_nonblocking(conn->sd);\n        if (status < 0) {\n            log_error(\"set nonblock on c %d failed: %s\", \n                conn->sd, strerror(errno));\n            conn_put(conn);\n            return;\n        }\n    \n        if (vlisten->info.family == AF_INET || vlisten->info.family == AF_INET6) {\n            status = vr_set_tcpnodelay(conn->sd);\n            if (status < 0) {\n                log_warn(\"set tcpnodelay on c %d failed, ignored: %s\",\n                    conn->sd, strerror(errno));\n            }\n        }\n\n        c = createClient(&worker->vel, conn);\n        if (c == NULL) {\n            log_error(\"Create client failed\");\n            conn_put(conn);\n            return;\n        }\n        c->curidx = worker->id;\n        status = aeCreateFileEvent(worker->vel.el, conn->sd, AE_READABLE, \n            readQueryFromClient, c);\n        if (status == AE_ERR) {\n            log_error(\"Unrecoverable error creating worker ipfd file event.\");\n            return;\n        }\n\n        update_stats_add(c->vel->stats, numconnections, 1);\n        \n        break;\n    case 'j':\n        csu = csul_pop(worker);\n        if (csu == NULL) {\n            return;\n        }\n        c = csu->data;\n        csui_free(csu);\n        c->vel = &worker->vel;\n        c->curidx = worker->id;\n        c->steps ++;\n        c->cmd->proc(c);\n        \n        if (c->flags&CLIENT_JUMP) {\n            dispatch_conn_exist(c,c->taridx);\n        } else {\n            resetClient(c);\n            linkClientToEventloop(c,c->vel);\n        }\n        break;\n    default:\n        log_error(\"read error char '%c' for worker(id:%d) socketpairs[1](%d)\", \n            buf[0], worker->vel.thread.id, worker->socketpairs[1]);\n        break;\n    }\n}\n\nstatic int\nsetup_worker(vr_worker *worker)\n{\n    rstatus_t status;\n    \n    status = aeCreateFileEvent(worker->vel.el, worker->socketpairs[1], AE_READABLE, \n        thread_event_process, worker);\n    if (status == AE_ERR) {\n        log_error(\"Unrecoverable error creating worker ipfd file event.\");\n        return VR_ERROR;\n    }\n\n    aeSetBeforeSleepProc(worker->vel.el, worker_before_sleep, worker);\n\n    /* Create the serverCron() time event, that's our main way to process\n     * background operations. */\n    if(aeCreateTimeEvent(worker->vel.el, 1, worker_cron, worker, NULL) == AE_ERR) {\n        serverPanic(\"Can't create the serverCron time event.\");\n        return VR_ERROR;\n    }\n    \n    return VR_OK;\n}\n\nstatic void *\nworker_thread_run(void *args)\n{\n    vr_worker *worker = args;\n    \n    /* vire worker run */\n    aeMain(worker->vel.el);\n\n    return NULL;\n}\n\nint\nworkers_init(uint32_t worker_count)\n{\n    rstatus_t status;\n    uint32_t idx;\n    vr_worker *worker;\n    \n    csui_freelist = NULL;\n    pthread_mutex_init(&csui_freelist_lock, NULL);\n\n    darray_init(&workers, worker_count, sizeof(vr_worker));\n\n    for (idx = 0; idx < worker_count; idx ++) {\n        worker = darray_push(&workers);\n        vr_worker_init(worker);\n        worker->id = idx;\n        status = setup_worker(worker);\n        if (status != VR_OK) {\n            exit(1);\n        }\n    }\n    \n    num_worker_threads = (int)darray_n(&workers);\n\n    return VR_OK;\n}\n\nint\nworkers_run(void)\n{\n    uint32_t i, thread_count;\n    vr_worker *worker;\n\n    thread_count = (uint32_t)num_worker_threads;\n\n    for (i = 0; i < thread_count; i ++) {\n        worker = darray_get(&workers, i);\n        vr_thread_start(&worker->vel.thread);\n    }\n\n    return VR_OK;\n}\n\nint\nworkers_wait(void)\n{\n    uint32_t i, thread_count;\n    vr_worker *worker;\n\n    thread_count = (uint32_t)num_worker_threads;\n\n    for (i = 0; i < thread_count; i ++) {\n        worker = darray_get(&workers, i);\n        pthread_join(worker->vel.thread.thread_id, NULL);\n    }\n\n    return VR_OK;\n}\n\nvoid\nworkers_deinit(void)\n{\n    vr_worker *worker;\n\n    while(darray_n(&workers)) {\n        worker = darray_pop(&workers);\n\t\tvr_worker_deinit(worker);\n    }\n}\n\n/* This function gets called every time Redis is entering the\n * main loop of the event driven library, that is, before to sleep\n * for ready file descriptors. */\nvoid\nworker_before_sleep(struct aeEventLoop *eventLoop, void *private_data) {\n    vr_worker *worker = private_data;\n\n    UNUSED(eventLoop);\n    UNUSED(private_data);\n\n    ASSERT(eventLoop == worker->vel.el);\n\n    /* Handle writes with pending output buffers. */\n    handleClientsWithPendingWrites(&worker->vel);\n\n    //activeExpireCycle(worker, ACTIVE_EXPIRE_CYCLE_FAST);\n}\n\nint\nworker_cron(struct aeEventLoop *eventLoop, long long id, void *clientData) {\n    vr_worker *worker = clientData;\n    vr_eventloop *vel = &worker->vel;\n    size_t stat_used_memory, stats_peak_memory;\n\n    UNUSED(eventLoop);\n    UNUSED(id);\n    UNUSED(clientData);\n\n    ASSERT(eventLoop == vel->el);\n\n    vel->unixtime = time(NULL);\n    vel->mstime = vr_msec_now();\n\n    run_with_period(100, vel->cronloops) {\n        long long stats_value;\n        update_stats_get(vel->stats,numcommands,&stats_value);\n        trackInstantaneousMetric(vel->stats,STATS_METRIC_COMMAND,stats_value);\n        update_stats_get(vel->stats,net_input_bytes,&stats_value);\n        trackInstantaneousMetric(vel->stats,STATS_METRIC_NET_INPUT,stats_value);\n        update_stats_get(vel->stats,net_output_bytes,&stats_value);\n        trackInstantaneousMetric(vel->stats,STATS_METRIC_NET_OUTPUT,stats_value);\n    }\n\n    /* Sample the RSS here since this is a relatively slow call. */\n    run_with_period(1000, vel->cronloops) {\n        vel->resident_set_size = dalloc_get_rss();\n    }\n\n    /* Record the max memory used since the server was started. */\n    /*stat_used_memory = dalloc_used_memory();\n    update_stats_get(vel->stats, peak_memory, &stats_peak_memory);\n    if (stat_used_memory > stats_peak_memory) {\n        update_stats_set(vel->stats, peak_memory, stat_used_memory);\n    }*/\n\n    /* Close clients that need to be closed asynchronous */\n    freeClientsInAsyncFreeQueue(vel);\n\n    //databasesCron(worker);\n\n    /* Update the config cache */\n    run_with_period(1000, vel->cronloops) {\n        conf_cache_update(&vel->cc);\n    }\n    \n    vel->cronloops ++;\n    return 1000/vel->hz;\n}\n"
  },
  {
    "path": "src/vr_worker.h",
    "content": "#ifndef _VR_WORKER_H_\n#define _VR_WORKER_H_\n\ntypedef struct vr_worker {\n\n    int id;\n    vr_eventloop vel;\n    \n    int socketpairs[2];         /*0: belong to master thread, 1: belong to myself*/\n    \n    dlist *csul;    /* Connect swap unit list */\n    pthread_mutex_t csullock;   /* swap unit list locker */\n\n    /* Some global state in order to continue the work incrementally \n       * across calls for activeExpireCycle() to expire some keys. */\n    unsigned int current_db;    /* Last DB tested. */\n    int timelimit_exit;         /* Time limit hit in previous call? */\n    long long last_fast_cycle;  /* When last fast cycle ran. */\n\n    /* We use global counters so if we stop the computation at a given\n       * DB we'll be able to start from the successive in the next\n       * cron loop iteration for databasesCron() to resize and reshash db. */\n    unsigned int resize_db;\n    unsigned int rehash_db;\n}vr_worker;\n\nstruct connswapunit {\n    int num;\n    void *data;\n    struct connswapunit *next;\n};\n\nextern struct darray workers;\n\nint workers_init(uint32_t worker_count);\nint workers_run(void);\nint workers_wait(void);\nvoid workers_deinit(void);\n\nstruct connswapunit *csui_new(void);\nvoid csui_free(struct connswapunit *item);\n\nvoid csul_push(vr_worker *worker, struct connswapunit *su);\nstruct connswapunit *csul_pop(vr_worker *worker);\n\nint worker_get_next_idx(int curidx);\n\nvoid dispatch_conn_new(vr_listen *vlisten, int sd);\n\nvoid worker_before_sleep(struct aeEventLoop *eventLoop, void *private_data);\nint worker_cron(struct aeEventLoop *eventLoop, long long id, void *clientData);\n\n#endif\n"
  },
  {
    "path": "src/vr_ziplist.c",
    "content": "/* The ziplist is a specially encoded dually linked list that is designed\n * to be very memory efficient. It stores both strings and integer values,\n * where integers are encoded as actual integers instead of a series of\n * characters. It allows push and pop operations on either side of the list\n * in O(1) time. However, because every operation requires a reallocation of\n * the memory used by the ziplist, the actual complexity is related to the\n * amount of memory used by the ziplist.\n *\n * ----------------------------------------------------------------------------\n *\n * ZIPLIST OVERALL LAYOUT:\n * The general layout of the ziplist is as follows:\n * <zlbytes><zltail><zllen><entry><entry><zlend>\n *\n * <zlbytes> is an unsigned integer to hold the number of bytes that the\n * ziplist occupies. This value needs to be stored to be able to resize the\n * entire structure without the need to traverse it first.\n *\n * <zltail> is the offset to the last entry in the list. This allows a pop\n * operation on the far side of the list without the need for full traversal.\n *\n * <zllen> is the number of entries.When this value is larger than 2**16-2,\n * we need to traverse the entire list to know how many items it holds.\n *\n * <zlend> is a single byte special value, equal to 255, which indicates the\n * end of the list.\n *\n * ZIPLIST ENTRIES:\n * Every entry in the ziplist is prefixed by a header that contains two pieces\n * of information. First, the length of the previous entry is stored to be\n * able to traverse the list from back to front. Second, the encoding with an\n * optional string length of the entry itself is stored.\n *\n * The length of the previous entry is encoded in the following way:\n * If this length is smaller than 254 bytes, it will only consume a single\n * byte that takes the length as value. When the length is greater than or\n * equal to 254, it will consume 5 bytes. The first byte is set to 254 to\n * indicate a larger value is following. The remaining 4 bytes take the\n * length of the previous entry as value.\n *\n * The other header field of the entry itself depends on the contents of the\n * entry. When the entry is a string, the first 2 bits of this header will hold\n * the type of encoding used to store the length of the string, followed by the\n * actual length of the string. When the entry is an integer the first 2 bits\n * are both set to 1. The following 2 bits are used to specify what kind of\n * integer will be stored after this header. An overview of the different\n * types and encodings is as follows:\n *\n * |00pppppp| - 1 byte\n *      String value with length less than or equal to 63 bytes (6 bits).\n * |01pppppp|qqqqqqqq| - 2 bytes\n *      String value with length less than or equal to 16383 bytes (14 bits).\n * |10______|qqqqqqqq|rrrrrrrr|ssssssss|tttttttt| - 5 bytes\n *      String value with length greater than or equal to 16384 bytes.\n * |11000000| - 1 byte\n *      Integer encoded as int16_t (2 bytes).\n * |11010000| - 1 byte\n *      Integer encoded as int32_t (4 bytes).\n * |11100000| - 1 byte\n *      Integer encoded as int64_t (8 bytes).\n * |11110000| - 1 byte\n *      Integer encoded as 24 bit signed (3 bytes).\n * |11111110| - 1 byte\n *      Integer encoded as 8 bit signed (1 byte).\n * |1111xxxx| - (with xxxx between 0000 and 1101) immediate 4 bit integer.\n *      Unsigned integer from 0 to 12. The encoded value is actually from\n *      1 to 13 because 0000 and 1111 can not be used, so 1 should be\n *      subtracted from the encoded 4 bit value to obtain the right value.\n * |11111111| - End of ziplist.\n *\n * All the integers are represented in little endian byte order.\n *\n * ----------------------------------------------------------------------------\n *\n * Copyright (c) 2009-2012, Pieter Noordhuis <pcnoordhuis at gmail dot com>\n * Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n *   * Redistributions of source code must retain the above copyright notice,\n *     this list of conditions and the following disclaimer.\n *   * Redistributions in binary form must reproduce the above copyright\n *     notice, this list of conditions and the following disclaimer in the\n *     documentation and/or other materials provided with the distribution.\n *   * Neither the name of Redis nor the names of its contributors may be used\n *     to endorse or promote products derived from this software without\n *     specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <limits.h>\n\n#include <vr_core.h>\n\n#define ZIP_END 255\n#define ZIP_BIGLEN 254\n\n/* Different encoding/length possibilities */\n#define ZIP_STR_MASK 0xc0\n#define ZIP_INT_MASK 0x30\n#define ZIP_STR_06B (0 << 6)\n#define ZIP_STR_14B (1 << 6)\n#define ZIP_STR_32B (2 << 6)\n#define ZIP_INT_16B (0xc0 | 0<<4)\n#define ZIP_INT_32B (0xc0 | 1<<4)\n#define ZIP_INT_64B (0xc0 | 2<<4)\n#define ZIP_INT_24B (0xc0 | 3<<4)\n#define ZIP_INT_8B 0xfe\n/* 4 bit integer immediate encoding */\n#define ZIP_INT_IMM_MASK 0x0f\n#define ZIP_INT_IMM_MIN 0xf1    /* 11110001 */\n#define ZIP_INT_IMM_MAX 0xfd    /* 11111101 */\n#define ZIP_INT_IMM_VAL(v) (v & ZIP_INT_IMM_MASK)\n\n#define INT24_MAX 0x7fffff\n#define INT24_MIN (-INT24_MAX - 1)\n\n/* Macro to determine type */\n#define ZIP_IS_STR(enc) (((enc) & ZIP_STR_MASK) < ZIP_STR_MASK)\n\n/* Utility macros */\n#define ZIPLIST_BYTES(zl)       (*((uint32_t*)(zl)))\n#define ZIPLIST_TAIL_OFFSET(zl) (*((uint32_t*)((zl)+sizeof(uint32_t))))\n#define ZIPLIST_LENGTH(zl)      (*((uint16_t*)((zl)+sizeof(uint32_t)*2)))\n#define ZIPLIST_HEADER_SIZE     (sizeof(uint32_t)*2+sizeof(uint16_t))\n#define ZIPLIST_END_SIZE        (sizeof(uint8_t))\n#define ZIPLIST_ENTRY_HEAD(zl)  ((zl)+ZIPLIST_HEADER_SIZE)\n#define ZIPLIST_ENTRY_TAIL(zl)  ((zl)+intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl)))\n#define ZIPLIST_ENTRY_END(zl)   ((zl)+intrev32ifbe(ZIPLIST_BYTES(zl))-1)\n\n/* We know a positive increment can only be 1 because entries can only be\n * pushed one at a time. */\n#define ZIPLIST_INCR_LENGTH(zl,incr) { \\\n    if (ZIPLIST_LENGTH(zl) < UINT16_MAX) \\\n        ZIPLIST_LENGTH(zl) = intrev16ifbe(intrev16ifbe(ZIPLIST_LENGTH(zl))+incr); \\\n}\n\ntypedef struct zlentry {\n    unsigned int prevrawlensize, prevrawlen;\n    unsigned int lensize, len;\n    unsigned int headersize;\n    unsigned char encoding;\n    unsigned char *p;\n} zlentry;\n\n#define ZIPLIST_ENTRY_ZERO(zle) { \\\n    (zle)->prevrawlensize = (zle)->prevrawlen = 0; \\\n    (zle)->lensize = (zle)->len = (zle)->headersize = 0; \\\n    (zle)->encoding = 0; \\\n    (zle)->p = NULL; \\\n}\n\n/* Extract the encoding from the byte pointed by 'ptr' and set it into\n * 'encoding'. */\n#define ZIP_ENTRY_ENCODING(ptr, encoding) do {  \\\n    (encoding) = (ptr[0]); \\\n    if ((encoding) < ZIP_STR_MASK) (encoding) &= ZIP_STR_MASK; \\\n} while(0)\n\nvoid ziplistRepr(unsigned char *zl);\n\n/* Return bytes needed to store integer encoded by 'encoding' */\nstatic unsigned int zipIntSize(unsigned char encoding) {\n    switch(encoding) {\n    case ZIP_INT_8B:  return 1;\n    case ZIP_INT_16B: return 2;\n    case ZIP_INT_24B: return 3;\n    case ZIP_INT_32B: return 4;\n    case ZIP_INT_64B: return 8;\n    default: return 0; /* 4 bit immediate */\n    }\n    ASSERT(NULL);\n    return 0;\n}\n\n/* Encode the length 'rawlen' writing it in 'p'. If p is NULL it just returns\n * the amount of bytes required to encode such a length. */\nstatic unsigned int zipEncodeLength(unsigned char *p, unsigned char encoding, unsigned int rawlen) {\n    unsigned char len = 1, buf[5];\n\n    if (ZIP_IS_STR(encoding)) {\n        /* Although encoding is given it may not be set for strings,\n         * so we determine it here using the raw length. */\n        if (rawlen <= 0x3f) {\n            if (!p) return len;\n            buf[0] = ZIP_STR_06B | rawlen;\n        } else if (rawlen <= 0x3fff) {\n            len += 1;\n            if (!p) return len;\n            buf[0] = ZIP_STR_14B | ((rawlen >> 8) & 0x3f);\n            buf[1] = rawlen & 0xff;\n        } else {\n            len += 4;\n            if (!p) return len;\n            buf[0] = ZIP_STR_32B;\n            buf[1] = (rawlen >> 24) & 0xff;\n            buf[2] = (rawlen >> 16) & 0xff;\n            buf[3] = (rawlen >> 8) & 0xff;\n            buf[4] = rawlen & 0xff;\n        }\n    } else {\n        /* Implies integer encoding, so length is always 1. */\n        if (!p) return len;\n        buf[0] = encoding;\n    }\n\n    /* Store this length at p */\n    memcpy(p,buf,len);\n    return len;\n}\n\n/* Decode the length encoded in 'ptr'. The 'encoding' variable will hold the\n * entries encoding, the 'lensize' variable will hold the number of bytes\n * required to encode the entries length, and the 'len' variable will hold the\n * entries length. */\n#define ZIP_DECODE_LENGTH(ptr, encoding, lensize, len) do {                    \\\n    ZIP_ENTRY_ENCODING((ptr), (encoding));                                     \\\n    if ((encoding) < ZIP_STR_MASK) {                                           \\\n        if ((encoding) == ZIP_STR_06B) {                                       \\\n            (lensize) = 1;                                                     \\\n            (len) = (ptr)[0] & 0x3f;                                           \\\n        } else if ((encoding) == ZIP_STR_14B) {                                \\\n            (lensize) = 2;                                                     \\\n            (len) = (((ptr)[0] & 0x3f) << 8) | (ptr)[1];                       \\\n        } else if (encoding == ZIP_STR_32B) {                                  \\\n            (lensize) = 5;                                                     \\\n            (len) = ((ptr)[1] << 24) |                                         \\\n                    ((ptr)[2] << 16) |                                         \\\n                    ((ptr)[3] <<  8) |                                         \\\n                    ((ptr)[4]);                                                \\\n        } else {                                                               \\\n            ASSERT(NULL);                                                      \\\n        }                                                                      \\\n    } else {                                                                   \\\n        (lensize) = 1;                                                         \\\n        (len) = zipIntSize(encoding);                                          \\\n    }                                                                          \\\n} while(0);\n\n/* Encode the length of the previous entry and write it to \"p\". Return the\n * number of bytes needed to encode this length if \"p\" is NULL. */\nstatic unsigned int zipPrevEncodeLength(unsigned char *p, unsigned int len) {\n    if (p == NULL) {\n        return (len < ZIP_BIGLEN) ? 1 : sizeof(len)+1;\n    } else {\n        if (len < ZIP_BIGLEN) {\n            p[0] = len;\n            return 1;\n        } else {\n            p[0] = ZIP_BIGLEN;\n            memcpy(p+1,&len,sizeof(len));\n            memrev32ifbe(p+1);\n            return 1+sizeof(len);\n        }\n    }\n}\n\n/* Encode the length of the previous entry and write it to \"p\". This only\n * uses the larger encoding (required in __ziplistCascadeUpdate). */\nstatic void zipPrevEncodeLengthForceLarge(unsigned char *p, unsigned int len) {\n    if (p == NULL) return;\n    p[0] = ZIP_BIGLEN;\n    memcpy(p+1,&len,sizeof(len));\n    memrev32ifbe(p+1);\n}\n\n/* Decode the number of bytes required to store the length of the previous\n * element, from the perspective of the entry pointed to by 'ptr'. */\n#define ZIP_DECODE_PREVLENSIZE(ptr, prevlensize) do {                          \\\n    if ((ptr)[0] < ZIP_BIGLEN) {                                               \\\n        (prevlensize) = 1;                                                     \\\n    } else {                                                                   \\\n        (prevlensize) = 5;                                                     \\\n    }                                                                          \\\n} while(0);\n\n/* Decode the length of the previous element, from the perspective of the entry\n * pointed to by 'ptr'. */\n#define ZIP_DECODE_PREVLEN(ptr, prevlensize, prevlen) do {                     \\\n    ZIP_DECODE_PREVLENSIZE(ptr, prevlensize);                                  \\\n    if ((prevlensize) == 1) {                                                  \\\n        (prevlen) = (ptr)[0];                                                  \\\n    } else if ((prevlensize) == 5) {                                           \\\n        ASSERT(sizeof((prevlensize)) == 4);                                    \\\n        memcpy(&(prevlen), ((char*)(ptr)) + 1, 4);                             \\\n        memrev32ifbe(&prevlen);                                                \\\n    }                                                                          \\\n} while(0);\n\n/* Return the difference in number of bytes needed to store the length of the\n * previous element 'len', in the entry pointed to by 'p'. */\nstatic int zipPrevLenByteDiff(unsigned char *p, unsigned int len) {\n    unsigned int prevlensize;\n    ZIP_DECODE_PREVLENSIZE(p, prevlensize);\n    return zipPrevEncodeLength(NULL, len) - prevlensize;\n}\n\n/* Return the total number of bytes used by the entry pointed to by 'p'. */\nstatic unsigned int zipRawEntryLength(unsigned char *p) {\n    unsigned int prevlensize, encoding, lensize, len;\n    ZIP_DECODE_PREVLENSIZE(p, prevlensize);\n    ZIP_DECODE_LENGTH(p + prevlensize, encoding, lensize, len);\n    return prevlensize + lensize + len;\n}\n\n/* Check if string pointed to by 'entry' can be encoded as an integer.\n * Stores the integer value in 'v' and its encoding in 'encoding'. */\nstatic int zipTryEncoding(unsigned char *entry, unsigned int entrylen, long long *v, unsigned char *encoding) {\n    long long value;\n\n    if (entrylen >= 32 || entrylen == 0) return 0;\n    if (string2ll((char*)entry,entrylen,&value)) {\n        /* Great, the string can be encoded. Check what's the smallest\n         * of our encoding types that can hold this value. */\n        if (value >= 0 && value <= 12) {\n            *encoding = ZIP_INT_IMM_MIN+value;\n        } else if (value >= INT8_MIN && value <= INT8_MAX) {\n            *encoding = ZIP_INT_8B;\n        } else if (value >= INT16_MIN && value <= INT16_MAX) {\n            *encoding = ZIP_INT_16B;\n        } else if (value >= INT24_MIN && value <= INT24_MAX) {\n            *encoding = ZIP_INT_24B;\n        } else if (value >= INT32_MIN && value <= INT32_MAX) {\n            *encoding = ZIP_INT_32B;\n        } else {\n            *encoding = ZIP_INT_64B;\n        }\n        *v = value;\n        return 1;\n    }\n    return 0;\n}\n\n/* Store integer 'value' at 'p', encoded as 'encoding' */\nstatic void zipSaveInteger(unsigned char *p, int64_t value, unsigned char encoding) {\n    int16_t i16;\n    int32_t i32;\n    int64_t i64;\n    if (encoding == ZIP_INT_8B) {\n        ((int8_t*)p)[0] = (int8_t)value;\n    } else if (encoding == ZIP_INT_16B) {\n        i16 = value;\n        memcpy(p,&i16,sizeof(i16));\n        memrev16ifbe(p);\n    } else if (encoding == ZIP_INT_24B) {\n        i32 = value<<8;\n        memrev32ifbe(&i32);\n        memcpy(p,((uint8_t*)&i32)+1,sizeof(i32)-sizeof(uint8_t));\n    } else if (encoding == ZIP_INT_32B) {\n        i32 = value;\n        memcpy(p,&i32,sizeof(i32));\n        memrev32ifbe(p);\n    } else if (encoding == ZIP_INT_64B) {\n        i64 = value;\n        memcpy(p,&i64,sizeof(i64));\n        memrev64ifbe(p);\n    } else if (encoding >= ZIP_INT_IMM_MIN && encoding <= ZIP_INT_IMM_MAX) {\n        /* Nothing to do, the value is stored in the encoding itself. */\n    } else {\n        ASSERT(NULL);\n    }\n}\n\n/* Read integer encoded as 'encoding' from 'p' */\nstatic int64_t zipLoadInteger(unsigned char *p, unsigned char encoding) {\n    int16_t i16;\n    int32_t i32;\n    int64_t i64, ret = 0;\n    if (encoding == ZIP_INT_8B) {\n        ret = ((int8_t*)p)[0];\n    } else if (encoding == ZIP_INT_16B) {\n        memcpy(&i16,p,sizeof(i16));\n        memrev16ifbe(&i16);\n        ret = i16;\n    } else if (encoding == ZIP_INT_32B) {\n        memcpy(&i32,p,sizeof(i32));\n        memrev32ifbe(&i32);\n        ret = i32;\n    } else if (encoding == ZIP_INT_24B) {\n        i32 = 0;\n        memcpy(((uint8_t*)&i32)+1,p,sizeof(i32)-sizeof(uint8_t));\n        memrev32ifbe(&i32);\n        ret = i32>>8;\n    } else if (encoding == ZIP_INT_64B) {\n        memcpy(&i64,p,sizeof(i64));\n        memrev64ifbe(&i64);\n        ret = i64;\n    } else if (encoding >= ZIP_INT_IMM_MIN && encoding <= ZIP_INT_IMM_MAX) {\n        ret = (encoding & ZIP_INT_IMM_MASK)-1;\n    } else {\n        ASSERT(NULL);\n    }\n    return ret;\n}\n\n/* Return a struct with all information about an entry. */\nstatic void zipEntry(unsigned char *p, zlentry *e) {\n\n    ZIP_DECODE_PREVLEN(p, e->prevrawlensize, e->prevrawlen);\n    ZIP_DECODE_LENGTH(p + e->prevrawlensize, e->encoding, e->lensize, e->len);\n    e->headersize = e->prevrawlensize + e->lensize;\n    e->p = p;\n}\n\n/* Create a new empty ziplist. */\nunsigned char *ziplistNew(void) {\n    unsigned int bytes = ZIPLIST_HEADER_SIZE+1;\n    unsigned char *zl = dalloc(bytes);\n    ZIPLIST_BYTES(zl) = intrev32ifbe(bytes);\n    ZIPLIST_TAIL_OFFSET(zl) = intrev32ifbe(ZIPLIST_HEADER_SIZE);\n    ZIPLIST_LENGTH(zl) = 0;\n    zl[bytes-1] = ZIP_END;\n    return zl;\n}\n\n/* Resize the ziplist. */\nstatic unsigned char *ziplistResize(unsigned char *zl, unsigned int len) {\n    zl = drealloc(zl,len);\n    ZIPLIST_BYTES(zl) = intrev32ifbe(len);\n    zl[len-1] = ZIP_END;\n    return zl;\n}\n\n/* When an entry is inserted, we need to set the prevlen field of the next\n * entry to equal the length of the inserted entry. It can occur that this\n * length cannot be encoded in 1 byte and the next entry needs to be grow\n * a bit larger to hold the 5-byte encoded prevlen. This can be done for free,\n * because this only happens when an entry is already being inserted (which\n * causes a realloc and memmove). However, encoding the prevlen may require\n * that this entry is grown as well. This effect may cascade throughout\n * the ziplist when there are consecutive entries with a size close to\n * ZIP_BIGLEN, so we need to check that the prevlen can be encoded in every\n * consecutive entry.\n *\n * Note that this effect can also happen in reverse, where the bytes required\n * to encode the prevlen field can shrink. This effect is deliberately ignored,\n * because it can cause a \"flapping\" effect where a chain prevlen fields is\n * first grown and then shrunk again after consecutive inserts. Rather, the\n * field is allowed to stay larger than necessary, because a large prevlen\n * field implies the ziplist is holding large entries anyway.\n *\n * The pointer \"p\" points to the first entry that does NOT need to be\n * updated, i.e. consecutive fields MAY need an update. */\nstatic unsigned char *__ziplistCascadeUpdate(unsigned char *zl, unsigned char *p) {\n    size_t curlen = intrev32ifbe(ZIPLIST_BYTES(zl)), rawlen, rawlensize;\n    size_t offset, noffset, extra;\n    unsigned char *np;\n    zlentry cur, next;\n\n    while (p[0] != ZIP_END) {\n        zipEntry(p, &cur);\n        rawlen = cur.headersize + cur.len;\n        rawlensize = zipPrevEncodeLength(NULL,rawlen);\n\n        /* Abort if there is no next entry. */\n        if (p[rawlen] == ZIP_END) break;\n        zipEntry(p+rawlen, &next);\n\n        /* Abort when \"prevlen\" has not changed. */\n        if (next.prevrawlen == rawlen) break;\n\n        if (next.prevrawlensize < rawlensize) {\n            /* The \"prevlen\" field of \"next\" needs more bytes to hold\n             * the raw length of \"cur\". */\n            offset = p-zl;\n            extra = rawlensize-next.prevrawlensize;\n            zl = ziplistResize(zl,curlen+extra);\n            p = zl+offset;\n\n            /* Current pointer and offset for next element. */\n            np = p+rawlen;\n            noffset = np-zl;\n\n            /* Update tail offset when next element is not the tail element. */\n            if ((zl+intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))) != np) {\n                ZIPLIST_TAIL_OFFSET(zl) =\n                    intrev32ifbe(intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))+extra);\n            }\n\n            /* Move the tail to the back. */\n            memmove(np+rawlensize,\n                np+next.prevrawlensize,\n                curlen-noffset-next.prevrawlensize-1);\n            zipPrevEncodeLength(np,rawlen);\n\n            /* Advance the cursor */\n            p += rawlen;\n            curlen += extra;\n        } else {\n            if (next.prevrawlensize > rawlensize) {\n                /* This would result in shrinking, which we want to avoid.\n                 * So, set \"rawlen\" in the available bytes. */\n                zipPrevEncodeLengthForceLarge(p+rawlen,rawlen);\n            } else {\n                zipPrevEncodeLength(p+rawlen,rawlen);\n            }\n\n            /* Stop here, as the raw length of \"next\" has not changed. */\n            break;\n        }\n    }\n    return zl;\n}\n\n/* Delete \"num\" entries, starting at \"p\". Returns pointer to the ziplist. */\nstatic unsigned char *__ziplistDelete(unsigned char *zl, unsigned char *p, unsigned int num) {\n    unsigned int i, totlen, deleted = 0;\n    size_t offset;\n    int nextdiff = 0;\n    zlentry first, tail;\n\n    zipEntry(p, &first);\n    for (i = 0; p[0] != ZIP_END && i < num; i++) {\n        p += zipRawEntryLength(p);\n        deleted++;\n    }\n\n    totlen = p-first.p;\n    if (totlen > 0) {\n        if (p[0] != ZIP_END) {\n            /* Storing `prevrawlen` in this entry may increase or decrease the\n             * number of bytes required compare to the current `prevrawlen`.\n             * There always is room to store this, because it was previously\n             * stored by an entry that is now being deleted. */\n            nextdiff = zipPrevLenByteDiff(p,first.prevrawlen);\n            p -= nextdiff;\n            zipPrevEncodeLength(p,first.prevrawlen);\n\n            /* Update offset for tail */\n            ZIPLIST_TAIL_OFFSET(zl) =\n                intrev32ifbe(intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))-totlen);\n\n            /* When the tail contains more than one entry, we need to take\n             * \"nextdiff\" in account as well. Otherwise, a change in the\n             * size of prevlen doesn't have an effect on the *tail* offset. */\n            zipEntry(p, &tail);\n            if (p[tail.headersize+tail.len] != ZIP_END) {\n                ZIPLIST_TAIL_OFFSET(zl) =\n                   intrev32ifbe(intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))+nextdiff);\n            }\n\n            /* Move tail to the front of the ziplist */\n            memmove(first.p,p,\n                intrev32ifbe(ZIPLIST_BYTES(zl))-(p-zl)-1);\n        } else {\n            /* The entire tail was deleted. No need to move memory. */\n            ZIPLIST_TAIL_OFFSET(zl) =\n                intrev32ifbe((first.p-zl)-first.prevrawlen);\n        }\n\n        /* Resize and update length */\n        offset = first.p-zl;\n        zl = ziplistResize(zl, intrev32ifbe(ZIPLIST_BYTES(zl))-totlen+nextdiff);\n        ZIPLIST_INCR_LENGTH(zl,-deleted);\n        p = zl+offset;\n\n        /* When nextdiff != 0, the raw length of the next entry has changed, so\n         * we need to cascade the update throughout the ziplist */\n        if (nextdiff != 0)\n            zl = __ziplistCascadeUpdate(zl,p);\n    }\n    return zl;\n}\n\n/* Insert item at \"p\". */\nstatic unsigned char *__ziplistInsert(unsigned char *zl, unsigned char *p, unsigned char *s, unsigned int slen) {\n    size_t curlen = intrev32ifbe(ZIPLIST_BYTES(zl)), reqlen;\n    unsigned int prevlensize, prevlen = 0;\n    size_t offset;\n    int nextdiff = 0;\n    unsigned char encoding = 0;\n    long long value = 123456789; /* initialized to avoid warning. Using a value\n                                    that is easy to see if for some reason\n                                    we use it uninitialized. */\n    zlentry tail;\n\n    /* Find out prevlen for the entry that is inserted. */\n    if (p[0] != ZIP_END) {\n        ZIP_DECODE_PREVLEN(p, prevlensize, prevlen);\n    } else {\n        unsigned char *ptail = ZIPLIST_ENTRY_TAIL(zl);\n        if (ptail[0] != ZIP_END) {\n            prevlen = zipRawEntryLength(ptail);\n        }\n    }\n\n    /* See if the entry can be encoded */\n    if (zipTryEncoding(s,slen,&value,&encoding)) {\n        /* 'encoding' is set to the appropriate integer encoding */\n        reqlen = zipIntSize(encoding);\n    } else {\n        /* 'encoding' is untouched, however zipEncodeLength will use the\n         * string length to figure out how to encode it. */\n        reqlen = slen;\n    }\n    /* We need space for both the length of the previous entry and\n     * the length of the payload. */\n    reqlen += zipPrevEncodeLength(NULL,prevlen);\n    reqlen += zipEncodeLength(NULL,encoding,slen);\n\n    /* When the insert position is not equal to the tail, we need to\n     * make sure that the next entry can hold this entry's length in\n     * its prevlen field. */\n    nextdiff = (p[0] != ZIP_END) ? zipPrevLenByteDiff(p,reqlen) : 0;\n\n    /* Store offset because a realloc may change the address of zl. */\n    offset = p-zl;\n    zl = ziplistResize(zl,curlen+reqlen+nextdiff);\n    p = zl+offset;\n\n    /* Apply memory move when necessary and update tail offset. */\n    if (p[0] != ZIP_END) {\n        /* Subtract one because of the ZIP_END bytes */\n        memmove(p+reqlen,p-nextdiff,curlen-offset-1+nextdiff);\n\n        /* Encode this entry's raw length in the next entry. */\n        zipPrevEncodeLength(p+reqlen,reqlen);\n\n        /* Update offset for tail */\n        ZIPLIST_TAIL_OFFSET(zl) =\n            intrev32ifbe(intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))+reqlen);\n\n        /* When the tail contains more than one entry, we need to take\n         * \"nextdiff\" in account as well. Otherwise, a change in the\n         * size of prevlen doesn't have an effect on the *tail* offset. */\n        zipEntry(p+reqlen, &tail);\n        if (p[reqlen+tail.headersize+tail.len] != ZIP_END) {\n            ZIPLIST_TAIL_OFFSET(zl) =\n                intrev32ifbe(intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))+nextdiff);\n        }\n    } else {\n        /* This element will be the new tail. */\n        ZIPLIST_TAIL_OFFSET(zl) = intrev32ifbe(p-zl);\n    }\n\n    /* When nextdiff != 0, the raw length of the next entry has changed, so\n     * we need to cascade the update throughout the ziplist */\n    if (nextdiff != 0) {\n        offset = p-zl;\n        zl = __ziplistCascadeUpdate(zl,p+reqlen);\n        p = zl+offset;\n    }\n\n    /* Write the entry */\n    p += zipPrevEncodeLength(p,prevlen);\n    p += zipEncodeLength(p,encoding,slen);\n    if (ZIP_IS_STR(encoding)) {\n        memcpy(p,s,slen);\n    } else {\n        zipSaveInteger(p,value,encoding);\n    }\n    ZIPLIST_INCR_LENGTH(zl,1);\n    return zl;\n}\n\n/* Merge ziplists 'first' and 'second' by appending 'second' to 'first'.\n *\n * NOTE: The larger ziplist is reallocated to contain the new merged ziplist.\n * Either 'first' or 'second' can be used for the result.  The parameter not\n * used will be free'd and set to NULL.\n *\n * After calling this function, the input parameters are no longer valid since\n * they are changed and free'd in-place.\n *\n * The result ziplist is the contents of 'first' followed by 'second'.\n *\n * On failure: returns NULL if the merge is impossible.\n * On success: returns the merged ziplist (which is expanded version of either\n * 'first' or 'second', also frees the other unused input ziplist, and sets the\n * input ziplist argument equal to newly reallocated ziplist return value. */\nunsigned char *ziplistMerge(unsigned char **first, unsigned char **second) {\n    /* If any params are null, we can't merge, so NULL. */\n    if (first == NULL || *first == NULL || second == NULL || *second == NULL)\n        return NULL;\n\n    /* Can't merge same list into itself. */\n    if (*first == *second)\n        return NULL;\n\n    size_t first_bytes = intrev32ifbe(ZIPLIST_BYTES(*first));\n    size_t first_len = intrev16ifbe(ZIPLIST_LENGTH(*first));\n\n    size_t second_bytes = intrev32ifbe(ZIPLIST_BYTES(*second));\n    size_t second_len = intrev16ifbe(ZIPLIST_LENGTH(*second));\n\n    int append;\n    unsigned char *source, *target;\n    size_t target_bytes, source_bytes;\n    /* Pick the largest ziplist so we can resize easily in-place.\n     * We must also track if we are now appending or prepending to\n     * the target ziplist. */\n    if (first_len >= second_len) {\n        /* retain first, append second to first. */\n        target = *first;\n        target_bytes = first_bytes;\n        source = *second;\n        source_bytes = second_bytes;\n        append = 1;\n    } else {\n        /* else, retain second, prepend first to second. */\n        target = *second;\n        target_bytes = second_bytes;\n        source = *first;\n        source_bytes = first_bytes;\n        append = 0;\n    }\n\n    /* Calculate final bytes (subtract one pair of metadata) */\n    size_t zlbytes = first_bytes + second_bytes -\n                     ZIPLIST_HEADER_SIZE - ZIPLIST_END_SIZE;\n    size_t zllength = first_len + second_len;\n\n    /* Combined zl length should be limited within UINT16_MAX */\n    zllength = zllength < UINT16_MAX ? zllength : UINT16_MAX;\n\n    /* Save offset positions before we start ripping memory apart. */\n    size_t first_offset = intrev32ifbe(ZIPLIST_TAIL_OFFSET(*first));\n    size_t second_offset = intrev32ifbe(ZIPLIST_TAIL_OFFSET(*second));\n\n    /* Extend target to new zlbytes then append or prepend source. */\n    target = drealloc(target, zlbytes);\n    if (append) {\n        /* append == appending to target */\n        /* Copy source after target (copying over original [END]):\n         *   [TARGET - END, SOURCE - HEADER] */\n        memcpy(target + target_bytes - ZIPLIST_END_SIZE,\n               source + ZIPLIST_HEADER_SIZE,\n               source_bytes - ZIPLIST_HEADER_SIZE);\n    } else {\n        /* !append == prepending to target */\n        /* Move target *contents* exactly size of (source - [END]),\n         * then copy source into vacataed space (source - [END]):\n         *   [SOURCE - END, TARGET - HEADER] */\n        memmove(target + source_bytes - ZIPLIST_END_SIZE,\n                target + ZIPLIST_HEADER_SIZE,\n                target_bytes - ZIPLIST_HEADER_SIZE);\n        memcpy(target, source, source_bytes - ZIPLIST_END_SIZE);\n    }\n\n    /* Update header metadata. */\n    ZIPLIST_BYTES(target) = intrev32ifbe(zlbytes);\n    ZIPLIST_LENGTH(target) = intrev16ifbe(zllength);\n    /* New tail offset is:\n     *   + N bytes of first ziplist\n     *   - 1 byte for [END] of first ziplist\n     *   + M bytes for the offset of the original tail of the second ziplist\n     *   - J bytes for HEADER because second_offset keeps no header. */\n    ZIPLIST_TAIL_OFFSET(target) = intrev32ifbe(\n                                   (first_bytes - ZIPLIST_END_SIZE) +\n                                   (second_offset - ZIPLIST_HEADER_SIZE));\n\n    /* __ziplistCascadeUpdate just fixes the prev length values until it finds a\n     * correct prev length value (then it assumes the rest of the list is okay).\n     * We tell CascadeUpdate to start at the first ziplist's tail element to fix\n     * the merge seam. */\n    target = __ziplistCascadeUpdate(target, target+first_offset);\n\n    /* Now free and NULL out what we didn't realloc */\n    if (append) {\n        dfree(*second);\n        *second = NULL;\n        *first = target;\n    } else {\n        dfree(*first);\n        *first = NULL;\n        *second = target;\n    }\n    return target;\n}\n\nunsigned char *ziplistPush(unsigned char *zl, unsigned char *s, unsigned int slen, int where) {\n    unsigned char *p;\n    p = (where == ZIPLIST_HEAD) ? ZIPLIST_ENTRY_HEAD(zl) : ZIPLIST_ENTRY_END(zl);\n    return __ziplistInsert(zl,p,s,slen);\n}\n\n/* Returns an offset to use for iterating with ziplistNext. When the given\n * index is negative, the list is traversed back to front. When the list\n * doesn't contain an element at the provided index, NULL is returned. */\nunsigned char *ziplistIndex(unsigned char *zl, int index) {\n    unsigned char *p;\n    unsigned int prevlensize, prevlen = 0;\n    if (index < 0) {\n        index = (-index)-1;\n        p = ZIPLIST_ENTRY_TAIL(zl);\n        if (p[0] != ZIP_END) {\n            ZIP_DECODE_PREVLEN(p, prevlensize, prevlen);\n            while (prevlen > 0 && index--) {\n                p -= prevlen;\n                ZIP_DECODE_PREVLEN(p, prevlensize, prevlen);\n            }\n        }\n    } else {\n        p = ZIPLIST_ENTRY_HEAD(zl);\n        while (p[0] != ZIP_END && index--) {\n            p += zipRawEntryLength(p);\n        }\n    }\n    return (p[0] == ZIP_END || index > 0) ? NULL : p;\n}\n\n/* Return pointer to next entry in ziplist.\n *\n * zl is the pointer to the ziplist\n * p is the pointer to the current element\n *\n * The element after 'p' is returned, otherwise NULL if we are at the end. */\nunsigned char *ziplistNext(unsigned char *zl, unsigned char *p) {\n    ((void) zl);\n\n    /* \"p\" could be equal to ZIP_END, caused by ziplistDelete,\n     * and we should return NULL. Otherwise, we should return NULL\n     * when the *next* element is ZIP_END (there is no next entry). */\n    if (p[0] == ZIP_END) {\n        return NULL;\n    }\n\n    p += zipRawEntryLength(p);\n    if (p[0] == ZIP_END) {\n        return NULL;\n    }\n\n    return p;\n}\n\n/* Return pointer to previous entry in ziplist. */\nunsigned char *ziplistPrev(unsigned char *zl, unsigned char *p) {\n    unsigned int prevlensize, prevlen = 0;\n\n    /* Iterating backwards from ZIP_END should return the tail. When \"p\" is\n     * equal to the first element of the list, we're already at the head,\n     * and should return NULL. */\n    if (p[0] == ZIP_END) {\n        p = ZIPLIST_ENTRY_TAIL(zl);\n        return (p[0] == ZIP_END) ? NULL : p;\n    } else if (p == ZIPLIST_ENTRY_HEAD(zl)) {\n        return NULL;\n    } else {\n        ZIP_DECODE_PREVLEN(p, prevlensize, prevlen);\n        ASSERT(prevlen > 0);\n        return p-prevlen;\n    }\n}\n\n/* Get entry pointed to by 'p' and store in either '*sstr' or 'sval' depending\n * on the encoding of the entry. '*sstr' is always set to NULL to be able\n * to find out whether the string pointer or the integer value was set.\n * Return 0 if 'p' points to the end of the ziplist, 1 otherwise. */\nunsigned int ziplistGet(unsigned char *p, unsigned char **sstr, unsigned int *slen, long long *sval) {\n    zlentry entry;\n    if (p == NULL || p[0] == ZIP_END) return 0;\n    if (sstr) *sstr = NULL;\n\n    zipEntry(p, &entry);\n    if (ZIP_IS_STR(entry.encoding)) {\n        if (sstr) {\n            *slen = entry.len;\n            *sstr = p+entry.headersize;\n        }\n    } else {\n        if (sval) {\n            *sval = zipLoadInteger(p+entry.headersize,entry.encoding);\n        }\n    }\n    return 1;\n}\n\n/* Insert an entry at \"p\". */\nunsigned char *ziplistInsert(unsigned char *zl, unsigned char *p, unsigned char *s, unsigned int slen) {\n    return __ziplistInsert(zl,p,s,slen);\n}\n\n/* Delete a single entry from the ziplist, pointed to by *p.\n * Also update *p in place, to be able to iterate over the\n * ziplist, while deleting entries. */\nunsigned char *ziplistDelete(unsigned char *zl, unsigned char **p) {\n    size_t offset = *p-zl;\n    zl = __ziplistDelete(zl,*p,1);\n\n    /* Store pointer to current element in p, because ziplistDelete will\n     * do a realloc which might result in a different \"zl\"-pointer.\n     * When the delete direction is back to front, we might delete the last\n     * entry and end up with \"p\" pointing to ZIP_END, so check this. */\n    *p = zl+offset;\n    return zl;\n}\n\n/* Delete a range of entries from the ziplist. */\nunsigned char *ziplistDeleteRange(unsigned char *zl, int index, unsigned int num) {\n    unsigned char *p = ziplistIndex(zl,index);\n    return (p == NULL) ? zl : __ziplistDelete(zl,p,num);\n}\n\n/* Compare entry pointer to by 'p' with 'sstr' of length 'slen'. */\n/* Return 1 if equal. */\nunsigned int ziplistCompare(unsigned char *p, unsigned char *sstr, unsigned int slen) {\n    zlentry entry;\n    unsigned char sencoding;\n    long long zval, sval;\n    if (p[0] == ZIP_END) return 0;\n\n    zipEntry(p, &entry);\n    if (ZIP_IS_STR(entry.encoding)) {\n        /* Raw compare */\n        if (entry.len == slen) {\n            return memcmp(p+entry.headersize,sstr,slen) == 0;\n        } else {\n            return 0;\n        }\n    } else {\n        /* Try to compare encoded values. Don't compare encoding because\n         * different implementations may encoded integers differently. */\n        if (zipTryEncoding(sstr,slen,&sval,&sencoding)) {\n          zval = zipLoadInteger(p+entry.headersize,entry.encoding);\n          return zval == sval;\n        }\n    }\n    return 0;\n}\n\n/* Find pointer to the entry equal to the specified entry. Skip 'skip' entries\n * between every comparison. Returns NULL when the field could not be found. */\nunsigned char *ziplistFind(unsigned char *p, unsigned char *vstr, unsigned int vlen, unsigned int skip) {\n    int skipcnt = 0;\n    unsigned char vencoding = 0;\n    long long vll = 0;\n\n    while (p[0] != ZIP_END) {\n        unsigned int prevlensize, encoding, lensize, len;\n        unsigned char *q;\n\n        ZIP_DECODE_PREVLENSIZE(p, prevlensize);\n        ZIP_DECODE_LENGTH(p + prevlensize, encoding, lensize, len);\n        q = p + prevlensize + lensize;\n\n        if (skipcnt == 0) {\n            /* Compare current entry with specified entry */\n            if (ZIP_IS_STR(encoding)) {\n                if (len == vlen && memcmp(q, vstr, vlen) == 0) {\n                    return p;\n                }\n            } else {\n                /* Find out if the searched field can be encoded. Note that\n                 * we do it only the first time, once done vencoding is set\n                 * to non-zero and vll is set to the integer value. */\n                if (vencoding == 0) {\n                    if (!zipTryEncoding(vstr, vlen, &vll, &vencoding)) {\n                        /* If the entry can't be encoded we set it to\n                         * UCHAR_MAX so that we don't retry again the next\n                         * time. */\n                        vencoding = UCHAR_MAX;\n                    }\n                    /* Must be non-zero by now */\n                    ASSERT(vencoding);\n                }\n\n                /* Compare current entry with specified entry, do it only\n                 * if vencoding != UCHAR_MAX because if there is no encoding\n                 * possible for the field it can't be a valid integer. */\n                if (vencoding != UCHAR_MAX) {\n                    long long ll = zipLoadInteger(q, encoding);\n                    if (ll == vll) {\n                        return p;\n                    }\n                }\n            }\n\n            /* Reset skip count */\n            skipcnt = skip;\n        } else {\n            /* Skip entry */\n            skipcnt--;\n        }\n\n        /* Move to next entry */\n        p = q + len;\n    }\n\n    return NULL;\n}\n\n/* Return length of ziplist. */\nunsigned int ziplistLen(unsigned char *zl) {\n    unsigned int len = 0;\n    if (intrev16ifbe(ZIPLIST_LENGTH(zl)) < UINT16_MAX) {\n        len = intrev16ifbe(ZIPLIST_LENGTH(zl));\n    } else {\n        unsigned char *p = zl+ZIPLIST_HEADER_SIZE;\n        while (*p != ZIP_END) {\n            p += zipRawEntryLength(p);\n            len++;\n        }\n\n        /* Re-store length if small enough */\n        if (len < UINT16_MAX) ZIPLIST_LENGTH(zl) = intrev16ifbe(len);\n    }\n    return len;\n}\n\n/* Return ziplist blob size in bytes. */\nsize_t ziplistBlobLen(unsigned char *zl) {\n    return intrev32ifbe(ZIPLIST_BYTES(zl));\n}\n\nvoid ziplistRepr(unsigned char *zl) {\n    unsigned char *p;\n    int index = 0;\n    zlentry entry;\n\n    printf(\n        \"{total bytes %d} \"\n        \"{length %u}\\n\"\n        \"{tail offset %u}\\n\",\n        intrev32ifbe(ZIPLIST_BYTES(zl)),\n        intrev16ifbe(ZIPLIST_LENGTH(zl)),\n        intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl)));\n    p = ZIPLIST_ENTRY_HEAD(zl);\n    while(*p != ZIP_END) {\n        zipEntry(p, &entry);\n        printf(\n            \"{\"\n                \"addr 0x%08lx, \"\n                \"index %2d, \"\n                \"offset %5ld, \"\n                \"rl: %5u, \"\n                \"hs %2u, \"\n                \"pl: %5u, \"\n                \"pls: %2u, \"\n                \"payload %5u\"\n            \"} \",\n            (long unsigned)p,\n            index,\n            (unsigned long) (p-zl),\n            entry.headersize+entry.len,\n            entry.headersize,\n            entry.prevrawlen,\n            entry.prevrawlensize,\n            entry.len);\n        p += entry.headersize;\n        if (ZIP_IS_STR(entry.encoding)) {\n            if (entry.len > 40) {\n                if (fwrite(p,40,1,stdout) == 0) perror(\"fwrite\");\n                printf(\"...\");\n            } else {\n                if (entry.len &&\n                    fwrite(p,entry.len,1,stdout) == 0) perror(\"fwrite\");\n            }\n        } else {\n            printf(\"%lld\", (long long) zipLoadInteger(p,entry.encoding));\n        }\n        printf(\"\\n\");\n        p += entry.len;\n        index++;\n    }\n    printf(\"{end}\\n\\n\");\n}\n\n#ifdef REDIS_TEST\n#include <sys/time.h>\n#include \"adlist.h\"\n#include \"sds.h\"\n\n#define debug(f, ...) { if (DEBUG) printf(f, __VA_ARGS__); }\n\nstatic unsigned char *createList() {\n    unsigned char *zl = ziplistNew();\n    zl = ziplistPush(zl, (unsigned char*)\"foo\", 3, ZIPLIST_TAIL);\n    zl = ziplistPush(zl, (unsigned char*)\"quux\", 4, ZIPLIST_TAIL);\n    zl = ziplistPush(zl, (unsigned char*)\"hello\", 5, ZIPLIST_HEAD);\n    zl = ziplistPush(zl, (unsigned char*)\"1024\", 4, ZIPLIST_TAIL);\n    return zl;\n}\n\nstatic unsigned char *createIntList() {\n    unsigned char *zl = ziplistNew();\n    char buf[32];\n\n    sprintf(buf, \"100\");\n    zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_TAIL);\n    sprintf(buf, \"128000\");\n    zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_TAIL);\n    sprintf(buf, \"-100\");\n    zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_HEAD);\n    sprintf(buf, \"4294967296\");\n    zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_HEAD);\n    sprintf(buf, \"non integer\");\n    zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_TAIL);\n    sprintf(buf, \"much much longer non integer\");\n    zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_TAIL);\n    return zl;\n}\n\nstatic long long usec(void) {\n    struct timeval tv;\n    gettimeofday(&tv,NULL);\n    return (((long long)tv.tv_sec)*1000000)+tv.tv_usec;\n}\n\nstatic void stress(int pos, int num, int maxsize, int dnum) {\n    int i,j,k;\n    unsigned char *zl;\n    char posstr[2][5] = { \"HEAD\", \"TAIL\" };\n    long long start;\n    for (i = 0; i < maxsize; i+=dnum) {\n        zl = ziplistNew();\n        for (j = 0; j < i; j++) {\n            zl = ziplistPush(zl,(unsigned char*)\"quux\",4,ZIPLIST_TAIL);\n        }\n\n        /* Do num times a push+pop from pos */\n        start = usec();\n        for (k = 0; k < num; k++) {\n            zl = ziplistPush(zl,(unsigned char*)\"quux\",4,pos);\n            zl = ziplistDeleteRange(zl,0,1);\n        }\n        printf(\"List size: %8d, bytes: %8d, %dx push+pop (%s): %6lld usec\\n\",\n            i,intrev32ifbe(ZIPLIST_BYTES(zl)),num,posstr[pos],usec()-start);\n        dfree(zl);\n    }\n}\n\nstatic unsigned char *pop(unsigned char *zl, int where) {\n    unsigned char *p, *vstr;\n    unsigned int vlen;\n    long long vlong;\n\n    p = ziplistIndex(zl,where == ZIPLIST_HEAD ? 0 : -1);\n    if (ziplistGet(p,&vstr,&vlen,&vlong)) {\n        if (where == ZIPLIST_HEAD)\n            printf(\"Pop head: \");\n        else\n            printf(\"Pop tail: \");\n\n        if (vstr) {\n            if (vlen && fwrite(vstr,vlen,1,stdout) == 0) perror(\"fwrite\");\n        }\n        else {\n            printf(\"%lld\", vlong);\n        }\n\n        printf(\"\\n\");\n        return ziplistDelete(zl,&p);\n    } else {\n        printf(\"ERROR: Could not pop\\n\");\n        exit(1);\n    }\n}\n\nstatic int randstring(char *target, unsigned int min, unsigned int max) {\n    int p = 0;\n    int len = min+rand()%(max-min+1);\n    int minval, maxval;\n    switch(rand() % 3) {\n    case 0:\n        minval = 0;\n        maxval = 255;\n    break;\n    case 1:\n        minval = 48;\n        maxval = 122;\n    break;\n    case 2:\n        minval = 48;\n        maxval = 52;\n    break;\n    default:\n        ASSERT(NULL);\n    }\n\n    while(p < len)\n        target[p++] = minval+rand()%(maxval-minval+1);\n    return len;\n}\n\nstatic void verify(unsigned char *zl, zlentry *e) {\n    int len = ziplistLen(zl);\n    zlentry _e;\n\n    ZIPLIST_ENTRY_ZERO(&_e);\n\n    for (int i = 0; i < len; i++) {\n        memset(&e[i], 0, sizeof(zlentry));\n        zipEntry(ziplistIndex(zl, i), &e[i]);\n\n        memset(&_e, 0, sizeof(zlentry));\n        zipEntry(ziplistIndex(zl, -len+i), &_e);\n\n        ASSERT(memcmp(&e[i], &_e, sizeof(zlentry)) == 0);\n    }\n}\n\nint ziplistTest(int argc, char **argv) {\n    int ret;\n    unsigned char *zl, *p;\n    unsigned char *entry;\n    unsigned int elen;\n    long long value;\n\n    /* If an argument is given, use it as the random seed. */\n    if (argc == 2)\n        srand(atoi(argv[1]));\n\n    zl = createIntList();\n    ziplistRepr(zl);\n\n    dfree(zl);\n\n    zl = createList();\n    ziplistRepr(zl);\n\n    zl = pop(zl,ZIPLIST_TAIL);\n    ziplistRepr(zl);\n\n    zl = pop(zl,ZIPLIST_HEAD);\n    ziplistRepr(zl);\n\n    zl = pop(zl,ZIPLIST_TAIL);\n    ziplistRepr(zl);\n\n    zl = pop(zl,ZIPLIST_TAIL);\n    ziplistRepr(zl);\n\n    dfree(zl);\n\n    printf(\"Get element at index 3:\\n\");\n    {\n        zl = createList();\n        p = ziplistIndex(zl, 3);\n        if (!ziplistGet(p, &entry, &elen, &value)) {\n            printf(\"ERROR: Could not access index 3\\n\");\n            return 1;\n        }\n        if (entry) {\n            if (elen && fwrite(entry,elen,1,stdout) == 0) perror(\"fwrite\");\n            printf(\"\\n\");\n        } else {\n            printf(\"%lld\\n\", value);\n        }\n        printf(\"\\n\");\n        dfree(zl);\n    }\n\n    printf(\"Get element at index 4 (out of range):\\n\");\n    {\n        zl = createList();\n        p = ziplistIndex(zl, 4);\n        if (p == NULL) {\n            printf(\"No entry\\n\");\n        } else {\n            printf(\"ERROR: Out of range index should return NULL, returned offset: %ld\\n\", p-zl);\n            return 1;\n        }\n        printf(\"\\n\");\n        dfree(zl);\n    }\n\n    printf(\"Get element at index -1 (last element):\\n\");\n    {\n        zl = createList();\n        p = ziplistIndex(zl, -1);\n        if (!ziplistGet(p, &entry, &elen, &value)) {\n            printf(\"ERROR: Could not access index -1\\n\");\n            return 1;\n        }\n        if (entry) {\n            if (elen && fwrite(entry,elen,1,stdout) == 0) perror(\"fwrite\");\n            printf(\"\\n\");\n        } else {\n            printf(\"%lld\\n\", value);\n        }\n        printf(\"\\n\");\n        dfree(zl);\n    }\n\n    printf(\"Get element at index -4 (first element):\\n\");\n    {\n        zl = createList();\n        p = ziplistIndex(zl, -4);\n        if (!ziplistGet(p, &entry, &elen, &value)) {\n            printf(\"ERROR: Could not access index -4\\n\");\n            return 1;\n        }\n        if (entry) {\n            if (elen && fwrite(entry,elen,1,stdout) == 0) perror(\"fwrite\");\n            printf(\"\\n\");\n        } else {\n            printf(\"%lld\\n\", value);\n        }\n        printf(\"\\n\");\n        dfree(zl);\n    }\n\n    printf(\"Get element at index -5 (reverse out of range):\\n\");\n    {\n        zl = createList();\n        p = ziplistIndex(zl, -5);\n        if (p == NULL) {\n            printf(\"No entry\\n\");\n        } else {\n            printf(\"ERROR: Out of range index should return NULL, returned offset: %ld\\n\", p-zl);\n            return 1;\n        }\n        printf(\"\\n\");\n        dfree(zl);\n    }\n\n    printf(\"Iterate list from 0 to end:\\n\");\n    {\n        zl = createList();\n        p = ziplistIndex(zl, 0);\n        while (ziplistGet(p, &entry, &elen, &value)) {\n            printf(\"Entry: \");\n            if (entry) {\n                if (elen && fwrite(entry,elen,1,stdout) == 0) perror(\"fwrite\");\n            } else {\n                printf(\"%lld\", value);\n            }\n            p = ziplistNext(zl,p);\n            printf(\"\\n\");\n        }\n        printf(\"\\n\");\n        dfree(zl);\n    }\n\n    printf(\"Iterate list from 1 to end:\\n\");\n    {\n        zl = createList();\n        p = ziplistIndex(zl, 1);\n        while (ziplistGet(p, &entry, &elen, &value)) {\n            printf(\"Entry: \");\n            if (entry) {\n                if (elen && fwrite(entry,elen,1,stdout) == 0) perror(\"fwrite\");\n            } else {\n                printf(\"%lld\", value);\n            }\n            p = ziplistNext(zl,p);\n            printf(\"\\n\");\n        }\n        printf(\"\\n\");\n        dfree(zl);\n    }\n\n    printf(\"Iterate list from 2 to end:\\n\");\n    {\n        zl = createList();\n        p = ziplistIndex(zl, 2);\n        while (ziplistGet(p, &entry, &elen, &value)) {\n            printf(\"Entry: \");\n            if (entry) {\n                if (elen && fwrite(entry,elen,1,stdout) == 0) perror(\"fwrite\");\n            } else {\n                printf(\"%lld\", value);\n            }\n            p = ziplistNext(zl,p);\n            printf(\"\\n\");\n        }\n        printf(\"\\n\");\n        dfree(zl);\n    }\n\n    printf(\"Iterate starting out of range:\\n\");\n    {\n        zl = createList();\n        p = ziplistIndex(zl, 4);\n        if (!ziplistGet(p, &entry, &elen, &value)) {\n            printf(\"No entry\\n\");\n        } else {\n            printf(\"ERROR\\n\");\n        }\n        printf(\"\\n\");\n        dfree(zl);\n    }\n\n    printf(\"Iterate from back to front:\\n\");\n    {\n        zl = createList();\n        p = ziplistIndex(zl, -1);\n        while (ziplistGet(p, &entry, &elen, &value)) {\n            printf(\"Entry: \");\n            if (entry) {\n                if (elen && fwrite(entry,elen,1,stdout) == 0) perror(\"fwrite\");\n            } else {\n                printf(\"%lld\", value);\n            }\n            p = ziplistPrev(zl,p);\n            printf(\"\\n\");\n        }\n        printf(\"\\n\");\n        dfree(zl);\n    }\n\n    printf(\"Iterate from back to front, deleting all items:\\n\");\n    {\n        zl = createList();\n        p = ziplistIndex(zl, -1);\n        while (ziplistGet(p, &entry, &elen, &value)) {\n            printf(\"Entry: \");\n            if (entry) {\n                if (elen && fwrite(entry,elen,1,stdout) == 0) perror(\"fwrite\");\n            } else {\n                printf(\"%lld\", value);\n            }\n            zl = ziplistDelete(zl,&p);\n            p = ziplistPrev(zl,p);\n            printf(\"\\n\");\n        }\n        printf(\"\\n\");\n        dfree(zl);\n    }\n\n    printf(\"Delete inclusive range 0,0:\\n\");\n    {\n        zl = createList();\n        zl = ziplistDeleteRange(zl, 0, 1);\n        ziplistRepr(zl);\n        dfree(zl);\n    }\n\n    printf(\"Delete inclusive range 0,1:\\n\");\n    {\n        zl = createList();\n        zl = ziplistDeleteRange(zl, 0, 2);\n        ziplistRepr(zl);\n        dfree(zl);\n    }\n\n    printf(\"Delete inclusive range 1,2:\\n\");\n    {\n        zl = createList();\n        zl = ziplistDeleteRange(zl, 1, 2);\n        ziplistRepr(zl);\n        dfree(zl);\n    }\n\n    printf(\"Delete with start index out of range:\\n\");\n    {\n        zl = createList();\n        zl = ziplistDeleteRange(zl, 5, 1);\n        ziplistRepr(zl);\n        dfree(zl);\n    }\n\n    printf(\"Delete with num overflow:\\n\");\n    {\n        zl = createList();\n        zl = ziplistDeleteRange(zl, 1, 5);\n        ziplistRepr(zl);\n        dfree(zl);\n    }\n\n    printf(\"Delete foo while iterating:\\n\");\n    {\n        zl = createList();\n        p = ziplistIndex(zl,0);\n        while (ziplistGet(p,&entry,&elen,&value)) {\n            if (entry && strncmp(\"foo\",(char*)entry,elen) == 0) {\n                printf(\"Delete foo\\n\");\n                zl = ziplistDelete(zl,&p);\n            } else {\n                printf(\"Entry: \");\n                if (entry) {\n                    if (elen && fwrite(entry,elen,1,stdout) == 0)\n                        perror(\"fwrite\");\n                } else {\n                    printf(\"%lld\",value);\n                }\n                p = ziplistNext(zl,p);\n                printf(\"\\n\");\n            }\n        }\n        printf(\"\\n\");\n        ziplistRepr(zl);\n        dfree(zl);\n    }\n\n    printf(\"Regression test for >255 byte strings:\\n\");\n    {\n        char v1[257] = {0}, v2[257] = {0};\n        memset(v1,'x',256);\n        memset(v2,'y',256);\n        zl = ziplistNew();\n        zl = ziplistPush(zl,(unsigned char*)v1,strlen(v1),ZIPLIST_TAIL);\n        zl = ziplistPush(zl,(unsigned char*)v2,strlen(v2),ZIPLIST_TAIL);\n\n        /* Pop values again and compare their value. */\n        p = ziplistIndex(zl,0);\n        ret = (int)ziplistGet(p,&entry,&elen,&value);\n        ASSERT(ret > 0);\n        ASSERT(strncmp(v1,(char*)entry,elen) == 0);\n        p = ziplistIndex(zl,1);\n        ret = (int)ziplistGet(p,&entry,&elen,&value);\n        ASSERT(ret > 0);\n        ASSERT(strncmp(v2,(char*)entry,elen) == 0);\n        printf(\"SUCCESS\\n\\n\");\n        dfree(zl);\n    }\n\n    printf(\"Regression test deleting next to last entries:\\n\");\n    {\n        char v[3][257] = {{0}};\n        zlentry e[3] = {{.prevrawlensize = 0, .prevrawlen = 0, .lensize = 0,\n                         .len = 0, .headersize = 0, .encoding = 0, .p = NULL}};\n        size_t i;\n\n        for (i = 0; i < (sizeof(v)/sizeof(v[0])); i++) {\n            memset(v[i], 'a' + i, sizeof(v[0]));\n        }\n\n        v[0][256] = '\\0';\n        v[1][  1] = '\\0';\n        v[2][256] = '\\0';\n\n        zl = ziplistNew();\n        for (i = 0; i < (sizeof(v)/sizeof(v[0])); i++) {\n            zl = ziplistPush(zl, (unsigned char *) v[i], strlen(v[i]), ZIPLIST_TAIL);\n        }\n\n        verify(zl, e);\n\n        ASSERT(e[0].prevrawlensize == 1);\n        ASSERT(e[1].prevrawlensize == 5);\n        ASSERT(e[2].prevrawlensize == 1);\n\n        /* Deleting entry 1 will increase `prevrawlensize` for entry 2 */\n        unsigned char *p = e[1].p;\n        zl = ziplistDelete(zl, &p);\n\n        verify(zl, e);\n\n        ASSERT(e[0].prevrawlensize == 1);\n        ASSERT(e[1].prevrawlensize == 5);\n\n        printf(\"SUCCESS\\n\\n\");\n        dfree(zl);\n    }\n\n    printf(\"Create long list and check indices:\\n\");\n    {\n        zl = ziplistNew();\n        char buf[32];\n        int i,len;\n        for (i = 0; i < 1000; i++) {\n            len = sprintf(buf,\"%d\",i);\n            zl = ziplistPush(zl,(unsigned char*)buf,len,ZIPLIST_TAIL);\n        }\n        for (i = 0; i < 1000; i++) {\n            p = ziplistIndex(zl,i);\n            ret = (int)ziplistGet(p,NULL,NULL,&value);\n            ASSERT(ret > 0);\n            ASSERT(i == value);\n\n            p = ziplistIndex(zl,-i-1);\n            ret = (int)ziplistGet(p,NULL,NULL,&value);\n            ASSERT(ret > 0);\n            ASSERT(999-i == value);\n        }\n        printf(\"SUCCESS\\n\\n\");\n        dfree(zl);\n    }\n\n    printf(\"Compare strings with ziplist entries:\\n\");\n    {\n        zl = createList();\n        p = ziplistIndex(zl,0);\n        if (!ziplistCompare(p,(unsigned char*)\"hello\",5)) {\n            printf(\"ERROR: not \\\"hello\\\"\\n\");\n            return 1;\n        }\n        if (ziplistCompare(p,(unsigned char*)\"hella\",5)) {\n            printf(\"ERROR: \\\"hella\\\"\\n\");\n            return 1;\n        }\n\n        p = ziplistIndex(zl,3);\n        if (!ziplistCompare(p,(unsigned char*)\"1024\",4)) {\n            printf(\"ERROR: not \\\"1024\\\"\\n\");\n            return 1;\n        }\n        if (ziplistCompare(p,(unsigned char*)\"1025\",4)) {\n            printf(\"ERROR: \\\"1025\\\"\\n\");\n            return 1;\n        }\n        printf(\"SUCCESS\\n\\n\");\n        dfree(zl);\n    }\n\n    printf(\"Merge test:\\n\");\n    {\n        /* create list gives us: [hello, foo, quux, 1024] */\n        zl = createList();\n        unsigned char *zl2 = createList();\n\n        unsigned char *zl3 = ziplistNew();\n        unsigned char *zl4 = ziplistNew();\n\n        if (ziplistMerge(&zl4, &zl4)) {\n            printf(\"ERROR: Allowed merging of one ziplist into itself.\\n\");\n            return 1;\n        }\n\n        /* Merge two empty ziplists, get empty result back. */\n        zl4 = ziplistMerge(&zl3, &zl4);\n        ziplistRepr(zl4);\n        if (ziplistLen(zl4)) {\n            printf(\"ERROR: Merging two empty ziplists created entries.\\n\");\n            return 1;\n        }\n        dfree(zl4);\n\n        zl2 = ziplistMerge(&zl, &zl2);\n        /* merge gives us: [hello, foo, quux, 1024, hello, foo, quux, 1024] */\n        ziplistRepr(zl2);\n\n        if (ziplistLen(zl2) != 8) {\n            printf(\"ERROR: Merged length not 8, but: %u\\n\", ziplistLen(zl2));\n            return 1;\n        }\n\n        p = ziplistIndex(zl2,0);\n        if (!ziplistCompare(p,(unsigned char*)\"hello\",5)) {\n            printf(\"ERROR: not \\\"hello\\\"\\n\");\n            return 1;\n        }\n        if (ziplistCompare(p,(unsigned char*)\"hella\",5)) {\n            printf(\"ERROR: \\\"hella\\\"\\n\");\n            return 1;\n        }\n\n        p = ziplistIndex(zl2,3);\n        if (!ziplistCompare(p,(unsigned char*)\"1024\",4)) {\n            printf(\"ERROR: not \\\"1024\\\"\\n\");\n            return 1;\n        }\n        if (ziplistCompare(p,(unsigned char*)\"1025\",4)) {\n            printf(\"ERROR: \\\"1025\\\"\\n\");\n            return 1;\n        }\n\n        p = ziplistIndex(zl2,4);\n        if (!ziplistCompare(p,(unsigned char*)\"hello\",5)) {\n            printf(\"ERROR: not \\\"hello\\\"\\n\");\n            return 1;\n        }\n        if (ziplistCompare(p,(unsigned char*)\"hella\",5)) {\n            printf(\"ERROR: \\\"hella\\\"\\n\");\n            return 1;\n        }\n\n        p = ziplistIndex(zl2,7);\n        if (!ziplistCompare(p,(unsigned char*)\"1024\",4)) {\n            printf(\"ERROR: not \\\"1024\\\"\\n\");\n            return 1;\n        }\n        if (ziplistCompare(p,(unsigned char*)\"1025\",4)) {\n            printf(\"ERROR: \\\"1025\\\"\\n\");\n            return 1;\n        }\n        printf(\"SUCCESS\\n\\n\");\n        dfree(zl);\n    }\n\n    printf(\"Stress with random payloads of different encoding:\\n\");\n    {\n        int i,j,len,where;\n        unsigned char *p;\n        char buf[1024];\n        int buflen;\n        dlist *ref;\n        dlistNode *refnode;\n\n        /* Hold temp vars from ziplist */\n        unsigned char *sstr;\n        unsigned int slen;\n        long long sval;\n\n        for (i = 0; i < 20000; i++) {\n            zl = ziplistNew();\n            ref = dlistCreate();\n            dlistSetFreeMethod(ref,(void (*)(void*))sdsfree);\n            len = rand() % 256;\n\n            /* Create lists */\n            for (j = 0; j < len; j++) {\n                where = (rand() & 1) ? ZIPLIST_HEAD : ZIPLIST_TAIL;\n                if (rand() % 2) {\n                    buflen = randstring(buf,1,sizeof(buf)-1);\n                } else {\n                    switch(rand() % 3) {\n                    case 0:\n                        buflen = sprintf(buf,\"%lld\",(0LL + rand()) >> 20);\n                        break;\n                    case 1:\n                        buflen = sprintf(buf,\"%lld\",(0LL + rand()));\n                        break;\n                    case 2:\n                        buflen = sprintf(buf,\"%lld\",(0LL + rand()) << 20);\n                        break;\n                    default:\n                        ASSERT(NULL);\n                    }\n                }\n\n                /* Add to ziplist */\n                zl = ziplistPush(zl, (unsigned char*)buf, buflen, where);\n\n                /* Add to reference list */\n                if (where == ZIPLIST_HEAD) {\n                    dlistAddNodeHead(ref,sdsnewlen(buf, buflen));\n                } else if (where == ZIPLIST_TAIL) {\n                    dlistAddNodeTail(ref,sdsnewlen(buf, buflen));\n                } else {\n                    ASSERT(NULL);\n                }\n            }\n\n            ASSERT(dlistLength(ref) == ziplistLen(zl));\n            for (j = 0; j < len; j++) {\n                /* Naive way to get elements, but similar to the stresser\n                 * executed from the Tcl test suite. */\n                p = ziplistIndex(zl,j);\n                refnode = dlistIndex(ref,j);\n\n                ret = (int)ziplistGet(p,&sstr,&slen,&sval);\n                ASSERT(ret > 0);\n                if (sstr == NULL) {\n                    buflen = sprintf(buf,\"%lld\",sval);\n                } else {\n                    buflen = slen;\n                    memcpy(buf,sstr,buflen);\n                    buf[buflen] = '\\0';\n                }\n                ASSERT(memcmp(buf,dlistNodeValue(refnode),buflen) == 0);\n            }\n            dfree(zl);\n            dlistRelease(ref);\n        }\n        printf(\"SUCCESS\\n\\n\");\n    }\n\n    printf(\"Stress with variable ziplist size:\\n\");\n    {\n        stress(ZIPLIST_HEAD,100000,16384,256);\n        stress(ZIPLIST_TAIL,100000,16384,256);\n    }\n\n    return 0;\n}\n#endif\n"
  },
  {
    "path": "src/vr_ziplist.h",
    "content": "#ifndef _ZIPLIST_H\n#define _ZIPLIST_H\n\n#define ZIPLIST_HEAD 0\n#define ZIPLIST_TAIL 1\n\nunsigned char *ziplistNew(void);\nunsigned char *ziplistMerge(unsigned char **first, unsigned char **second);\nunsigned char *ziplistPush(unsigned char *zl, unsigned char *s, unsigned int slen, int where);\nunsigned char *ziplistIndex(unsigned char *zl, int index);\nunsigned char *ziplistNext(unsigned char *zl, unsigned char *p);\nunsigned char *ziplistPrev(unsigned char *zl, unsigned char *p);\nunsigned int ziplistGet(unsigned char *p, unsigned char **sval, unsigned int *slen, long long *lval);\nunsigned char *ziplistInsert(unsigned char *zl, unsigned char *p, unsigned char *s, unsigned int slen);\nunsigned char *ziplistDelete(unsigned char *zl, unsigned char **p);\nunsigned char *ziplistDeleteRange(unsigned char *zl, int index, unsigned int num);\nunsigned int ziplistCompare(unsigned char *p, unsigned char *s, unsigned int slen);\nunsigned char *ziplistFind(unsigned char *p, unsigned char *vstr, unsigned int vlen, unsigned int skip);\nunsigned int ziplistLen(unsigned char *zl);\nsize_t ziplistBlobLen(unsigned char *zl);\n\n#ifdef REDIS_TEST\nint ziplistTest(int argc, char *argv[]);\n#endif\n\n#endif /* _ZIPLIST_H */\n"
  },
  {
    "path": "src/vr_zipmap.c",
    "content": "/* String -> String Map data structure optimized for size.\n * This file implements a data structure mapping strings to other strings\n * implementing an O(n) lookup data structure designed to be very memory\n * efficient.\n *\n * The Redis Hash type uses this data structure for hashes composed of a small\n * number of elements, to switch to a hash table once a given number of\n * elements is reached.\n *\n * Given that many times Redis Hashes are used to represent objects composed\n * of few fields, this is a very big win in terms of used memory.\n *\n * --------------------------------------------------------------------------\n *\n * Copyright (c) 2009-2010, Salvatore Sanfilippo <antirez at gmail dot com>\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n *   * Redistributions of source code must retain the above copyright notice,\n *     this list of conditions and the following disclaimer.\n *   * Redistributions in binary form must reproduce the above copyright\n *     notice, this list of conditions and the following disclaimer in the\n *     documentation and/or other materials provided with the distribution.\n *   * Neither the name of Redis nor the names of its contributors may be used\n *     to endorse or promote products derived from this software without\n *     specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\n/* Memory layout of a zipmap, for the map \"foo\" => \"bar\", \"hello\" => \"world\":\n *\n * <zmlen><len>\"foo\"<len><free>\"bar\"<len>\"hello\"<len><free>\"world\"\n *\n * <zmlen> is 1 byte length that holds the current size of the zipmap.\n * When the zipmap length is greater than or equal to 254, this value\n * is not used and the zipmap needs to be traversed to find out the length.\n *\n * <len> is the length of the following string (key or value).\n * <len> lengths are encoded in a single value or in a 5 bytes value.\n * If the first byte value (as an unsigned 8 bit value) is between 0 and\n * 253, it's a single-byte length. If it is 254 then a four bytes unsigned\n * integer follows (in the host byte ordering). A value of 255 is used to\n * signal the end of the hash.\n *\n * <free> is the number of free unused bytes after the string, resulting\n * from modification of values associated to a key. For instance if \"foo\"\n * is set to \"bar\", and later \"foo\" will be set to \"hi\", it will have a\n * free byte to use if the value will enlarge again later, or even in\n * order to add a key/value pair if it fits.\n *\n * <free> is always an unsigned 8 bit number, because if after an\n * update operation there are more than a few free bytes, the zipmap will be\n * reallocated to make sure it is as small as possible.\n *\n * The most compact representation of the above two elements hash is actually:\n *\n * \"\\x02\\x03foo\\x03\\x00bar\\x05hello\\x05\\x00world\\xff\"\n *\n * Note that because keys and values are prefixed length \"objects\",\n * the lookup will take O(N) where N is the number of elements\n * in the zipmap and *not* the number of bytes needed to represent the zipmap.\n * This lowers the constant times considerably.\n */\n\n#include <stdio.h>\n#include <string.h>\n\n#include <vr_core.h>\n\n#define ZIPMAP_BIGLEN 254\n#define ZIPMAP_END 255\n\n/* The following defines the max value for the <free> field described in the\n * comments above, that is, the max number of trailing bytes in a value. */\n#define ZIPMAP_VALUE_MAX_FREE 4\n\n/* The following macro returns the number of bytes needed to encode the length\n * for the integer value _l, that is, 1 byte for lengths < ZIPMAP_BIGLEN and\n * 5 bytes for all the other lengths. */\n#define ZIPMAP_LEN_BYTES(_l) (((_l) < ZIPMAP_BIGLEN) ? 1 : sizeof(unsigned int)+1)\n\n/* Create a new empty zipmap. */\nunsigned char *zipmapNew(void) {\n    unsigned char *zm = dalloc(2);\n\n    zm[0] = 0; /* Length */\n    zm[1] = ZIPMAP_END;\n    return zm;\n}\n\n/* Decode the encoded length pointed by 'p' */\nstatic unsigned int zipmapDecodeLength(unsigned char *p) {\n    unsigned int len = *p;\n\n    if (len < ZIPMAP_BIGLEN) return len;\n    memcpy(&len,p+1,sizeof(unsigned int));\n    memrev32ifbe(&len);\n    return len;\n}\n\n/* Encode the length 'l' writing it in 'p'. If p is NULL it just returns\n * the amount of bytes required to encode such a length. */\nstatic unsigned int zipmapEncodeLength(unsigned char *p, unsigned int len) {\n    if (p == NULL) {\n        return ZIPMAP_LEN_BYTES(len);\n    } else {\n        if (len < ZIPMAP_BIGLEN) {\n            p[0] = len;\n            return 1;\n        } else {\n            p[0] = ZIPMAP_BIGLEN;\n            memcpy(p+1,&len,sizeof(len));\n            memrev32ifbe(p+1);\n            return 1+sizeof(len);\n        }\n    }\n}\n\n/* Search for a matching key, returning a pointer to the entry inside the\n * zipmap. Returns NULL if the key is not found.\n *\n * If NULL is returned, and totlen is not NULL, it is set to the entire\n * size of the zimap, so that the calling function will be able to\n * reallocate the original zipmap to make room for more entries. */\nstatic unsigned char *zipmapLookupRaw(unsigned char *zm, unsigned char *key, unsigned int klen, unsigned int *totlen) {\n    unsigned char *p = zm+1, *k = NULL;\n    unsigned int l,llen;\n\n    while(*p != ZIPMAP_END) {\n        unsigned char free;\n\n        /* Match or skip the key */\n        l = zipmapDecodeLength(p);\n        llen = zipmapEncodeLength(NULL,l);\n        if (key != NULL && k == NULL && l == klen && !memcmp(p+llen,key,l)) {\n            /* Only return when the user doesn't care\n             * for the total length of the zipmap. */\n            if (totlen != NULL) {\n                k = p;\n            } else {\n                return p;\n            }\n        }\n        p += llen+l;\n        /* Skip the value as well */\n        l = zipmapDecodeLength(p);\n        p += zipmapEncodeLength(NULL,l);\n        free = p[0];\n        p += l+1+free; /* +1 to skip the free byte */\n    }\n    if (totlen != NULL) *totlen = (unsigned int)(p-zm)+1;\n    return k;\n}\n\nstatic unsigned long zipmapRequiredLength(unsigned int klen, unsigned int vlen) {\n    unsigned int l;\n\n    l = klen+vlen+3;\n    if (klen >= ZIPMAP_BIGLEN) l += 4;\n    if (vlen >= ZIPMAP_BIGLEN) l += 4;\n    return l;\n}\n\n/* Return the total amount used by a key (encoded length + payload) */\nstatic unsigned int zipmapRawKeyLength(unsigned char *p) {\n    unsigned int l = zipmapDecodeLength(p);\n    return zipmapEncodeLength(NULL,l) + l;\n}\n\n/* Return the total amount used by a value\n * (encoded length + single byte free count + payload) */\nstatic unsigned int zipmapRawValueLength(unsigned char *p) {\n    unsigned int l = zipmapDecodeLength(p);\n    unsigned int used;\n\n    used = zipmapEncodeLength(NULL,l);\n    used += p[used] + 1 + l;\n    return used;\n}\n\n/* If 'p' points to a key, this function returns the total amount of\n * bytes used to store this entry (entry = key + associated value + trailing\n * free space if any). */\nstatic unsigned int zipmapRawEntryLength(unsigned char *p) {\n    unsigned int l = zipmapRawKeyLength(p);\n    return l + zipmapRawValueLength(p+l);\n}\n\nstatic inline unsigned char *zipmapResize(unsigned char *zm, unsigned int len) {\n    zm = drealloc(zm, len);\n    zm[len-1] = ZIPMAP_END;\n    return zm;\n}\n\n/* Set key to value, creating the key if it does not already exist.\n * If 'update' is not NULL, *update is set to 1 if the key was\n * already preset, otherwise to 0. */\nunsigned char *zipmapSet(unsigned char *zm, unsigned char *key, unsigned int klen, unsigned char *val, unsigned int vlen, int *update) {\n    unsigned int zmlen, offset;\n    unsigned int freelen, reqlen = zipmapRequiredLength(klen,vlen);\n    unsigned int empty, vempty;\n    unsigned char *p;\n\n    freelen = reqlen;\n    if (update) *update = 0;\n    p = zipmapLookupRaw(zm,key,klen,&zmlen);\n    if (p == NULL) {\n        /* Key not found: enlarge */\n        zm = zipmapResize(zm, zmlen+reqlen);\n        p = zm+zmlen-1;\n        zmlen = zmlen+reqlen;\n\n        /* Increase zipmap length (this is an insert) */\n        if (zm[0] < ZIPMAP_BIGLEN) zm[0]++;\n    } else {\n        /* Key found. Is there enough space for the new value? */\n        /* Compute the total length: */\n        if (update) *update = 1;\n        freelen = zipmapRawEntryLength(p);\n        if (freelen < reqlen) {\n            /* Store the offset of this key within the current zipmap, so\n             * it can be resized. Then, move the tail backwards so this\n             * pair fits at the current position. */\n            offset = p-zm;\n            zm = zipmapResize(zm, zmlen-freelen+reqlen);\n            p = zm+offset;\n\n            /* The +1 in the number of bytes to be moved is caused by the\n             * end-of-zipmap byte. Note: the *original* zmlen is used. */\n            memmove(p+reqlen, p+freelen, zmlen-(offset+freelen+1));\n            zmlen = zmlen-freelen+reqlen;\n            freelen = reqlen;\n        }\n    }\n\n    /* We now have a suitable block where the key/value entry can\n     * be written. If there is too much free space, move the tail\n     * of the zipmap a few bytes to the front and shrink the zipmap,\n     * as we want zipmaps to be very space efficient. */\n    empty = freelen-reqlen;\n    if (empty >= ZIPMAP_VALUE_MAX_FREE) {\n        /* First, move the tail <empty> bytes to the front, then resize\n         * the zipmap to be <empty> bytes smaller. */\n        offset = p-zm;\n        memmove(p+reqlen, p+freelen, zmlen-(offset+freelen+1));\n        zmlen -= empty;\n        zm = zipmapResize(zm, zmlen);\n        p = zm+offset;\n        vempty = 0;\n    } else {\n        vempty = empty;\n    }\n\n    /* Just write the key + value and we are done. */\n    /* Key: */\n    p += zipmapEncodeLength(p,klen);\n    memcpy(p,key,klen);\n    p += klen;\n    /* Value: */\n    p += zipmapEncodeLength(p,vlen);\n    *p++ = vempty;\n    memcpy(p,val,vlen);\n    return zm;\n}\n\n/* Remove the specified key. If 'deleted' is not NULL the pointed integer is\n * set to 0 if the key was not found, to 1 if it was found and deleted. */\nunsigned char *zipmapDel(unsigned char *zm, unsigned char *key, unsigned int klen, int *deleted) {\n    unsigned int zmlen, freelen;\n    unsigned char *p = zipmapLookupRaw(zm,key,klen,&zmlen);\n    if (p) {\n        freelen = zipmapRawEntryLength(p);\n        memmove(p, p+freelen, zmlen-((p-zm)+freelen+1));\n        zm = zipmapResize(zm, zmlen-freelen);\n\n        /* Decrease zipmap length */\n        if (zm[0] < ZIPMAP_BIGLEN) zm[0]--;\n\n        if (deleted) *deleted = 1;\n    } else {\n        if (deleted) *deleted = 0;\n    }\n    return zm;\n}\n\n/* Call before iterating through elements via zipmapNext() */\nunsigned char *zipmapRewind(unsigned char *zm) {\n    return zm+1;\n}\n\n/* This function is used to iterate through all the zipmap elements.\n * In the first call the first argument is the pointer to the zipmap + 1.\n * In the next calls what zipmapNext returns is used as first argument.\n * Example:\n *\n * unsigned char *i = zipmapRewind(my_zipmap);\n * while((i = zipmapNext(i,&key,&klen,&value,&vlen)) != NULL) {\n *     printf(\"%d bytes key at $p\\n\", klen, key);\n *     printf(\"%d bytes value at $p\\n\", vlen, value);\n * }\n */\nunsigned char *zipmapNext(unsigned char *zm, unsigned char **key, unsigned int *klen, unsigned char **value, unsigned int *vlen) {\n    if (zm[0] == ZIPMAP_END) return NULL;\n    if (key) {\n        *key = zm;\n        *klen = zipmapDecodeLength(zm);\n        *key += ZIPMAP_LEN_BYTES(*klen);\n    }\n    zm += zipmapRawKeyLength(zm);\n    if (value) {\n        *value = zm+1;\n        *vlen = zipmapDecodeLength(zm);\n        *value += ZIPMAP_LEN_BYTES(*vlen);\n    }\n    zm += zipmapRawValueLength(zm);\n    return zm;\n}\n\n/* Search a key and retrieve the pointer and len of the associated value.\n * If the key is found the function returns 1, otherwise 0. */\nint zipmapGet(unsigned char *zm, unsigned char *key, unsigned int klen, unsigned char **value, unsigned int *vlen) {\n    unsigned char *p;\n\n    if ((p = zipmapLookupRaw(zm,key,klen,NULL)) == NULL) return 0;\n    p += zipmapRawKeyLength(p);\n    *vlen = zipmapDecodeLength(p);\n    *value = p + ZIPMAP_LEN_BYTES(*vlen) + 1;\n    return 1;\n}\n\n/* Return 1 if the key exists, otherwise 0 is returned. */\nint zipmapExists(unsigned char *zm, unsigned char *key, unsigned int klen) {\n    return zipmapLookupRaw(zm,key,klen,NULL) != NULL;\n}\n\n/* Return the number of entries inside a zipmap */\nunsigned int zipmapLen(unsigned char *zm) {\n    unsigned int len = 0;\n    if (zm[0] < ZIPMAP_BIGLEN) {\n        len = zm[0];\n    } else {\n        unsigned char *p = zipmapRewind(zm);\n        while((p = zipmapNext(p,NULL,NULL,NULL,NULL)) != NULL) len++;\n\n        /* Re-store length if small enough */\n        if (len < ZIPMAP_BIGLEN) zm[0] = len;\n    }\n    return len;\n}\n\n/* Return the raw size in bytes of a zipmap, so that we can serialize\n * the zipmap on disk (or everywhere is needed) just writing the returned\n * amount of bytes of the C array starting at the zipmap pointer. */\nsize_t zipmapBlobLen(unsigned char *zm) {\n    unsigned int totlen;\n    zipmapLookupRaw(zm,NULL,0,&totlen);\n    return totlen;\n}\n"
  },
  {
    "path": "src/vr_zipmap.h",
    "content": "#ifndef _ZIPMAP_H\n#define _ZIPMAP_H\n\nunsigned char *zipmapNew(void);\nunsigned char *zipmapSet(unsigned char *zm, unsigned char *key, unsigned int klen, unsigned char *val, unsigned int vlen, int *update);\nunsigned char *zipmapDel(unsigned char *zm, unsigned char *key, unsigned int klen, int *deleted);\nunsigned char *zipmapRewind(unsigned char *zm);\nunsigned char *zipmapNext(unsigned char *zm, unsigned char **key, unsigned int *klen, unsigned char **value, unsigned int *vlen);\nint zipmapGet(unsigned char *zm, unsigned char *key, unsigned int klen, unsigned char **value, unsigned int *vlen);\nint zipmapExists(unsigned char *zm, unsigned char *key, unsigned int klen);\nunsigned int zipmapLen(unsigned char *zm);\nsize_t zipmapBlobLen(unsigned char *zm);\nvoid zipmapRepr(unsigned char *p);\n\n#endif\n"
  },
  {
    "path": "tests/.gitignore",
    "content": "*.pyc\n*.out\n*.log"
  },
  {
    "path": "tests/Makefile.am",
    "content": "MAINTAINERCLEANFILES = Makefile.in\n\nAM_CPPFLAGS =\nif !OS_SOLARIS\nAM_CPPFLAGS += -D_GNU_SOURCE\nendif\nAM_CPPFLAGS += -I $(top_srcdir)/dep/ae\nAM_CPPFLAGS += -I $(top_srcdir)/dep/jemalloc-4.2.0/include\nAM_CPPFLAGS += -I $(top_srcdir)/dep/hiredis-0.13.3\nAM_CPPFLAGS += -I $(top_srcdir)/dep/dhashkit\nAM_CPPFLAGS += -I $(top_srcdir)/dep/dlist\nAM_CPPFLAGS += -I $(top_srcdir)/dep/darray\nAM_CPPFLAGS += -I $(top_srcdir)/dep/util\nAM_CPPFLAGS += -I $(top_srcdir)/dep/himemcached-0.1.0\n\nAM_CFLAGS = \nAM_CFLAGS += -fno-strict-aliasing\nAM_CFLAGS += -Wall -Wshadow\nAM_CFLAGS += -Wpointer-arith\nAM_CFLAGS += -Winline\nAM_CFLAGS += -Wunused-function -Wunused-variable -Wunused-value\nAM_CFLAGS += -Wno-unused-parameter -Wno-unused-value\nAM_CFLAGS += -Wconversion -Wsign-compare\nAM_CFLAGS += -Wstrict-prototypes -Wmissing-prototypes -Wredundant-decls -Wmissing-declarations\n\nAM_LDFLAGS =\nAM_LDFLAGS += -lm -lpthread -rdynamic\nif !OS_DARWIN\nAM_LDFLAGS += -lrt\nendif\nif OS_SOLARIS\nAM_LDFLAGS += -lnsl -lsocket\nendif\nif OS_FREEBSD\nAM_LDFLAGS += -lexecinfo\nendif\n\nnoinst_PROGRAMS = viretest\n\nviretest_SOURCES =                      \\\n    vrt_util.c vrt_util.h               \\\n    vrt_public.c vrt_public.h           \\\n    vrt_simple.c vrt_simple.h           \\\n    vrtest.c\n    \nviretest_LDADD = $(top_builddir)/dep/ae/libae.a\nviretest_LDADD += $(top_builddir)/dep/hiredis-0.13.3/libhiredis.a\nviretest_LDADD += $(top_builddir)/dep/darray/libdarray.a\nviretest_LDADD += $(top_builddir)/dep/dmalloc/libdmalloc.a\nviretest_LDADD += $(top_builddir)/dep/util/libdutil.a\nviretest_LDADD += $(top_builddir)/dep/jemalloc-4.2.0/lib/libjemalloc.a\n\nnoinst_PROGRAMS += vireabtest\n\nvireabtest_SOURCES =                        \\\n    vrt_util.c vrt_util.h                   \\\n    vrt_public.c vrt_public.h               \\\n    vrt_produce_data.c vrt_produce_data.h   \\\n    vrt_dispatch_data.c vrt_dispatch_data.h \\\n    vrt_check_data.c vrt_check_data.h       \\\n    vrt_backend.c vrt_backend.h             \\\n    vrabtest.c vrabtest.h\n    \nvireabtest_LDADD = $(top_builddir)/dep/ae/libae.a\nvireabtest_LDADD += $(top_builddir)/dep/hiredis-0.13.3/libhiredis.a\nvireabtest_LDADD += $(top_builddir)/dep/dhashkit/libdhashkit.a\nvireabtest_LDADD += $(top_builddir)/dep/dlist/libdlist.a\nvireabtest_LDADD += $(top_builddir)/dep/darray/libdarray.a\nvireabtest_LDADD += $(top_builddir)/dep/dmalloc/libdmalloc.a\nvireabtest_LDADD += $(top_builddir)/dep/util/libdutil.a\nvireabtest_LDADD += $(top_builddir)/dep/jemalloc-4.2.0/lib/libjemalloc.a\n\nnoinst_PROGRAMS += vire-benchmark\n\nvire_benchmark_SOURCES =                    \\\n    vrt_util.c vrt_util.h                   \\\n    vrt_public.c vrt_public.h               \\\n    vrt_benchmark.c\n    \nvire_benchmark_LDADD = $(top_builddir)/dep/ae/libae.a\nvire_benchmark_LDADD += $(top_builddir)/dep/hiredis-0.13.3/libhiredis.a\nvire_benchmark_LDADD += $(top_builddir)/dep/himemcached-0.1.0/libhimemcached.a\nvire_benchmark_LDADD += $(top_builddir)/dep/dlist/libdlist.a\nvire_benchmark_LDADD += $(top_builddir)/dep/darray/libdarray.a\nvire_benchmark_LDADD += $(top_builddir)/dep/dmalloc/libdmalloc.a\nvire_benchmark_LDADD += $(top_builddir)/dep/util/libdutil.a\nvire_benchmark_LDADD += $(top_builddir)/dep/jemalloc-4.2.0/lib/libjemalloc.a"
  },
  {
    "path": "tests/vrabtest.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <getopt.h>\n#include <fcntl.h>\n#include <errno.h>\n#include <signal.h>\n#include <assert.h>\n#include <sys/stat.h>\n#include <sys/utsname.h>\n\n#include <hiredis.h>\n#include <darray.h>\n#include <dlog.h>\n\n#include <vrt_util.h>\n#include <vrt_public.h>\n#include <vrt_produce_data.h>\n#include <vrt_dispatch_data.h>\n#include <vrt_check_data.h>\n#include <vrt_backend.h>\n#include <vrabtest.h>\n\n#define CONFIG_DEFAULT_PIDFILE                      NULL\n#define CONFIG_DEFAULT_CHECKER                      \"myself\"\n#define CONFIG_DEFAULT_TEST_INTERVAL                3600\n#define CONFIG_DEFAULT_KEY_LENGTH_RANGE_BEGIN       0\n#define CONFIG_DEFAULT_KEY_LENGTH_RANGE_END         100\n#define CONFIG_DEFAULT_STRING_MAX_LENGTH            512\n#define CONFIG_DEFAULT_FIELDS_MAX_COUNT             16\n#define CONFIG_DEFAULT_TEST_TARGET                  \"\"\n#define CONFIG_DEFAULT_PRODUCE_THREADS_COUNT        1\n#define CONFIG_DEFAULT_CACHED_KEYS_COUNT            10000\n#define CONFIG_DEFAULT_HIT_RATIO                    75\n#define CONFIG_DEFAULT_DISPATCH_THREADS_COUNT       1\n#define CONFIG_DEFAULT_CLIENTS_PER_DISPATCH_THREAD  10\n#define CONFIG_DEFAULT_LOGFILE                      NULL\n\n#define VRABTEST_GROUP_TYPE_REDIS   0\n#define VRABTEST_GROUP_TYPE_VIRE    1\n\nstruct config {\n    char *checker;\n    long long test_interval;\n    int key_length_range_begin;\n    int key_length_range_end;\n    int string_max_length;\n    int fields_max_count;\n    int cmd_type;\n    darray *cmd_blacklist;\n    darray *cmd_whitelist;\n    char *test_targets; \n    int produce_data_threads;\n    long long cached_keys_per_produce_thread;\n    int hit_ratio;\n    int dispatch_data_threads;\n    int clients_per_dispatch_thread;\n    char *pid_filename;\n    char *log_filename;\n};\n\nstatic struct config config;\n\nstatic int show_help;\nstatic int show_version;\nstatic int daemonize;\n\n/* 0 or 1\n * 1: used the expire,expireat,pexpire and pexpireat commands\n * 0 is the opposite. */\nint expire_enabled;\n\n/* Interval time for test data dispatched to test targets. \n * Unit is second */\nlong long test_interval;\n\n/* Last begin time to test the data.\n * Unit is second */\nlong long last_test_begin_time;\n\nstatic struct option long_options[] = {\n    { \"help\",                   no_argument,        NULL,   'h' },\n    { \"version\",                no_argument,        NULL,   'V' },\n    { \"daemonize\",              no_argument,        NULL,   'D' },\n    { \"enable-expire\",          no_argument,        NULL,   'E' },\n    { \"pid-file\",               required_argument,  NULL,   'P' },\n    { \"checker\",                required_argument,  NULL,   'C' },\n    { \"test-interval\",          required_argument,  NULL,   'i' },\n    { \"key-length-range\",       required_argument,  NULL,   'k' },\n    { \"string-max-length\",      required_argument,  NULL,   's' },\n    { \"fields-max-count\",       required_argument,  NULL,   'f' },\n    { \"command-types\",          required_argument,  NULL,   'T' },\n    { \"command-black-list\",     required_argument,  NULL,   'B' },\n    { \"command-white-list\",     required_argument,  NULL,   'W' },\n    { \"test-targets\",           required_argument,  NULL,   't' },\n    { \"produce-data-threads\",   required_argument,  NULL,   'p' },\n    { \"cached-keys\",            required_argument,  NULL,   'K' },\n    { \"hit-ratio\",              required_argument,  NULL,   'H' },\n    { \"dispatch-data-threads\",  required_argument,  NULL,   'd' },\n    { \"clients\",                required_argument,  NULL,   'c' },\n    { \"log-file\",               required_argument,  NULL,   'o' },\n    { NULL,                     0,                  NULL,    0  }\n};\n\nstatic char short_options[] = \"hVDEP:C:i:k:s:f:T:B:W:t:p:K:H:d:c:o:\";\n\nstatic void\nvrt_show_usage(void)\n{\n    printf(\n        \"Usage: vireabtest [-?hVDE]\" CRLF\n        \"\" CRLF);\n    printf(\n        \"Options:\" CRLF\n        \"  -h, --help                   : this help\" CRLF\n        \"  -V, --version                : show version and exit\" CRLF\n        \"  -D, --daemonize              : run as a daemon\" CRLF\n        \"  -E, --enable-expire          : enable the expire\" CRLF);\n    printf(\n        \"  -P, --pid-file               : pid file\" CRLF\n        \"  -C, --checker                : the checker to check data consistency\" CRLF\n        \"  -i, --test-interval          : the interval for checking data consistency, unit is second\" CRLF\n        \"  -k, --key-length-range       : the key length range to generate for test, like 0-100\" CRLF\n        \"  -s, --string-max-length      : the max string length to generate for test, string is for STRING/LIST... value element\" CRLF\n        \"  -f, --fields-max-count       : the max fields count to generate for test, field is the LIST/HASH...'s element\" CRLF\n        \"  -T, --command-types          : the command types to generate for test, like string,hash,key\" CRLF\n        \"  -B, --command-black-list     : the commands not want to test, like del,lrange,mget\" CRLF\n        \"  -W, --command-white-list     : the commands only allows to test, like del,lrange,mget\" CRLF\n        \"  -t, --test-targets           : the test targets for test, like vire[127.0.0.1:12301]-redis[127.0.0.1:12311]\" CRLF\n        \"  -p, --produce-data-threads   : the threads count to produce test data\" CRLF\n        \"  -K, --cached-keys            : the cached keys count for every produce data thread\" CRLF\n        \"  -H, --hit-ratio              : the hit ratio for readonly commands, between 0 and 100\" CRLF\n        \"  -d, --dispatch-data-threads  : the threads count to dispatch test data to target groups\" CRLF\n        \"  -c, --clients                : the clients count for every dispatch data thread\" CRLF\n        \"  -o, --log-file               : set logging file (default: %s)\" CRLF\n        \"\", \n        CONFIG_DEFAULT_LOGFILE != NULL ? CONFIG_DEFAULT_LOGFILE : \"stderr\");\n}\n\nstatic void\nvrt_set_default_options(void)\n{\n    config.pid_filename = CONFIG_DEFAULT_PIDFILE;\n    config.checker = CONFIG_DEFAULT_CHECKER;\n    config.test_interval = CONFIG_DEFAULT_TEST_INTERVAL;\n    config.key_length_range_begin = CONFIG_DEFAULT_KEY_LENGTH_RANGE_BEGIN;\n    config.key_length_range_end = CONFIG_DEFAULT_KEY_LENGTH_RANGE_END;\n    config.string_max_length = CONFIG_DEFAULT_STRING_MAX_LENGTH;\n    config.fields_max_count = CONFIG_DEFAULT_FIELDS_MAX_COUNT;\n    config.cmd_type = TEST_CMD_TYPE_STRING|TEST_CMD_TYPE_LIST|\n        TEST_CMD_TYPE_SET|TEST_CMD_TYPE_ZSET|TEST_CMD_TYPE_HASH|\n        TEST_CMD_TYPE_SERVER|TEST_CMD_TYPE_KEY;\n    config.cmd_blacklist = NULL;\n    config.cmd_whitelist = NULL;\n    config.test_targets = CONFIG_DEFAULT_TEST_TARGET; \n    config.produce_data_threads = CONFIG_DEFAULT_PRODUCE_THREADS_COUNT;\n    config.cached_keys_per_produce_thread = CONFIG_DEFAULT_CACHED_KEYS_COUNT;\n    config.hit_ratio = CONFIG_DEFAULT_HIT_RATIO;\n    config.dispatch_data_threads = CONFIG_DEFAULT_DISPATCH_THREADS_COUNT;\n    config.clients_per_dispatch_thread = CONFIG_DEFAULT_CLIENTS_PER_DISPATCH_THREAD;\n    config.log_filename = CONFIG_DEFAULT_LOGFILE;\n    \n    expire_enabled = 0;\n}\n\nstatic void\nvrt_clean_options(void)\n{\n    if (config.cmd_blacklist != NULL) {\n        sds *command;\n        while (darray_n(config.cmd_blacklist) > 0) {\n            command = darray_pop(config.cmd_blacklist);\n            sdsfree(command);\n        }\n        darray_destroy(config.cmd_blacklist);\n        config.cmd_blacklist = NULL;\n    }\n\n    if (config.cmd_whitelist != NULL) {\n        sds *command;\n        while (darray_n(config.cmd_whitelist) > 0) {\n            command = darray_pop(config.cmd_whitelist);\n            sdsfree(command);\n        }\n        darray_destroy(config.cmd_whitelist);\n        config.cmd_whitelist = NULL;\n    }\n}\n\nstatic int\nvrt_get_options(int argc, char **argv)\n{\n    int c;\n    long lvalue;\n    long long llvalue;\n    long long *range;\n    int range_count;\n\n    opterr = 0;\n\n    for (;;) {\n        c = getopt_long(argc, argv, short_options, long_options, NULL);\n        if (c == -1) {\n            /* no more options */\n            break;\n        }\n\n        switch (c) {\n        case 'h':\n            show_version = 1;\n            show_help = 1;\n            break;\n\n        case 'V':\n            show_version = 1;\n            break;\n            \n        case 'D':\n            daemonize = 1;\n            break;\n\n        case 'E':\n            expire_enabled = 1;\n            break;\n            \n        case 'C':\n            config.checker = optarg;\n            break;\n\n        case 'i':\n            if (string2ll(optarg,strlen(optarg),&llvalue) != 1) {\n                log_stderr(\"vireabtest: option -i requires a number\");\n                return VRT_ERROR;\n            }\n            config.test_interval = llvalue;\n            break;\n            \n        case 'k':\n            range = get_range_from_string(optarg,strlen(optarg),&range_count);\n            if (range == NULL) {\n                log_stderr(\"vireabtest: option -k is invalid, you need input a range like 0-100\");\n                return VRT_ERROR;\n            }\n            config.key_length_range_begin = (int)range[0];\n            if (range_count == 1) config.key_length_range_end = (int)range[0];\n            else if (range_count == 2) config.key_length_range_end = (int)range[1];\n            else assert(0);\n\n            free(range);\n            \n            break;\n\n        case 's':\n            if (string2l(optarg,strlen(optarg),&lvalue) != 1) {\n                log_stderr(\"vireabtest: option -s requires a number\");\n                return VRT_ERROR;\n            }\n            config.string_max_length = (int)lvalue;\n            break;\n\n        case 'f':\n            if (string2l(optarg,strlen(optarg),&lvalue) != 1) {\n                log_stderr(\"vireabtest: option -f requires a number\");\n                return VRT_ERROR;\n            }\n            config.fields_max_count = (int)lvalue;\n            break;\n            \n        case 'T':\n            config.cmd_type = parse_command_types(optarg);\n            if (config.cmd_type <= 0) {\n                log_stderr(\"vireabtest: option -T requires the correct command types\");\n                return VRT_ERROR;\n            }\n            break;\n\n        case 'B':\n            config.cmd_blacklist = parse_command_list(optarg);\n            if (config.cmd_blacklist == NULL) {\n                log_stderr(\"vireabtest: option -B requires the correct command list\");\n                return VRT_ERROR;\n            }\n            break;\n\n        case 'W':\n            config.cmd_whitelist = parse_command_list(optarg);\n            if (config.cmd_whitelist == NULL) {\n                log_stderr(\"vireabtest: option -W requires the correct command list\");\n                return VRT_ERROR;\n            }\n            break;\n            \n        case 't':\n            config.test_targets = optarg;\n            break;\n\n        case 'p':\n            if (string2l(optarg,strlen(optarg),&lvalue) != 1) {\n                log_stderr(\"vireabtest: option -p requires a number\");\n                return VRT_ERROR;\n            }\n            config.produce_data_threads = (int)lvalue;\n            break;\n\n        case 'K':\n            if (string2ll(optarg,strlen(optarg),&llvalue) != 1) {\n                log_stderr(\"vireabtest: option -K requires a number\");\n                return VRT_ERROR;\n            }\n            if (llvalue < 1000) {\n                log_stderr(\"vireabtest: option -K requires a number that must bigger than 1000\");\n                return VRT_ERROR;\n            }\n            \n            config.cached_keys_per_produce_thread = llvalue;\n            break;\n\n        case 'H':\n            if (string2l(optarg,strlen(optarg),&lvalue) != 1) {\n                log_stderr(\"vireabtest: option -H requires a number\");\n                return VRT_ERROR;\n            }\n            if (lvalue < 0 || lvalue > 100) {\n                log_stderr(\"vireabtest: option hit-ratio need between 0 and 100\");\n                return VRT_ERROR;\n            }\n            config.hit_ratio = (int)lvalue;\n            break;\n\n        case 'd':\n            if (string2l(optarg,strlen(optarg),&lvalue) != 1) {\n                log_stderr(\"vireabtest: option -d requires a number\");\n                return VRT_ERROR;\n            }\n            config.dispatch_data_threads = (int)lvalue;\n            break;\n\n        case 'c':\n            if (string2l(optarg,strlen(optarg),&lvalue) != 1) {\n                log_stderr(\"vireabtest: option -c requires a number\");\n                return VRT_ERROR;\n            }\n            config.clients_per_dispatch_thread = (int)lvalue;\n            break;\n            \n        case 'P':\n            config.pid_filename = optarg;\n            break;\n\n        case 'o':\n            config.log_filename = optarg;\n            break;\n            \n        case '?':\n            switch (optopt) {\n            case 'C':\n            case 'k':\n            case 'T':\n            case 'B':\n            case 'W':\n            case 't':\n            case 'P':\n            case 'o':\n                log_stderr(\"vire: option -%c requires string\",\n                           optopt);\n                break;\n\n            case 'i':\n            case 'p':\n            case 'd':\n            case 'c':\n            case 's':\n                log_stderr(\"vire: option -%c requires number\",\n                           optopt);\n                break;\n                \n            default:\n                log_stderr(\"vire: invalid option -- '%c'\", optopt);\n                break;\n            }\n            return VRT_ERROR;\n\n        default:\n            log_stderr(\"vire: invalid option -- '%c'\", optopt);\n            return VRT_ERROR;\n\n        }\n    }\n\n    return VRT_OK;\n}\n\nstatic int vrt_daemonize(int dump_core)\n{\n    int ret;\n    pid_t pid, sid;\n    int fd;\n\n    pid = fork();\n    switch (pid) {\n    case -1:\n        log_error(\"fork() failed: %s\", strerror(errno));\n        return VRT_ERROR;\n\n    case 0:\n        break;\n\n    default:\n        /* parent terminates */\n        _exit(0);\n    }\n\n    /* 1st child continues and becomes the session leader */\n\n    sid = setsid();\n    if (sid < 0) {\n        log_error(\"setsid() failed: %s\", strerror(errno));\n        return VRT_ERROR;\n    }\n\n    if (signal(SIGHUP, SIG_IGN) == SIG_ERR) {\n        log_error(\"signal(SIGHUP, SIG_IGN) failed: %s\", strerror(errno));\n        return VRT_ERROR;\n    }\n\n    pid = fork();\n    switch (pid) {\n    case -1:\n        log_error(\"fork() failed: %s\", strerror(errno));\n        return VRT_ERROR;\n\n    case 0:\n        break;\n\n    default:\n        /* 1st child terminates */\n        _exit(0);\n    }\n\n    /* 2nd child continues */\n\n    /* change working directory */\n    if (dump_core == 0) {\n        ret = chdir(\"/\");\n        if (ret < 0) {\n            log_error(\"chdir(\\\"/\\\") failed: %s\", strerror(errno));\n            return VRT_ERROR;\n        }\n    }\n\n    /* clear file mode creation mask */\n    umask(0);\n\n    /* redirect stdin, stdout and stderr to \"/dev/null\" */\n\n    fd = open(\"/dev/null\", O_RDWR);\n    if (fd < 0) {\n        log_error(\"open(\\\"/dev/null\\\") failed: %s\", strerror(errno));\n        return VRT_ERROR;\n    }\n\n    ret = dup2(fd, STDIN_FILENO);\n    if (ret < 0) {\n        log_error(\"dup2(%d, STDIN) failed: %s\", fd, strerror(errno));\n        close(fd);\n        return VRT_ERROR;\n    }\n\n    ret = dup2(fd, STDOUT_FILENO);\n    if (ret < 0) {\n        log_error(\"dup2(%d, STDOUT) failed: %s\", fd, strerror(errno));\n        close(fd);\n        return VRT_ERROR;\n    }\n\n    ret = dup2(fd, STDERR_FILENO);\n    if (ret < 0) {\n        log_error(\"dup2(%d, STDERR) failed: %s\", fd, strerror(errno));\n        close(fd);\n        return VRT_ERROR;\n    }\n\n    if (fd > STDERR_FILENO) {\n        ret = close(fd);\n        if (ret < 0) {\n            log_error(\"close(%d) failed: %s\", fd, strerror(errno));\n            return VRT_ERROR;\n        }\n    }\n\n    return VRT_OK;\n}\n\nstatic int abtest_server_init(abtest_server *abs, char *address)\n{\n    sds *host_port;\n    int count;\n    long value;\n\n    abs->host = NULL;\n    abs->port = 0;\n    abs->conn_contexts = NULL;\n    abs->data = NULL;\n\n    host_port = sdssplitlen(address,strlen(address),\":\",1,&count);\n    if (host_port == NULL) {\n        return VRT_ERROR;\n    } else if (count != 2) {\n        sdsfreesplitres(host_port,count);\n        return VRT_ERROR;\n    }\n\n    abs->host = host_port[0];\n    host_port[0] = NULL;\n\n    if (string2l(host_port[1],sdslen(host_port[1]),&value) != 1) {\n        sdsfreesplitres(host_port,count);\n        return VRT_ERROR;\n    }\n\n    abs->port = (int)value;\n    sdsfreesplitres(host_port,count);\n    \n    return VRT_OK;\n}\n\nstatic void abtest_server_deinit(abtest_server *abs)\n{\n    if (abs->host) {\n        sdsfree(abs->host);\n        abs->host = NULL;\n    }\n\n    if (abs->port > 0) abs->port = 0;\n\n    if (abs->conn_contexts) {\n        ASSERT(darray_n(abs->conn_contexts) == 0);\n        darray_destroy(abs->conn_contexts);\n        abs->conn_contexts = NULL;\n    }\n}\n\nunsigned int get_backend_server_idx(abtest_group *abg, char *key, size_t keylen)\n{\n    unsigned int hashvalue, servers_count;\n\n    servers_count = darray_n(&abg->abtest_servers);\n    if (servers_count == 1) {\n        return 0;\n    }\n\n    hashvalue = (unsigned int)hash_crc32a(key, keylen);\n    \n    return hashvalue%servers_count;\n}\n\nabtest_server *get_backend_server(abtest_group *abg, char *key, size_t keylen)\n{\n    abtest_server *abs;\n    unsigned int idx;\n\n    idx = abg->get_backend_server_idx(abg,key,keylen);\n    abs = darray_get(&abg->abtest_servers, idx);\n\n    return abs;\n}\n\nstatic int abtest_group_init(abtest_group *abg, char *group_string)\n{\n    sds *type_addrs, *addrs;\n    int type_addrs_count, addrs_count;\n    int j;\n\n    abg->type = 0;\n    darray_init(&abg->abtest_servers, 1, sizeof(abtest_server));\n\n    type_addrs = sdssplitlen(group_string,sdslen(group_string),\"[\",1,&type_addrs_count);\n    if (type_addrs == NULL) {\n        return VRT_ERROR;\n    } else if (type_addrs_count != 2) {\n        sdsfreesplitres(type_addrs,type_addrs_count);\n        return VRT_ERROR;\n    }\n\n    if (!strcasecmp(type_addrs[0],\"vire\")) {\n        abg->type = VRABTEST_GROUP_TYPE_VIRE;\n    } else if (!strcasecmp(type_addrs[0],\"redis\")) {\n        abg->type = VRABTEST_GROUP_TYPE_REDIS;\n    } else {\n        sdsfreesplitres(type_addrs,type_addrs_count);\n        return VRT_ERROR;\n    }\n\n    if (sdslen(type_addrs[1]) <= 1 || \n        type_addrs[1][sdslen(type_addrs[1])-1] != ']') {\n        sdsfreesplitres(type_addrs,type_addrs_count);\n        return VRT_ERROR;\n    }\n\n    sdsrange(type_addrs[1],0,-2);\n\n    addrs = sdssplitlen(type_addrs[1],sdslen(type_addrs[1]),\",\",1,&addrs_count);\n    if (addrs == NULL) {\n        sdsfreesplitres(type_addrs,type_addrs_count);\n        return VRT_ERROR;\n    } else if (addrs_count < 1) {\n        sdsfreesplitres(addrs,addrs_count);\n        sdsfreesplitres(type_addrs,type_addrs_count);\n        return VRT_ERROR;\n    }\n\n    for (j = 0; j < addrs_count; j ++) {\n        abtest_server *abs = darray_push(&abg->abtest_servers);\n        if (abtest_server_init(abs,addrs[j]) != VRT_OK) {\n            sdsfreesplitres(addrs,addrs_count);\n            sdsfreesplitres(type_addrs,type_addrs_count);\n            return VRT_ERROR;\n        }\n    }\n\n    sdsfreesplitres(addrs,addrs_count);\n    sdsfreesplitres(type_addrs,type_addrs_count);\n\n    abg->get_backend_server_idx = get_backend_server_idx;\n    abg->get_backend_server = get_backend_server;\n\n    return VRT_OK;\n}\n\nstatic void abtest_group_deinit(abtest_group *abg)\n{\n    abtest_server *abs;\n    \n    abg->type = 0;\n    \n    while (darray_n(&abg->abtest_servers) > 0) {\n        abs = darray_pop(&abg->abtest_servers);\n        abtest_server_deinit(abs);\n    }\n    darray_deinit(&abg->abtest_servers);\n}\n\n/* groups_string is like \"vire[127.0.0.1:12301,127.0.0.1:12302]-redis[127.0.0.1:12311,127.0.0.1:12312]\" */\ndarray *abtest_groups_create(char *groups_string)\n{\n    darray *abgs;\n    sds *group_strings;\n    int group_count, j;\n\n    group_strings = sdssplitlen(groups_string,strlen(groups_string),\"-\",1,&group_count);\n    if (group_strings == NULL) {\n        return NULL;\n    } else if (group_count < 1) {\n        sdsfreesplitres(group_strings,group_count);\n        return NULL;\n    }\n\n    abgs = darray_create(2, sizeof(abtest_group));\n    if (abgs == NULL) {\n        sdsfreesplitres(group_strings,group_count);\n        return NULL;\n    }\n    \n    for (j = 0; j < group_count; j ++) {\n        abtest_group *abg;\n        sds group_string = group_strings[j];\n        sds *type_addrs;\n        int elem_count;\n\n        abg = darray_push(abgs);\n        if (abtest_group_init(abg,group_string) != VRT_OK) {\n            sdsfreesplitres(group_strings,group_count);\n            abtest_groups_destroy(abgs);\n            return NULL;\n        }\n    }\n\n    return abgs;\n}\n\nvoid abtest_groups_destroy(darray *abgs)\n{\n    while (darray_n(abgs) > 0) {\n        abtest_group *abg = darray_pop(abgs);\n        abtest_group_deinit(abg);\n    }\n    \n    darray_destroy(abgs);\n}\n\nint\nmain(int argc, char **argv)\n{\n    int ret;\n\n    vrt_set_default_options();\n\n    ret = vrt_get_options(argc, argv);\n    if (ret != VRT_OK) {\n        vrt_show_usage();\n        exit(1);\n    }\n\n    if (show_version) {\n        log_stdout(\"This is vireabtest-%s\", VR_VERSION_STRING);\n        if (show_help) {\n            vrt_show_usage();\n        }\n        exit(0);\n    }\n\n    ret = log_init(LOG_INFO, config.log_filename);\n    if (ret < 0) {\n        exit(1);\n    }\n\n    if (daemonize) {\n        ret = vrt_daemonize(1);\n        if (ret != VRT_OK) {\n            exit(1);\n        }\n    }\n\n    test_interval = config.test_interval;\n    \n    ret = vrt_produce_data_init(config.key_length_range_begin,\n        config.key_length_range_end,\n        config.string_max_length,config.fields_max_count,\n        config.cmd_type,config.cmd_blacklist,config.cmd_whitelist,\n        config.produce_data_threads,\n        config.cached_keys_per_produce_thread, \n        config.hit_ratio);\n    if (ret != VRT_OK) {\n        log_error(\"Init data producer failed\");\n        exit(1);\n    }\n    ret = vrt_dispatch_data_init(config.dispatch_data_threads, \n        config.test_targets, config.clients_per_dispatch_thread);\n    if (ret != VRT_OK) {\n        log_error(\"Init data dispatcher failed\");\n        exit(1);\n    }\n    ret = vrt_backend_init(config.dispatch_data_threads, \n        config.test_targets);\n    if (ret != VRT_OK) {\n        log_error(\"Init backend thread failed\");\n        exit(1);\n    }\n    ret = vrt_data_checker_init(config.checker, config.test_targets);\n    if (ret != VRT_OK) {\n        log_error(\"Init check data thread failed\");\n        exit(1);\n    }\n\n    log_debug(LOG_INFO,\"State lock type: %s\", TEST_STATE_LOCK_TYPE);\n\n    vrt_start_produce_data();\n    vrt_start_dispatch_data();\n    vrt_start_backend();\n    vrt_start_data_checker();\n\n    vrt_wait_produce_data();\n    vrt_wait_dispatch_data();\n    vrt_wait_backend();\n    vrt_wait_data_checker();\n\n    vrt_data_checker_deinit();\n    vrt_backend_deinit();\n    vrt_dispatch_data_deinit();\n    vrt_produce_data_deinit();\n\n    log_deinit();\n    vrt_clean_options();\n    \n    return VRT_OK;\n}\n"
  },
  {
    "path": "tests/vrabtest.h",
    "content": "#ifndef _VRABTEST_H_\n#define _VRABTEST_H_\n\n#include <darray.h>\n\nstruct redisContext;\nstruct redisAsyncContext;\nstruct abtest_group;\n\ntypedef struct conn_context {\n    struct redisContext *ctx;\n    struct redisAsyncContext *actx;    \n} conn_context;\n\ntypedef struct abtest_server {\n    sds host;\n    int port;\n\n    darray *conn_contexts;  /* connection context */\n\n    void *data;\n} abtest_server;\n\ntypedef unsigned int (*backend_server_idx_t)(struct abtest_group*, char *, size_t);\ntypedef abtest_server *(*backend_server_t)(struct abtest_group*, char *, size_t);\n\ntypedef struct abtest_group {\n    int type;\n    \n    darray abtest_servers;    /* type: abtest_server */\n\n    backend_server_idx_t    get_backend_server_idx;\n    backend_server_t        get_backend_server;\n} abtest_group;\n\nextern int expire_enabled;\nextern long long test_interval;\nextern long long last_test_begin_time;\n\ndarray *abtest_groups_create(char *groups_string);\nvoid abtest_groups_destroy(darray *abgs);\n\n#endif\n"
  },
  {
    "path": "tests/vrt_backend.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <getopt.h>\n#include <fcntl.h>\n#include <errno.h>\n#include <signal.h>\n#include <sys/stat.h>\n#include <sys/utsname.h>\n\n#include <hiredis.h>\n#include <async.h>\n#include <adapters/ae.h>\n\n#include <dhashkit.h>\n#include <dlist.h>\n#include <dmtqueue.h>\n\n#include <vrt_util.h>\n#include <vrt_public.h>\n#include <vrabtest.h>\n#include <vrt_produce_data.h>\n#include <vrt_dispatch_data.h>\n#include <vrt_backend.h>\n\ntypedef struct task_data {\n    long long maxmemory;\n    long long used_memory;\n    long long total_system_memory;\n    \n    int deleting;   /* backend thread is deleting keys */\n    long long cursor;   /* scan cursor */\n} task_data;\n\nint backend_threads_count;\nstatic darray *backend_threads = NULL;\n\nint backend_threads_pause_finished_count;\n\nstatic int task_data_create(void)\n{\n    task_data *td;\n\n    td = malloc(sizeof(*td));\n\n    td->maxmemory = 0;\n    td->used_memory = 0;\n    td->total_system_memory = 0;\n    td->deleting = 0;\n    td->cursor = 0;\n\n    return td;\n}\n\nstatic void task_data_destroy(task_data *td)\n{\n    free(td);\n}\n\nstatic int backend_conn_context_init(conn_context *cc, char *host, int port)\n{\n    cc->ctx = NULL;\n    cc->actx = NULL;\n\n    cc->actx = redisAsyncConnect(host, port);\n    if (cc->actx == NULL) {\n        return VRT_ERROR;\n    }\n    \n    return VRT_OK;\n}\n\nstatic void backend_conn_context_deinit(conn_context *cc)\n{\n    if (cc->ctx) {\n        redisFree(cc->ctx);\n        cc->ctx == NULL;\n    }\n\n    if (cc->actx) {\n        redisAsyncFree(cc->actx);\n        cc->actx == NULL;\n    }\n}\n\nstatic void connect_callback(const redisAsyncContext *c, int status) {\n    backend_thread *bt = c->data;\n    if (status != REDIS_OK) {\n        test_log_out(\"Error: %s\\n\", c->errstr);\n        //aeStop(loop);\n        return;\n    }\n\n    //test_log_out(\"Connected...\\n\");\n}\n\nstatic void disconnect_callback(const redisAsyncContext *c, int status) {\n    backend_thread *bt = c->data;\n    if (status != REDIS_OK) {\n        test_log_out(\"Error: %s\\n\", c->errstr);\n        //aeStop(loop);\n        return;\n    }\n\n    //test_log_out(\"Disconnected...\\n\");\n    //aeStop(loop);\n}\n\nstatic void scan_for_delete_callback(redisAsyncContext *c, void *r, void *privdata) {\n    redisReply *reply = r, *reply_sub, *reply_elem;\n    abtest_server *abs = privdata;\n    task_data *td = abs->data;\n    conn_context *cc;\n    long long value;\n    size_t k;\n    \n    if (reply == NULL) return;\n\n    if (!td->deleting) {\n        return;\n    }\n\n    if (reply->type != REDIS_REPLY_ARRAY) {\n        return;\n    }\n\n    if (reply->elements != 2) {\n        return;\n    }\n\n    reply_sub = reply->element[0];\n    if (reply_sub->type != REDIS_REPLY_STRING || \n        string2ll(reply_sub->str,reply_sub->len,&value) != 1) {\n        return;\n    }\n\n    td->cursor = value;\n\n    reply_sub = reply->element[1];\n    if (reply_sub->type != REDIS_REPLY_ARRAY) {\n        return;\n    }\n\n    for (k = 0; k < reply_sub->elements; k ++) {\n        reply_elem = reply_sub->element[k];\n        if (reply_elem->type != REDIS_REPLY_STRING) {\n            return;\n        }\n\n        data_unit *du = data_unit_get();\n        du->dp = delete_data_producer;\n        du->argc = 2;\n        du->argv = malloc(du->argc*sizeof(sds));\n        du->argv[0] = sdsnew(delete_data_producer->name);\n        du->argv[1] = sdsnewlen(reply_elem->str,reply_elem->len);\n        data_dispatch(du);\n    }\n\n    cc = darray_get(abs->conn_contexts, 0);\n    redisAsyncCommand(cc->actx, scan_for_delete_callback, \n        abs, \"scan %lld count 1000\", td->cursor);\n}\n\nstatic void update_memory_callback(redisAsyncContext *c, void *r, void *privdata) {\n    redisReply *reply = r;\n    abtest_server *abs = privdata;\n    task_data *td = abs->data;\n    \n    if (reply == NULL) return;\n\n    td->used_memory = get_longlong_from_info_reply(reply, \"used_memory\");\n\n    if (td->maxmemory == 0) {\n        td->total_system_memory = get_longlong_from_info_reply(reply, \"total_system_memory\");\n    }\n}\n\nstatic void update_maxmemory_callback(redisAsyncContext *c, void *r, void *privdata) {\n    redisReply *reply = r;\n    abtest_server *abs = privdata;\n    task_data *td = abs->data;\n    redisReply *reply_sub;\n    long long value;\n    \n    if (reply == NULL) return;\n\n    if (reply->type != REDIS_REPLY_ARRAY) {\n        return;\n    }\n\n    if (reply->elements != 2) {\n        return;\n    }\n\n    reply_sub = reply->element[0];\n    if (reply_sub->type != REDIS_REPLY_STRING || \n        strcmp(reply_sub->str, \"maxmemory\")) {\n        return;\n    }\n\n    reply_sub = reply->element[1];\n    if (reply_sub->type != REDIS_REPLY_STRING || \n        string2ll(reply_sub->str,reply_sub->len,&value) != 1) {\n        return;\n    }\n\n    td->maxmemory = value;\n}\n\nstatic void update_memory_info(darray *abgs)\n{\n    long long i, j;\n    \n    for (i = 0; i < darray_n(abgs); i ++) {\n        abtest_group *abg = darray_get(abgs, i);\n        for (j = 0; j < darray_n(&abg->abtest_servers); j ++) {\n            abtest_server *abs = darray_get(&abg->abtest_servers, j);\n            conn_context *cc = darray_get(abs->conn_contexts, 0);\n            \n            redisAsyncCommand(cc->actx, update_memory_callback, abs, \"info memory\");\n            redisAsyncCommand(cc->actx, update_maxmemory_callback, abs, \"config get maxmemory\");\n        }\n    }\n}\n\nstatic void check_memory_enough(backend_thread *bt)\n{\n    long long i, j;\n    darray *abgs = bt->abgs;\n    \n    for (i = 0; i < darray_n(abgs); i ++) {\n        abtest_group *abg = darray_get(abgs, i);\n        for (j = 0; j < darray_n(&abg->abtest_servers); j ++) {\n            abtest_server *abs = darray_get(&abg->abtest_servers, j);\n            task_data *td = abs->data;\n            long long max_memory_allowed = 0;\n            \n            if (td->used_memory) {\n                if (td->maxmemory) {\n                    max_memory_allowed = td->maxmemory;\n                } else if (td->total_system_memory) {\n                    max_memory_allowed = td->total_system_memory;\n                }\n\n                if (max_memory_allowed) { \n                    if (td->used_memory*100/max_memory_allowed > 80) {\n                        if (!td->deleting) {\n                            conn_context *cc = darray_get(abs->conn_contexts, 0);\n                            redisAsyncCommand(cc->actx, scan_for_delete_callback, \n                                abs, \"scan %lld count 1000\", td->cursor);\n                            td->deleting = 1;\n                            bt->deleting ++;\n                        }\n                    } else if (td->deleting) {\n                        td->deleting = 0;\n                        bt->deleting --;\n                    }\n                }\n            }\n        }\n    }\n}\n\nstatic int backend_thread_cron(aeEventLoop *eventLoop, long long id, void *clientData)\n{\n    backend_thread *bt = clientData;\n    \n    ASSERT(eventLoop == bt->el);\n\n    /* At the begin of this loop */\n    if (bt->pause) {\n        if (!test_if_need_pause()) {\n            bt->pause = 0;\n        } else {\n            bt->cronloops ++;\n            return 1000;\n        }\n    }\n\n    update_memory_info(bt->abgs);\n    check_memory_enough(bt);\n\n    /* At the end of this loop */\n    if (!bt->pause && test_if_need_pause() && !bt->deleting) {\n        bt->pause = 1;\n        one_backend_thread_paused();\n    }\n    \n    bt->cronloops ++;\n    return 1000/bt->hz;\n}\n\nstatic int backend_thread_init(backend_thread *bt, char *test_target_groups)\n{\n    int i, j, k;\n\n    bt->id = 0;\n    bt->thread_id = 0;\n    bt->el = NULL;\n    bt->hz = 10;\n    bt->cronloops = 0;\n    bt->deleting = 0;\n    bt->pause = 0;\n    \n    bt->el = aeCreateEventLoop(1);\n    if (bt->el == NULL) {\n        return VRT_ERROR;\n    }\n\n    bt->abgs = abtest_groups_create(test_target_groups);\n    if (bt->abgs == NULL) {\n        return VRT_ERROR;\n    }\n\n    /* Init connection context for each server */\n    for (i = 0; i < darray_n(bt->abgs); i ++) {\n        abtest_group *abg = darray_get(bt->abgs, i);\n        for (j = 0; j < darray_n(&abg->abtest_servers); j ++) {\n            abtest_server *abs = darray_get(&abg->abtest_servers, j);\n            \n            abs->conn_contexts = darray_create(1, sizeof(conn_context));\n            for (k = 0; k < 1; k ++) {\n                conn_context *cc = darray_push(abs->conn_contexts);\n                if (backend_conn_context_init(cc,abs->host,abs->port) != VRT_OK) {\n                    return VRT_ERROR;\n                }\n                cc->actx->data = bt;\n                redisAeAttach(bt->el, cc->actx);\n                redisAsyncSetConnectCallback(cc->actx,connect_callback);\n                redisAsyncSetDisconnectCallback(cc->actx,disconnect_callback);\n            }\n\n            abs->data = task_data_create();\n        }\n    }\n\n    if (aeCreateTimeEvent(bt->el, 1, backend_thread_cron, bt, NULL) == AE_ERR) {\n        return VRT_ERROR;\n    }\n    \n    return VRT_OK;\n}\n\nstatic void backend_thread_deinit(backend_thread *bt)\n{\n    if (bt->el) {\n        aeDeleteEventLoop(bt->el);\n        bt->el = NULL;\n    }\n\n    if (bt->abgs) {\n        int i, j, k;\n        /* Deinit connection context for each server */\n        for (i = 0; i < darray_n(bt->abgs); i ++) {\n            abtest_group *abg = darray_get(bt->abgs, i);\n            for (j = 0; j < darray_n(&abg->abtest_servers); j ++) {\n                abtest_server *abs = darray_get(&abg->abtest_servers, j);\n                while (darray_n(abs->conn_contexts) > 0) {\n                    conn_context *cc = darray_pop(abs->conn_contexts);\n                    backend_conn_context_deinit(cc);\n                }\n\n                if (abs->data) {\n                    task_data_destroy(abs->data);\n                    abs->data;\n                }\n            }\n        }\n        \n        abtest_groups_destroy(bt->abgs);\n        bt->abgs = NULL;\n    }\n}\n\nint vrt_backend_init(int threads_count, char *test_target_groups)\n{\n    int j;\n    \n    backend_threads_count = threads_count;\n    backend_threads = darray_create(threads_count, sizeof(backend_thread));\n    if (backend_threads == NULL) {\n        return VRT_ERROR;\n    }\n\n    for (j = 0; j < threads_count; j ++) {\n        backend_thread *bt = darray_push(backend_threads);\n        if (backend_thread_init(bt, test_target_groups) != VRT_OK) {\n            return VRT_ERROR;\n        }\n        bt->id = j;\n    }\n    \n    return VRT_OK;\n}\n\nvoid vrt_backend_deinit(void)\n{\n    if (backend_threads) {\n        while (darray_n(backend_threads) > 0) {\n            backend_thread *bt = darray_pop(backend_threads);\n            backend_thread_deinit(bt);\n        }\n        darray_destroy(backend_threads);\n        backend_threads = NULL;\n    }\n}\n\nstatic void *vrt_backend_thread_run(void *args)\n{\n    backend_thread *bt = args;\n    srand(vrt_usec_now()^(int)pthread_self());\n\n    aeMain(bt->el);\n    \n    return NULL;\n}\n\nint vrt_start_backend(void)\n{\n    unsigned int i;\n    for (i = 0; i < darray_n(backend_threads); i ++) {\n        pthread_attr_t attr;\n        backend_thread *bt;\n        pthread_attr_init(&attr);\n        bt = darray_get(backend_threads, i);\n        pthread_create(&bt->thread_id, \n            &attr, vrt_backend_thread_run, bt);\n    }\n    \n    return VRT_OK;\n}\n\nint vrt_wait_backend(void)\n{\n    unsigned int i;\n    /* wait for the produce threads finish */\n\tfor(i = 0; i < darray_n(backend_threads); i ++){\n\t\tbackend_thread *bt = darray_get(backend_threads, i);\n\t\tpthread_join(bt->thread_id, NULL);\n\t}\n    \n    return VRT_OK;\n}\n"
  },
  {
    "path": "tests/vrt_backend.h",
    "content": "#ifndef _VRT_BACKEND_H_\n#define _VRT_BACKEND_H_\n\n#include <darray.h>\n\nstruct abtest_group;\nstruct dlist;\nstruct dmtlist;\nstruct data_unit;\nstruct aeEventLoop;\n\ntypedef struct backend_thread {\n    int id;\n    pthread_t thread_id;\n    \n    struct aeEventLoop *el;\n    int hz;\n    int cronloops;          /* Number of times the cron function run */\n\n    darray *abgs; /* type is abtest_group */\n\n    int deleting;\n    int pause;\n} backend_thread;\n\nextern int backend_threads_count;\n\nextern int backend_threads_pause_finished_count;\n\nint vrt_backend_init(int threads_count, char *test_target_groups);\nvoid vrt_backend_deinit(void);\n\nint vrt_start_backend(void);\nint vrt_wait_backend(void);\n\n#endif\n"
  },
  {
    "path": "tests/vrt_benchmark.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <getopt.h>\n#include <fcntl.h>\n#include <errno.h>\n#include <signal.h>\n#include <assert.h>\n#include <sys/stat.h>\n#include <sys/utsname.h>\n\n#include <ae.h>\n\n#include <hiredis.h>\n#include <sds.h>\n\n#include <darray.h>\n#include <dlist.h>\n#include <dutil.h>\n#include <dlog.h>\n\n#include <vrt_util.h>\n#include <vrt_public.h>\n#include <himemcached.h>\n\n#define TEST_CMD_PROTOCOL_REDIS     0\n#define TEST_CMD_PROTOCOL_MEMCACHE  1\n\n#define RANDPTR_INITIAL_SIZE 8\n\nstatic struct config {\n    const char *hostip;\n    int hostport;\n    const char *hostsocket;\n    int numclients;\n    int liveclients;\n    int requests;\n    int requests_issued;\n    int requests_finished;\n    int keysize;\n    int datasize;\n    int randomkeys;\n    int randomkeys_keyspacelen;\n    int randomfields;\n    int randomfields_fieldspacelen;\n    int keepalive;\n    int pipeline;\n    int showerrors;\n    long long start;\n    long long totlatency;\n    long long *latency;\n    const char *title;\n    int quiet;\n    int csv;\n    int loop;\n    int idlemode;\n    int dbnum;\n    sds dbnumstr;\n    char *tests;\n    char *types;\n    char *auth;\n    int threads_count;\n    int protocol;\n    int noinline;\n} config;\n\ntypedef struct benchmark_thread {\n    int id;\n    pthread_t thread_id;\n    \n    struct aeEventLoop *el;\n    int hz;\n    int cronloops;          /* Number of times the cron function run */\n\n    dlist *clients;\n    int numclients;\n    int liveclients;\n\n    int requests;\n    int requests_issued;\n    int requests_finished;\n\n    long long start;\n    long long totlatency;\n    long long *latency;\n} benchmark_thread;\n\ntypedef struct _benchmark_client {\n    benchmark_thread *bt;\n    \n    redisContext *rc;\n    mcContext *mc;\n    sds obuf;\n    char **randkeyptr;      /* Pointers to :randkey: strings inside the command buf */\n    size_t randkeylen;      /* Number of pointers in client->randkeyptr */\n    size_t randkeyfree;     /* Number of unused pointers in client->randkeyptr */\n    char **randfieldptr;    /* Pointers to :randfield: strings inside the command buf */\n    size_t randfieldlen;    /* Number of pointers in client->randfieldptr */\n    size_t randfieldfree;   /* Number of unused pointers in client->randfieldptr */\n    size_t written;         /* Bytes of 'obuf' already written */\n    long long start;        /* Start time of a request */\n    long long latency;      /* Request latency */\n    int pending;            /* Number of pending requests (replies to consume) */\n    int prefix_pending;     /* If non-zero, number of pending prefix commands. Commands\n                               such as auth and select are prefixed to the pipeline of\n                               benchmark commands and discarded after the first send. */\n    int prefixlen;          /* Size in bytes of the pending prefix commands */\n} *benchmark_client;\n\nstatic darray *bts; /* Benchmark threads */\n\n/* Prototypes */\nstatic void writeHandler(aeEventLoop *el, int fd, void *privdata, int mask);\nstatic benchmark_client createClient(char *cmd, size_t len, benchmark_client from, benchmark_thread *thread);\nstatic void createMissingClients(benchmark_client c);\nstatic int showThroughput(struct aeEventLoop *eventLoop, long long id, void *clientData);\n\nstatic void freeClient(benchmark_client c) {\n    benchmark_thread *bt = c->bt;\n    dlistNode *ln;\n\n    if (bt->el) {\n        aeDeleteFileEvent(bt->el,c->rc->fd,AE_WRITABLE);\n        aeDeleteFileEvent(bt->el,c->rc->fd,AE_READABLE);\n    }\n    redisFree(c->rc);\n    if (c->mc) {\n        c->mc->fd = -1;\n        memcachedFree(c->mc);\n    }\n    sdsfree(c->obuf);\n    free(c->randkeyptr);\n    free(c->randfieldptr);\n    free(c);\n    update_state_sub(bt->liveclients,1);\n    ln = dlistSearchKey(bt->clients,c);\n    ASSERT(ln != NULL);\n    dlistDelNode(bt->clients,ln);\n}\n\nstatic void freeAllClients(dlist *clients) {\n    dlistNode *ln = clients->head, *next;\n\n    while(ln) {\n        next = ln->next;\n        freeClient(ln->value);\n        ln = next;\n    }\n}\n\nstatic void resetClient(benchmark_client c) {\n    benchmark_thread *bt = c->bt;\n    \n    aeDeleteFileEvent(bt->el,c->rc->fd,AE_WRITABLE);\n    aeDeleteFileEvent(bt->el,c->rc->fd,AE_READABLE);\n    aeCreateFileEvent(bt->el,c->rc->fd,AE_WRITABLE,writeHandler,c);\n    c->written = 0;\n    c->pending = config.pipeline;\n}\n\nstatic void randomizeClientKey(benchmark_client c) {\n    size_t i;\n\n    for (i = 0; i < c->randkeylen; i++) {\n        char *p = c->randkeyptr[i]+11;\n        size_t r = random() % config.randomkeys_keyspacelen;\n        size_t j;\n\n        for (j = 0; j < 12; j++) {\n            *p = '0'+r%10;\n            r/=10;\n            p--;\n        }\n    }\n}\n\nstatic void randomizeClientField(benchmark_client c) {\n    size_t i;\n\n    for (i = 0; i < c->randfieldlen; i++) {\n        char *p = c->randfieldptr[i]+13;\n        size_t r = random() % config.randomfields_fieldspacelen;\n        size_t j;\n\n        for (j = 0; j < 14; j++) {\n            *p = '0'+r%10;\n            r/=10;\n            p--;\n        }\n    }\n}\n\n\nstatic void clientDone(benchmark_client c) {\n    benchmark_thread *bt = c->bt;\n    int requests_finished;\n\n    update_state_get(bt->requests_finished,&requests_finished);\n    if (requests_finished == bt->requests) {\n        freeClient(c);\n        aeStop(bt->el);\n        return;\n    }\n    if (config.keepalive) {\n        resetClient(c);\n    } else {\n        update_state_sub(bt->liveclients,1);\n        createMissingClients(c);\n        update_state_add(bt->liveclients,1);\n        freeClient(c);\n    }\n}\n\nstatic int benchmark_thread_init(benchmark_thread *bt, int requests, int numclients, char *cmd, size_t len)\n{    \n    benchmark_client c;\n    \n    bt->thread_id = 0;\n    bt->el = NULL;\n    bt->hz = 10;\n    bt->cronloops = 0;\n    bt->clients = NULL;\n    bt->numclients = numclients;\n    bt->liveclients = 0;\n    bt->requests = requests;\n    bt->requests_issued = 0;\n    bt->requests_finished = 0;\n    bt->start = 0;\n    bt->totlatency = 0;\n    bt->latency = NULL;\n\n    bt->el = aeCreateEventLoop(1024*10);\n    if (bt->el == NULL) {\n        return VRT_ERROR;\n    }\n\n    bt->clients = dlistCreate();\n    if (bt->clients == NULL) {\n        return VRT_ERROR;\n    }\n\n    bt->latency = malloc(sizeof(long long)*bt->requests);\n\n    c = createClient(cmd,len,NULL,bt);\n    createMissingClients(c);\n\n    if (bt->id == 0) {\n        aeCreateTimeEvent(bt->el,1,showThroughput,NULL,NULL);\n    }\n    \n    return VRT_OK;\n}\n\nstatic void benchmark_thread_deinit(benchmark_thread *bt)\n{\n    if (bt->clients) {\n        freeAllClients(bt->clients);\n        dlistRelease(bt->clients);\n        bt->clients = NULL;\n    }\n\n    if (bt->el) {\n        aeDeleteEventLoop(bt->el);\n        bt->el = NULL;\n    }\n    \n    if (bt->latency) {\n        free(bt->latency);\n        bt->latency = NULL;\n    }\n}\n\nstatic void *benchmark_thread_run(void *args)\n{\n    benchmark_thread *bt = args;\n    srand(vrt_usec_now()^(int)pthread_self());\n\n    aeMain(bt->el);\n    \n    return NULL;\n}\n\nstatic int start_benchmark_threads_until_finish(void)\n{\n    int i;\n    benchmark_thread *bt;\n    \n    for (i = 0; i < config.threads_count; i ++) {\n        pthread_attr_t attr;\n        pthread_attr_init(&attr);\n        bt = darray_get(bts, i);\n        pthread_create(&bt->thread_id, \n            &attr, benchmark_thread_run, bt);\n    }\n\n    for (i = 0; i < config.threads_count; i ++) {\n        bt = darray_get(bts, i);\n        pthread_join(bt->thread_id, NULL);\n    }\n    \n    return VRT_OK;\n}\n\nstatic void readHandlerMC(aeEventLoop *el, int fd, void *privdata, int mask) {\n    benchmark_client c = privdata;\n    benchmark_thread *bt = c->bt;\n    mcContext *mc = c->mc;\n    int requests_finished;\n    void *reply = NULL;\n    UNUSED(el);\n    UNUSED(fd);\n    UNUSED(mask);\n\n    /* Calculate latency only for the first read event. This means that the\n     * server already sent the reply and we need to parse it. Parsing overhead\n     * is not part of the latency, so calculate it only once, here. */\n    if (c->latency < 0) c->latency = dusec_now()-(c->start);\n\n    if (memcachedBufferRead(mc) != MC_OK) {\n        fprintf(stderr,\"Error: %s\\n\",mc->errstr);\n        exit(1);\n    } else {\n        while(c->pending) {\n            if (memcachedGetReply(mc,&reply) != MC_OK) {\n                fprintf(stderr,\"Error: %s\\n\",mc->errstr);\n                exit(1);\n            }\n            \n            if (reply != NULL) {\n                if (reply == (void*)MC_REPLY_ERROR) {\n                    fprintf(stderr,\"Unexpected error reply, exiting...\\n\");\n                    exit(1);\n                }\n\n                if (config.showerrors) {\n                    static time_t lasterr_time = 0;\n                    time_t now = time(NULL);\n                    mcReply *r = reply;\n                    if (r->type == MC_REPLY_ERROR && lasterr_time != now) {\n                        lasterr_time = now;\n                        printf(\"Error from server: %s\\n\", r->str);\n                    }\n                }\n\n                freeMcReplyObject(reply);\n                /* This is an OK for prefix commands such as auth and select.*/\n                if (c->prefix_pending > 0) {\n                    c->prefix_pending--;\n                    c->pending--;\n                    /* Discard prefix commands on first response.*/\n                    if (c->prefixlen > 0) {\n                        size_t j;\n                        sdsrange(c->obuf, c->prefixlen, -1);\n                        /* We also need to fix the pointers to the strings\n                        * we need to randomize. */\n                        for (j = 0; j < c->randkeylen; j++)\n                            c->randkeyptr[j] -= c->prefixlen;\n                        for (j = 0; j < c->randfieldlen; j++)\n                            c->randfieldptr[j] -= c->prefixlen;\n                        c->prefixlen = 0;\n                    }\n                    continue;\n                }\n\n                update_state_get(bt->requests_finished,&requests_finished);\n                if (requests_finished < bt->requests) {\n                    bt->latency[requests_finished] = c->latency;\n                    update_state_add(bt->requests_finished,1);\n                }\n                c->pending--;\n                if (c->pending == 0) {\n                    clientDone(c);\n                    break;\n                }\n            } else {\n                break;\n            }\n        }\n    }\n}\n\nstatic void readHandler(aeEventLoop *el, int fd, void *privdata, int mask) {\n    benchmark_client c = privdata;\n    benchmark_thread *bt = c->bt;\n    int requests_finished;\n    void *reply = NULL;\n    UNUSED(el);\n    UNUSED(fd);\n    UNUSED(mask);\n\n    /* Calculate latency only for the first read event. This means that the\n     * server already sent the reply and we need to parse it. Parsing overhead\n     * is not part of the latency, so calculate it only once, here. */\n    if (c->latency < 0) c->latency = dusec_now()-(c->start);\n\n    if (redisBufferRead(c->rc) != REDIS_OK) {\n        fprintf(stderr,\"Error: %s\\n\",c->rc->errstr);\n        exit(1);\n    } else {\n        while(c->pending) {\n            if (redisGetReply(c->rc,&reply) != REDIS_OK) {\n                fprintf(stderr,\"Error: %s\\n\",c->rc->errstr);\n                exit(1);\n            }\n            if (reply != NULL) {\n                if (reply == (void*)REDIS_REPLY_ERROR) {\n                    fprintf(stderr,\"Unexpected error reply, exiting...\\n\");\n                    exit(1);\n                }\n\n                if (config.showerrors) {\n                    static time_t lasterr_time = 0;\n                    time_t now = time(NULL);\n                    redisReply *r = reply;\n                    if (r->type == REDIS_REPLY_ERROR && lasterr_time != now) {\n                        lasterr_time = now;\n                        printf(\"Error from server: %s\\n\", r->str);\n                    }\n                }\n\n                freeReplyObject(reply);\n                /* This is an OK for prefix commands such as auth and select.*/\n                if (c->prefix_pending > 0) {\n                    c->prefix_pending--;\n                    c->pending--;\n                    /* Discard prefix commands on first response.*/\n                    if (c->prefixlen > 0) {\n                        size_t j;\n                        sdsrange(c->obuf, c->prefixlen, -1);\n                        /* We also need to fix the pointers to the strings\n                        * we need to randomize. */\n                        for (j = 0; j < c->randkeylen; j++)\n                            c->randkeyptr[j] -= c->prefixlen;\n                        for (j = 0; j < c->randfieldlen; j++)\n                            c->randfieldptr[j] -= c->prefixlen;\n                        c->prefixlen = 0;\n                    }\n                    continue;\n                }\n\n                update_state_get(bt->requests_finished,&requests_finished);\n                if (requests_finished < bt->requests) {\n                    bt->latency[requests_finished] = c->latency;\n                    update_state_add(bt->requests_finished,1);\n                }\n                c->pending--;\n                if (c->pending == 0) {\n                    clientDone(c);\n                    break;\n                }\n            } else {\n                break;\n            }\n        }\n    }\n}\n\nstatic void writeHandler(aeEventLoop *el, int fd, void *privdata, int mask) {\n    benchmark_client c = privdata;\n    benchmark_thread *bt = c->bt;\n    UNUSED(el);\n    UNUSED(fd);\n    UNUSED(mask);\n\n    /* Initialize request when nothing was written. */\n    if (c->written == 0) {\n        /* Enforce upper bound to number of requests. */\n        if (bt->requests_issued++ >= bt->requests) {\n            freeClient(c);\n            return;\n        }\n\n        /* Really initialize: randomize keys and set start time. */\n        if (config.randomkeys) randomizeClientKey(c);\n        if (config.randomfields) randomizeClientField(c);\n        c->start = dusec_now();\n        c->latency = -1;\n    }\n\n    if (sdslen(c->obuf) > c->written) {\n        void *ptr = c->obuf+c->written;\n        ssize_t nwritten = write(c->rc->fd,ptr,sdslen(c->obuf)-c->written);\n        \n        if (nwritten == -1) {\n            if (errno != EPIPE)\n                fprintf(stderr, \"Writing to socket: %s\\n\", strerror(errno));\n            freeClient(c);\n            return;\n        }\n        c->written += nwritten;\n        if (sdslen(c->obuf) == c->written) {\n            aeDeleteFileEvent(bt->el,c->rc->fd,AE_WRITABLE);\n            if (config.protocol == TEST_CMD_PROTOCOL_REDIS) {\n                aeCreateFileEvent(bt->el,c->rc->fd,AE_READABLE,readHandler,c);\n            } else if (config.protocol == TEST_CMD_PROTOCOL_MEMCACHE) {\n                aeCreateFileEvent(bt->el,c->rc->fd,AE_READABLE,readHandlerMC,c);\n            } else {\n                NOT_REACHED();\n            }\n        }\n    }\n}\n\n/* Create a benchmark client, configured to send the command passed as 'cmd' of\n * 'len' bytes.\n *\n * The command is copied N times in the client output buffer (that is reused\n * again and again to send the request to the server) accordingly to the configured\n * pipeline size.\n *\n * Also an initial SELECT command is prepended in order to make sure the right\n * database is selected, if needed. The initial SELECT will be discarded as soon\n * as the first reply is received.\n *\n * To create a client from scratch, the 'from' pointer is set to NULL. If instead\n * we want to create a client using another client as reference, the 'from' pointer\n * points to the client to use as reference. In such a case the following\n * information is take from the 'from' client:\n *\n * 1) The command line to use.\n * 2) The offsets of the __rand_key__ elements inside the command line, used\n *    for arguments randomization.\n *\n * Even when cloning another client, prefix commands are applied if needed.*/\nstatic benchmark_client createClient(char *cmd, size_t len, benchmark_client from, benchmark_thread *thread) {\n    int j;\n    benchmark_thread *bt;\n    benchmark_client c = malloc(sizeof(struct _benchmark_client));\n\n    c->bt = NULL;\n    c->rc = NULL;\n    c->mc = NULL;\n    c->obuf = NULL;\n    c->randkeyptr = NULL;\n    c->randkeylen = 0;\n    c->randkeyfree = 0;\n    c->randfieldptr = NULL;\n    c->randfieldlen = 0;\n    c->randfieldfree = 0;\n    c->written = 0;\n    c->start = 0;\n    c->latency = 0;\n    c->pending = 0;\n    c->prefix_pending = 0;\n    c->prefixlen = 0;\n    \n    if (from == NULL) {\n        ASSERT(thread != NULL);\n        bt = thread;\n    } else {\n        bt = from->bt;\n    }\n\n    c->bt = bt;\n\n    if (config.hostsocket == NULL) {\n        c->rc = redisConnectNonBlock(config.hostip,config.hostport);\n    } else {\n        c->rc = redisConnectUnixNonBlock(config.hostsocket);\n    }\n    if (c->rc->err) {\n        fprintf(stderr,\"Could not connect to Redis at \");\n        if (config.hostsocket == NULL)\n            fprintf(stderr,\"%s:%d: %s\\n\",config.hostip,config.hostport,c->rc->errstr);\n        else\n            fprintf(stderr,\"%s: %s\\n\",config.hostsocket,c->rc->errstr);\n        exit(1);\n    }\n    /* Suppress hiredis cleanup of unused buffers for max speed. */\n    c->rc->reader->maxbuf = 0;\n\n    /* Build the request buffer:\n     * Queue N requests accordingly to the pipeline size, or simply clone\n     * the example client buffer. */\n    c->obuf = sdsempty();\n    /* Prefix the request buffer with AUTH and/or SELECT commands, if applicable.\n     * These commands are discarded after the first response, so if the client is\n     * reused the commands will not be used again. */\n    c->prefix_pending = 0;\n    if (config.auth) {\n        char *buf = NULL;\n        int len = redisFormatCommand(&buf, \"AUTH %s\", config.auth);\n        c->obuf = sdscatlen(c->obuf, buf, len);\n        free(buf);\n        c->prefix_pending++;\n    }\n\n    /* If a DB number different than zero is selected, prefix our request\n     * buffer with the SELECT command, that will be discarded the first\n     * time the replies are received, so if the client is reused the\n     * SELECT command will not be used again. */\n    if (config.dbnum != 0) {\n        c->obuf = sdscatprintf(c->obuf,\"*2\\r\\n$6\\r\\nSELECT\\r\\n$%d\\r\\n%s\\r\\n\",\n            (int)sdslen(config.dbnumstr),config.dbnumstr);\n        c->prefix_pending++;\n    }\n    c->prefixlen = sdslen(c->obuf);\n    /* Append the request itself. */\n    if (from) {\n        c->obuf = sdscatlen(c->obuf,\n            from->obuf+from->prefixlen,\n            sdslen(from->obuf)-from->prefixlen);\n    } else {\n        for (j = 0; j < config.pipeline; j++)\n            c->obuf = sdscatlen(c->obuf,cmd,len);\n    }\n\n    c->written = 0;\n    c->pending = config.pipeline+c->prefix_pending;\n    c->randkeyptr = NULL;\n    c->randkeylen = 0;\n    c->randfieldptr = NULL;\n    c->randfieldlen = 0;\n\n    /* Find substrings in the output buffer that need to be randomized. */\n    if (config.randomkeys) {\n        if (from) {\n            c->randkeylen = from->randkeylen;\n            c->randkeyfree = 0;\n            c->randkeyptr = malloc(sizeof(char*)*c->randkeylen);\n            /* copy the offsets. */\n            for (j = 0; j < (int)c->randkeylen; j++) {\n                c->randkeyptr[j] = c->obuf + (from->randkeyptr[j]-from->obuf);\n                /* Adjust for the different select prefix length. */\n                c->randkeyptr[j] += c->prefixlen - from->prefixlen;\n            }\n        } else {\n            char *p = c->obuf;\n\n            c->randkeylen = 0;\n            c->randkeyfree = RANDPTR_INITIAL_SIZE;\n            c->randkeyptr = malloc(sizeof(char*)*c->randkeyfree);\n            while ((p = strstr(p,\"__rand_key__\")) != NULL) {\n                if (c->randkeyfree == 0) {\n                    c->randkeyptr = realloc(c->randkeyptr,sizeof(char*)*c->randkeylen*2);\n                    c->randkeyfree += c->randkeylen;\n                }\n                c->randkeyptr[c->randkeylen++] = p;\n                c->randkeyfree--;\n                p += 12; /* 12 is strlen(\"__rand_key__\"). */\n            }\n        }\n    }\n    if (config.randomfields) {\n        if (from) {\n            c->randfieldlen = from->randfieldlen;\n            c->randfieldfree = 0;\n            c->randfieldptr = malloc(sizeof(char*)*c->randfieldlen);\n            /* copy the offsets. */\n            for (j = 0; j < (int)c->randfieldlen; j++) {\n                c->randfieldptr[j] = c->obuf + (from->randfieldptr[j]-from->obuf);\n                /* Adjust for the different select prefix length. */\n                c->randfieldptr[j] += c->prefixlen - from->prefixlen;\n            }\n        } else {\n            char *p = c->obuf;\n\n            c->randfieldlen = 0;\n            c->randfieldfree = RANDPTR_INITIAL_SIZE;\n            c->randfieldptr = malloc(sizeof(char*)*c->randfieldfree);\n            while ((p = strstr(p,\"__rand_field__\")) != NULL) {\n                if (c->randfieldfree == 0) {\n                    c->randfieldptr = realloc(c->randfieldptr,sizeof(char*)*c->randfieldlen*2);\n                    c->randfieldfree += c->randfieldlen;\n                }\n                c->randfieldptr[c->randfieldlen++] = p;\n                c->randfieldfree--;\n                p += 14; /* 14 is strlen(\"__rand_field__\"). */\n            }\n        }\n    }\n    if (config.idlemode == 0)\n        aeCreateFileEvent(bt->el,c->rc->fd,AE_WRITABLE,writeHandler,c);\n\n    /* Attach the redis fd to memcached fd */\n    if (config.protocol == TEST_CMD_PROTOCOL_MEMCACHE) {\n        c->mc = memcachedContextInit();\n        c->mc->fd = c->rc->fd;\n        c->mc->flags &= ~MC_BLOCK;\n    }\n    \n    dlistAddNodeTail(bt->clients,c);\n    update_state_add(bt->liveclients,1);\n\n    return c;\n}\n\nstatic void createMissingClients(benchmark_client c) {\n    int n = 0;\n    benchmark_thread *bt = c->bt;\n    int liveclients;\n\n    update_state_get(bt->liveclients,&liveclients);\n    \n    while(liveclients < bt->numclients) {\n        createClient(NULL,0,c,NULL);\n\n        /* Listen backlog is quite limited on most systems */\n        if (++n > 64) {\n            usleep(50000);\n            n = 0;\n        }\n        update_state_get(bt->liveclients,&liveclients);\n    }\n}\n\nstatic int compareLatency(const void *a, const void *b) {\n    return (*(long long*)a)-(*(long long*)b);\n}\n\nstatic void updateBenchmarkStats(void)\n{\n    int i;\n    int count;\n\n    config.liveclients = 0;\n    config.requests_finished = 0;\n\n    for (i = 0; i < config.threads_count; i ++) {\n        benchmark_thread *bt = darray_get(bts, i);\n        update_state_get(bt->liveclients,&count);\n        config.liveclients += count;\n        update_state_get(bt->requests_finished,&count);\n        config.requests_finished += count;\n    }\n}\n\nstatic void showLatencyReport(void) {\n    int i, j, curlat = 0;\n    int n = 0;\n    float perc, reqpersec;\n\n    updateBenchmarkStats();\n\n    reqpersec = (float)config.requests_finished/((float)config.totlatency/1000);\n    if (!config.quiet && !config.csv) {\n        printf(\"====== %s ======\\n\", config.title);\n        printf(\"  %d requests completed in %.2f seconds\\n\", config.requests_finished,\n            (float)config.totlatency/1000);\n        printf(\"  %d parallel clients\\n\", config.numclients);\n        printf(\"  %d bytes payload\\n\", config.datasize);\n        printf(\"  keep alive: %d\\n\", config.keepalive);\n        printf(\"\\n\");\n\n        for (i = 0; i < config.threads_count; i++) {\n            benchmark_thread *bt = darray_get(bts, i);\n            for (j = 0; j < bt->requests; j ++) {\n                config.latency[n++] = bt->latency[j];\n            }\n        }\n        \n        qsort(config.latency,config.requests,sizeof(long long),compareLatency);\n        for (i = 0; i < config.requests; i++) {\n            if (config.latency[i]/1000 != curlat || i == (config.requests-1)) {\n                curlat = config.latency[i]/1000;\n                perc = ((float)(i+1)*100)/config.requests;\n                printf(\"%.2f%% <= %d milliseconds\\n\", perc, curlat);\n            }\n        }\n        printf(\"%.2f requests per second\\n\\n\", reqpersec);\n    } else if (config.csv) {\n        printf(\"\\\"%s\\\",\\\"%.2f\\\"\\n\", config.title, reqpersec);\n    } else {\n        printf(\"%s: %.2f requests per second\\n\", config.title, reqpersec);\n    }\n}\n\nstatic void benchmark(char *title, char *cmd, int len) {\n    int i;\n    int requests_per_thread, requests_remainder;\n    int clients_per_thread, clients_remainder;\n    benchmark_client c;\n\n    config.title = title;\n    config.requests_issued = 0;\n    config.requests_finished = 0;\n    \n    requests_per_thread = config.requests/config.threads_count;\n    requests_remainder = config.requests%config.threads_count;\n    clients_per_thread = config.numclients/config.threads_count;\n    clients_remainder = config.numclients%config.threads_count;\n\n    bts = darray_create(config.threads_count, sizeof(benchmark_thread));\n    for (i = 0; i < config.threads_count; i ++) {\n        benchmark_thread *bt = darray_push(bts);\n        bt->id = i;\n        benchmark_thread_init(bt,\n            requests_remainder-->0?requests_per_thread+1:requests_per_thread,\n            clients_remainder-->0?clients_per_thread+1:clients_per_thread,\n            cmd,len);\n    }\n\n    config.start = dmsec_now();\n    start_benchmark_threads_until_finish();\n    config.totlatency = dmsec_now()-config.start;\n\n    showLatencyReport();\n\n    while (darray_n(bts) > 0) {\n        benchmark_thread *bt = darray_pop(bts);\n        benchmark_thread_deinit(bt);\n    }\n    darray_destroy(bts);\n    bts = NULL;\n}\n\n/* Returns number of consumed options. */\nint parseOptions(int argc, const char **argv) {\n    int i;\n    int lastarg;\n    int exit_status = 1;\n\n    for (i = 1; i < argc; i++) {\n        lastarg = (i == (argc-1));\n\n        if (!strcmp(argv[i],\"-c\")) {\n            if (lastarg) goto invalid;\n            config.numclients = atoi(argv[++i]);\n        } else if (!strcmp(argv[i],\"-n\")) {\n            if (lastarg) goto invalid;\n            config.requests = atoi(argv[++i]);\n        } else if (!strcmp(argv[i],\"-k\")) {\n            if (lastarg) goto invalid;\n            config.keepalive = atoi(argv[++i]);\n        } else if (!strcmp(argv[i],\"-h\")) {\n            if (lastarg) goto invalid;\n            config.hostip = strdup(argv[++i]);\n        } else if (!strcmp(argv[i],\"-p\")) {\n            if (lastarg) goto invalid;\n            config.hostport = atoi(argv[++i]);\n        } else if (!strcmp(argv[i],\"-s\")) {\n            if (lastarg) goto invalid;\n            config.hostsocket = strdup(argv[++i]);\n        } else if (!strcmp(argv[i],\"-a\") ) {\n            if (lastarg) goto invalid;\n            config.auth = strdup(argv[++i]);\n        } else if (!strcmp(argv[i],\"-d\")) {\n            if (lastarg) goto invalid;\n            config.datasize = atoi(argv[++i]);\n            if (config.datasize < 1) config.datasize=1;\n            if (config.datasize > 1024*1024*1024) config.datasize = 1024*1024*1024;\n        } else if (!strcmp(argv[i],\"-P\")) {\n            if (lastarg) goto invalid;\n            config.pipeline = atoi(argv[++i]);\n            if (config.pipeline <= 0) config.pipeline=1;\n        } else if (!strcmp(argv[i],\"-r\")) {\n            if (lastarg) goto invalid;\n            config.randomkeys = 1;\n            config.randomkeys_keyspacelen = atoi(argv[++i]);\n            if (config.randomkeys_keyspacelen < 0)\n                config.randomkeys_keyspacelen = 0;\n        } else if (!strcmp(argv[i],\"-f\")) {\n            if (lastarg) goto invalid;\n            config.randomfields = 1;\n            config.randomfields_fieldspacelen = atoi(argv[++i]);\n            if (config.randomfields_fieldspacelen < 0)\n                config.randomfields_fieldspacelen = 0;\n        } else if (!strcmp(argv[i],\"-q\")) {\n            config.quiet = 1;\n        } else if (!strcmp(argv[i],\"--csv\")) {\n            config.csv = 1;\n        } else if (!strcmp(argv[i],\"-l\")) {\n            config.loop = 1;\n        } else if (!strcmp(argv[i],\"-I\")) {\n            config.idlemode = 1;\n        } else if (!strcmp(argv[i],\"-e\")) {\n            config.showerrors = 1;\n        } else if (!strcmp(argv[i],\"-t\")) {\n            if (lastarg) goto invalid;\n            /* We get the list of tests to run as a string in the form\n             * get,set,lrange,...,test_N. Then we add a comma before and\n             * after the string in order to make sure that searching\n             * for \",testname,\" will always get a match if the test is\n             * enabled. */\n            config.tests = sdsnew(\",\");\n            config.tests = sdscat(config.tests,(char*)argv[++i]);\n            config.tests = sdscat(config.tests,\",\");\n            sdstolower(config.tests);\n        } else if (!strcmp(argv[i],\"-S\")) {\n            if (lastarg) goto invalid;\n            /* We get the list of redis special type commands to run as a string in the form\n             * server,list,string,hash,set,...,sortedset. Then we add a comma before and\n             * after the string in order to make sure that searching\n             * for \",typename,\" will always get a match if the type is\n             * enabled. */\n            config.types = sdsnew(\",\");\n            config.types = sdscat(config.types,(char*)argv[++i]);\n            config.types = sdscat(config.types,\",\");\n            sdstolower(config.types);\n        } else if (!strcmp(argv[i],\"--dbnum\")) {\n            if (lastarg) goto invalid;\n            config.dbnum = atoi(argv[++i]);\n            config.dbnumstr = sdsfromlonglong(config.dbnum);\n        } else if (!strcmp(argv[i],\"-T\")) {\n            if (lastarg) goto invalid;\n            config.threads_count = atoi(argv[++i]);\n        } else if (!strcmp(argv[i],\"-m\")) {\n            config.protocol = TEST_CMD_PROTOCOL_MEMCACHE;\n        } else if (!strcmp(argv[i],\"--noinline\")) {\n            config.noinline = 1;\n        } else if (!strcmp(argv[i],\"--help\")) {\n            exit_status = 0;\n            goto usage;\n        } else {\n            /* Assume the user meant to provide an option when the arg starts\n             * with a dash. We're done otherwise and should use the remainder\n             * as the command and arguments for running the benchmark. */\n            if (argv[i][0] == '-') goto invalid;\n            return i;\n        }\n    }\n\n    return i;\n\ninvalid:\n    printf(\"Invalid option \\\"%s\\\" or option argument missing\\n\\n\",argv[i]);\n\nusage:\n    printf(\n\"Usage: vire-benchmark [-h <host>] [-p <port>] [-c <clients>] [-n <requests]> [-k <boolean>]\\n\\n\"\n\" -h <hostname>      Server hostname (default 127.0.0.1)\\n\"\n\" -p <port>          Server port (default 6379)\\n\"\n\" -s <socket>        Server socket (overrides host and port)\\n\"\n\" -a <password>      Password for Redis Auth\\n\"\n\" -c <clients>       Number of parallel connections (default 100)\\n\"\n\" -n <requests>      Total number of requests (default 1000000)\\n\"\n\" -T <threads>       Threads count to run (default 2)\\n\"\n\" -d <size>          Data size of SET/GET/... value in bytes (default 16)\\n\"\n\" -dbnum <db>        SELECT the specified db number (default 0)\\n\"\n\" -k <boolean>       1=keep alive 0=reconnect (default 1)\\n\"\n\" -r <keyspacelen>   Use random keys for SET/GET/INCR/... (default 10000)\\n\"\n\"  Using this option the benchmark will expand the string __rand_key__\\n\"\n\"  inside an argument with a 12 digits number in the specified range\\n\"\n\"  from 0 to keyspacelen-1. The substitution changes every time a command\\n\"\n\"  is executed. Default tests use this to hit random keys in the\\n\"\n\"  specified range.\\n\"\n\" -f <fieldspacelen>   Use random fields for SADD/HSET/... (default 100)\\n\"\n\"  Using this option the benchmark will expand the string __rand_field__\\n\"\n\"  inside an argument with a 14 digits number in the specified range\\n\"\n\"  from 0 to fieldspacelen-1. The substitution changes every time a command\\n\"\n\"  is executed. Default tests use this to hit random fields in the\\n\"\n\"  specified range.\\n\"\n\" -P <numreq>        Pipeline <numreq> requests. Default 1 (no pipeline).\\n\"\n\" -e                 If server replies with errors, show them on stdout.\\n\"\n\"                    (no more than 1 error per second is displayed)\\n\"\n\" -q                 Quiet. Just show query/sec values\\n\"\n\" --csv              Output in CSV format\\n\"\n\" -l                 Loop. Run the tests forever\\n\"\n\" -t <tests>         Only run the comma separated list of tests. The test\\n\"\n\"                    names are the same as the ones produced as output.\\n\"\n\" -S <types>         Only run the comma separated list of the redis special types commands.\\n\"\n\"                    The type names are like 'server,string,hash,list,set,sortedset'.\\n\"\n\" -I                 Idle mode. Just open N idle connections and wait.\\n\"\n\" -m                 Use memcached protocol. This option is used for testing memcached.\\n\"\n\" --noinline         Not test redis inline commands.\\n\\n\"\n\"Examples:\\n\\n\"\n\" Run the benchmark with the default configuration against 127.0.0.1:6379:\\n\"\n\"   $ vire-benchmark\\n\\n\"\n\" Use 20 parallel clients, for a total of 100k requests, against 192.168.1.1:\\n\"\n\"   $ vire-benchmark -h 192.168.1.1 -p 6379 -n 100000 -c 20\\n\\n\"\n\" Fill 127.0.0.1:6379 with about 1 million keys only using the SET test:\\n\"\n\"   $ vire-benchmark -t set -n 1000000 -r 100000000\\n\\n\"\n\" Benchmark 127.0.0.1:6379 for a few commands producing CSV output:\\n\"\n\"   $ vire-benchmark -t ping,set,get -n 100000 --csv\\n\\n\"\n\" Benchmark a specific command line:\\n\"\n\"   $ vire-benchmark -r 10000 -n 10000 eval 'return redis.call(\\\"ping\\\")' 0\\n\\n\"\n\" Fill a list with 10000 random elements:\\n\"\n\"   $ vire-benchmark -r 10000 -n 10000 lpush mylist __rand_field__\\n\\n\"\n\" On user specified command lines __rand_key__ and __rand_field__ are replaced\\n\"\n\" with a random integer with a range of values selected by the -r and -f option.\\n\"\n    );\n    exit(exit_status);\n}\n\nstatic int showThroughput(struct aeEventLoop *eventLoop, long long id, void *clientData) {\n    UNUSED(eventLoop);\n    UNUSED(id);\n    UNUSED(clientData);\n\n    updateBenchmarkStats();\n\n    if (config.liveclients == 0) {\n        fprintf(stderr,\"All clients disconnected... aborting.\\n\");\n        exit(1);\n    }\n    if (config.csv) return 250;\n    if (config.idlemode == 1) {\n        printf(\"clients: %d\\r\", config.liveclients);\n        fflush(stdout);\n\t    return 250;\n    }\n    float dt = (float)(dmsec_now()-config.start)/1000.0;\n    float rps = (float)config.requests_finished/dt;\n    printf(\"%s: %.2f\\r\", config.title, rps);\n    fflush(stdout);\n    return 250; /* every 250ms */\n}\n\n/* Return true if the named test was selected using the -t command line\n * switch, or if all the tests are selected (no -t passed by user). */\nint test_is_selected(char *name) {\n    char buf[256];\n    int l = strlen(name);\n\n    if (config.tests == NULL) return 1;\n    buf[0] = ',';\n    memcpy(buf+1,name,l);\n    buf[l+1] = ',';\n    buf[l+2] = '\\0';\n    return strstr(config.tests,buf) != NULL;\n}\n\nint types_is_selected(char *name) {\n    char buf[256];\n    int l = strlen(name);\n\n    if (config.types == NULL) return 1;\n    buf[0] = ',';\n    memcpy(buf+1,name,l);\n    buf[l+1] = ',';\n    buf[l+2] = '\\0';\n    return strstr(config.types,buf) != NULL;\n}\n\nstatic int requests_temporarily_stats = 0;\nstatic int requests_original = 0;\nvoid set_requests_temporarily(int num) {\n    if (requests_temporarily_stats != 0) return;\n    requests_original = config.requests;\n    config.requests = num;\n    requests_temporarily_stats = 1;\n}\nvoid retrieval_requests_to_original() {\n    if (requests_temporarily_stats != 1) return;\n    config.requests = requests_original;\n    requests_original = 0;\n    requests_temporarily_stats = 0;\n}\n\nstatic int random_keys_temporarily_stats = 0;\nstatic int randomkeys_original = 0;\nstatic int randomkeys_keyspacelen_original = 0;\nvoid set_random_keys_temporarily(int num) {\n    if (random_keys_temporarily_stats != 0) return;\n    randomkeys_original = config.randomkeys;\n    randomkeys_keyspacelen_original = config.randomkeys_keyspacelen;\n    config.randomkeys = 1;\n    config.randomkeys_keyspacelen = num;\n    random_keys_temporarily_stats = 1;\n}\nvoid retrieval_random_keys_to_original() {\n    if (random_keys_temporarily_stats != 1) return;\n    config.randomkeys = randomkeys_original;\n    config.randomkeys_keyspacelen = randomkeys_keyspacelen_original;\n    randomkeys_original = 0;\n    randomkeys_keyspacelen_original = 0;\n    random_keys_temporarily_stats = 0;\n}\n\nstatic int test_redis(int argc, const char **argv)\n{\n    int i;\n    char *data, *cmd;\n    int len;\n\n    /* Run benchmark with command in the remainder of the arguments. */\n    if (argc) {\n        sds title = sdsnew(argv[0]);\n        for (i = 1; i < argc; i++) {\n            title = sdscatlen(title, \" \", 1);\n            title = sdscatlen(title, (char*)argv[i], strlen(argv[i]));\n        }\n\n        do {\n            len = redisFormatCommandArgv(&cmd,argc,argv,NULL);\n            benchmark(title,cmd,len);\n            free(cmd);\n        } while(config.loop);\n\n        return 0;\n    }\n\n    /* Run default benchmark suite. */\n    data = malloc(config.datasize+1);\n    do {\n        memset(data,'x',config.datasize);\n        data[config.datasize] = '\\0';\n\n        if (!config.noinline && \n            (test_is_selected(\"ping_inline\") ||\n            test_is_selected(\"ping\")) &&\n            types_is_selected(\"server\"))\n            benchmark(\"PING_INLINE\",\"PING\\r\\n\",6);\n\n        if ((test_is_selected(\"ping_mbulk\") ||\n            test_is_selected(\"ping\")) &&\n            types_is_selected(\"server\")) {\n            len = redisFormatCommand(&cmd,\"PING\");\n            benchmark(\"PING_BULK\",cmd,len);\n            free(cmd);\n        }\n\n        if (test_is_selected(\"set\") && types_is_selected(\"string\")) {\n            len = redisFormatCommand(&cmd,\"SET mystring:__rand_key__ %s\",data);\n            benchmark(\"SET\",cmd,len);\n            free(cmd);\n        }\n\n        if (test_is_selected(\"get\") && types_is_selected(\"string\")) {\n            len = redisFormatCommand(&cmd,\"GET mystring:__rand_key__\");\n            benchmark(\"GET\",cmd,len);\n            free(cmd);\n        }\n\n        if (test_is_selected(\"incr\") && types_is_selected(\"string\")) {\n            len = redisFormatCommand(&cmd,\"INCR mycounter:__rand_key__\");\n            benchmark(\"INCR\",cmd,len);\n            free(cmd);\n        }\n\n        if (test_is_selected(\"mset\") && types_is_selected(\"string\")) {\n            const char *argv[21];\n            argv[0] = \"MSET\";\n            for (i = 1; i < 21; i += 2) {\n                argv[i] = \"mystring:__rand_key__\";\n                argv[i+1] = data;\n            }\n            len = redisFormatCommandArgv(&cmd,21,argv,NULL);\n            benchmark(\"MSET (10 keys)\",cmd,len);\n            free(cmd);\n        }\n\n        if ((test_is_selected(\"mget\") ||\n            test_is_selected(\"mget_10\")) &&\n            types_is_selected(\"string\")) {\n            const char *argv[11];\n            argv[0] = \"MGET\";\n            for (i = 1; i < 11; i ++) {\n                argv[i] = \"mystring:__rand_key__\";\n            }\n            len = redisFormatCommandArgv(&cmd,11,argv,NULL);\n            benchmark(\"MGET (10 keys)\",cmd,len);\n            free(cmd);\n        }\n\n        if ((test_is_selected(\"mget\") ||\n            test_is_selected(\"mget_100\"))\n            && types_is_selected(\"string\")) {\n            const char *argv[101];\n            argv[0] = \"MGET\";\n            for (i = 1; i < 101; i ++) {\n                argv[i] = \"mystring:__rand_key__\";\n            }\n            len = redisFormatCommandArgv(&cmd,101,argv,NULL);\n            benchmark(\"MGET (100 keys)\",cmd,len);\n            free(cmd);\n        }\n\n        if ((test_is_selected(\"mget\") ||\n            test_is_selected(\"mget_200\")) &&\n            types_is_selected(\"string\")) {\n            const char *argv[201];\n            argv[0] = \"MGET\";\n            for (i = 1; i < 201; i ++) {\n                argv[i] = \"mystring:__rand_key__\";\n            }\n            len = redisFormatCommandArgv(&cmd,201,argv,NULL);\n            benchmark(\"MGET (200 keys)\",cmd,len);\n            free(cmd);\n        }\n\n        if (test_is_selected(\"lpush\") && types_is_selected(\"list\")) {\n            len = redisFormatCommand(&cmd,\"LPUSH mylist:__rand_key__ %s\",data);\n            benchmark(\"LPUSH\",cmd,len);\n            free(cmd);\n        }\n\n        if (test_is_selected(\"rpush\") && types_is_selected(\"list\")) {\n            len = redisFormatCommand(&cmd,\"RPUSH mylist:__rand_key__ %s\",data);\n            benchmark(\"RPUSH\",cmd,len);\n            free(cmd);\n        }\n\n        if (test_is_selected(\"lpop\") && types_is_selected(\"list\")) {\n            len = redisFormatCommand(&cmd,\"LPOP mylist:__rand_key__\");\n            benchmark(\"LPOP\",cmd,len);\n            free(cmd);\n        }\n\n        if (test_is_selected(\"rpop\") && types_is_selected(\"list\")) {\n            len = redisFormatCommand(&cmd,\"RPOP mylist:__rand_key__\");\n            benchmark(\"RPOP\",cmd,len);\n            free(cmd);\n        }\n\n        if ((test_is_selected(\"lrange\") ||\n            test_is_selected(\"lrange_10\") ||\n            test_is_selected(\"lrange_100\") ||\n            test_is_selected(\"lrange_300\") ||\n            test_is_selected(\"lrange_450\") ||\n            test_is_selected(\"lrange_600\")) &&\n            types_is_selected(\"list\"))\n        {\n            set_random_keys_temporarily(1000);\n            if (config.requests < 1000*1000)\n                set_requests_temporarily(1000*1000);\n            len = redisFormatCommand(&cmd,\"LPUSH mylist:__rand_key__ %s\",data);\n            benchmark(\"LPUSH (needed to benchmark LRANGE)\",cmd,len);\n            free(cmd);\n            retrieval_requests_to_original();\n            retrieval_random_keys_to_original();\n        }\n\n        if ((test_is_selected(\"lrange\") || \n            test_is_selected(\"lrange_10\")) &&\n            types_is_selected(\"list\")) {\n            set_random_keys_temporarily(1000);\n            if (config.requests > 500*1000)\n                set_requests_temporarily(500*1000);\n            len = redisFormatCommand(&cmd,\"LRANGE mylist:__rand_key__ 0 9\");\n            benchmark(\"LRANGE_10 (first 10 elements)\",cmd,len);\n            free(cmd);\n            retrieval_requests_to_original();\n            retrieval_random_keys_to_original();\n        }\n\n        if ((test_is_selected(\"lrange\") || \n            test_is_selected(\"lrange_100\")) &&\n            types_is_selected(\"list\")) {\n            set_random_keys_temporarily(1000);\n            if (config.requests > 320000)\n                set_requests_temporarily(320000);\n            len = redisFormatCommand(&cmd,\"LRANGE mylist:__rand_key__ 0 99\");\n            benchmark(\"LRANGE_100 (first 100 elements)\",cmd,len);\n            free(cmd);\n            retrieval_requests_to_original();\n            retrieval_random_keys_to_original();\n        }\n\n        if ((test_is_selected(\"lrange\") ||\n            test_is_selected(\"lrange_300\")) &&\n            types_is_selected(\"list\")) {\n            set_random_keys_temporarily(1000);\n            if (config.requests > 160000)\n                set_requests_temporarily(160000);\n            len = redisFormatCommand(&cmd,\"LRANGE mylist:__rand_key__ 0 299\");\n            benchmark(\"LRANGE_300 (first 300 elements)\",cmd,len);\n            free(cmd);\n            retrieval_requests_to_original();\n            retrieval_random_keys_to_original();\n        }\n\n        if ((test_is_selected(\"lrange\") ||\n            test_is_selected(\"lrange_450\")) &&\n            types_is_selected(\"list\")) {\n            set_random_keys_temporarily(1000);\n            if (config.requests > 100000)\n                set_requests_temporarily(100000);\n            len = redisFormatCommand(&cmd,\"LRANGE mylist:__rand_key__ 0 449\");\n            benchmark(\"LRANGE_450 (first 450 elements)\",cmd,len);\n            free(cmd);\n            retrieval_requests_to_original();\n            retrieval_random_keys_to_original();\n        }\n\n        if ((test_is_selected(\"lrange\") ||\n            test_is_selected(\"lrange_600\")) &&\n            types_is_selected(\"list\")) {\n            set_random_keys_temporarily(1000);\n            if (config.requests > 100000)\n                set_requests_temporarily(100000);\n            len = redisFormatCommand(&cmd,\"LRANGE mylist:__rand_key__ 0 599\");\n            benchmark(\"LRANGE_600 (first 600 elements)\",cmd,len);\n            free(cmd);\n            retrieval_requests_to_original();\n            retrieval_random_keys_to_original();\n        }\n\n        if (test_is_selected(\"sadd\") && types_is_selected(\"set\")) {\n            len = redisFormatCommand(&cmd,\n                \"SADD myset:__rand_key__ %s:__rand_field__\", data);\n            benchmark(\"SADD\",cmd,len);\n            free(cmd);\n        }\n\n        if (test_is_selected(\"spop\") && types_is_selected(\"set\")) {\n            len = redisFormatCommand(&cmd,\"SPOP myset:__rand_key__\");\n            benchmark(\"SPOP\",cmd,len);\n            free(cmd);\n        }\n\n        if (test_is_selected(\"hset\") && types_is_selected(\"hash\")) {\n            len = redisFormatCommand(&cmd,\"HSET myhash:__rand_key__ field:__rand_field__ %s\", data);\n            benchmark(\"HSET\",cmd,len);\n            free(cmd);\n        }\n\n        if (test_is_selected(\"hincrby\") && types_is_selected(\"hash\")) {\n            len = redisFormatCommand(&cmd,\"HINCRBY myhashcounter:__rand_key__ field:__rand_field__ 19\");\n            benchmark(\"HINCRBY\",cmd,len);\n            free(cmd);\n        }\n\n        if (test_is_selected(\"hincrbyfloat\") && types_is_selected(\"hash\")) {\n            len = redisFormatCommand(&cmd,\"HINCRBYFLOAT myhashcounterf:__rand_key__ field:__rand_field__ 19.963\");\n            benchmark(\"HINCRBYFLOAT\",cmd,len);\n            free(cmd);\n        }\n\n        if (test_is_selected(\"hget\") && types_is_selected(\"hash\")) {\n            len = redisFormatCommand(&cmd,\"HGET myhash:__rand_key__ field:__rand_field__\");\n            benchmark(\"HGET\",cmd,len);\n            free(cmd);\n        }\n\n        if (test_is_selected(\"hmset\") && types_is_selected(\"hash\")) {\n            const char *argv[21];\n            argv[0] = \"HMSET\";\n            argv[1] = \"myhashm:__rand_key__\";\n            for (i = 2; i < 22; i += 2) {\n                argv[i] = \"field:__rand_field__\";\n                argv[i+1] = data;\n            }\n            len = redisFormatCommandArgv(&cmd,22,argv,NULL);\n            benchmark(\"HMSET (10 fields)\",cmd,len);\n            free(cmd);\n        }\n\n        if (test_is_selected(\"hmget\") && types_is_selected(\"hash\")) {\n            const char *argv[21];\n            argv[0] = \"HMGET\";\n            argv[1] = \"myhashm:__rand_key__\";\n            for (i = 2; i < 12; i ++) {\n                argv[i] = \"field:__rand_field__\";\n            }\n            len = redisFormatCommandArgv(&cmd,12,argv,NULL);\n            benchmark(\"HMGET (10 fields)\",cmd,len);\n            free(cmd);\n        }\n\n        if (test_is_selected(\"hgetall\") && types_is_selected(\"hash\")) {\n            len = redisFormatCommand(&cmd,\"HGETALL myhash:__rand_key__\");\n            benchmark(\"HGETALL\",cmd,len);\n            free(cmd);\n        }\n\n        if (test_is_selected(\"zadd\") && types_is_selected(\"sortedset\")) {\n            len = redisFormatCommand(&cmd,\"ZADD mysortedset:__rand_key__ __rand_field__ %s:__rand_field__\", data);\n            benchmark(\"ZADD\",cmd,len);\n            free(cmd);\n        }\n\n        if (test_is_selected(\"zrem\") && types_is_selected(\"sortedset\")) {\n            len = redisFormatCommand(&cmd,\"ZREM mysortedset:__rand_key__ %s:__rand_field__\", data);\n            benchmark(\"ZREM\",cmd,len);\n            free(cmd);\n        }\n\n        if (test_is_selected(\"pfadd\") && types_is_selected(\"hyperloglog\")) {\n            len = redisFormatCommand(&cmd,\"PFADD myhll:__rand_key__ %s:__rand_field__\", data);\n            benchmark(\"PFADD\",cmd,len);\n            free(cmd);\n        }\n\n        if (test_is_selected(\"pfcount\") && types_is_selected(\"hyperloglog\")) {\n            len = redisFormatCommand(&cmd,\"PFCOUNT myhll:__rand_key__\");\n            benchmark(\"PFCOUNT\",cmd,len);\n            free(cmd);\n        }\n\n        if (test_is_selected(\"pfmerge\") && types_is_selected(\"hyperloglog\")) {\n            len = redisFormatCommand(&cmd,\"PFADD myhll:__rand_key__ %s:__rand_field__\", data);\n            benchmark(\"PFADD (needed to benchmark PFMERGE)\",cmd,len);\n            free(cmd);\n            \n            len = redisFormatCommand(&cmd,\"PFMERGE myhllm:__rand_key__ myhll:__rand_key__ myhll:__rand_key__\");\n            benchmark(\"PFMERGE\",cmd,len);\n            free(cmd);\n        }\n\n        if (!config.csv) printf(\"\\n\");\n    } while(config.loop);\n\n    return VRT_OK;\n}\n\nstatic int test_memcached(int argc, const char **argv)\n{\n    int i;\n    char *data, *cmd;\n    int len;\n\n    /* Run benchmark with command in the remainder of the arguments. */\n    if (argc) {\n        sds title = sdsnew(argv[0]);\n        for (i = 1; i < argc; i++) {\n            title = sdscatlen(title, \" \", 1);\n            title = sdscatlen(title, (char*)argv[i], strlen(argv[i]));\n        }\n\n        do {\n            len = memcachedFormatCommandArgv(&cmd,argc,argv,NULL);\n            if (len < 0) {\n                return 0;\n            }\n            \n            benchmark(title,cmd,len);\n            free(cmd);\n        } while(config.loop);\n\n        return 0;\n    }\n\n    /* Run default benchmark suite. */\n    data = malloc(config.datasize+1);\n    do {\n        memset(data,'x',config.datasize);\n        data[config.datasize] = '\\0';\n\n        if (test_is_selected(\"set\")) {\n            len = memcachedFormatCommand(&cmd,\"set key:__rand_key__ 0 0 %d %s\", config.datasize, data);\n            \n            benchmark(\"SET\",cmd,len);\n            free(cmd);\n        }\n\n        if (test_is_selected(\"get\")) {\n            len = memcachedFormatCommand(&cmd,\"get key:__rand_key__\");\n            benchmark(\"GET\",cmd,len);\n            free(cmd);\n        }\n        \n        if (!config.csv) printf(\"\\n\");\n    } while(config.loop);\n\n    return VRT_OK;\n}\n\nint main(int argc, const char **argv) {\n    int i;\n\n    benchmark_client c;\n\n    srandom(time(NULL));\n    signal(SIGHUP, SIG_IGN);\n    signal(SIGPIPE, SIG_IGN);\n\n    config.numclients = 100;\n    config.requests = 1000000;\n    config.liveclients = 0;\n    config.keepalive = 1;\n    config.datasize = 16;\n    config.pipeline = 1;\n    config.showerrors = 0;\n    config.randomkeys = 1;\n    config.randomkeys_keyspacelen = 10000;\n    config.randomfields = 1;\n    config.randomfields_fieldspacelen = 100;\n    config.quiet = 0;\n    config.csv = 0;\n    config.loop = 0;\n    config.idlemode = 0;\n    config.latency = NULL;\n    config.hostip = \"127.0.0.1\";\n    config.hostport = 6379;\n    config.hostsocket = NULL;\n    config.tests = NULL;\n    config.types = NULL;\n    config.dbnum = 0;\n    config.auth = NULL;\n    config.threads_count = 2;\n    config.protocol = TEST_CMD_PROTOCOL_REDIS;\n    config.noinline = 0;\n\n    i = parseOptions(argc,argv);\n    argc -= i;\n    argv += i;\n\n    /* Init the benchmark threads */\n    if (config.threads_count <= 0) {\n        printf(\"ERROR: threads count need bigger than zero\\n\");\n        return -1;\n    }\n    if (config.requests <= 0) {\n        printf(\"ERROR: requests count need bigger than zero\\n\");\n        return -1;\n    }\n    if (config.numclients <= 0) {\n        printf(\"ERROR: clients count need bigger than zero\\n\");\n        return -1;\n    }\n    if (config.requests < config.numclients) config.numclients = config.requests;\n    if (config.requests < config.threads_count) config.threads_count = config.requests;\n    if (config.numclients < config.threads_count) config.threads_count = config.numclients;\n\n    config.latency = malloc(sizeof(long long)*config.requests);\n\n    if (config.keepalive == 0) {\n        printf(\"WARNING: keepalive disabled, you probably need 'echo 1 > /proc/sys/net/ipv4/tcp_tw_reuse' for Linux and 'sudo sysctl -w net.inet.tcp.msl=1000' for Mac OS X in order to use a lot of clients/requests\\n\");\n    }\n\n    //if (config.idlemode) {\n    //    printf(\"Creating %d idle connections and waiting forever (Ctrl+C when done)\\n\", config.numclients);\n    //    c = createClient(\"\",0,NULL); /* will never receive a reply */\n    //    createMissingClients(c);\n    //    aeMain(config.el);\n        /* and will wait for every */\n    //}\n\n    if (config.protocol == TEST_CMD_PROTOCOL_REDIS) {\n        test_redis(argc, argv);\n    } else if (config.protocol == TEST_CMD_PROTOCOL_MEMCACHE) {\n        test_memcached(argc, argv);\n    } else {\n        NOT_REACHED();\n    }\n\n    return 0;\n}\n"
  },
  {
    "path": "tests/vrt_check_data.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <getopt.h>\n#include <fcntl.h>\n#include <errno.h>\n#include <signal.h>\n#include <pthread.h>\n#include <sys/stat.h>\n#include <sys/utsname.h>\n\n#include <hiredis.h>\n#include <async.h>\n#include <adapters/ae.h>\n\n#include <dhashkit.h>\n#include <dlist.h>\n#include <dmtqueue.h>\n#include <dlog.h>\n\n#include <vrt_util.h>\n#include <vrt_public.h>\n#include <vrabtest.h>\n#include <vrt_produce_data.h>\n#include <vrt_dispatch_data.h>\n#include <vrt_backend.h>\n#include <vrt_check_data.h>\n\n#define CHECK_DATA_FLAG_NONE        (1<<0)\n#define CHECK_DATA_FLAG_MASTER      (1<<1)\n#define CHECK_DATA_FLAG_SLAVE       (1<<2)\n\n#define CHECK_UNIT_STATE_NULL           0\n#define CHECK_UNIT_STATE_GET_EXPIRE     1\n#define CHECK_UNIT_STATE_GET_TYPE       2\n#define CHECK_UNIT_STATE_GET_VALUE      3\n\ntypedef struct check_data_thread {\n    int id;\n    pthread_t thread_id;\n    \n    aeEventLoop *el;\n    int hz;\n    int cronloops;  /* Number of times the cron function run */\n\n    darray *abgs;   /* Type is abtest_group */\n    int scan_group_idx; /* The group idx to scan keys */\n    darray *scan_servers;   /* The servers in the scan group, type is abtest_server */\n    int scan_finished_count;\n    long long cursor;   /* scan cursor */\n    dlist *check_units;\n\n    long long check_begin_time; /* Unit is second */\n    long long scan_keys_count;\n} check_data_thread;\n\ntypedef struct check_unit {\n    check_data_thread *cdt;\n\n    dlistNode *lnode;\n    \n    sds key;\n\n    int key_persist;\n    long long min_ttl, max_ttl_gap;\n    \n    int key_type;\n    int state;\n\n    darray servers; /* Servers used to send the check messages, type is pointer of abtest_server */\n    darray replys;  /* Used to cache the replys from the servers, type is pointer of redisReply */\n\n    unsigned int servers_count;\n    unsigned int replys_count;\n    unsigned int not_exist_count;\n} check_unit;\n\ntypedef struct data_checker {\n    pthread_t thread_id;\n    \n    aeEventLoop *el;\n    int hz;\n    int cronloops;          /* Number of times the cron function run */\n\n    sds test_target_groups;\n\n    int flags;\n    sds checker;\n    conn_context *master;   /* If this is a slave */\n\n    long long check_begin_time; /* Unit is second */\n} data_checker;\n\nstatic data_checker dc;\n\n/* Last begin time to check the data.\n * Unit is second */\nstatic long long last_check_begin_time;\n\nstatic darray *cdts = NULL;\n\nstatic check_unit *check_unit_create(void)\n{\n    check_unit *cunit;\n\n    cunit = malloc(sizeof(*cunit));\n    if (cunit == NULL) {\n        return NULL;\n    }\n\n    cunit->cdt = NULL;\n\n    cunit->lnode = NULL;\n    \n    cunit->key = NULL;\n    cunit->key_persist = 0;\n    cunit->min_ttl = 0;\n    cunit->max_ttl_gap = 0;\n    cunit->key_type = -1;\n    cunit->state = CHECK_UNIT_STATE_NULL;\n    darray_init(&cunit->servers, 2, sizeof(abtest_server*));\n    darray_init(&cunit->replys, 2, sizeof(redisReply*));\n\n    cunit->servers_count = 0;\n    cunit->replys_count = 0;\n    cunit->not_exist_count = 0;\n    \n    return cunit;\n}\n\nstatic void check_unit_destroy(check_unit *cunit)\n{\n    if (cunit->cdt != NULL && cunit->lnode != NULL) {\n        dlistDelNode(cunit->cdt->check_units,cunit->lnode);\n        cunit->lnode = NULL;\n    }\n\n    if (cunit->key != NULL) {\n        sdsfree(cunit->key);\n        cunit->key = NULL;\n    }\n\n    while (darray_n(&cunit->servers) > 0) {\n        darray_pop(&cunit->servers);\n    }\n    darray_deinit(&cunit->servers);\n\n    while (darray_n(&cunit->replys) > 0) {\n        redisReply **reply = darray_pop(&cunit->replys);\n        freeReplyObject(*reply);\n    }\n    darray_deinit(&cunit->replys);\n\n    free(cunit);\n}\n\nstatic int check_conn_context_init(conn_context *cc, char *host, int port)\n{\n    cc->ctx = NULL;\n    cc->actx = NULL;\n\n    cc->actx = redisAsyncConnect(host, port);\n    if (cc->actx == NULL) {\n        return VRT_ERROR;\n    }\n    \n    return VRT_OK;\n}\n\nstatic void check_conn_context_deinit(conn_context *cc)\n{\n    if (cc->ctx) {\n        redisFree(cc->ctx);\n        cc->ctx == NULL;\n    }\n\n    if (cc->actx) {\n        cc->actx->ev.cleanup = NULL;\n        redisAsyncFree(cc->actx);\n        cc->actx == NULL;\n    }\n}\n\nstatic void connect_callback(const redisAsyncContext *c, int status) {\n    check_data_thread *cdt = c->data;\n    if (status != REDIS_OK) {\n        test_log_out(\"Error: %s\\n\", c->errstr);\n        //aeStop(loop);\n        return;\n    }\n\n    //test_log_out(\"Connected...\\n\");\n}\n\nstatic void disconnect_callback(const redisAsyncContext *c, int status) {\n    check_data_thread *cdt = c->data;\n    if (status != REDIS_OK) {\n        test_log_out(\"Error: %s\\n\", c->errstr);\n        //aeStop(loop);\n        return;\n    }\n\n    //test_log_out(\"Disconnected...\\n\");\n    //aeStop(loop);\n}\n\nstatic int sort_replys_if_needed(check_unit *cunit)\n{\n    int step = 0, idx_cmp = 0;\n\n    if (cunit->key_type == REDIS_SET) {\n        step = 1;\n    } else if (cunit->key_type == REDIS_HASH) {\n        step = 2;\n    }\n\n    if (step > 0) {\n        int i;\n        redisReply **reply;\n        for (i = 0; i < darray_n(&cunit->replys); i ++) {\n            reply = darray_get(&cunit->replys, i);\n            if ((*reply)->type != REDIS_REPLY_ARRAY)\n                continue;\n            sort_array_by_step((*reply)->element, (*reply)->elements, \n                step, idx_cmp, reply_string_binary_compare);\n        }\n    }\n    \n    return VRT_OK;\n}\n\n/* 1: All replys are same\n * 0: replys are different */\nstatic int check_replys_if_same(check_unit *cunit)\n{\n    unsigned int j;\n    redisReply **replyb, **reply;\n\n    sort_replys_if_needed(cunit);\n\n    replyb = darray_get(&cunit->replys,0);\n    \n    for (j = 1; j < cunit->replys_count ; j ++) {\n        reply = darray_get(&cunit->replys,j);\n        if (check_two_replys_if_same(*replyb, *reply)) {\n            return 0;\n        }\n    }\n    \n    return 1;\n}\n\n#define TTL_MISTAKE_CAN_BE_ACCEPT   3\nstatic void check_data_callback(redisAsyncContext *c, void *r, void *privdata) {\n    redisReply *reply = r, *reply_sub, *reply_elem;\n    redisReply *reply_clone, **elem;\n    check_unit *cunit = privdata;\n    check_data_thread *cdt = cunit->cdt;\n    conn_context *cc;\n    long long value;\n    char *errmsg;\n    int j;\n    \n    if (reply == NULL) return;\n\n    if (cunit->state == CHECK_UNIT_STATE_GET_EXPIRE) {\n        if (reply->type != REDIS_REPLY_INTEGER) {\n            errmsg = \"ttl command reply type is not integer\";\n            goto error;\n        }\n\n        reply_clone = steal_hiredis_redisreply(reply);\n        elem = darray_push(&cunit->replys);\n        *elem = reply_clone;\n        cunit->replys_count ++;\n        \n        if (cunit->replys_count >= cunit->servers_count) {\n            char *argv[2];\n            size_t argvlen[2];\n            long long min, max;\n            int persist;\n\n            elem = darray_get(&cunit->replys, 0);\n            reply_elem = *elem;\n            if (reply_elem->integer == -1) {\n                persist = 1;\n            } else if (reply_elem->integer == -2) {\n                cunit->not_exist_count ++;\n                min = max = 0;\n            } else if (reply_elem->integer < -2) {\n                errmsg = \"ttl command reply integer is less than -2\";\n                goto error;\n            } else {\n                min = max =  reply_elem->integer;\n            }\n            \n            for (j = 1; j < darray_n(&cunit->replys); j ++) {\n                elem = darray_get(&cunit->replys, j);\n                reply_elem = *elem;\n                if (persist && reply_elem->integer != -1) {\n                    errmsg = \"key in some server is persist, but others are not\";\n                    goto error;\n                }\n                \n                if (reply_elem->integer == -1) {\n                    if (persist != 1) {\n                        errmsg = \"key in some server is persist, but others are not\";\n                        goto error;\n                    }\n                } else if (reply_elem->integer == -2) {\n                    cunit->not_exist_count ++;\n                    if (min > 0) min = 0;\n                } else if (reply_elem->integer < -2) {\n                    errmsg = \"ttl command reply integer is less than -2\";\n                    goto error;\n                } else {\n                    if (reply_elem->integer < min) min = reply_elem->integer;\n                    if (reply_elem->integer > max) max = reply_elem->integer;\n                }\n            }\n\n            if (cunit->not_exist_count >= cunit->servers_count) {\n                /* The key in all the target group is expired */\n                goto done;\n            }\n            \n            if (persist) {\n                cunit->key_persist = 1;\n            } else {\n                cunit->min_ttl = min;\n                cunit->max_ttl_gap = max-min;\n                if (cunit->max_ttl_gap > TTL_MISTAKE_CAN_BE_ACCEPT) {\n                    errmsg = \"ttl mistake is too big between groups\";\n                    goto error;\n                }\n            }\n            \n            /* Check step 2: get the key type */\n            argv[0] = \"type\";\n            argvlen[0] = 4;\n            argv[1] = cunit->key;\n            argvlen[1] = sdslen(cunit->key);\n            for (j = 0; j < darray_n(&cunit->servers); j ++) {\n                abtest_server **abs = darray_get(&cunit->servers,j);\n                conn_context *cc = darray_get((*abs)->conn_contexts, 0);\n                \n                redisAsyncCommandArgv(cc->actx, check_data_callback, \n                    cunit, 2, argv, argvlen);\n            }\n            \n            cunit->state = CHECK_UNIT_STATE_GET_TYPE;\n            goto next_step;\n        }\n\n        return;\n    }\n\n    if (cunit->state == CHECK_UNIT_STATE_GET_TYPE) {        \n        if (reply->type != REDIS_REPLY_STATUS) {\n            errmsg = \"type command reply type is not status\";\n            goto error;\n        }\n\n        if (!strcmp(reply->str, \"none\")) {\n            /* This key doesn't exit, may be expired or evicted */\n            cunit->not_exist_count ++;\n        } else {\n            reply_clone = steal_hiredis_redisreply(reply);\n            elem = darray_push(&cunit->replys);\n            *elem = reply_clone;\n            cunit->replys_count ++;\n        }\n\n        if (cunit->not_exist_count >= cunit->servers_count) {\n            /* The key in all the target group is expired */\n            goto done;\n        } else if (cunit->replys_count >= (cunit->servers_count-cunit->not_exist_count)) {\n            int argc;\n            char **argv;\n            size_t *argvlen;\n\n            if (cunit->not_exist_count > 0 && cunit->key_persist) {\n                errmsg = \"key is persist, but not exist in some servers\";\n                goto error;\n            }\n            \n            if (check_replys_if_same(cunit) != 1) {\n                errmsg = \"type command replys are not same\";\n                goto error;\n            }\n\n            elem = darray_get(&cunit->replys,0);\n            if (!strcmp((*elem)->str,\"string\")) {\n                cunit->key_type = REDIS_STRING;\n                \n                argc = 2;\n                argv = malloc(argc*sizeof(char *));\n                argvlen = malloc(argc*sizeof(size_t));\n\n                argv[0] = \"get\";\n                argvlen[0] = 3;\n                argv[1] = cunit->key;\n                argvlen[1] = sdslen(cunit->key);\n            } else if (!strcmp((*elem)->str,\"list\")) {\n                cunit->key_type = REDIS_LIST;\n\n                argc = 4;\n                argv = malloc(argc*sizeof(char *));\n                argvlen = malloc(argc*sizeof(size_t));\n\n                argv[0] = \"lrange\";\n                argvlen[0] = 6;\n                argv[1] = cunit->key;\n                argvlen[1] = sdslen(cunit->key);\n                argv[2] = \"0\";\n                argvlen[2] = 1;\n                argv[3] = \"-1\";\n                argvlen[3] = 2;\n            } else if (!strcmp((*elem)->str,\"set\")) {\n                cunit->key_type = REDIS_SET;\n\n                argc = 2;\n                argv = malloc(argc*sizeof(char *));\n                argvlen = malloc(argc*sizeof(size_t));\n\n                argv[0] = \"smembers\";\n                argvlen[0] = 8;\n                argv[1] = cunit->key;\n                argvlen[1] = sdslen(cunit->key);\n            } else if (!strcmp((*elem)->str,\"zset\")) {\n                cunit->key_type = REDIS_ZSET;\n\n                argc = 4;\n                argv = malloc(argc*sizeof(char *));\n                argvlen = malloc(argc*sizeof(size_t));\n\n                argv[0] = \"zrange\";\n                argvlen[0] = 6;\n                argv[1] = cunit->key;\n                argvlen[1] = sdslen(cunit->key);\n                argv[2] = \"0\";\n                argvlen[2] = 1;\n                argv[3] = \"-1\";\n                argvlen[3] = 2;\n            } else if (!strcmp((*elem)->str,\"hash\")) {\n                cunit->key_type = REDIS_HASH;\n\n                argc = 2;\n                argv = malloc(argc*sizeof(char *));\n                argvlen = malloc(argc*sizeof(size_t));\n\n                argv[0] = \"hgetall\";\n                argvlen[0] = 7;\n                argv[1] = cunit->key;\n                argvlen[1] = sdslen(cunit->key);\n            } else {\n                errmsg = \"not supported key type\";\n                goto error;\n            }\n\n            /* Check step 3: get the value */\n            for (j = 0; j < darray_n(&cunit->servers); j ++) {\n                abtest_server **abs = darray_get(&cunit->servers,j);\n                conn_context *cc = darray_get((*abs)->conn_contexts, 0);\n                \n                redisAsyncCommandArgv(cc->actx, check_data_callback, \n                    cunit, argc, argv, argvlen);\n            }\n            free(argv);\n            free(argvlen);\n            \n            cunit->state = CHECK_UNIT_STATE_GET_VALUE;\n            goto next_step;\n        }\n\n        return;\n    }\n\n    if (cunit->state == CHECK_UNIT_STATE_GET_VALUE) {\n        int not_exist = 0;\n        if (cunit->key_type == REDIS_STRING) {\n            if (reply->type == REDIS_REPLY_NIL) {\n                not_exist = 1;\n            } else if (reply->type != REDIS_REPLY_STRING) {\n                errmsg = \"get command reply type is not string\";\n                goto error;\n            }\n        } else if (cunit->key_type == REDIS_LIST) {\n            if (reply->type != REDIS_REPLY_ARRAY) {\n                errmsg = \"lrange command reply type is not array\";\n                goto error;\n            }\n            if (reply->elements == 0) {\n                not_exist = 1;\n            }\n        } else if (cunit->key_type == REDIS_SET) {\n            if (reply->type != REDIS_REPLY_ARRAY) {\n                errmsg = \"smembers command reply type is not array\";\n                goto error;\n            }\n            if (reply->elements == 0) {\n                not_exist = 1;\n            }\n        } else if (cunit->key_type == REDIS_ZSET) {\n            if (reply->type != REDIS_REPLY_ARRAY) {\n                errmsg = \"zrange command reply type is not array\";\n                goto error;\n            }\n            if (reply->elements == 0) {\n                not_exist = 1;\n            }\n        } else if (cunit->key_type == REDIS_HASH) {\n            if (reply->type != REDIS_REPLY_ARRAY) {\n                errmsg = \"hgetall command reply type is not array\";\n                goto error;\n            }\n            if (reply->elements == 0) {\n                not_exist = 1;\n            }\n        } else {\n            errmsg = \"not supported key type\";\n            goto error;\n        }\n\n        if (not_exist) {\n            cunit->not_exist_count ++;\n        } else {\n            reply_clone = steal_hiredis_redisreply(reply);\n            elem = darray_push(&cunit->replys);\n            *elem = reply_clone;\n            cunit->replys_count ++;\n        }\n\n        if (cunit->not_exist_count >= cunit->servers_count) {\n            /* The key in all the target group is expired */\n            goto done;\n        } else if (cunit->replys_count >= (cunit->servers_count-cunit->not_exist_count)) {\n            if (cunit->not_exist_count > 0 && cunit->key_persist) {\n                errmsg = \"key is persist, but not exist in some servers\";\n                goto error;\n            }\n        \n            if (check_replys_if_same(cunit) != 1) {\n                errmsg = \"values for reply are not same\";\n                goto error;\n            }\n            \n            goto done;\n        }\n\n        return;\n    }\n\ndone:\n\n    check_unit_destroy(cunit);\n    \n    return;\n    \nnext_step:\n\n    cunit->replys_count = 0;\n    cunit->not_exist_count = 0;\n    while (darray_n(&cunit->replys) > 0) {\n        elem = darray_pop(&cunit->replys);\n        freeReplyObject(*elem);\n    }\n    \n    return;\n    \nerror:\n\n    log_hexdump(LOG_ERR,cunit->key,sdslen(cunit->key),\n        \"%s, scan group id: %d, key(len:%zu, type: %s): \", \n        errmsg, \n        cdt->scan_group_idx, \n        sdslen(cunit->key),get_key_type_string(cunit->key_type));\n    \n    check_unit_destroy(cunit);\n}\n\nstatic int start_check_data(char *key, size_t keylen, check_data_thread *cdt)\n{\n    check_unit *cu = check_unit_create();\n    int j;\n\n    cu->cdt = cdt;\n    cu->key = sdsnewlen(key,keylen);\n    dlistPush(cdt->check_units,cu);\n    cu->lnode = dlistLast(cdt->check_units);\n\n    for (j = 0; j < darray_n(cdt->abgs); j ++) {\n        abtest_group *abg = darray_get(cdt->abgs, j);\n        abtest_server *abs = abg->get_backend_server(abg,key,keylen);\n        abtest_server **elem = darray_push(&cu->servers);\n        conn_context *cc = darray_get(abs->conn_contexts, 0);\n        char *argv[2];\n        size_t argvlen[2];\n        \n        *elem = abs;\n        cu->servers_count ++;\n\n        /* Check step 1: get the expire */\n        argv[0] = \"ttl\";\n        argvlen[0] = 3;\n        argv[1] = key;\n        argvlen[1] = keylen;\n        redisAsyncCommandArgv(cc->actx, check_data_callback, \n            cu, 2, argv, argvlen);\n    }\n    cu->state = CHECK_UNIT_STATE_GET_EXPIRE;\n\n    return VRT_OK;\n}\n\nstatic void scan_for_check_callback(redisAsyncContext *c, void *r, void *privdata) {\n    redisReply *reply = r, *reply_sub, *reply_elem;\n    abtest_server *abs = privdata;\n    check_data_thread *cdt = abs->data;\n    conn_context *cc;\n    long long value;\n    size_t k;\n    \n    if (reply == NULL) return;\n\n\n    if (reply->type != REDIS_REPLY_ARRAY) {\n        return;\n    }\n\n    if (reply->elements != 2) {\n        return;\n    }\n\n    reply_sub = reply->element[0];\n    if (reply_sub->type != REDIS_REPLY_STRING || \n        string2ll(reply_sub->str,reply_sub->len,&value) != 1) {\n        return;\n    }\n\n    cdt->cursor = value;\n\n    reply_sub = reply->element[1];\n    if (reply_sub->type != REDIS_REPLY_ARRAY) {\n        return;\n    }\n\n    for (k = 0; k < reply_sub->elements; k ++) {\n        reply_elem = reply_sub->element[k];\n        if (reply_elem->type != REDIS_REPLY_STRING) {\n            return;\n        }\n\n        start_check_data(reply_elem->str,reply_elem->len,cdt);\n    }\n\n    cdt->scan_keys_count += reply_sub->elements;\n\n    if (cdt->cursor == 0) {\n        cdt->scan_finished_count ++;\n    }\n}\n\nstatic int check_data_threads_finished_count = 0;\nstatic void one_check_data_thread_finished(void)\n{\n    update_state_add(check_data_threads_finished_count,1);\n}\n\nstatic int all_check_data_threads_finished(void)\n{\n    int finished_count;\n    update_state_get(check_data_threads_finished_count,&finished_count);\n\n    if (finished_count >= darray_n(cdts)) {\n        return 1;\n    }\n\n    return 0;\n}\n\nstatic int check_data_thread_cron(aeEventLoop *eventLoop, long long id, void *clientData)\n{\n    check_data_thread *cdt = clientData;\n\n    ASSERT(eventLoop == cdt->el);\n\n    if (cdt->scan_finished_count >= darray_n(cdt->scan_servers)) {\n        if (dlistLength(cdt->check_units) == 0) {\n            aeStop(cdt->el);\n            one_check_data_thread_finished();\n            log_debug(LOG_NOTICE, \"One check thread finished,scaned %lld keys\",\n                cdt->scan_keys_count);\n            return 1;\n        }\n    } else if (dlistLength(cdt->check_units) < 3000) {\n        abtest_group *abg;\n        abtest_server **abs;\n        int *idx;\n        conn_context *cc;\n        \n        abg = darray_get(cdt->abgs, cdt->scan_group_idx);\n        abs = darray_get(cdt->scan_servers, cdt->scan_finished_count);\n        cc = darray_get((*abs)->conn_contexts, 0);\n\n        redisAsyncCommand(cc->actx, scan_for_check_callback, \n            *abs, \"scan %lld count 1000\", cdt->cursor);\n    }\n\n    cdt->cronloops ++;\n    return 1000/cdt->hz;\n}\n\nstatic int check_data_thread_init(check_data_thread *cdt, char *test_target_groups)\n{\n    int i, j, k;\n\n    cdt->id = 0;\n    cdt->thread_id = 0;\n    cdt->el = NULL;\n    cdt->hz = 200;\n    cdt->cronloops = 0;\n    \n    cdt->abgs = NULL;\n    cdt->scan_group_idx = 0;\n    cdt->scan_servers = NULL;\n    cdt->scan_finished_count = 0;\n    cdt->cursor = 0;\n    cdt->check_units = NULL;\n\n    cdt->check_begin_time = 0;\n    cdt->scan_keys_count = 0;\n\n    cdt->el = aeCreateEventLoop(200);\n    if (cdt->el == NULL) {\n        return VRT_ERROR;\n    }\n\n    cdt->scan_servers = darray_create(1,sizeof(abtest_server*));\n    \n    cdt->abgs = abtest_groups_create(test_target_groups);\n    if (cdt->abgs == NULL) {\n        return VRT_ERROR;\n    }\n\n    /* Init connection context for each server */\n    for (i = 0; i < darray_n(cdt->abgs); i ++) {\n        abtest_group *abg = darray_get(cdt->abgs, i);\n        for (j = 0; j < darray_n(&abg->abtest_servers); j ++) {\n            abtest_server *abs = darray_get(&abg->abtest_servers, j);\n            abs->conn_contexts = darray_create(1, sizeof(conn_context));\n            for (k = 0; k < 1; k ++) {\n                conn_context *cc = darray_push(abs->conn_contexts);\n                if (check_conn_context_init(cc,abs->host,abs->port) != VRT_OK) {\n                    return VRT_ERROR;\n                }\n                cc->actx->data = cdt;\n                redisAeAttach(cdt->el, cc->actx);\n                redisAsyncSetConnectCallback(cc->actx,connect_callback);\n                redisAsyncSetDisconnectCallback(cc->actx,disconnect_callback);\n            }\n        }\n    }\n\n    if (aeCreateTimeEvent(cdt->el, 1, check_data_thread_cron, cdt, NULL) == AE_ERR) {\n        return VRT_ERROR;\n    }\n\n    cdt->check_units = dlistCreate();\n    \n    return VRT_OK;\n}\n\nstatic void check_data_thread_deinit(check_data_thread *cdt)\n{\n    if (cdt->el) {\n        aeDeleteEventLoop(cdt->el);\n        cdt->el = NULL;\n    }\n\n    if (cdt->scan_servers) {\n        while (darray_n(cdt->scan_servers) > 0) {\n            darray_pop(cdt->scan_servers);\n        }\n        darray_destroy(cdt->scan_servers);\n        cdt->scan_servers = NULL;\n    }\n\n    if (cdt->abgs) {\n        int i, j, k;\n        /* Deinit connection context for each server */\n        for (i = 0; i < darray_n(cdt->abgs); i ++) {\n            abtest_group *abg = darray_get(cdt->abgs, i);\n            for (j = 0; j < darray_n(&abg->abtest_servers); j ++) {\n                abtest_server *abs = darray_get(&abg->abtest_servers, j);\n                while (darray_n(abs->conn_contexts) > 0) {\n                    conn_context *cc = darray_pop(abs->conn_contexts);\n                    check_conn_context_deinit(cc);\n                }\n            }\n        }\n        \n        abtest_groups_destroy(cdt->abgs);\n        cdt->abgs = NULL;\n    }\n\n    if (cdt->check_units) {\n        while (dlistLength(cdt->check_units) > 0) {\n            check_unit *cu = dlistPop(cdt->check_units);\n            check_unit_destroy(cu);\n        }\n        dlistRelease(cdt->check_units);\n        cdt->check_units = NULL;\n    }\n}\n\nstatic int checking_data;\nstatic int checking_data_or_not(void)\n{\n    int checking;\n\n    update_state_get(checking_data,&checking);\n\n    if (checking) return 1;\n    else return 0;\n}\n\nstatic int check_data_threads_count = 8;\nstatic void destroy_check_data_threads(void);\n/* return value :\n * -1: error\n * 0 : ok\n * 1 : not need */\nstatic int create_check_data_threads(void)\n{\n    darray *abgs = NULL;\n    abtest_group *abg;\n    int groups_count;\n    int threads_count_per_group;\n    int check_thread_id = 0;\n    int i, j, k;\n    \n    if (cdts != NULL) {\n        destroy_check_data_threads();\n    }\n\n    cdts = darray_create(2,sizeof(check_data_thread));\n    if (cdts == NULL) {\n        return -1;\n    }\n\n    abgs = abtest_groups_create(dc.test_target_groups);\n    if (abgs == NULL) {\n        return -1;\n    }\n\n    groups_count = darray_n(abgs);\n    if (groups_count == 1) {\n        abtest_groups_destroy(abgs);\n        return 1;\n    }\n\n    threads_count_per_group = check_data_threads_count/groups_count;\n    if (threads_count_per_group <= 0) {\n        threads_count_per_group = 1;\n    }\n    \n    for (i = 0; i < groups_count; i ++) {\n        int servers_count, threads_count;\n        int servers_count_per_thread;\n        int server_idx = 0;\n\n        threads_count = threads_count_per_group;\n        abg = darray_get(abgs, i);\n        servers_count = darray_n(&abg->abtest_servers);\n        servers_count_per_thread = servers_count/threads_count;\n        if (servers_count_per_thread == 0) {\n            servers_count_per_thread = 1;\n            threads_count = servers_count;\n        }\n        for (j = 0; j < threads_count; j ++) {\n            abtest_server *abs;\n            \n            check_data_thread *cdt = darray_push(cdts);\n            check_data_thread_init(cdt,dc.test_target_groups);\n            cdt->id = check_thread_id++;\n            cdt->scan_group_idx = i;\n\n            abg = darray_get(cdt->abgs, cdt->id);\n            \n            for (k = 0; k < servers_count_per_thread; k ++) {\n                abtest_server **elem = darray_push(cdt->scan_servers);\n                abs = darray_get(&abg->abtest_servers, server_idx++);\n                abs->data = cdt;\n                *elem = abs;\n            }\n\n            if (j == threads_count-1) {\n                while (server_idx < servers_count) {\n                    abtest_server **elem = darray_push(cdt->scan_servers);\n                    abs = darray_get(&abg->abtest_servers, server_idx++);\n                    abs->data = cdt;\n                    *elem = abs;\n                }\n            }\n        }\n    }\n    \n    abtest_groups_destroy(abgs);\n    \n    return 0;\n}\n\nstatic void destroy_check_data_threads(void)\n{\n    if (cdts != NULL) {\n        while (darray_n(cdts) > 0) {\n            check_data_thread *cdt = darray_pop(cdts);\n            check_data_thread_deinit(cdt);\n        }\n        darray_destroy(cdts);\n        cdts = NULL;\n    }\n}\n\nstatic void *check_data_thread_run(void *args)\n{\n    check_data_thread *cdt = args;\n    \n    srand(vrt_usec_now()^(int)pthread_self());\n\n    aeMain(cdt->el);\n    \n    return NULL;\n}\n\nstatic int start_check_data_threads(void)\n{\n    int j;\n    check_data_thread *cdt;\n\n    if (cdts == NULL) return VRT_ERROR;\n\n    for (j = 0; j < darray_n(cdts); j ++) {\n        pthread_attr_t attr;\n        \n        cdt = darray_get(cdts, j);\n        pthread_attr_init(&attr);\n        pthread_create(&cdt->thread_id, \n            &attr, check_data_thread_run, cdt);  \n    }\n    \n    return VRT_OK;\n}\n\nstatic int begin_check_data(void)\n{\n    create_check_data_threads();\n    start_check_data_threads();\n    \n    update_state_set(checking_data,1);\n\n    return VRT_OK;\n}\n\nstatic void end_check_data(void)\n{\n    update_state_set(check_data_threads_finished_count,0);\n    update_state_set(checking_data,0);\n}\n\nstatic int data_checker_cron(aeEventLoop *eventLoop, long long id, void *clientData)\n{\n    ASSERT(eventLoop == dc.el);\n\n    if (!test_if_need_pause() && vrt_sec_now()-last_test_begin_time > test_interval) {\n        test_need_to_pause();\n        log_notice(\"Start pause the test...\");\n    }\n\n    if (!checking_data_or_not() && test_if_need_pause() && \n        all_threads_paused()) {\n        \n        log_notice(\"Finished pause the test. Tested %lld commands, %lld error reply(%.2f%%).\", \n            get_total_tested_commands_count_per_cycle(),\n            get_total_reply_err_count_per_cycle(),\n            (float)get_total_reply_err_count_per_cycle()/(float)get_total_tested_commands_count_per_cycle()*100);\n        reset_total_count_per_cycle();\n        sleep(1);\n        last_check_begin_time = vrt_sec_now();\n        begin_check_data();\n        log_notice(\"Start checking the data...\");\n    }\n\n    if (checking_data_or_not() && all_check_data_threads_finished()) {\n        end_check_data();\n        log_notice(\"Finished checking the data\\n\");\n        test_can_continue();\n        last_test_begin_time = vrt_sec_now();\n    }\n\n    dc.cronloops ++;\n    return 1000/dc.hz;\n}\n\nint vrt_data_checker_init(char *checker, char *test_target_groups)\n{\n    int ret;\n    \n    dc.thread_id = 0;\n    dc.el = NULL;\n    dc.hz = 10;\n    dc.cronloops = 0;\n    dc.test_target_groups = NULL;\n    dc.flags = CHECK_DATA_FLAG_NONE;\n    dc.checker = NULL;\n    dc.master = NULL;\n    dc.check_begin_time = 0;\n    \n    dc.el = aeCreateEventLoop(10);\n    if (dc.el == NULL) {\n        return VRT_ERROR;\n    }\n\n    if (aeCreateTimeEvent(dc.el, 1, data_checker_cron, NULL, NULL) == AE_ERR) {\n        return VRT_ERROR;\n    }\n\n    dc.test_target_groups = sdsnew(test_target_groups);\n\n    dc.checker = sdsnew(checker);\n\n    if (!strcasecmp(checker,\"myself\")) {\n        dc.flags |= CHECK_DATA_FLAG_MASTER;\n    } else {\n        sds host;\n        int port;\n        dc.flags |= CHECK_DATA_FLAG_SLAVE;\n        host = get_host_port_from_address_string(checker, &port);\n        if (host == NULL) {\n            return VRT_ERROR;\n        }\n        dc.master = malloc(sizeof(conn_context));\n        ret = check_conn_context_init(dc.master, host, port);\n        sdsfree(host);\n        if (ret != VRT_OK) {\n            return VRT_ERROR;\n        }\n    }\n\n    return VRT_OK;\n}\n\nvoid vrt_data_checker_deinit(void)\n{\n    if (dc.el) {\n        aeDeleteEventLoop(dc.el);\n        dc.el = NULL;\n    }\n\n    if (dc.test_target_groups) {\n        sdsfree(dc.test_target_groups);\n        dc.test_target_groups = NULL;\n    }\n\n    if (dc.checker) {\n        sdsfree(dc.checker);\n        dc.checker = NULL;\n    }\n\n    if (dc.master) {\n        check_conn_context_deinit(dc.master);\n        free(dc.master);\n        dc.master = NULL;\n    }\n\n    destroy_check_data_threads();\n}\n\nstatic void *vrt_data_checker_run(void *args)\n{\n    srand(vrt_usec_now()^(int)pthread_self());\n\n    aeMain(dc.el);\n    \n    return NULL;\n}\n\nint vrt_start_data_checker(void)\n{\n    pthread_attr_t attr;\n    pthread_attr_init(&attr);\n    pthread_create(&dc.thread_id, \n        &attr, vrt_data_checker_run, NULL);   \n    return VRT_OK;\n}\n\nint vrt_wait_data_checker(void)\n{\n\tpthread_join(dc.thread_id, NULL);\n   \n    return VRT_OK;\n}\n\nstatic int test_need_pause = 0;\n\nint test_if_need_pause(void)\n{\n    int need_pause;\n\n    update_state_get(test_need_pause,&need_pause);\n\n    if (need_pause) return 1;\n    else return 0;\n}\n\nvoid test_can_continue(void)\n{\n     update_state_set(test_need_pause,0);\n     update_state_set(produce_threads_pause_finished_count,0);\n     update_state_set(dispatch_threads_pause_finished_count,0);\n     update_state_set(backend_threads_pause_finished_count,0);\n}\n\nvoid test_need_to_pause(void)\n{\n    update_state_set(test_need_pause,1);\n}\n\nvoid one_produce_thread_paused(void)\n{\n    update_state_add(produce_threads_pause_finished_count,1);\n}\n\nvoid one_dispatch_thread_paused(void)\n{\n    update_state_add(dispatch_threads_pause_finished_count,1);\n}\n\nvoid one_backend_thread_paused(void)\n{\n    update_state_add(backend_threads_pause_finished_count,1);\n}\n\nint all_produce_threads_paused(void)\n{\n    int paused_threads;\n\n    update_state_get(produce_threads_pause_finished_count,&paused_threads);\n    if (paused_threads < produce_data_threads_count) {\n        return 0;\n    }\n\n    return 1;\n}\n\nint all_dispatch_threads_paused(void)\n{\n    int paused_threads;\n\n    update_state_get(dispatch_threads_pause_finished_count,&paused_threads);\n    if (paused_threads < dispatch_data_threads_count) {\n        return 0;\n    }\n\n    return 1;\n}\n\nint all_backend_threads_paused(void)\n{\n    int paused_threads;\n\n    update_state_get(backend_threads_pause_finished_count,&paused_threads);\n    if (paused_threads < backend_threads_count) {\n        return 0;\n    }\n\n    return 1;\n}\n\nint all_threads_paused(void)\n{\n    int paused_threads;\n\n    update_state_get(produce_threads_pause_finished_count,&paused_threads);\n    if (paused_threads < produce_data_threads_count) {\n        return 0;\n    }\n\n    update_state_get(dispatch_threads_pause_finished_count,&paused_threads);\n    if (paused_threads < dispatch_data_threads_count) {\n        return 0;\n    }\n\n    update_state_get(backend_threads_pause_finished_count,&paused_threads);\n    if (paused_threads < backend_threads_count) {\n        return 0;\n    }\n\n    return 1;\n}\n"
  },
  {
    "path": "tests/vrt_check_data.h",
    "content": "#ifndef _VRT_CHECK_DATA_H_\n#define _VRT_CHECK_DATA_H_\n\nint vrt_data_checker_init(char *checker, char *test_target_groups);\nvoid vrt_data_checker_deinit(void);\n\nint vrt_start_data_checker(void);\nint vrt_wait_data_checker(void);\n\nint test_if_need_pause(void);\nvoid test_can_continue(void);\nvoid test_need_to_pause(void);\n\nvoid one_produce_thread_paused(void);\nvoid one_dispatch_thread_paused(void);\nvoid one_backend_thread_paused(void);\n\nint all_produce_threads_paused(void);\nint all_dispatch_threads_paused(void);\nint all_backend_threads_paused(void);\nint all_threads_paused(void);\n\n#endif\n"
  },
  {
    "path": "tests/vrt_dispatch_data.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <getopt.h>\n#include <fcntl.h>\n#include <errno.h>\n#include <signal.h>\n#include <sys/stat.h>\n#include <sys/utsname.h>\n\n#include <hiredis.h>\n#include <async.h>\n#include <adapters/ae.h>\n\n#include <dhashkit.h>\n#include <dlist.h>\n#include <dmtqueue.h>\n#include <dlog.h>\n\n#include <vrt_util.h>\n#include <vrt_public.h>\n#include <vrabtest.h>\n#include <vrt_dispatch_data.h>\n#include <vrt_produce_data.h>\n\nint dispatch_data_threads_count;\nstatic darray *dispatch_data_threads = NULL;\n\nint dispatch_threads_pause_finished_count;\n\nstatic long long total_tested_commands_count_per_cycle = 0;\nstatic long long total_reply_err_count_per_cycle = 0;\nstatic long long total_tested_commands_count = 0;\nstatic long long total_reply_err_count = 0;\n\nlong long get_total_tested_commands_count_per_cycle(void)\n{\n    long long count;\n    update_state_get(total_tested_commands_count_per_cycle,&count);\n    return count;\n}\n\nlong long get_total_reply_err_count_per_cycle(void)\n{\n    long long count;\n    update_state_get(total_reply_err_count_per_cycle,&count);\n    return count;\n}\n\nvoid reset_total_count_per_cycle(void)\n{\n    update_state_set(total_tested_commands_count_per_cycle,0);\n    update_state_set(total_reply_err_count_per_cycle,0);\n}\n\ntypedef struct reply_unit {\n    int total_count;\n    int received_count;\n    redisReply **replys;\n    data_unit *du;\n} reply_unit;\n\nstatic void show_replys_inconsistency_msg(data_unit *du, redisReply *reply1, redisReply *reply2)\n{\n    int *keyindex, numkeys;\n    sds key = NULL;\n\n    keyindex = get_keys_from_data_producer(du->dp, du->argv, du->argc, &numkeys);\n    if (numkeys > 0) {\n        key = du->argv[keyindex[0]];\n    }\n    free(keyindex);\n\n    if (key) {\n        log_hexdump(LOG_ERR,key,sdslen(key),\n            \"%s command replys are inconsistency, \"\n            \"reply type: %d %d, \"\n            \"reply status: %s %s, \"\n            \"reply error: %s %s, \"\n            \"reply integer: %lld %lld, \"\n            \"reply array len: %zu %zu, \"\n            \"key(len:%zu): \", \n            du->dp->name, \n            reply1->type, reply2->type, \n            reply1->type==REDIS_REPLY_STATUS?reply1->str:\"NULL\",\n            reply2->type==REDIS_REPLY_STATUS?reply2->str:\"NULL\",\n            reply1->type==REDIS_REPLY_ERROR?reply1->str:\"NULL\",\n            reply2->type==REDIS_REPLY_ERROR?reply2->str:\"NULL\",\n            reply1->type==REDIS_REPLY_INTEGER?reply1->integer:0,\n            reply2->type==REDIS_REPLY_INTEGER?reply2->integer:0,\n            reply1->type==REDIS_REPLY_ARRAY?reply1->elements:0,\n            reply2->type==REDIS_REPLY_ARRAY?reply2->elements:0,\n            sdslen(key));\n    } else {\n        log_error(\"%s command replys are inconsistency, \"\n            \"reply type: %d %d, \"\n            \"reply status: %s %s, \"\n            \"reply error: %s %s, \"\n            \"reply integer: %lld %lld, \"\n            \"reply array len: %zu %zu\",\n            du->dp->name,\n            reply1->type, reply2->type, \n            reply1->type==REDIS_REPLY_STATUS?reply1->str:\"NULL\",\n            reply2->type==REDIS_REPLY_STATUS?reply2->str:\"NULL\",\n            reply1->type==REDIS_REPLY_ERROR?reply1->str:\"NULL\",\n            reply2->type==REDIS_REPLY_ERROR?reply2->str:\"NULL\",\n            reply1->type==REDIS_REPLY_INTEGER?reply1->integer:0,\n            reply2->type==REDIS_REPLY_INTEGER?reply2->integer:0,\n            reply1->type==REDIS_REPLY_ARRAY?reply1->elements:0,\n            reply2->type==REDIS_REPLY_ARRAY?reply2->elements:0);\n    }\n\n}\n\nstatic int sort_replys_if_needed(reply_unit *ru)\n{\n    data_unit *du = ru->du;\n    data_producer *dp = du->dp;\n    int step = 0, idx_cmp = 0;\n\n    if (dp->cmd_type&TEST_CMD_TYPE_SET) {\n        if (!strcmp(dp->name,\"smembers\") || \n            !strcmp(dp->name,\"sunion\") || \n            !strcmp(dp->name,\"sdiff\") || \n            !strcmp(dp->name,\"sinter\")) {\n            step = 1;\n        }\n    } else if (dp->cmd_type&TEST_CMD_TYPE_HASH) {\n        if (!strcmp(dp->name,\"hkeys\") || \n            !strcmp(dp->name,\"hvals\")) {\n            step = 1;\n        } else if (!strcmp(dp->name,\"hgetall\")) {\n            step = 2;\n            idx_cmp = 0;\n        }\n    }\n\n    if (step > 0) {\n        int i;\n        redisReply *reply;\n        for (i = 0; i < ru->received_count; i ++) {\n            reply = ru->replys[i];\n            if (reply->type != REDIS_REPLY_ARRAY)\n                continue;\n            sort_array_by_step(reply->element, reply->elements, \n                step, idx_cmp, reply_string_binary_compare);\n        }\n    }\n    \n    return VRT_OK;\n}\n\nstatic int check_replys_if_same(reply_unit *ru)\n{\n    int j;\n    redisReply **replys = ru->replys;\n    redisReply *replyb, *reply;\n\n    sort_replys_if_needed(ru);\n\n    replyb = replys[0];\n    \n    for (j = 1; j < ru->total_count ; j ++) {\n        reply = replys[j];\n        if (check_two_replys_if_same(replyb, reply)) {\n            show_replys_inconsistency_msg(ru->du, replyb, reply);\n            return 0;\n        }\n    }\n    \n    return 1;\n}\n\nstruct callback_data {\n    dispatch_data_thread *ddt;\n    reply_unit *ru;\n    int idx;\n};\n\nstatic void reply_callback(redisAsyncContext *c, void *r, void *privdata) {\n    int ret;\n    redisReply *reply;\n    struct callback_data *cbd = privdata;\n    dispatch_data_thread *ddt = cbd->ddt;\n    reply_unit *ru = cbd->ru;\n    \n    if (r == NULL) {\n        reply = NULL;\n    } else {\n        /* Beacause reply will be freed by hiredis in async way. */\n        reply = steal_hiredis_redisreply(r);\n    }\n\n    ru->replys[cbd->idx] = reply;\n    ru->received_count ++;\n    free(cbd);\n\n    if (ru->received_count >= ru->total_count) {\n        int j;\n        \n        ret = check_replys_if_same(ru);\n        if (ret == 1 && reply != NULL) {\n            data_unit *du = ru->du;\n            data_producer *dp = du->dp;\n            if (reply->type == REDIS_REPLY_ERROR) {\n                ddt->reply_type_err_count_per_cycle++;\n            }\n\n            /* Cache this key if needed. */\n            if (dp->need_cache_key_proc != NULL) {\n                produce_scheme *ps = du->data;\n                if (dp->need_cache_key_proc(reply)) {\n                    key_cache_array *kcp = kcp_get_from_ps(ps, dp);\n                    sds key = get_one_key_from_data_unit(du);\n                    key_cache_array_input(kcp,key,sdslen(key));\n                }\n            }\n        }\n\n        /* release the reply_unit */\n        for (j = 0; j < ru->total_count; j ++) {\n            freeReplyObject(ru->replys[j]);\n            ru->replys[j] = NULL;\n        }\n        free(ru->replys);\n        data_unit_put(ru->du);\n        free(ru);\n        \n        ddt->count_wait_for_reply --;\n        ASSERT(ddt->count_wait_for_reply >= 0);\n        \n        ddt->reply_total_count_per_cycle++;\n    }\n}\n\nstatic int dispatch_thread_send_data(dispatch_data_thread *ddt)\n{\n    int count_per_time = 1000;\n    data_unit *du;\n\n    while ((du = dmtqueue_pop(ddt->datas)) != NULL) {\n        redisAsyncContext *actx;\n        int j;\n        \n        size_t *argvlen = malloc(du->argc*sizeof(size_t));\n        reply_unit *ru = malloc(sizeof(reply_unit));\n        ru->du = du;\n        ru->total_count = darray_n(ddt->abgs);\n        ru->received_count = 0;\n        ru->replys = malloc(ru->total_count*sizeof(redisReply *));\n        for (j = 0; j < du->argc; j ++) {\n            argvlen[j] = sdslen(du->argv[j]);\n        }\n        for (j = 0; j < darray_n(ddt->abgs); j ++) {\n            struct callback_data *cbd;\n            int *keyindex, numkeys;\n            abtest_server *abs;\n\n            cbd = malloc(sizeof(struct callback_data));\n            cbd->ddt = ddt;\n            cbd->ru = ru;\n            cbd->idx = j;\n            abtest_group *abg = darray_get(ddt->abgs, j);\n\n            keyindex = get_keys_from_data_producer(du->dp, du->argv, du->argc, &numkeys);\n            if (numkeys == 0) {\n                unsigned int idx;\n                idx = (unsigned int)rand()%darray_n(&abg->abtest_servers);\n                abs = darray_get(&abg->abtest_servers,idx);\n            } else {\n                sds key = du->argv[keyindex[0]];\n                abs = abg->get_backend_server(abg,key,sdslen(key));\n            }\n            free(keyindex);\n            \n            conn_context *cc = darray_get(abs->conn_contexts, \n                du->hashvalue%darray_n(abs->conn_contexts));\n            actx = cc->actx;\n            redisAsyncCommandArgv(actx, reply_callback, cbd, du->argc, du->argv, argvlen);\n        }\n        free(argvlen);\n\n        ddt->count_wait_for_reply ++;\n        \n        if (count_per_time-- <= 0) break;\n    }\n\n    return VRT_OK;\n}\n\nstatic int dispatch_data_thread_cron(aeEventLoop *eventLoop, long long id, void *clientData)\n{\n    dispatch_data_thread *ddt = clientData;\n\n    ASSERT(eventLoop == ddt->el);\n\n    /* At the begin of this loop */\n    if (ddt->pause) {\n        if (!test_if_need_pause()) {\n            ddt->pause = 0;\n        } else {\n            ddt->cronloops ++;\n            return 1000;\n        }\n    }\n\n    if (ddt->count_wait_for_reply < 4000 && \n        !dmtqueue_empty(ddt->datas)) {\n        dispatch_thread_send_data(ddt);\n    }\n\n    /* At the end of this loop */\n    if (test_if_need_pause() && \n        all_produce_threads_paused() && \n        all_backend_threads_paused() && \n        dmtqueue_empty(ddt->datas) &&\n        dlistLength(ddt->rdatas) == 0) {\n        \n        ddt->pause = 1;\n\n        /* Update the dispatch state */\n        update_state_add(total_tested_commands_count_per_cycle,\n            ddt->reply_total_count_per_cycle);\n        update_state_add(total_tested_commands_count,\n            ddt->reply_total_count_per_cycle);\n        ddt->reply_total_count_per_cycle = 0;\n        update_state_add(total_reply_err_count_per_cycle,\n            ddt->reply_type_err_count_per_cycle);\n        update_state_add(total_reply_err_count,\n            ddt->reply_type_err_count_per_cycle);\n        ddt->reply_type_err_count_per_cycle = 0;\n\n        one_dispatch_thread_paused();\n    }\n\n    ddt->cronloops ++;\n    return 1000/ddt->hz;\n}\n\nstatic void connect_callback(const redisAsyncContext *c, int status) {\n    dispatch_data_thread *ddt = c->data;\n    if (status != REDIS_OK) {\n        log_error(\"Error: %s\\n\", c->errstr);\n        //aeStop(loop);\n        return;\n    }\n\n    //test_log_out(\"Connected...\\n\");\n}\n\nstatic void disconnect_callback(const redisAsyncContext *c, int status) {\n    dispatch_data_thread *ddt = c->data;\n    if (status != REDIS_OK) {\n        log_error(\"Error: %s\\n\", c->errstr);\n        //aeStop(loop);\n        return;\n    }\n\n    //test_log_out(\"Disconnected...\\n\");\n    //aeStop(loop);\n}\n\nstatic int dispatch_conn_context_init(conn_context *cc, char *host, int port)\n{\n    cc->ctx = NULL;\n    cc->actx = NULL;\n\n    cc->actx = redisAsyncConnect(host, port);\n    if (cc->actx == NULL) {\n        return VRT_ERROR;\n    }\n    \n    return VRT_OK;\n}\n\nstatic void dispatch_conn_context_deinit(conn_context *cc)\n{\n    if (cc->ctx) {\n        redisFree(cc->ctx);\n        cc->ctx == NULL;\n    }\n\n    if (cc->actx) {\n        redisAsyncFree(cc->actx);\n        cc->actx == NULL;\n    }\n}\n\nstatic int dispatch_data_thread_init(dispatch_data_thread *ddt, char *test_target_groups, int connections)\n{\n    int i, j, k;\n\n    ddt->id = 0;\n    ddt->thread_id = 0;\n    ddt->el = NULL;\n    ddt->hz = 10;\n    ddt->cronloops = 0;\n    ddt->datas = NULL;\n    ddt->rdatas = NULL;\n    ddt->abgs = NULL;\n    ddt->pause = 0;\n    ddt->count_wait_for_reply = 0;\n    ddt->reply_total_count_per_cycle = 0;\n    ddt->reply_type_err_count_per_cycle = 0;\n\n    ddt->el = aeCreateEventLoop(200);\n    if (ddt->el == NULL) {\n        return VRT_ERROR;\n    }\n\n    ddt->datas = dmtqueue_create();\n    if (ddt->datas == NULL) {\n        return VRT_ERROR;\n    }\n\n    if (dmtqueue_init_with_lockqueue(ddt->datas, NULL) != 0) {\n        return VRT_ERROR;\n    }\n\n    ddt->rdatas = dlistCreate();\n    if (ddt->rdatas == NULL) {\n        return VRT_ERROR;\n    }\n    \n    ddt->abgs = abtest_groups_create(test_target_groups);\n    if (ddt->abgs == NULL) {\n        return VRT_ERROR;\n    }\n\n    /* Init connection context for each server */\n    for (i = 0; i < darray_n(ddt->abgs); i ++) {\n        abtest_group *abg = darray_get(ddt->abgs, i);\n        for (j = 0; j < darray_n(&abg->abtest_servers); j ++) {\n            abtest_server *abs = darray_get(&abg->abtest_servers, j);\n            abs->conn_contexts = darray_create(connections, sizeof(conn_context));\n            for (k = 0; k < connections; k ++) {\n                conn_context *cc = darray_push(abs->conn_contexts);\n                if (dispatch_conn_context_init(cc,abs->host,abs->port) != VRT_OK) {\n                    return VRT_ERROR;\n                }\n                cc->actx->data = ddt;\n                redisAeAttach(ddt->el, cc->actx);\n                redisAsyncSetConnectCallback(cc->actx,connect_callback);\n                redisAsyncSetDisconnectCallback(cc->actx,disconnect_callback);\n            }\n        }\n    }\n\n    if (aeCreateTimeEvent(ddt->el, 1, dispatch_data_thread_cron, ddt, NULL) == AE_ERR) {\n        return VRT_ERROR;\n    }\n    \n    return VRT_OK;\n}\n\nstatic void dispatch_data_thread_deinit(dispatch_data_thread *ddt)\n{\n    if (ddt->el) {\n        aeDeleteEventLoop(ddt->el);\n        ddt->el = NULL;\n    }\n\n    if (ddt->datas) {\n        dmtqueue_destroy(ddt->datas);\n        ddt->datas = NULL;\n    }\n\n    if (ddt->abgs) {\n        int i, j, k;\n        /* Deinit connection context for each server */\n        for (i = 0; i < darray_n(ddt->abgs); i ++) {\n            abtest_group *abg = darray_get(ddt->abgs, i);\n            for (j = 0; j < darray_n(&abg->abtest_servers); j ++) {\n                abtest_server *abs = darray_get(&abg->abtest_servers, j);\n                while (darray_n(abs->conn_contexts) > 0) {\n                    conn_context *cc = darray_pop(abs->conn_contexts);\n                    dispatch_conn_context_deinit(cc);\n                }\n            }\n        }\n        \n        abtest_groups_destroy(ddt->abgs);\n        ddt->abgs = NULL;\n    }\n}\n\nint vrt_dispatch_data_init(int threads_count, char *test_target_groups, int connections)\n{\n    int j;\n    \n    dispatch_data_threads_count = threads_count;\n    dispatch_data_threads = darray_create(threads_count, sizeof(dispatch_data_thread));\n    if (dispatch_data_threads == NULL) {\n        return VRT_ERROR;\n    }\n\n    for (j = 0; j < threads_count; j ++) {\n        dispatch_data_thread *ddt = darray_push(dispatch_data_threads);\n        if (dispatch_data_thread_init(ddt, test_target_groups, connections) != VRT_OK) {\n            return VRT_ERROR;\n        }\n        ddt->id = j;\n    }\n    \n    return VRT_OK;\n}\n\nvoid vrt_dispatch_data_deinit(void)\n{\n    if (dispatch_data_threads) {\n        while (darray_n(dispatch_data_threads) > 0) {\n            dispatch_data_thread *ddt = darray_pop(dispatch_data_threads);\n            dispatch_data_thread_deinit(ddt);\n        }\n        darray_destroy(dispatch_data_threads);\n        dispatch_data_threads = NULL;\n    }\n}\n\nstatic void *vrt_dispatch_data_thread_run(void *args)\n{\n    dispatch_data_thread *ddt = args;\n    srand(vrt_usec_now()^(int)pthread_self());\n\n    aeMain(ddt->el);\n    \n    return NULL;\n}\n\nint vrt_start_dispatch_data(void)\n{\n    unsigned int i;\n    for (i = 0; i < darray_n(dispatch_data_threads); i ++) {\n        pthread_attr_t attr;\n        dispatch_data_thread *ddt;\n        pthread_attr_init(&attr);\n        ddt = darray_get(dispatch_data_threads, i);\n        pthread_create(&ddt->thread_id, \n            &attr, vrt_dispatch_data_thread_run, ddt);\n    }\n    \n    return VRT_OK;\n}\n\nint vrt_wait_dispatch_data(void)\n{\n    unsigned int i;\n    /* wait for the produce threads finish */\n\tfor(i = 0; i < darray_n(dispatch_data_threads); i ++){\n\t\tdispatch_data_thread *ddt = darray_get(dispatch_data_threads, i);\n\t\tpthread_join(ddt->thread_id, NULL);\n\t}\n    \n    return VRT_OK;\n}\n\n/* return value \n  * 1: need sleep a while because of there are too many data cached\n  * 0: can normally continue\n  * -1: error occur */\nint data_dispatch(data_unit *du)\n{\n    int thread_idx;\n    dispatch_data_thread *ddt;\n    long long length;\n    int *keyindex, numkeys;\n\n    keyindex = get_keys_from_data_producer(du->dp, du->argv, du->argc, &numkeys);\n\n    if (numkeys == 0) {\n        du->hashvalue = (unsigned int)rand();\n    } else {\n        sds key = du->argv[keyindex[0]];\n        du->hashvalue = (unsigned int)hash_crc32a(key, sdslen(key));\n    }\n    free(keyindex);\n    \n    thread_idx = du->hashvalue%dispatch_data_threads_count;\n    ddt = darray_get(dispatch_data_threads, thread_idx);\n    length = dmtqueue_push(ddt->datas, du);\n    if (length <= 0) {\n        test_log_error(\"Data unit push to dispatch thread %d failed\", ddt->id);\n        return -1;\n    } else if (length > 2000) {\n        return 1;\n    }\n\n    return 0;\n}\n"
  },
  {
    "path": "tests/vrt_dispatch_data.h",
    "content": "#ifndef _VRT_DISPATCH_DATA_H_\n#define _VRT_DISPATCH_DATA_H_\n\n#include <darray.h>\n\nstruct abtest_group;\nstruct dlist;\nstruct dmtqueue;\nstruct data_unit;\nstruct aeEventLoop;\n\ntypedef struct dispatch_data_thread {\n    int id;\n    pthread_t thread_id;\n    \n    struct aeEventLoop *el;\n    int hz;\n    int cronloops;          /* Number of times the cron function run */\n\n    struct dmtqueue *datas;  /* Value is data_unit, used receive data \n                                        from produce data thread, and send to the abtest groups. */\n    struct dlist *rdatas;   /* Value is reply_unit, used to cache data \n                                        that has not received from abtest groups completely */\n\n    darray *abgs; /* type is abtest_group */\n\n    int pause;\n\n    int count_wait_for_reply;\n\n    long long reply_total_count_per_cycle;\n    long long reply_type_err_count_per_cycle;\n} dispatch_data_thread;\n\nextern int dispatch_data_threads_count;\n\nextern int dispatch_threads_pause_finished_count;\n\nlong long get_total_tested_commands_count_per_cycle(void);\nlong long get_total_reply_err_count_per_cycle(void);\nvoid reset_total_count_per_cycle(void);\n\nint vrt_dispatch_data_init(int threads_count, char *test_target_groups, int connections);\nvoid vrt_dispatch_data_deinit(void);\n\nint vrt_start_dispatch_data(void);\nint vrt_wait_dispatch_data(void);\n\nint data_dispatch(struct data_unit *du);\n\n#endif\n"
  },
  {
    "path": "tests/vrt_produce_data.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <getopt.h>\n#include <fcntl.h>\n#include <errno.h>\n#include <signal.h>\n#include <assert.h>\n#include <math.h>\n#include <sys/stat.h>\n#include <sys/utsname.h>\n\n#include <dspecialconfig.h>\n\n#include <hiredis.h>\n#include <darray.h>\n#include <dutil.h>\n#include <dlog.h>\n\n#include <vrt_util.h>\n#include <vrt_public.h>\n#include <vrabtest.h>\n#include <vrt_produce_data.h>\n\n#define PRODUCE_KEY_CACHE_POOL_COUNT 5\n\ntypedef struct produce_thread {\n    int id;\n    pthread_t thread_id;\n\n    produce_scheme *ps;\n\n    int pause;\n    long long looptimes;\n} produce_thread;\n\ndata_producer *delete_data_producer = NULL;\n\nstatic unsigned int key_length_min;\nstatic unsigned int key_length_max;\nstatic unsigned int key_length_range_gap;\nstatic unsigned int field_length_max;\nstatic unsigned int string_length_max;\n\nstatic int cmd_type;\n\nstatic int key_cache_pools_count = 0;\n\nstatic darray needed_cmd_type_producer;  /* type:  data_producer*/\nstatic unsigned int needed_cmd_type_producer_count;\n\nint produce_data_threads_count;\nstatic darray produce_threads;\n\nint produce_threads_pause_finished_count;\n\nstatic int non_empty_kcps_count = 0;\nunsigned int non_empty_kcps_idx[PRODUCE_KEY_CACHE_POOL_COUNT] = {-1};\n\nstatic sds get_random_cached_key(produce_scheme *ps, data_producer *dp)\n{\n    key_cache_array *kcp = kcp_get_from_ps(ps,dp);\n    return key_cache_array_random(kcp);\n}\n\nstatic int get_random_int(void)\n{\n    if (rand()%2 == 1) {\n        return 0 - (int)rand();\n    } else {\n        return (int)rand();\n    }\n}\n\nstatic unsigned int get_random_unsigned_int(void)\n{\n    return (unsigned int)rand();\n}\n\nstatic char get_random_char(void)\n{\n    return (char)rand()%250 + 5;\n    //return (char)(rand()%25 + 97);\n}\n\nstatic sds get_random_key(void)\n{\n    unsigned int i, len;\n    sds str = sdsempty();\n    \n    len = key_length_range_gap==0?key_length_min:\n        (get_random_unsigned_int()%key_length_range_gap+key_length_min);\n    if (len == 0) len ++;\n    str = sdsMakeRoomFor(str,(size_t)len);\n    sdsIncrLen(str, (int)len);\n\n    for (i = 0; i < len; i ++) {\n        str[i] = (char)get_random_char();\n    }\n\n    return str;\n}\n\nstatic sds get_random_string(void)\n{\n    unsigned int i, len;\n    sds str = sdsempty();\n    \n    len = get_random_unsigned_int()%string_length_max;\n    str = sdsMakeRoomFor(str,(size_t)len);\n    sdsIncrLen(str, (int)len);\n\n    for (i = 0; i < len; i ++) {\n        str[i] = get_random_char();\n    }\n\n    return str;\n}\n\nstatic sds get_random_float_str(void)\n{\n    unsigned int decimal_len;\n    sds str;\n\n    if (rand()%2 == 1) {\n        str = sdsnew(\"-\");\n    } else {\n        str = sdsempty();\n    }\n\n    if (rand()%2 == 1) {\n        str = sdscatfmt(str,\"%u.%u\",\n            get_random_unsigned_int(),\n            get_random_unsigned_int());\n    } else {\n        str = sdscatfmt(str,\"%u\",\n            get_random_unsigned_int());\n    }\n\n    return str;\n}\n\n#define ZSET_RANGE_MIN_MAX_TYPE_RANK    0\n#define ZSET_RANGE_MIN_MAX_TYPE_SCORE   1\n#define ZSET_RANGE_MIN_MAX_TYPE_LEX     2\nstatic sds *get_random_zset_range_min_max_str(int range_type)\n{\n    sds *range; /* range[0] is the min, range[1] is the max */\n    unsigned int probability = rand()%100;\n\n    range = malloc(2*sizeof(sds));\n    if (range_type == ZSET_RANGE_MIN_MAX_TYPE_RANK) {\n        unsigned int min = get_random_unsigned_int();\n        unsigned int max = get_random_unsigned_int();\n\n        if (probability >= 95 && min <= max || \n            probability < 95 && min > max) {\n            range[0] = sdsfromlonglong((long long)max);\n            range[1] = sdsfromlonglong((long long)min);\n        } else {\n            range[0] = sdsfromlonglong((long long)min);\n            range[1] = sdsfromlonglong((long long)max);\n        }\n    } else if (range_type == ZSET_RANGE_MIN_MAX_TYPE_SCORE) {\n        sds min_str = get_random_float_str();\n        sds max_str = get_random_float_str();\n        float min, max;\n        char *eptr;\n        sds swap;\n        unsigned int min_probability = rand()%3;\n        unsigned int max_probability = rand()%3;\n\n        min = strtod(min_str,&eptr);\n        if (eptr[0] != '\\0' || isnan(min)) {\n            sdsfree(min_str);\n            sdsfree(max_str);\n            free(range);\n            return NULL;\n        }\n        max = strtod(max_str,&eptr);\n        if (eptr[0] != '\\0' || isnan(max)) {\n            sdsfree(min_str);\n            sdsfree(max_str);\n            free(range);\n            return NULL;\n        }\n        if (probability >= 95 && min <= max || \n            probability < 95 && min > max) {\n            swap = min_str;\n            min_str = max_str;\n            max_str = swap;\n        }\n        \n        if (min_probability == 0) {\n            range[0] = sdsnew(\"-inf\");\n        } else if (min_probability == 1) {\n            range[0] = sdsnew(\"(\");\n            range[0] = sdscatfmt(range[0],\"%S\",min_str);\n            sdsfree(min_str);\n        } else {\n            range[0] = min_str;\n        }\n        if (max_probability == 0) {\n            range[1] = sdsnew(\"+inf\");\n        } else if (max_probability == 1) {\n            range[1] = sdsnew(\"(\");\n            range[1] = sdscatfmt(range[1],\"%S\",max_str);\n            sdsfree(max_str);\n        } else {\n            range[1] = max_str;\n        }\n    } else if (range_type == ZSET_RANGE_MIN_MAX_TYPE_LEX) {\n        sds min_str = get_random_string();\n        sds max_str = get_random_string();\n        sds swap;\n        unsigned int min_probability = rand()%3;\n        unsigned int max_probability = rand()%3;\n\n        if (probability >= 95 && sdscmp(min_str,max_str) < 0 || \n            probability < 95 && sdscmp(min_str,max_str) > 0) {\n            swap = min_str;\n            min_str = max_str;\n            max_str = swap;\n        }\n        \n        if (min_probability == 0) {\n            range[0] = sdsnew(\"-\");\n        } else if (min_probability == 1) {\n            range[0] = sdsnew(\"(\");\n            range[0] = sdscatfmt(range[0],\"%S\",min_str);\n            sdsfree(min_str);\n        } else {\n            range[0] = sdsnew(\"[\");\n            range[0] = sdscatfmt(range[0],\"%S\",min_str);\n            sdsfree(min_str);\n        }\n        if (max_probability == 0) {\n            range[1] = sdsnew(\"+\");\n        } else if (max_probability == 1) {\n            range[1] = sdsnew(\"(\");\n            range[1] = sdscatfmt(range[1],\"%S\",max_str);\n            sdsfree(max_str);\n        } else {\n            range[1] = sdsnew(\"[\");\n            range[1] = sdscatfmt(range[1],\"%S\",max_str);\n            sdsfree(max_str);\n        }\n    } else {\n        free(range);\n        range = NULL; \n    }\n\n    return range;\n}\n\nstatic unsigned int get_random_field_len(void)\n{\n    return get_random_unsigned_int()%field_length_max + 1;\n}\n\nstatic sds get_random_key_with_hit_ratio(produce_scheme *ps, data_producer *dp)\n{\n    sds key;\n    if (ps->hit_ratio_array[ps->hit_ratio_idx++] == 0) {\n        key = get_random_key();\n    } else {\n        key = get_random_cached_key(ps,dp);\n        if (key == NULL) key = get_random_key();\n    }\n    if (ps->hit_ratio_idx >= ps->hit_ratio_array_len) {\n        ps->hit_ratio_idx = 0;\n    }\n    return key;\n}\n\n/************** Need cache key implement ************/\nstatic int nck_when_noerror(redisReply *reply)\n{\n    if (reply == NULL) return 0;\n\n    if (reply->type != REDIS_REPLY_ERROR) {\n        return 1;\n    }\n\n    return 0;\n}\n\nstatic int nck_when_ok(redisReply *reply)\n{\n    if (reply == NULL) return 0;\n\n    if (reply->type == REDIS_REPLY_STATUS && \n        !strcmp(reply->str, \"OK\")) {\n        return 1;\n    }\n\n    return 0;\n}\n\nstatic int nck_when_str(redisReply *reply)\n{\n    if (reply == NULL) return 0;\n\n    if (reply->type == REDIS_REPLY_STRING) {\n        return 1;\n    }\n\n    return 0;\n}\n\nstatic int nck_when_unsigned_integer(redisReply *reply)\n{\n    if (reply == NULL) return 0;\n\n    if (reply->type == REDIS_REPLY_INTEGER && \n        reply->integer >= 0) {\n        return 1;\n    }\n\n    return 0;\n}\n\nstatic int nck_when_nonzero_unsigned_integer(redisReply *reply)\n{\n    if (reply == NULL) return 0;\n\n    if (reply->type == REDIS_REPLY_INTEGER && \n        reply->integer > 0) {\n        return 1;\n    }\n\n    return 0;\n}\n\nstatic int nck_when_zero_or_one(redisReply *reply)\n{\n    if (reply == NULL) return 0;\n\n    if (reply->type == REDIS_REPLY_INTEGER && \n        (reply->integer == 0 || reply->integer == 1)) {\n        return 1;\n    }\n\n    return 0;\n}\n\nstatic int nck_when_one(redisReply *reply)\n{\n    if (reply == NULL) return 0;\n\n    if (reply->type == REDIS_REPLY_INTEGER && \n        reply->integer == 1) {\n        return 1;\n    }\n\n    return 0;\n}\n\n/************** Need cache key implement end ************/\n\nstatic data_unit *get_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n\n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = 2;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key_with_hit_ratio(ps,dp);\n    \n    return du;\n}\n\nstatic data_unit *set_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n\n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = 3;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key();\n    du->argv[2] = get_random_string();\n    \n    return du;\n}\n\nstatic data_unit *setnx_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n\n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = 3;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key_with_hit_ratio(ps,dp);\n    du->argv[2] = get_random_string();\n    \n    return du;\n}\n\n/* Need cache key? */\nstatic int setnx_cmd_nck(redisReply *reply)\n{\n    if (reply == NULL) return 0;\n\n    if (reply->type == REDIS_REPLY_INTEGER && \n        reply->integer == 1) {\n        return 1;\n    }\n\n    return 0;\n}\n\nstatic data_unit *setex_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n\n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = 4;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key();\n    du->argv[2] = sdsfromlonglong(rand()%10000);\n    du->argv[3] = get_random_string();\n    \n    return du;\n}\n\nstatic data_unit *psetex_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n\n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = 4;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key();\n    du->argv[2] = sdsfromlonglong(rand()%10000);\n    du->argv[3] = get_random_string();\n    \n    return du;\n}\n\nstatic data_unit *del_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n\n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = 2;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key();\n    \n    return du;\n}\n\nstatic data_unit *expire_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n\n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = 3;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key_with_hit_ratio(ps,dp);\n    du->argv[2] = sdsfromlonglong(rand()%10000);\n    \n    return du;\n}\n\nstatic data_unit *expireat_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n\n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = 3;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key_with_hit_ratio(ps,dp);\n    du->argv[2] = sdsfromlonglong(vrt_msec_now()/1000LL+rand()%10000);\n    \n    return du;\n}\n\nstatic data_unit *exists_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n\n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = 2;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key_with_hit_ratio(ps,dp);\n    \n    return du;\n}\n\nstatic data_unit *ttl_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n\n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = 2;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key_with_hit_ratio(ps,dp);\n    \n    return du;\n}\n\nstatic data_unit *pttl_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n\n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = 2;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key_with_hit_ratio(ps,dp);\n    \n    return du;\n}\n\nstatic data_unit *incr_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n\n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = 2;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key();\n    \n    return du;\n}\n\nstatic data_unit *decr_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n\n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = 2;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key();\n    \n    return du;\n}\n\nstatic data_unit *incrby_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n\n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = 3;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key();\n    du->argv[2] = sdsfromlonglong(rand()%10000);\n    \n    return du;\n}\n\nstatic data_unit *decrby_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n\n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = 3;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key();\n    du->argv[2] = sdsfromlonglong(rand()%10000);\n    \n    return du;\n}\n\nstatic data_unit *append_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n\n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = 3;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key();\n    du->argv[2] = get_random_string();\n    \n    return du;\n}\n\n/* Need cache key? */\nstatic int append_cmd_nck(redisReply *reply)\n{\n    if (reply == NULL) return 0;\n\n    if (reply->type == REDIS_REPLY_INTEGER) {\n        return 1;\n    }\n\n    return 0;\n}\n\nstatic data_unit *strlen_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n\n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = 2;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key_with_hit_ratio(ps,dp);\n    \n    return du;\n}\n\nstatic data_unit *getset_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n\n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = 3;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key_with_hit_ratio(ps,dp);\n    du->argv[2] = get_random_string();\n    \n    return du;\n}\n\nstatic data_unit *incrbyfloat_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n\n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = 3;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key_with_hit_ratio(ps,dp);\n    du->argv[2] = get_random_float_str();\n    \n    return du;\n}\n\nstatic data_unit *setbit_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n\n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = 4;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key_with_hit_ratio(ps,dp);\n    du->argv[2] = sdsfromlonglong(get_random_unsigned_int()%30000);\n    if (rand()%2) {\n        du->argv[3] = sdsnew(\"1\");\n    } else {\n        du->argv[3] = sdsnew(\"0\");\n    }\n    \n    return du;\n}\n\nstatic data_unit *getbit_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n\n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = 3;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key_with_hit_ratio(ps,dp);\n    du->argv[2] = sdsfromlonglong(get_random_unsigned_int()%30000);\n    \n    return du;\n}\n\nstatic data_unit *setrange_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n\n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = 4;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key_with_hit_ratio(ps,dp);\n    du->argv[2] = sdsfromlonglong(get_random_unsigned_int()%30000);\n    du->argv[3] = get_random_string();\n    \n    return du;\n}\n\nstatic data_unit *getrange_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n\n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = 4;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key_with_hit_ratio(ps,dp);\n    du->argv[2] = sdsfromlonglong(get_random_int()%30000);\n    du->argv[3] = sdsfromlonglong(get_random_int()%30000);\n    \n    return du;\n}\n\nstatic data_unit *bitcount_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n    int with_range = 0;\n\n    if (rand()%2)\n        with_range = 1;\n\n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = with_range?4:2;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key_with_hit_ratio(ps,dp);\n    if (with_range) {\n        du->argv[2] = sdsfromlonglong(get_random_int()%30000);\n        du->argv[3] = sdsfromlonglong(get_random_int()%30000);\n    }\n    return du;\n}\n\nstatic data_unit *bitpos_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n    int with_range = 0; /* 0: no range; 1: just have start; 2: have start and end. */\n    unsigned int probability = rand()%3;\n    \n    if (probability == 0)\n        with_range = 0;\n    else if (probability == 1)\n        with_range = 1;\n    else if (probability == 2)\n        with_range = 2;\n\n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = with_range==0?3:(with_range==1?4:5);\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key_with_hit_ratio(ps,dp);\n    if (rand()%2)\n        du->argv[2] = sdsnew(\"0\");\n    else\n        du->argv[2] = sdsnew(\"1\");\n    if (with_range > 0)\n        du->argv[3] = sdsfromlonglong(get_random_int()%30000);\n    if (with_range == 2)\n        du->argv[4] = sdsfromlonglong(get_random_int()%30000);\n\n    return du;\n}\n\nstatic data_unit *mget_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n\n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = 2;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key_with_hit_ratio(ps,dp);\n    \n    return du;\n}\n\nstatic data_unit *mset_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n\n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = 3;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key();\n    du->argv[2] = get_random_string();\n    \n    return du;\n}\n\nstatic data_unit *hset_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n\n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = 4;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key();\n    du->argv[2] = get_random_string();\n    du->argv[3] = get_random_string();\n    \n    return du;\n}\n\nstatic data_unit *hget_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n\n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = 3;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key_with_hit_ratio(ps,dp);\n    du->argv[2] = get_random_string();\n    \n    return du;\n}\n\nstatic data_unit *hlen_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n\n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = 2;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key_with_hit_ratio(ps,dp);\n    \n    return du;\n}\n\nstatic data_unit *hdel_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n    unsigned int j, field_length;\n\n    field_length = get_random_field_len();\n\n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = 2 + field_length;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key_with_hit_ratio(ps,dp);\n    for (j = 0; j < field_length; j ++) {\n        du->argv[2+j] = get_random_string();\n    }\n    \n    return du;\n}\n\nstatic data_unit *hexists_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n\n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = 3;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key_with_hit_ratio(ps,dp);\n    du->argv[2] = get_random_string();\n    \n    return du;\n}\n\nstatic data_unit *hkeys_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n\n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = 2;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key_with_hit_ratio(ps,dp);\n    \n    return du;\n}\n\nstatic data_unit *hvals_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n\n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = 2;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key_with_hit_ratio(ps,dp);\n    \n    return du;\n}\n\nstatic data_unit *hgetall_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n\n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = 2;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key_with_hit_ratio(ps,dp);\n    \n    return du;\n}\n\nstatic data_unit *hincrby_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n\n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = 4;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key_with_hit_ratio(ps,dp);\n    du->argv[2] = get_random_string();\n    du->argv[3] = sdsfromlonglong(get_random_int());\n    \n    return du;\n}\n\nstatic data_unit *hincrbyfloat_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n\n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = 4;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key_with_hit_ratio(ps,dp);\n    du->argv[2] = get_random_string();\n    du->argv[3] = get_random_float_str();\n    \n    return du;\n}\n\nstatic data_unit *hmget_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n    unsigned int j, field_length;\n\n    field_length = get_random_field_len();\n\n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = 2+field_length;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key_with_hit_ratio(ps,dp);\n    for (j = 0; j < field_length; j ++) {\n        du->argv[2+j] = get_random_string();\n    }\n    \n    return du;\n}\n\nstatic data_unit *hmset_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n    unsigned int j, field_length;\n\n    field_length = get_random_field_len();\n\n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = 2+field_length*2;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key();\n    for (j = 2; j < 2+field_length*2; j += 2) {\n        du->argv[j] = get_random_string();\n        du->argv[j+1] = get_random_string();\n    }\n    \n    return du;\n}\n\nstatic data_unit *hsetnx_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n\n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = 4;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key();\n    du->argv[2] = get_random_string();\n    du->argv[3] = get_random_string();\n    \n    return du;\n}\n\nstatic data_unit *hstrlen_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n\n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = 3;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key_with_hit_ratio(ps,dp);\n    du->argv[2] = get_random_string();\n    \n    return du;\n}\n\nstatic data_unit *rpush_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n    unsigned int j, field_length;\n\n    field_length = get_random_field_len();\n\n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = 2+field_length;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key_with_hit_ratio(ps,dp);\n    for (j = 0; j < field_length; j ++) {\n        du->argv[2+j] = get_random_string();\n    }\n    \n    return du;\n}\n\n/* Need cache key? */\nstatic int rpush_cmd_nck(redisReply *reply)\n{\n    if (reply == NULL) return 0;\n\n    if (reply->type == REDIS_REPLY_INTEGER) {\n        return 1;\n    }\n\n    return 0;\n}\n\nstatic data_unit *lpush_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n    unsigned int j, field_length;\n\n    field_length = get_random_field_len();\n\n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = 2+field_length;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key_with_hit_ratio(ps,dp);\n\n    for (j = 0; j < field_length; j ++) {\n        du->argv[2+j] = get_random_string();\n    }\n    \n    return du;\n}\n\nstatic data_unit *lrange_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n\n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = 4;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key_with_hit_ratio(ps,dp);\n    du->argv[2] = sdsfromlonglong((long long)get_random_int()%(field_length_max+1));\n    du->argv[3] = sdsfromlonglong((long long)get_random_int()%(field_length_max+1));\n    \n    return du;\n}\n\nstatic data_unit *rpop_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n\n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = 2;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key_with_hit_ratio(ps,dp);\n    \n    return du;\n}\n\nstatic data_unit *lpop_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n\n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = 2;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key_with_hit_ratio(ps,dp);\n    \n    return du;\n}\n\nstatic data_unit *llen_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n\n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = 2;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key_with_hit_ratio(ps,dp);\n    \n    return du;\n}\n\nstatic data_unit *lrem_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n\n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = 4;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key_with_hit_ratio(ps,dp);\n    du->argv[2] = sdsfromlonglong((long long)get_random_int()%(field_length_max+1));\n    du->argv[3] = get_random_string();\n    \n    return du;\n}\n\nstatic data_unit *ltrim_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n\n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = 4;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key_with_hit_ratio(ps,dp);\n    du->argv[2] = sdsfromlonglong((long long)get_random_int()%(field_length_max+1));\n    du->argv[3] = sdsfromlonglong((long long)get_random_int()%(field_length_max+1));\n    \n    return du;\n}\n\nstatic data_unit *lindex_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n\n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = 3;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key_with_hit_ratio(ps,dp);\n    du->argv[2] = sdsfromlonglong((long long)get_random_int()%(field_length_max+1));\n    \n    return du;\n}\n\nstatic data_unit *lset_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n\n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = 4;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key_with_hit_ratio(ps,dp);\n    du->argv[2] = sdsfromlonglong((long long)get_random_int()%(field_length_max+1));\n    du->argv[3] = get_random_string();\n    \n    return du;\n}\n\nstatic data_unit *sadd_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n    unsigned int j, field_length;\n\n    field_length = get_random_field_len();\n\n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = 2 + field_length;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key_with_hit_ratio(ps,dp);\n    for (j = 0; j < field_length; j ++) {\n        du->argv[2+j] = get_random_string();\n    }\n    \n    return du;\n}\n\nstatic data_unit *smembers_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n\n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = 2;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key_with_hit_ratio(ps,dp);\n    \n    return du;\n}\n\nstatic data_unit *scard_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n\n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = 2;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key_with_hit_ratio(ps,dp);\n    \n    return du;\n}\n\nstatic data_unit *srem_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n    unsigned int j, field_length;\n\n    field_length = get_random_field_len();\n\n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = 2+field_length;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key_with_hit_ratio(ps,dp);\n    for (j = 0; j < field_length; j ++) {\n        du->argv[2+j] = get_random_string();\n    }\n    \n    return du;\n}\n\nstatic data_unit *sismember_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n\n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = 3;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key_with_hit_ratio(ps,dp);\n    du->argv[2] = get_random_string();\n    \n    return du;\n}\n\nstatic data_unit *sunion_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n\n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = 2;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key_with_hit_ratio(ps,dp);\n    \n    return du;\n}\n\nstatic data_unit *sdiff_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n\n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = 2;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key_with_hit_ratio(ps,dp);\n    \n    return du;\n}\n\nstatic data_unit *sinter_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n\n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = 2;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key_with_hit_ratio(ps,dp);\n    \n    return du;\n}\n\n/* Need cache key? */\nstatic int lpush_cmd_nck(redisReply *reply)\n{\n    if (reply == NULL) return 0;\n\n    if (reply->type == REDIS_REPLY_INTEGER) {\n        return 1;\n    }\n\n    return 0;\n}\n\nstatic data_unit *zadd_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n    unsigned int j, field_length;\n\n    field_length = get_random_field_len();\n\n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = 2+field_length*2;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key();\n\n    for (j = 2; j < 2+field_length*2; j += 2) {\n        du->argv[j] = get_random_float_str();\n        du->argv[j+1] = get_random_string();\n    }\n    \n    return du;\n}\n\n/* Need cache key? */\nstatic int zadd_cmd_nck(redisReply *reply)\n{\n    if (reply == NULL) return 0;\n\n    if (reply->type == REDIS_REPLY_INTEGER) {\n        return 1;\n    }\n\n    return 0;\n}\n\nstatic data_unit *zincrby_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n    unsigned int j, field_length;\n\n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = 4;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key_with_hit_ratio(ps,dp);\n    du->argv[2] = get_random_float_str();;\n    du->argv[3] = get_random_string();\n    \n    return du;\n}\n\n/* Need cache key? */\nstatic int zincrby_cmd_nck(redisReply *reply)\n{\n    if (reply == NULL) return 0;\n\n    if (reply->type == REDIS_REPLY_INTEGER) {\n        return 1;\n    }\n\n    return 0;\n}\n\nstatic data_unit *zrange_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n    unsigned int j, field_length;\n    int withscores;\n\n    if (rand()%2 == 1) {\n        withscores = 1;\n    } else {\n        withscores = 0;\n    }\n\n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = withscores?5:4;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key_with_hit_ratio(ps,dp);\n    du->argv[2] = sdsfromlonglong(0);\n    du->argv[3] = sdsfromlonglong(get_random_int()%10000);\n    if (withscores) du->argv[4] = sdsnew(\"withscores\");\n    \n    return du;\n}\n\nstatic data_unit *zrevrange_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n    unsigned int j, field_length;\n    int withscores;\n\n    if (rand()%2 == 1) {\n        withscores = 1;\n    } else {\n        withscores = 0;\n    }\n\n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = withscores?5:4;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key_with_hit_ratio(ps,dp);\n    du->argv[2] = sdsfromlonglong(0);\n    du->argv[3] = sdsfromlonglong(get_random_int()%10000);\n    if (withscores) du->argv[4] = sdsnew(\"withscores\");\n    \n    return du;\n}\n\nstatic data_unit *zrem_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n    unsigned int j, field_length;\n\n    field_length = get_random_field_len();\n\n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = 2+field_length;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key_with_hit_ratio(ps,dp);\n\n    for (j = 2; j < 2+field_length; j ++) {\n        du->argv[j] = get_random_string();\n    }\n    \n    return du;\n}\n\nstatic data_unit *zcard_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n    \n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = 2;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key_with_hit_ratio(ps,dp);\n    \n    return du;\n}\n\nstatic data_unit *zcount_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n    sds *range = get_random_zset_range_min_max_str(ZSET_RANGE_MIN_MAX_TYPE_SCORE);\n    \n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = 4;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key_with_hit_ratio(ps,dp);\n    du->argv[2] = range[0];\n    du->argv[3] = range[1];\n\n    free(range);\n    return du;\n}\n\nstatic data_unit *zrangebyscore_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n    unsigned int idx = 0, arg_count = 0;\n    int withscores,limit;\n    sds *range = get_random_zset_range_min_max_str(ZSET_RANGE_MIN_MAX_TYPE_SCORE);\n\n    arg_count = 4;\n    if (rand()%2 == 1) {\n        withscores = 1;\n        arg_count ++;\n    } else {\n        withscores = 0;\n    }\n    if (rand()%2 == 1) {\n        limit = 1;\n        arg_count += 3;\n    } else {\n        limit = 0;\n    }\n\n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = arg_count;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[idx++] = sdsnew(dp->name);\n    du->argv[idx++] = get_random_key_with_hit_ratio(ps,dp);\n    du->argv[idx++] = range[0];\n    du->argv[idx++] = range[1];\n    if (withscores) du->argv[idx++] = sdsnew(\"withscores\");\n    if (limit) {\n        du->argv[idx++] = sdsnew(\"limit\");\n        du->argv[idx++] = sdsfromlonglong(get_random_unsigned_int());\n        du->argv[idx++] = sdsfromlonglong(get_random_unsigned_int());\n    }\n\n    ASSERT(arg_count == idx);\n\n    free(range);\n    return du;\n}\n\nstatic data_unit *zrevrangebyscore_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n    unsigned int idx = 0, arg_count = 0;\n    int withscores,limit;\n    sds *range = get_random_zset_range_min_max_str(ZSET_RANGE_MIN_MAX_TYPE_SCORE);\n\n    arg_count = 4;\n    if (rand()%2 == 1) {\n        withscores = 1;\n        arg_count ++;\n    } else {\n        withscores = 0;\n    }\n    if (rand()%2 == 1) {\n        limit = 1;\n        arg_count += 3;\n    } else {\n        limit = 0;\n    }\n\n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = arg_count;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[idx++] = sdsnew(dp->name);\n    du->argv[idx++] = get_random_key_with_hit_ratio(ps,dp);\n    du->argv[idx++] = range[0];\n    du->argv[idx++] = range[1];\n    if (withscores) du->argv[idx++] = sdsnew(\"withscores\");\n    if (limit) {\n        du->argv[idx++] = sdsnew(\"limit\");\n        du->argv[idx++] = sdsfromlonglong(get_random_unsigned_int());\n        du->argv[idx++] = sdsfromlonglong(get_random_unsigned_int());\n    }\n\n    ASSERT(arg_count == idx);\n\n    free(range);\n    return du;\n}\n\nstatic data_unit *zrank_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n    \n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = 3;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key_with_hit_ratio(ps,dp);\n    du->argv[2] = get_random_string();\n        \n    return du;\n}\n\nstatic data_unit *zrevrank_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n    \n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = 3;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key_with_hit_ratio(ps,dp);\n    du->argv[2] = get_random_string();\n        \n    return du;\n}\n\nstatic data_unit *zscore_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n    \n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = 3;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key_with_hit_ratio(ps,dp);\n    du->argv[2] = get_random_string();\n        \n    return du;\n}\n\nstatic data_unit *zremrangebyscore_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n    sds *range = get_random_zset_range_min_max_str(ZSET_RANGE_MIN_MAX_TYPE_SCORE);\n    \n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = 4;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key_with_hit_ratio(ps,dp);\n    du->argv[2] = range[0];\n    du->argv[3] = range[1];\n\n    free(range);\n    return du;\n}\n\nstatic data_unit *zremrangebyrank_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n    sds *range = get_random_zset_range_min_max_str(ZSET_RANGE_MIN_MAX_TYPE_RANK);\n    \n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = 4;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key_with_hit_ratio(ps,dp);\n    du->argv[2] = range[0];\n    du->argv[3] = range[1];\n\n    free(range);\n    return du;\n}\n\nstatic data_unit *zremrangebylex_cmd_producer(data_producer *dp, produce_scheme *ps)\n{\n    data_unit *du;\n    sds *range = get_random_zset_range_min_max_str(ZSET_RANGE_MIN_MAX_TYPE_LEX);\n    \n    du = data_unit_get();\n    du->dp = dp;\n    du->argc = 4;\n    du->argv = malloc(du->argc*sizeof(sds));\n    du->argv[0] = sdsnew(dp->name);\n    du->argv[1] = get_random_key_with_hit_ratio(ps,dp);\n    du->argv[2] = range[0];\n    du->argv[3] = range[1];\n\n    free(range);\n    return du;\n}\n\nstatic int producers_count;\ndata_producer redis_data_producer_table[] = {\n    /* Key */\n    {\"del\",del_cmd_producer,-2,\"w\",0,NULL,1,-1,1,TEST_CMD_TYPE_KEY,NULL},\n    {\"exists\",exists_cmd_producer,-2,\"rF\",0,NULL,1,-1,1,TEST_CMD_TYPE_KEY,NULL},\n    {\"ttl\",ttl_cmd_producer,2,\"rF\",0,NULL,1,1,1,TEST_CMD_TYPE_EXPIRE,NULL},\n    {\"pttl\",pttl_cmd_producer,2,\"rF\",0,NULL,1,1,1,TEST_CMD_TYPE_EXPIRE,NULL},\n    {\"expire\",expire_cmd_producer,3,\"wF\",0,NULL,1,1,1,TEST_CMD_TYPE_EXPIRE,NULL},\n    {\"expireat\",expireat_cmd_producer,3,\"wF\",0,NULL,1,1,1,TEST_CMD_TYPE_EXPIRE,NULL},\n    /* String */\n    {\"get\",get_cmd_producer,2,\"rF\",0,NULL,1,1,1,TEST_CMD_TYPE_STRING,NULL},\n    {\"set\",set_cmd_producer,-3,\"wmA\",0,NULL,1,1,1,TEST_CMD_TYPE_STRING,nck_when_ok},\n    {\"setnx\",setnx_cmd_producer,3,\"wmFA\",0,NULL,1,1,1,TEST_CMD_TYPE_STRING,setnx_cmd_nck},\n    {\"setex\",setex_cmd_producer,4,\"wmA\",0,NULL,1,1,1,TEST_CMD_TYPE_EXPIRE,nck_when_ok},\n    {\"psetex\",psetex_cmd_producer,4,\"wmA\",0,NULL,1,1,1,TEST_CMD_TYPE_EXPIRE,nck_when_ok},\n    {\"incr\",incr_cmd_producer,2,\"wmF\",0,NULL,1,1,1,TEST_CMD_TYPE_STRING,NULL},\n    {\"decr\",decr_cmd_producer,2,\"wmF\",0,NULL,1,1,1,TEST_CMD_TYPE_STRING,NULL},\n    {\"incrby\",incrby_cmd_producer,3,\"wmF\",0,NULL,1,1,1,TEST_CMD_TYPE_STRING,NULL},\n    {\"decrby\",decrby_cmd_producer,3,\"wmF\",0,NULL,1,1,1,TEST_CMD_TYPE_STRING,NULL},\n    {\"append\",append_cmd_producer,3,\"wmA\",0,NULL,1,1,1,TEST_CMD_TYPE_STRING,append_cmd_nck},\n    {\"strlen\",strlen_cmd_producer,2,\"rF\",0,NULL,1,1,1,TEST_CMD_TYPE_STRING,NULL},\n    {\"getset\",getset_cmd_producer,3,\"wmA\",0,NULL,1,1,1,TEST_CMD_TYPE_STRING,nck_when_noerror},\n    {\"incrbyfloat\",incrbyfloat_cmd_producer,3,\"wmFA\",0,NULL,1,1,1,TEST_CMD_TYPE_STRING,nck_when_str},\n    {\"setbit\",setbit_cmd_producer,4,\"wmA\",0,NULL,1,1,1,TEST_CMD_TYPE_STRING,nck_when_zero_or_one},\n    {\"getbit\",getbit_cmd_producer,3,\"rF\",0,NULL,1,1,1,TEST_CMD_TYPE_STRING,NULL},\n    {\"setrange\",setrange_cmd_producer,4,\"wmA\",0,NULL,1,1,1,TEST_CMD_TYPE_STRING,nck_when_nonzero_unsigned_integer},\n    {\"getrange\",getrange_cmd_producer,4,\"r\",0,NULL,1,1,1,TEST_CMD_TYPE_STRING,NULL},\n    {\"bitcount\",bitcount_cmd_producer,-2,\"r\",0,NULL,1,1,1,TEST_CMD_TYPE_STRING,NULL},\n    {\"bitpos\",bitpos_cmd_producer,-3,\"r\",0,NULL,1,1,1,TEST_CMD_TYPE_STRING,NULL},\n    {\"mget\",mget_cmd_producer,-2,\"r\",0,NULL,1,-1,1,TEST_CMD_TYPE_STRING,NULL},\n    {\"mset\",mset_cmd_producer,-3,\"wmA\",0,NULL,1,-1,2,TEST_CMD_TYPE_STRING,nck_when_ok},\n    /* Hash */\n    {\"hset\",hset_cmd_producer,4,\"wmFA\",0,NULL,1,1,1,TEST_CMD_TYPE_HASH,nck_when_one},\n    {\"hget\",hget_cmd_producer,3,\"rF\",0,NULL,1,1,1,TEST_CMD_TYPE_HASH,NULL},\n    {\"hlen\",hlen_cmd_producer,2,\"rF\",0,NULL,1,1,1,TEST_CMD_TYPE_HASH,NULL},\n    {\"hdel\",hdel_cmd_producer,-3,\"wF\",0,NULL,1,1,1,TEST_CMD_TYPE_HASH,NULL},\n    {\"hexists\",hexists_cmd_producer,3,\"rF\",0,NULL,1,1,1,TEST_CMD_TYPE_HASH,NULL},\n    {\"hkeys\",hkeys_cmd_producer,2,\"rS\",0,NULL,1,1,1,TEST_CMD_TYPE_HASH,NULL},\n    {\"hvals\",hvals_cmd_producer,2,\"rS\",0,NULL,1,1,1,TEST_CMD_TYPE_HASH,NULL},\n    {\"hgetall\",hgetall_cmd_producer,2,\"r\",0,NULL,1,1,1,TEST_CMD_TYPE_HASH,NULL},\n    {\"hincrby\",hincrby_cmd_producer,4,\"wmF\",0,NULL,1,1,1,TEST_CMD_TYPE_HASH,NULL},\n    {\"hincrbyfloat\",hincrbyfloat_cmd_producer,4,\"wmF\",0,NULL,1,1,1,TEST_CMD_TYPE_HASH,NULL},\n    {\"hmget\",hmget_cmd_producer,-3,\"r\",0,NULL,1,1,1,TEST_CMD_TYPE_HASH,NULL},\n    {\"hmset\",hmset_cmd_producer,-4,\"wmA\",0,NULL,1,1,1,TEST_CMD_TYPE_HASH,nck_when_ok},\n    {\"hsetnx\",hsetnx_cmd_producer,4,\"wmFA\",0,NULL,1,1,1,TEST_CMD_TYPE_HASH,nck_when_one},\n    {\"hstrlen\",hstrlen_cmd_producer,3,\"rF\",0,NULL,1,1,1,TEST_CMD_TYPE_HASH,NULL},\n    /* List */\n    {\"rpush\",rpush_cmd_producer,-3,\"wmFA\",0,NULL,1,1,1,TEST_CMD_TYPE_LIST,rpush_cmd_nck},\n    {\"lpush\",lpush_cmd_producer,-3,\"wmFA\",0,NULL,1,1,1,TEST_CMD_TYPE_LIST,lpush_cmd_nck},\n    {\"lrange\",lrange_cmd_producer,4,\"r\",0,NULL,1,1,1,TEST_CMD_TYPE_LIST,NULL},\n    {\"rpop\",rpop_cmd_producer,2,\"wF\",0,NULL,1,1,1,TEST_CMD_TYPE_LIST,NULL},\n    {\"lpop\",lpop_cmd_producer,2,\"wF\",0,NULL,1,1,1,TEST_CMD_TYPE_LIST,NULL},\n    {\"llen\",llen_cmd_producer,2,\"rF\",0,NULL,1,1,1,TEST_CMD_TYPE_LIST,NULL},\n    {\"lrem\",lrem_cmd_producer,4,\"w\",0,NULL,1,1,1,TEST_CMD_TYPE_LIST,NULL},\n    {\"ltrim\",ltrim_cmd_producer,4,\"w\",0,NULL,1,1,1,TEST_CMD_TYPE_LIST,NULL},\n    {\"lindex\",lindex_cmd_producer,3,\"r\",0,NULL,1,1,1,TEST_CMD_TYPE_LIST,NULL},\n    {\"lset\",lset_cmd_producer,4,\"wm\",0,NULL,1,1,1,TEST_CMD_TYPE_LIST,NULL},\n    /* Set */\n    {\"sadd\",sadd_cmd_producer,-3,\"wmFA\",0,NULL,1,1,1,TEST_CMD_TYPE_SET,nck_when_unsigned_integer},\n    {\"smembers\",smembers_cmd_producer,2,\"rS\",0,NULL,1,1,1,TEST_CMD_TYPE_SET,NULL},\n    {\"scard\",scard_cmd_producer,2,\"rF\",0,NULL,1,1,1,TEST_CMD_TYPE_SET,NULL},\n    {\"srem\",srem_cmd_producer,-3,\"wF\",0,NULL,1,1,1,TEST_CMD_TYPE_SET,NULL},\n    {\"sismember\",sismember_cmd_producer,3,\"rF\",0,NULL,1,1,1,TEST_CMD_TYPE_SET,NULL},\n    {\"sunion\",sunion_cmd_producer,-2,\"rS\",0,NULL,1,-1,1,TEST_CMD_TYPE_SET,NULL},\n    {\"sdiff\",sdiff_cmd_producer,-2,\"rS\",0,NULL,1,-1,1,TEST_CMD_TYPE_SET,NULL},\n    {\"sinter\",sinter_cmd_producer,-2,\"rS\",0,NULL,1,-1,1,TEST_CMD_TYPE_SET,NULL},\n    /* SortedSet */\n    {\"zadd\",zadd_cmd_producer,-4,\"wmFA\",0,NULL,1,1,1,TEST_CMD_TYPE_ZSET,zadd_cmd_nck},\n    {\"zincrby\",zincrby_cmd_producer,4,\"wmFA\",0,NULL,1,1,1,TEST_CMD_TYPE_ZSET,zincrby_cmd_nck},\n    {\"zrange\",zrange_cmd_producer,-4,\"r\",0,NULL,1,1,1,TEST_CMD_TYPE_ZSET,NULL},\n    {\"zrevrange\",zrevrange_cmd_producer,-4,\"r\",0,NULL,1,1,1,TEST_CMD_TYPE_ZSET,NULL},\n    {\"zrem\",zrem_cmd_producer,-3,\"wF\",0,NULL,1,1,1,TEST_CMD_TYPE_ZSET,NULL},\n    {\"zcard\",zcard_cmd_producer,2,\"rF\",0,NULL,1,1,1,TEST_CMD_TYPE_ZSET,NULL},\n    {\"zcount\",zcount_cmd_producer,4,\"rF\",0,NULL,1,1,1,TEST_CMD_TYPE_ZSET,NULL},\n    {\"zrangebyscore\",zrangebyscore_cmd_producer,-4,\"r\",0,NULL,1,1,1,TEST_CMD_TYPE_ZSET,NULL},\n    {\"zrevrangebyscore\",zrevrangebyscore_cmd_producer,-4,\"r\",0,NULL,1,1,1,TEST_CMD_TYPE_ZSET,NULL},\n    {\"zrank\",zrank_cmd_producer,3,\"rF\",0,NULL,1,1,1,TEST_CMD_TYPE_ZSET,NULL},\n    {\"zrevrank\",zrevrank_cmd_producer,3,\"rF\",0,NULL,1,1,1,TEST_CMD_TYPE_ZSET,NULL},\n    {\"zscore\",zscore_cmd_producer,3,\"rF\",0,NULL,1,1,1,TEST_CMD_TYPE_ZSET,NULL},\n    {\"zremrangebyscore\",zremrangebyscore_cmd_producer,4,\"w\",0,NULL,1,1,1,TEST_CMD_TYPE_ZSET,NULL},\n    {\"zremrangebyrank\",zremrangebyrank_cmd_producer,4,\"w\",0,NULL,1,1,1,TEST_CMD_TYPE_ZSET,NULL}\n};\n\ndata_unit *data_unit_get(void)\n{\n    data_unit *du = malloc(sizeof(data_unit));\n    du->dp = NULL;\n    du->argc = 0;\n    du->argv = NULL;\n    du->hashvalue = 0;\n    du->data = NULL;\n    return du;\n}\n\nvoid data_unit_put(data_unit *du)\n{\n    int idx;\n    \n    for (idx = 0; idx < du->argc; idx ++) {\n        if (du->argv[idx])\n            sdsfree(du->argv[idx]);\n    }\n    free(du->argv);\n    free(du);\n}\n\nstatic produce_scheme *produce_scheme_create(long long max_cached_keys, int hit_ratio)\n{\n    produce_scheme *ps;\n    int count, idx;\n    int ratio;\n    \n    ps = malloc(sizeof(*ps));\n    if (ps == NULL) return NULL;\n    ps->kcps = NULL;\n    ps->hit_ratio_array = NULL;\n\n    ps->kcps = darray_create(PRODUCE_KEY_CACHE_POOL_COUNT,sizeof(key_cache_array *));\n    for (idx = 0; idx < PRODUCE_KEY_CACHE_POOL_COUNT; idx ++) {\n        key_cache_array **kcp = darray_push(ps->kcps);\n        *kcp = key_cache_array_create(max_cached_keys/PRODUCE_KEY_CACHE_POOL_COUNT);\n        if (*kcp == NULL) {\n            return NULL;\n        }\n    }\n\n    /* Generate the hit ratio. */\n    ps->hit_ratio_array_len = 100;\n    ps->hit_ratio = hit_ratio;\n    ps->hit_ratio_idx = 0;\n    ps->hit_ratio_array = malloc(ps->hit_ratio_array_len*sizeof(int));\n    ratio = ps->hit_ratio_array_len/ps->hit_ratio;\n    if (ratio > 1) {\n        count = ps->hit_ratio;\n        for (idx = 0; idx < ps->hit_ratio_array_len; idx ++) {\n            ps->hit_ratio_array[idx] = 0;\n        }\n    } else {\n        count = ps->hit_ratio_array_len - ps->hit_ratio;\n        for (idx = 0; idx < ps->hit_ratio_array_len; idx ++) {\n            ps->hit_ratio_array[idx] = 1;\n        }\n    }\n    while (count > 0) {\n        idx = rand()%ps->hit_ratio_array_len;\n        if (ratio > 1) {\n            if (ps->hit_ratio_array[idx] == 0) {\n                count --;\n                ps->hit_ratio_array[idx] = 1;\n            }\n        } else {\n            if (ps->hit_ratio_array[idx] == 1) {\n                count --;\n                ps->hit_ratio_array[idx] = 0;\n            }\n        }\n    }\n\n    return ps;\n}\n\nstatic void produce_scheme_destroy(produce_scheme *ps)\n{\n    int j;\n    if (ps->kcps) {\n        for (j = 0; j < PRODUCE_KEY_CACHE_POOL_COUNT; j ++) {\n            key_cache_array **kcp = darray_pop(ps->kcps);\n            if (*kcp)key_cache_array_destroy(*kcp);\n        }\n        darray_destroy(ps->kcps);\n    }\n    \n    free(ps->hit_ratio_array);\n\n    free(ps);\n}\n\nstatic unsigned int get_kcp_idx(int type)\n{\n    unsigned int idx;\n    \n    switch(type)\n    {\n    case TEST_CMD_TYPE_STRING:\n        idx = 0;\n        break;\n        \n    case TEST_CMD_TYPE_LIST:\n        idx = 1;\n        break;\n\n    case TEST_CMD_TYPE_SET:\n        idx = 2;\n        break;\n\n    case TEST_CMD_TYPE_ZSET:\n        idx = 3;\n        break;\n\n    case TEST_CMD_TYPE_HASH:\n        idx = 4;\n        break;\n\n    default:\n        idx = -1;\n        break;\n    }\n\n    return idx;\n}\n\nstatic void set_non_empty_kcps_idx(void)\n{\n    if (cmd_type&TEST_CMD_TYPE_STRING) {\n        non_empty_kcps_idx[non_empty_kcps_count++] = \n            get_kcp_idx(TEST_CMD_TYPE_STRING);\n    }\n    if (cmd_type&TEST_CMD_TYPE_LIST) {\n        non_empty_kcps_idx[non_empty_kcps_count++] = \n            get_kcp_idx(TEST_CMD_TYPE_LIST);\n    }\n    if (cmd_type&TEST_CMD_TYPE_SET) {\n        non_empty_kcps_idx[non_empty_kcps_count++] = \n            get_kcp_idx(TEST_CMD_TYPE_SET);\n    }\n    if (cmd_type&TEST_CMD_TYPE_ZSET) {\n        non_empty_kcps_idx[non_empty_kcps_count++] = \n            get_kcp_idx(TEST_CMD_TYPE_ZSET);\n    }\n    if (cmd_type&TEST_CMD_TYPE_HASH) {\n        non_empty_kcps_idx[non_empty_kcps_count++] = \n            get_kcp_idx(TEST_CMD_TYPE_HASH);\n    }\n}\n\n/* Get a key cache pool from the produce scheme */\nkey_cache_array *kcp_get_from_ps(produce_scheme *ps, data_producer *dp)\n{\n    unsigned int idx;\n    key_cache_array **kcp;\n    \n    if (ps == NULL || ps->kcps == NULL || dp == NULL) return NULL;\n\n    if (dp->cmd_type == TEST_CMD_TYPE_KEY) {\n        if (non_empty_kcps_count==0) {\n            idx = -1;\n        } else {\n            idx = rand()%non_empty_kcps_count;\n            idx = non_empty_kcps_idx[idx];\n            ASSERT(idx >= 0);\n        }  \n    } else {\n        idx = get_kcp_idx(dp->cmd_type);\n    }\n    \n    if (idx >= PRODUCE_KEY_CACHE_POOL_COUNT || idx < 0) {\n        return NULL;\n    }\n\n    kcp = darray_get(ps->kcps, idx);\n\n    return *kcp;\n}\n\nstatic int vrt_produce_threads_init(unsigned int produce_threads_count, \n    long long cached_keys, int hit_ratio)\n{\n    unsigned int idx;\n    darray_init(&produce_threads, produce_threads_count, sizeof(produce_thread));\n    produce_data_threads_count = produce_threads_count;\n    for (idx = 0; idx < produce_threads_count; idx ++) {\n        produce_thread *pt = darray_push(&produce_threads);\n        pt->id = idx;\n        pt->thread_id = 0;\n        pt->ps = produce_scheme_create(cached_keys, hit_ratio);\n        pt->pause = 0;\n        pt->looptimes = 0;\n    }\n    \n    return VRT_OK;\n}\n\nstatic void vrt_produce_threads_deinit(void)\n{\n    produce_thread *pt;\n    while (darray_n(&produce_threads) > 0) {\n        pt = darray_pop(&produce_threads);\n        if (pt->ps) {\n            produce_scheme_destroy(pt->ps);\n            pt->ps = NULL;\n        }\n    }\n    darray_deinit(&produce_threads);\n}\n\nstatic void *vrt_produce_thread_run(void *args)\n{\n    int ret;\n    produce_thread *pt = args;\n    unsigned int idx, j;\n    data_producer **dp;\n    data_unit *du;\n\n    srand(vrt_usec_now()^(int)pthread_self());\n\n    while (1) {\n        /* At begin of this loop */\n        if (pt->pause) {\n            usleep(1000000);    /* sleep 1 second */\n            if (!test_if_need_pause()) {\n                pt->pause = 0;\n            } else {\n                continue;\n            }\n        } else if (pt->looptimes%10000 == 0) {\n            if (test_if_need_pause()) {\n                pt->pause = 1;\n                one_produce_thread_paused();\n                continue;\n            }\n        }\n        \n        idx = rand()%needed_cmd_type_producer_count;\n        dp = darray_get(&needed_cmd_type_producer,idx);\n        du = (*dp)->proc(*dp,pt->ps);\n\n        du->data = pt->ps;\n\n        /* Dispatch the test data */\n        ret = data_dispatch(du);\n        if (ret == -1) {\n            data_unit_put(du);\n        } else if (ret == 1) {\n            usleep(100000);\n        }\n\n        pt->looptimes ++;\n    }\n    \n    return NULL;\n}\n\nstatic int add_to_needed_cmd_type_producer(data_producer *dp)\n{\n    data_producer **dp_elem = darray_push(&needed_cmd_type_producer);\n\n    *dp_elem = dp;\n    needed_cmd_type_producer_count ++;\n    \n    return VRT_OK;\n}\n\nint vrt_produce_data_init(int key_length_range_min,int key_length_range_max, \n    int string_max_length,int fields_max_count,\n    int produce_cmd_types,darray *produce_cmd_blacklist,darray *produce_cmd_whitelist,\n    unsigned int produce_threads_count,long long cached_keys,\n    int hit_ratio)\n{\n    int j, k;\n    \n    key_length_min = key_length_range_min;\n    key_length_max = key_length_range_max;\n    if (key_length_max < key_length_min) return VRT_ERROR;\n    key_length_range_gap = key_length_max-key_length_min;\n    field_length_max = fields_max_count;\n    string_length_max = string_max_length;\n    cmd_type = produce_cmd_types;\n    darray_init(&needed_cmd_type_producer, 100, sizeof(data_producer*));\n\n    producers_count = sizeof(redis_data_producer_table)/sizeof(data_producer);\n    for (j = 0; j < producers_count; j++) {\n        data_producer *dp = redis_data_producer_table+j;\n        char *f = dp->sflags;\n\n        while(*f != '\\0') {\n            switch(*f) {\n            case 'w': dp->flags |= PRO_WRITE; break;\n            case 'r': dp->flags |= PRO_READONLY; break;\n            case 'm': dp->flags |= PRO_DENYOOM; break;\n            case 'a': dp->flags |= PRO_ADMIN; break;\n            case 'p': dp->flags |= PRO_PUBSUB; break;\n            case 's': dp->flags |= PRO_NOSCRIPT; break;\n            case 'R': dp->flags |= PRO_RANDOM; break;\n            case 'S': dp->flags |= PRO_SORT_FOR_SCRIPT; break;\n            case 'l': dp->flags |= PRO_LOADING; break;\n            case 't': dp->flags |= PRO_STALE; break;\n            case 'M': dp->flags |= PRO_SKIP_MONITOR; break;\n            case 'k': dp->flags |= PRO_ASKING; break;\n            case 'F': dp->flags |= PRO_FAST; break;\n            case 'A': dp->flags |= PRO_ADD; break;\n            default: return VRT_ERROR;\n            }\n            f++;\n        }\n\n        if (delete_data_producer == NULL && \n            !strcmp(dp->name,\"del\")) {\n            delete_data_producer = dp;\n        }\n\n        if (produce_cmd_whitelist != NULL) {\n            for (k = 0; k < darray_n(produce_cmd_whitelist); k ++) {\n                sds *cmdname = darray_get(produce_cmd_whitelist, k);\n                if (!strcasecmp(dp->name,*cmdname)) {\n                    add_to_needed_cmd_type_producer(dp);\n                    break;\n                }\n            }\n            continue;\n        }\n\n        /* Check if this is in the blacklist */\n        if (produce_cmd_blacklist != NULL) {\n            int is_in_blacklist = 0;\n            for (k = 0; k < darray_n(produce_cmd_blacklist); k ++) {\n                sds *cmdname = darray_get(produce_cmd_blacklist, k);\n                if (!strcasecmp(dp->name,*cmdname)) {\n                    is_in_blacklist = 1;\n                    break;\n                }\n            }\n            \n            if (is_in_blacklist) {\n                continue;\n            }\n        }\n\n        /* Add the needed command producer */\n        if (dp->cmd_type&cmd_type) {\n            add_to_needed_cmd_type_producer(dp);\n        }\n        if (dp->cmd_type&TEST_CMD_TYPE_EXPIRE && expire_enabled) {\n            add_to_needed_cmd_type_producer(dp);\n        }\n    }\n\n    set_non_empty_kcps_idx();\n\n    if (darray_n(&needed_cmd_type_producer) == 0) {\n        log_error(\"No command need to test\");\n        return VRT_ERROR;\n    }\n\n    if (delete_data_producer == NULL) {\n        return VRT_ERROR;\n    }\n    \n    if (needed_cmd_type_producer_count == 0) {\n        return VRT_ERROR;\n    }\n\n    for (j = 0; j < needed_cmd_type_producer_count; j ++) {\n        data_producer **dp_elem = darray_get(&needed_cmd_type_producer,j);\n        log_debug(LOG_INFO, \"needed test command[%d]: %s\", j, (*dp_elem)->name);\n    }\n\n    vrt_produce_threads_init(produce_threads_count, cached_keys, hit_ratio);\n    \n    return VRT_OK;\n}\n\nvoid vrt_produce_data_deinit(void)\n{\n    vrt_produce_threads_deinit();\n\n    needed_cmd_type_producer.nelem = 0;\n    darray_deinit(&needed_cmd_type_producer);\n}\n\nint vrt_start_produce_data(void)\n{\n    unsigned int i;\n    for (i = 0; i < darray_n(&produce_threads); i ++) {\n        pthread_attr_t attr;\n        produce_thread *pt;\n        pthread_attr_init(&attr);\n        pt = darray_get(&produce_threads, i);\n        pthread_create(&pt->thread_id, \n            &attr, vrt_produce_thread_run, pt);\n    }\n    \n    last_test_begin_time = vrt_sec_now();\n    return VRT_OK;\n}\n\nint vrt_wait_produce_data(void)\n{\n    unsigned int i;\n    /* wait for the produce threads finish */\n\tfor(i = 0; i < darray_n(&produce_threads); i ++){\n\t\tproduce_thread *pt = darray_get(&produce_threads, i);\n\t\tpthread_join(pt->thread_id, NULL);\n\t}\n    \n    return VRT_OK;\n}\n\n/* -----------------------------------------------------------------------------\n * API to get key arguments from data producers\n * ---------------------------------------------------------------------------*/\n\n/* The base case is to use the keys position as given in the data producer table\n * (firstkey, lastkey, step). */\nstatic int *get_keys_using_data_producer_table(data_producer *dp,sds *argv, int argc, int *numkeys) {\n    int j, i = 0, last, *keys;\n\n    if (dp->firstkey == 0) {\n        *numkeys = 0;\n        return NULL;\n    }\n    last = dp->lastkey;\n    if (last < 0) last = argc+last;\n    keys = malloc(sizeof(int)*((last - dp->firstkey)+1));\n    for (j = dp->firstkey; j <= last; j += dp->keystep) {\n        keys[i++] = j;\n    }\n    *numkeys = i;\n    return keys;\n}\n\n/* Return all the arguments that are keys in the command passed via argc / argv.\n *\n * The command returns the positions of all the key arguments inside the array,\n * so the actual return value is an heap allocated array of integers. The\n * length of the array is returned by reference into *numkeys.\n *\n * 'cmd' must be point to the corresponding entry into the redisCommand\n * table, according to the command name in argv[0].\n *\n * This function uses the command table if a command-specific helper function\n * is not required, otherwise it calls the command-specific function. */\nint *get_keys_from_data_producer(data_producer *dp, sds *argv, int argc, int *numkeys) {\n    if (dp->getkeys_proc) {\n        return dp->getkeys_proc(dp,argv,argc,numkeys);\n    } else {\n        return get_keys_using_data_producer_table(dp,argv,argc,numkeys);\n    }\n}\n\nsds get_one_key_from_data_unit(data_unit *du)\n{\n    int numkeys;\n    int *keyindex;\n    sds key;\n\n    keyindex = get_keys_from_data_producer(du->dp,du->argv,du->argc,&numkeys);\n    if (numkeys <= 0) {\n        NOT_REACHED();\n        return NULL;\n    }\n\n    key = du->argv[keyindex[0]];\n    free(keyindex);\n\n    return key;\n}\n\nvoid print_producer_command(data_unit *du)\n{\n    int j;\n    sds cmd = sdsempty();\n    \n    for (j = 0; j < du->argc; j ++) {\n        cmd = sdscatsds(cmd,du->argv[j]);\n        cmd = sdscat(cmd,\" \");\n    }\n    cmd = sdscat(cmd,\"\\n\");\n    log_write_len(cmd,sdslen(cmd));\n    sdsfree(cmd);\n}\n"
  },
  {
    "path": "tests/vrt_produce_data.h",
    "content": "#ifndef _VRT_PRODUCE_DATA_H_\n#define _VRT_PRODUCE_DATA_H_\n\n/* Producer flags. Please check the producer table defined in the vrt_produce_data.c file\n * for more information about the meaning of every flag.\n * This is the meaning of the flags:\n *\n * w: write command (may modify the key space).\n * r: read command  (will never modify the key space).\n * m: may increase memory usage once called. Don't allow if out of memory.\n * a: admin command, like SAVE or SHUTDOWN.\n * p: Pub/Sub related command.\n * f: force replication of this command, regardless of server.dirty.\n * s: command not allowed in scripts.\n * R: random command. Command is not deterministic, that is, the same command\n *    with the same arguments, with the same key space, may have different\n *    results. For instance SPOP and RANDOMKEY are two random commands.\n * S: Sort command output array if called from script, so that the output\n *    is deterministic.\n * l: Allow command while loading the database.\n * t: Allow command while a slave has stale data but is not allowed to\n *    server this data. Normally no command is accepted in this condition\n *    but just a few.\n * M: Do not automatically propagate the command on MONITOR.\n * k: Perform an implicit ASKING for this command, so the command will be\n *    accepted in cluster mode if the slot is marked as 'importing'.\n * F: Fast command: O(1) or O(log(N)) command that should never delay\n *    its execution as long as the kernel scheduler is giving us time.\n *    Note that commands that may trigger a DEL as a side effect (like SET)\n *    are not fast commands.\n * A: Add a new key if the key was not exist before.\n */\n\n#define PRO_WRITE 1                   /* \"w\" flag */\n#define PRO_READONLY 2                /* \"r\" flag */\n#define PRO_DENYOOM 4                 /* \"m\" flag */\n#define PRO_NOT_USED_1 8              /* no longer used flag */\n#define PRO_ADMIN 16                  /* \"a\" flag */\n#define PRO_PUBSUB 32                 /* \"p\" flag */\n#define PRO_NOSCRIPT  64              /* \"s\" flag */\n#define PRO_RANDOM 128                /* \"R\" flag */\n#define PRO_SORT_FOR_SCRIPT 256       /* \"S\" flag */\n#define PRO_LOADING 512               /* \"l\" flag */\n#define PRO_STALE 1024                /* \"t\" flag */\n#define PRO_SKIP_MONITOR 2048         /* \"M\" flag */\n#define PRO_ASKING 4096               /* \"k\" flag */\n#define PRO_FAST 8192                 /* \"F\" flag */\n#define PRO_ADD 16384                 /* \"A\" flag */\n\nstruct data_producer;\nstruct produce_scheme;\nstruct key_cache_array;\n\ntypedef struct data_unit *redis_command_proc(struct data_producer *dp, struct produce_scheme *ps);\ntypedef int *redis_get_keys_proc(struct data_producer *dp, sds *argv, int argc, int *numkeys);\ntypedef int produce_need_cache_key_proc(struct redisReply *reply);\ntypedef struct data_producer {\n    char *name;     /* Command name */\n    redis_command_proc *proc;\n    int arity;\n    \n    char *sflags; /* Flags as string representation, one char per flag. */\n    int flags;    /* The actual flags, obtained from the 'sflags' field. */\n    \n    /* Use a function to determine keys arguments in a command line. */\n    redis_get_keys_proc *getkeys_proc;\n    /* What keys should be loaded in background when calling this command? */\n    int firstkey; /* The first argument that's a key (0 = no keys) */\n    int lastkey;  /* The last argument that's a key */\n    int keystep;  /* The step between first and last key */\n    int cmd_type;\n    produce_need_cache_key_proc *need_cache_key_proc;\n} data_producer;\n\ntypedef struct data_unit {\n    data_producer *dp;\n    int argc;       /* Num of arguments of current command. */\n    sds *argv;    /* Arguments of current command. */\n    \n    unsigned int hashvalue;\n\n    void *data;\n} data_unit;\n\ntypedef struct produce_scheme {\n    darray *kcps;   /* Key cached pools for every type command. */\n\n    int hit_ratio;   /* Hit ratio for the read commands. [0%,100%] */\n    int hit_ratio_idx;   /* [0,hit_ratio_array_len-1] */\n    int hit_ratio_array_len; /* 100 usually */\n    int *hit_ratio_array;    /* Stored 0 or 1 for every element, 1 means used key in the cached keys array. */\n} produce_scheme;\n\nextern data_producer *delete_data_producer;\n\nextern int produce_data_threads_count;\n\nextern int produce_threads_pause_finished_count;\n\nstruct key_cache_array *kcp_get_from_ps(produce_scheme *ps, data_producer *dp);\n\ndata_unit *data_unit_get(void);\nvoid data_unit_put(data_unit *du);\n\nint vrt_produce_data_init(int key_length_range_min, int key_length_range_max, \n    int string_max_length,int fields_max_count,\n    int produce_cmd_types,darray *produce_cmd_blacklist,darray *produce_cmd_whitelist,\n    unsigned int produce_threads_count, long long cached_keys,\n    int hit_ratio);\nvoid vrt_produce_data_deinit(void);\n\nint vrt_start_produce_data(void);\nint vrt_wait_produce_data(void);\n\nint *get_keys_from_data_producer(data_producer *dp, sds *argv, int argc, int *numkeys);\n\nsds get_one_key_from_data_unit(data_unit *du);\n\nvoid print_producer_command(data_unit *du);\n\n#endif\n"
  },
  {
    "path": "tests/vrt_public.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <getopt.h>\n#include <fcntl.h>\n#include <errno.h>\n#include <signal.h>\n#include <pthread.h>\n#include <sys/stat.h>\n#include <sys/utsname.h>\n\n#include <hiredis.h>\n\n#include <darray.h>\n#include <dlog.h>\n\n#include <vrt_util.h>\n#include <vrt_public.h>\n\n/* GCC version >= 4.7 */\n#if defined(__ATOMIC_RELAXED)\n/* GCC version >= 4.1 */\n#elif defined(HAVE_ATOMIC)\n#else\npthread_mutex_t state_locker = PTHREAD_MUTEX_INITIALIZER;\n#endif\n\n#define VIRE_TEST_CONFIG_DEFAULT_EXECUTE_FILE \"src/vire\"\n\nstatic char *execute_file = VIRE_TEST_CONFIG_DEFAULT_EXECUTE_FILE;\n\nstatic sds workdir = NULL;\n\nstatic int vireport = 55556; /* The available port for vire to start */\n\nvoid set_execute_file(char *file)\n{\n    execute_file = file;\n}\n\nstatic sds vire_conf_create(char *dir, int port)\n{\n    sds conf_file;\n    int fd;\n    sds line;\n    \n    conf_file = sdscatfmt(sdsempty(),\"%s\\/vire.conf\",dir);\n\n    fd = open(conf_file,O_WRONLY|O_CREAT|O_TRUNC,0644);\n    if (fd < 0) {\n        test_log_error(\"Open conf file %s failed: %s\", conf_file, strerror(errno));\n        sdsfree(conf_file);\n        return NULL;\n    }\n\n    line = sdsempty();\n\n    line = sdscatfmt(line,\"port %i\\n\",port);\n    write(fd, line, sdslen(line));\n\n    sdsclear(line);\n    line = sdscatfmt(line,\"\\n\");\n    write(fd, line, sdslen(line));\n    \n    close(fd);\n    sdsfree(line);\n    return conf_file;\n}\n\nvire_instance *vire_instance_create(int port)\n{\n    vire_instance *vi;\n\n    vi = malloc(sizeof(vire_instance));\n    vi->host = NULL;\n    vi->port = 0;\n    vi->dir = NULL;\n    vi->conf_file = NULL;\n    vi->pid_file = NULL;\n    vi->log_file = NULL;\n    vi->running = 0;\n    vi->pid = -1;\n    vi->ctx = NULL;\n\n    vi->host = sdsnew(\"127.0.0.1\");\n    vi->port = port;\n    vi->dir = sdscatfmt(sdsempty(),\"%s\\/%i\",workdir,port);\n\n    if (mkdir(vi->dir,0755) < 0) {\n        vire_instance_destroy(vi);\n        return NULL;\n    }\n\n    vi->conf_file = vire_conf_create(vi->dir, port);\n    if (vi->conf_file == NULL) {\n        vire_instance_destroy(vi);\n        return NULL;\n    }\n\n    vi->pid_file = sdscatfmt(sdsempty(),\"%s\\/vire.pid\",vi->dir);\n    vi->log_file = sdscatfmt(sdsempty(),\"%s\\/vire.log\",vi->dir);\n\n    test_log_debug(\"vire host: %s\", vi->host);\n    test_log_debug(\"vire port: %d\", vi->port);\n    test_log_debug(\"vire dir: %s\", vi->dir);\n    test_log_debug(\"vire conf_file: %s\", vi->conf_file);\n    test_log_debug(\"vire pid_file: %s\", vi->pid_file);\n    test_log_debug(\"vire log_file: %s\", vi->log_file);\n\n    return vi;\n}\n\nvoid vire_instance_destroy(vire_instance *vi)\n{\n    if (vi->running) {\n        vire_server_stop(vi);\n    }\n\n    if (vi->dir) {\n        destroy_dir(vi->dir);\n        sdsfree(vi->dir);\n    }\n\n    if (vi->conf_file) {\n        sdsfree(vi->conf_file);\n    }\n\n    if (vi->pid_file) {\n        sdsfree(vi->pid_file);\n    }\n\n    if (vi->log_file) {\n        sdsfree(vi->log_file);\n    }\n\n    if (vi->ctx) {\n        redisFree(vi->ctx);        \n    }\n\n    if (vi->host) {\n        sdsfree(vi->host);\n    }\n\n    free(vi);\n}\n\nint vire_server_run(vire_instance *vi)\n{\n    int ret;\n    pid_t pid;\n    int status;\n    struct timeval timeout = { 3, 500000 }; // 3.5 seconds\n    \n    if ((pid = fork()) < 0) {\n        test_log_error(\"Fork a chind failed: %s\", strerror(errno));\n        return VRT_ERROR;\n    } else if (pid == 0) {\n        ret = execl(execute_file,\"vire\",\"-c\",vi->conf_file,\n            \"-p\",vi->pid_file,\"-o\",vi->log_file,\"-v\",\"8\",NULL);\n        if (ret < 0) {\n            test_log_error(\"Execl the vire server failed: %s\", strerror(errno));\n            return VRT_ERROR;\n        }\n        return;\n    }    \n\n    sleep(1);\n\n    ret = waitpid(pid,NULL,WNOHANG);\n    if (ret != 0) {\n        test_log_debug(\"Run vire server(port %d) failed\",vi->port);\n        return VRT_ERROR;\n    }\n\n    vi->ctx = redisConnectWithTimeout(vi->host,vi->port,timeout);\n    if (vi->ctx == NULL || vi->ctx->err) {\n        test_log_error(\"Connect to %s:%d failed: %s\", \n            vi->host, vi->port, vi->ctx?vi->ctx->errstr:\"out of memory\");\n        if (vi->ctx) {\n            redisFree(vi->ctx);\n            vi->ctx = NULL;\n        }\n        return VRT_ERROR;\n    }\n\n    vi->pid = get_pid_from_reply(vi->ctx,vi->host,vi->port);\n    if (vi->pid < 0) {\n        test_log_error(\"Get pid from %s:%d reply error\", vi->host, vi->port);\n        return VRT_ERROR;\n    } else if (vi->pid != pid) {\n        test_log_error(\"Get wrong pid from %s:%d reply\", vi->host, vi->port);\n        return VRT_ERROR;\n    }\n\n    test_log_debug(\"Run vire server(port %d) success\",vi->port);\n\n    vi->running = 1;\n\n    return VRT_OK;\n}\n\nvoid vire_server_stop(vire_instance *vi)\n{\n    long pid;\n\n    if (!vi->running) return;\n\n    if (vi->pid > 0) {\n        pid = vi->pid;\n    } else if (vi->pid_file) {\n        int fd;\n        char pid_str[20];\n        size_t nread;\n        fd = open(vi->pid_file, O_RDONLY);\n        if (fd < 0) {\n            test_log_error(\"Open pid file %s failed\", vi->pid_file);\n            return;\n        }\n        nread = read(fd,pid_str,20);\n        if (string2l(pid_str,nread,&pid) == 0) {\n            test_log_error(\"Convert pid string %.*s to long failed\",nread,pid_str);\n            return;\n        }\n    } else {\n        pid = get_pid_from_reply(vi->ctx, vi->host, vi->port);\n    }\n\n    if (pid < 0) {\n        test_log_error(\"Get pid failed\");\n        return;\n    }\n\n    kill(pid,9);\n\n    vi->running = 0;\n    vi->pid = -1;\n    if (vi->ctx) {\n        redisFree(vi->ctx);\n        vi->ctx = NULL;\n    }\n}\n\nint create_work_dir(void)\n{\n    sds dirname;\n    dirname = sdscatfmt(sdsempty(), \"tmp_test_%I\", vrt_usec_now());\n    workdir = getAbsolutePath(dirname);\n    sdsfree(dirname);\n\n    if (create_dir(workdir) != VRT_OK) {\n        test_log_error(\"Create workdir %s failed\",workdir);\n        return VRT_ERROR;\n    }\n\n    test_log_debug(\"Create workdir: %s\",workdir);\n    \n    return VRT_OK;\n}\n\nint destroy_work_dir(void)\n{\n    if (workdir == NULL) return VRT_OK;\n\n    if (destroy_dir(workdir) != VRT_OK) {\n        test_log_error(\"Delete the workdir %s failed\",workdir);\n    } else {\n        test_log_debug(\"Delete the workdir: %s\",workdir);\n    }\n    \n    sdsfree(workdir);\n    workdir = NULL;\n    \n    return VRT_OK;\n}\n\nstatic int get_next_port(void)\n{\n    int port = vireport;\n    vireport += 11;\n\n    return port;\n}\n\nvire_instance *start_one_vire_instance(void)\n{\n    int ret;\n    int retry = 0;\n    vire_instance *vi;\n    \n    vi = vire_instance_create(get_next_port());\n    if (vi == NULL) {\n        return NULL;\n    }\n    \n    ret = vire_server_run(vi);\n    while (ret != VRT_OK && retry++ < 10) {\n        vire_instance_destroy(vi);\n        vi = vire_instance_create(get_next_port());\n        if (vi == NULL) {\n            return NULL;\n        }\n        ret = vire_server_run(vi);\n    }\n\n    if (ret != VRT_OK) {\n        vire_instance_destroy(vi);\n        return NULL;\n    }\n\n    return vi;\n}\n\nvoid show_test_result(int result,char *test_content,char *errmsg)\n{\n    if (result == VRT_TEST_OK) {\n        test_log_out(\"[\\033[32mOK\\033[0m]: %s\", test_content);\n    } else if (result == VRT_TEST_ERR) {\n        test_log_out(\"[\\033[31mERR\\033[0m]: %s, \\033[33mfail cause: %s\\033[0m\", test_content, \n            (errmsg==NULL||strlen(errmsg)==0)?\"unknown\":errmsg);\n    }\n}\n\n/************** Key cache pool implement start *************/\nkey_cache_array *key_cache_array_create(long long max_pool_size)\n{\n    long long idx;\n    key_cache_array *kca;\n\n    /* It is too small */\n    if (max_pool_size < 10) return NULL;\n\n    kca = malloc(sizeof(*kca));\n    if (kca == NULL) return NULL;\n\n    kca->cached_keys_count = 0;\n    kca->ckeys_write_idx = 0;\n    kca->max_pool_size = max_pool_size;\n    kca->ckeys = NULL;\n    pthread_mutex_init(&kca->pmutex,NULL);\n\n    kca->ckeys = malloc(max_pool_size*sizeof(sds));\n    for (idx = 0; idx < max_pool_size; idx ++) {\n        kca->ckeys[idx] = sdsempty();\n    }\n\n    return kca;\n}\n\nvoid key_cache_array_destroy(key_cache_array *kca)\n{\n    long long idx;\n    \n    if (kca == NULL) return;\n\n    pthread_mutex_destroy(&kca->pmutex);\n    \n    if (kca->ckeys) {\n        for (idx = 0; idx < kca->max_pool_size; idx ++) {\n            sdsfree(kca->ckeys[idx]);\n        }\n        free(kca->ckeys);\n    }\n\n    free(kca);\n}\n\nint key_cache_array_input(key_cache_array *kca, char *key, size_t keylen)\n{\n    if (kca == NULL || key == NULL || keylen == 0) return VRT_ERROR;\n\n    pthread_mutex_lock(&kca->pmutex);\n    kca->ckeys[kca->ckeys_write_idx]=sdscpylen(kca->ckeys[kca->ckeys_write_idx],key,keylen);\n    kca->ckeys_write_idx++;\n    if (kca->ckeys_write_idx >= kca->max_pool_size) {\n        kca->ckeys_write_idx = 0;\n    }\n    \n    if (kca->cached_keys_count < kca->max_pool_size) {\n        kca->cached_keys_count++;\n    }\n    pthread_mutex_unlock(&kca->pmutex);\n    \n    return VRT_OK;\n}\n\nsds key_cache_array_random(key_cache_array *kca)\n{\n    unsigned int idx, randomval;\n    sds key;\n\n    if (kca == NULL) {\n        return NULL;\n    }\n\n    randomval = (unsigned int)rand();\n    \n    pthread_mutex_lock(&kca->pmutex);\n    if (kca->cached_keys_count == 0) {\n        pthread_mutex_unlock(&kca->pmutex);\n        return NULL;\n    }\n\n    idx = randomval%(unsigned int)kca->cached_keys_count;\n\n    key = sdsdup(kca->ckeys[idx]);\n    pthread_mutex_unlock(&kca->pmutex);\n    \n    return key;\n}\n\n/************** Key cache pool implement end *************/\n\nlong long get_longlong_from_info_reply(redisReply *reply, char *name)\n{\n    sds *lines;\n    size_t line_len, len;\n    int count, j;\n    long long value = -1;\n\n    len = strlen(name);\n    \n    if (reply->type != REDIS_REPLY_STRING) {\n        test_log_error(\"Reply for 'info' command from vire type %d is error\",\n            reply->type);\n        return -1;\n    }\n\n    lines = sdssplitlen(reply->str,reply->len,\"\\r\\n\",2,&count);\n    if (lines == NULL) {\n        test_log_error(\"Reply for 'info server' command from vire is error\");\n        return -1;\n    }\n\n    for (j = 0; j < count; j ++) {\n        line_len = sdslen(lines[j]);\n        if (line_len > len+1 && !strncmp(name, lines[j], len)) {\n            if (string2ll(lines[j]+len+1,line_len-len-1,&value) == 0) {\n                test_log_error(\"Convert pid string %.*s to long failed\",\n                    line_len-len-1,lines[j]+len+1);\n                sdsfreesplitres(lines,count);\n                return -1;\n            }\n            break;\n        }\n    }\n\n    sdsfreesplitres(lines,count);\n    return value;\n}\n\nredisReply *steal_hiredis_redisreply(redisReply *r)\n{\n    redisReply *reply;\n\n    reply = calloc(1,sizeof(*reply));\n    if (reply == NULL) {\n        return NULL;\n    }\n\n    reply->type = r->type;\n    reply->integer = r->integer;\n    reply->len = r->len;\n    reply->str = r->str;\n    reply->elements = r->elements;\n    reply->element = r->element;\n\n    r->len = 0;\n    r->str = NULL;\n    r->elements = 0;\n    r->element = NULL;\n\n    return reply;\n}\n\nint check_two_replys_if_same(redisReply *reply1, redisReply *reply2)\n{\n    if (reply1 == NULL || reply2 == NULL) {\n        return 1;\n    }\n    \n    if (reply1->type != reply2->type) {\n        return 1;\n    }\n\n    if (reply1->type == REDIS_REPLY_STRING || \n        reply1->type == REDIS_REPLY_STATUS ||\n        reply1->type == REDIS_REPLY_ERROR) {\n        if (reply1->len != reply2->len) {\n            return reply1->len-reply2->len;\n        }\n        \n        return memcmp(reply1->str, reply2->str, reply1->len);\n    } else if (reply1->type == REDIS_REPLY_ARRAY) {\n        size_t j;\n        if (reply1->elements != reply2->elements) {\n            return (reply1->elements-reply2->elements);\n        }\n\n        for (j = 0; j < reply1->elements; j ++) {\n            int ret = check_two_replys_if_same(reply1->element[j], reply2->element[j]);\n            if (ret != 0) return ret;\n        }\n        return 0;\n    } else if (reply1->type == REDIS_REPLY_INTEGER) {\n        return (reply1->integer-reply2->integer);\n    } else if (reply1->type == REDIS_REPLY_NIL) {\n        return 0;\n    } else {\n        test_log_error(\"reply type %d is error\", reply1->type);\n    }\n\n    return 0;\n}\n\nstruct sort_unit {\n    size_t nfield;\n    void **fields;\n    unsigned int idx_cmp;\n    int (*fcmp)(const void *,const void *);\n};\n\nstatic int element_cmp_multi_step(const void *ele1,const void *ele2)\n{\n    struct sort_unit *su1 = (struct sort_unit *)ele1, *su2 = (struct sort_unit *)ele2;\n\n    ASSERT(su1->fcmp == su2->fcmp);\n    ASSERT(su1->nfield == su2->nfield);\n    ASSERT(su1->idx_cmp == su2->idx_cmp);\n    ASSERT(su1->idx_cmp < su1->nfield);\n\n    return su1->fcmp(&(su1->fields[su1->idx_cmp]),&(su2->fields[su2->idx_cmp]));\n}\n\n/* The element in the array must a pointer. */\nint sort_array_by_step(void **element, size_t elements, \n    int step, int idx_cmp, int (*fcmp)(const void *,const void *))\n{\n    struct sort_unit *sus;\n    size_t count, j, k;\n\n    if (elements <= 1)\n        return VRT_OK;\n\n    if (step <= 0)\n        return VRT_ERROR;\n    \n    if (step == 1) {\n        qsort(element, elements, sizeof(void *), fcmp);\n        return VRT_OK;\n    }\n\n    if (elements%step != 0)\n        return VRT_ERROR;\n\n    count = elements/step;\n    if (count == 0)\n        return VRT_ERROR;\n    sus = calloc(count,sizeof(struct sort_unit));\n    for (j = 0; j < count; j ++) {\n        sus[j].nfield = step;\n        sus[j].idx_cmp = idx_cmp;\n        sus[j].fcmp = fcmp;\n        sus[j].fields = malloc(step*sizeof(void*));\n        for (k = 0; k < step; k ++) {\n            sus[j].fields[k] = element[j*step+k];\n        }\n    }\n\n    qsort(sus, count, sizeof(struct sort_unit), element_cmp_multi_step);\n\n    for (j = 0; j < count; j ++) {\n        for (k = 0; k < step; k ++) {\n            element[j*step+k] = sus[j].fields[k];\n        }\n        free(sus[j].fields);\n    }\n    free(sus);\n    return VRT_OK;\n}\n\n/* The reply type must be string */\nint reply_string_binary_compare(const void *r1,const void *r2)\n{\n    redisReply *reply1 = *(redisReply **)r1, *reply2 = *(redisReply **)r2;\n    int minlen;\n    int cmp;\n\n    minlen = (reply1->len < reply2->len) ? reply1->len:reply2->len;\n    cmp = memcmp(reply1->str,reply2->str,minlen);\n    if (cmp == 0) return reply1->len - reply2->len;\n    return cmp;\n}\n\n/* command types string is like 'string,list,set,zset,hash,server,key,expire' */\nint parse_command_types(char *command_types_str)\n{\n    int types = 0;\n    sds *types_strs;\n    int types_count, j;\n\n    types_strs = sdssplitlen(command_types_str,strlen(command_types_str),\",\",1,&types_count);\n    if (types_strs == NULL) {\n        return -1;\n    } else if (types_count <= 0) {\n        sdsfreesplitres(types_strs,types_count);\n        return -1;\n    }\n    \n    for (j = 0; j < types_count; j ++) {\n        if (!strcasecmp(types_strs[j],\"string\")) {\n            types |= TEST_CMD_TYPE_STRING;\n        } else if (!strcasecmp(types_strs[j],\"list\")) {\n            types |= TEST_CMD_TYPE_LIST;\n        } else if (!strcasecmp(types_strs[j],\"set\")) {\n            types |= TEST_CMD_TYPE_SET;\n        } else if (!strcasecmp(types_strs[j],\"zset\")) {\n            types |= TEST_CMD_TYPE_ZSET;\n        } else if (!strcasecmp(types_strs[j],\"hash\")) {\n            types |= TEST_CMD_TYPE_HASH;\n        } else if (!strcasecmp(types_strs[j],\"server\")) {\n            types |= TEST_CMD_TYPE_SERVER;\n        } else if (!strcasecmp(types_strs[j],\"key\")) {\n            types |= TEST_CMD_TYPE_KEY;\n        } else if (!strcasecmp(types_strs[j],\"expire\")) {\n            types |= TEST_CMD_TYPE_EXPIRE;\n        } else {\n            sdsfreesplitres(types_strs,types_count);\n            return -1;\n        } \n    }\n\n    sdsfreesplitres(types_strs,types_count);\n\n    return types;\n}\n\n/* command list string is like 'get,set,lrange,zrange' */\ndarray *parse_command_list(char *command_list_str)\n{\n    darray *commands;\n    sds *command_elem;\n    sds *command_strs;\n    int command_count, j;\n\n    command_strs = sdssplitlen(command_list_str,strlen(command_list_str),\",\",1,&command_count);\n    if (command_strs == NULL) {\n        return -1;\n    } else if (command_count <= 0) {\n        sdsfreesplitres(command_strs,command_count);\n        return -1;\n    }\n\n    commands = darray_create(command_count, sizeof(sds));\n    for (j = 0; j < command_count; j ++) {\n        command_elem = darray_push(commands);\n        *command_elem = command_strs[j];\n        command_strs[j] = NULL;\n    }\n\n    sdsfreesplitres(command_strs,command_count);\n\n    return commands;\n}\n\nchar *\nget_key_type_string(int keytype)\n{\n    switch (keytype) {\n    case REDIS_STRING:\n        return \"string\";\n        break;\n    case REDIS_LIST:\n        return \"list\";\n        break;\n    case REDIS_SET:\n        return \"set\";\n        break;\n    case REDIS_ZSET:\n        return \"zset\";\n        break;\n    case REDIS_HASH:\n        return \"hash\";\n        break;\n    default:\n        return \"unknow\";\n        break;\n    }\n\n    return \"unknow\";\n}\n\n"
  },
  {
    "path": "tests/vrt_public.h",
    "content": "#ifndef _VRT_PUBLIC_H_\n#define _VRT_PUBLIC_H_\n\n#ifdef HAVE_CONFIG_H\n# include <config.h>\n#endif\n\n#include <dspecialconfig.h>\n\n#include <unistd.h>\n\n#include <hiredis.h>\n\nstruct darray;\nstruct key_cache_pool;\n\n#define VRT_TEST_OK     0\n#define VRT_TEST_ERR    1\n\n#define TEST_CMD_TYPE_STRING    (1<<0)\n#define TEST_CMD_TYPE_LIST      (1<<1)\n#define TEST_CMD_TYPE_SET       (1<<2)\n#define TEST_CMD_TYPE_ZSET      (1<<3)\n#define TEST_CMD_TYPE_HASH      (1<<4)\n#define TEST_CMD_TYPE_SERVER    (1<<5)\n#define TEST_CMD_TYPE_KEY       (1<<6)\n#define TEST_CMD_TYPE_EXPIRE    (1<<7)\n\n/* key types */\n#define REDIS_STRING    0\n#define REDIS_LIST      1\n#define REDIS_SET       2\n#define REDIS_ZSET      3\n#define REDIS_HASH      4\n\n/* State control API */\n/* GCC version >= 4.7 */\n#if defined(__ATOMIC_RELAXED)\n#define update_state_add(_value, _n) __atomic_add_fetch(&_value, (_n), __ATOMIC_RELAXED)\n#define update_state_sub(_value, _n) __atomic_sub_fetch(&_value, (_n), __ATOMIC_RELAXED)\n#define update_state_set(_value, _n) __atomic_store_n(&_value, (_n), __ATOMIC_RELAXED)\n#define update_state_get(_value, _v) do {         \\\n    __atomic_load(&_value, _v, __ATOMIC_RELAXED); \\\n} while(0)\n\n#define TEST_STATE_LOCK_TYPE \"__ATOMIC_RELAXED\"\n/* GCC version >= 4.1 */\n#elif defined(HAVE_ATOMIC)\n#define update_state_add(_value, _n) __sync_add_and_fetch(&_value, (_n))\n#define update_state_sub(_value, _n) __sync_sub_and_fetch(&_value, (_n))\n#define update_state_set(_value, _n) __sync_lock_test_and_set(&_value, (_n))\n#define update_state_get(_value, _v) do {         \\\n    (*_v) = __sync_add_and_fetch(&_value, 0);     \\\n} while(0)\n\n#define TEST_STATE_LOCK_TYPE \"HAVE_ATOMIC\"\n#else\nextern pthread_mutex_t state_locker;\n\n#define update_state_add(_value, _n) do {   \\\n    pthread_mutex_lock(&state_locker);      \\\n    _value += (_n);                         \\\n    pthread_mutex_unlock(&state_locker);    \\\n} while(0)\n\n#define update_state_sub(_value, _n) do {   \\\n    pthread_mutex_lock(&state_locker);      \\\n    _value -= (_n);                         \\\n    pthread_mutex_unlock(&state_locker);    \\\n} while(0)\n\n#define update_state_set(_value, _n) do {   \\\n    pthread_mutex_lock(&state_locker);      \\\n    _value = (_n);                          \\\n    pthread_mutex_unlock(&state_locker);    \\\n} while(0)\n\n#define update_state_get(_value, _v) do {   \\\n    pthread_mutex_lock(&state_locker);      \\\n    (*_v) = _value;                         \\\n    pthread_mutex_unlock(&state_locker);    \\\n} while(0)\n\n#define TEST_STATE_LOCK_TYPE \"pthread_mutex_lock\"\n#endif\n\ntypedef struct vire_instance {\n    sds host;\n    int port;\n    \n    sds dir;\n    sds conf_file;\n    sds pid_file;\n    sds log_file;\n\n    int running;\n    int pid;\n    redisContext *ctx;\n} vire_instance;\n\nvoid set_execute_file(char *file);\n\nvire_instance *vire_instance_create(int port);\nvoid vire_instance_destroy(vire_instance *vi);\n\nint vire_server_run(vire_instance *vi);\nvoid vire_server_stop(vire_instance *vi);\n\nint create_work_dir(void);\nint destroy_work_dir(void);\n\nvire_instance *start_one_vire_instance(void);\n\nvoid show_test_result(int result,char *test_content,char *errmsg);\n\ntypedef struct key_cache_array {\n    long long cached_keys_count;\n    long long ckeys_write_idx;\n    long long max_pool_size;    /* Max keys count that can be cached in the ckeys array. */\n    sds *ckeys;    /* Cached keys that may exist in the target redis/vire servers. */\n    pthread_mutex_t pmutex;\n} key_cache_array;\n\nkey_cache_array *key_cache_array_create(long long max_pool_size);\nvoid key_cache_array_destroy(key_cache_array *kca);\nint key_cache_array_input(key_cache_array *kca, char *key, size_t keylen);\nsds key_cache_array_random(key_cache_array *kca);\n\nlong long get_longlong_from_info_reply(redisReply *reply, char *name);\n\nredisReply *steal_hiredis_redisreply(redisReply *r);\nint check_two_replys_if_same(redisReply *reply1, redisReply *reply2);\nint sort_array_by_step(void **element, size_t elements, int step, int idx_cmp, int (*fcmp)(const void *,const void *));\nint reply_string_binary_compare(const void *r1,const void *r2);\n\nint parse_command_types(char *command_types_str);\nstruct darray *parse_command_list(char *command_list_str);\n\nchar *get_key_type_string(int keytype);\n\n#endif\n"
  },
  {
    "path": "tests/vrt_simple.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <getopt.h>\n#include <fcntl.h>\n#include <errno.h>\n#include <signal.h>\n#include <sys/stat.h>\n#include <sys/utsname.h>\n\n#include <hiredis.h>\n\n#include <vrt_util.h>\n#include <vrt_public.h>\n#include <vrt_simple.h>\n\n#define ERRMSG_MAX_LEN LOG_MAX_LEN-100\nstatic char errmsg[ERRMSG_MAX_LEN];\n\nstatic int simple_test_cmd_get_set(vire_instance *vi)\n{\n    char *key = \"test_cmd_get_set-key\";\n    char *value = \"test_cmd_get_set-value\";\n    char *MESSAGE = \"GET/SET simple test\";\n    redisReply * reply = NULL;\n    \n    reply = redisCommand(vi->ctx, \"set %s %s\", key, value);\n    if (reply == NULL || reply->type != REDIS_REPLY_STATUS || \n        reply->len != 2 || strcmp(reply->str,\"OK\")) {\n        goto error;\n    }\n    freeReplyObject(reply);\n\n    reply = redisCommand(vi->ctx, \"get %s\", key);\n    if (reply == NULL || reply->type != REDIS_REPLY_STRING || \n        reply->len != strlen(value) || strcmp(reply->str,value)) {\n        goto error;\n    }\n    freeReplyObject(reply);\n    reply = NULL;\n\n    show_test_result(VRT_TEST_OK,MESSAGE,errmsg);\n\n    return 1;\n\nerror:\n\n    if (reply) freeReplyObject(reply);\n\n    show_test_result(VRT_TEST_ERR,MESSAGE,errmsg);\n    errmsg[0] = '\\0';\n\n    return 0;\n}\n\nstatic int simple_test_cmd_setnx(vire_instance *vi)\n{\n    char *key = \"test_cmd_setnx-key\";\n    char *value = \"test_cmd_setnx-value\";\n    char *MESSAGE = \"SETNX simple test\";\n    redisReply * reply = NULL;\n\n    reply = redisCommand(vi->ctx, \"del %s\", key);\n    if (reply == NULL || reply->type == REDIS_REPLY_ERROR) {\n        goto error;\n    }\n    freeReplyObject(reply);\n    \n    reply = redisCommand(vi->ctx, \"setnx %s %s\", key, value);\n    if (reply == NULL || reply->type != REDIS_REPLY_INTEGER || \n        reply->integer != 1) {\n        goto error;\n    }\n    freeReplyObject(reply);\n\n    reply = redisCommand(vi->ctx, \"get %s\", key);\n    if (reply == NULL || reply->type != REDIS_REPLY_STRING || \n        reply->len != strlen(value) || strcmp(reply->str,value)) {\n        goto error;\n    }\n    freeReplyObject(reply);\n\n    reply = redisCommand(vi->ctx, \"setnx %s %s\", key, value);\n    if (reply == NULL || reply->type != REDIS_REPLY_INTEGER || \n        reply->integer != 0) {\n        goto error;\n    }\n    freeReplyObject(reply);\n\n    show_test_result(VRT_TEST_OK,MESSAGE,errmsg);\n\n    return 1;\n\nerror:\n\n    if (reply) freeReplyObject(reply);\n\n    show_test_result(VRT_TEST_ERR,MESSAGE,errmsg);\n    errmsg[0] = '\\0';\n    \n    return 0;\n}\n\nstatic int simple_test_cmd_setex(vire_instance *vi)\n{\n    char *key = \"test_cmd_setex-key\";\n    char *value = \"test_cmd_setex-value\";\n    long long seconds = 100;\n    char *MESSAGE = \"SETEX simple test\";\n    redisReply * reply = NULL;\n    \n    reply = redisCommand(vi->ctx, \"setex %s %lld %s\", key, seconds, value);\n    if (reply == NULL || reply->type != REDIS_REPLY_STATUS || \n        reply->len != 2 || strcmp(reply->str,\"OK\")) {\n        goto error;\n    }\n    freeReplyObject(reply);\n\n    reply = redisCommand(vi->ctx, \"get %s\", key);\n    if (reply == NULL || reply->type != REDIS_REPLY_STRING || \n        reply->len != strlen(value) || strcmp(reply->str,value)) {\n        goto error;\n    }\n    freeReplyObject(reply);\n\n    reply = redisCommand(vi->ctx, \"ttl %s\", key);\n    if (reply == NULL || reply->type != REDIS_REPLY_INTEGER || \n        reply->integer > seconds ||  reply->integer < seconds - 2) {\n        goto error;\n    }\n    freeReplyObject(reply);\n\n    show_test_result(VRT_TEST_OK,MESSAGE,errmsg);\n\n    return 1;\n\nerror:\n\n    if (reply) freeReplyObject(reply);\n\n    show_test_result(VRT_TEST_ERR,MESSAGE,errmsg);\n    errmsg[0] = '\\0';\n\n    return 0;\n}\n\nstatic int simple_test_cmd_psetex(vire_instance *vi)\n{\n    char *key = \"test_cmd_psetex-key\";\n    char *value = \"test_cmd_psetex-value\";\n    long long milliseconds = 100000;\n    char *MESSAGE = \"PSETEX simple test\";\n    redisReply * reply = NULL;\n    \n    reply = redisCommand(vi->ctx, \"psetex %s %lld %s\", key, milliseconds, value);\n    if (reply == NULL || reply->type != REDIS_REPLY_STATUS || \n        reply->len != 2 || strcmp(reply->str,\"OK\")) {\n        goto error;\n    }\n    freeReplyObject(reply);\n\n    reply = redisCommand(vi->ctx, \"get %s\", key);\n    if (reply == NULL || reply->type != REDIS_REPLY_STRING || \n        reply->len != strlen(value) || strcmp(reply->str,value)) {\n        goto error;\n    }\n    freeReplyObject(reply);\n\n    reply = redisCommand(vi->ctx, \"pttl %s\", key);\n    if (reply == NULL || reply->type != REDIS_REPLY_INTEGER || \n        reply->integer > milliseconds ||  reply->integer < milliseconds - 2000) {\n        goto error;\n    }\n    freeReplyObject(reply);\n\n    show_test_result(VRT_TEST_OK,MESSAGE,errmsg);\n\n    return 1;\n\nerror:\n\n    if (reply) freeReplyObject(reply);\n\n    show_test_result(VRT_TEST_ERR,MESSAGE,errmsg);\n    errmsg[0] = '\\0';\n\n    return 0;\n}\n\nstatic int simple_test_cmd_incr(vire_instance *vi)\n{\n    char *key = \"test_cmd_incr-key\";\n    long long n = 0, incr_times = 100;\n    char *MESSAGE = \"INCR simple test\";\n    redisReply * reply = NULL;\n\n    reply = redisCommand(vi->ctx, \"del %s\", key);\n    if (reply == NULL || reply->type == REDIS_REPLY_ERROR) {\n        goto error;\n    }\n    freeReplyObject(reply);\n\n    while (n < incr_times) {\n        reply = redisCommand(vi->ctx, \"incr %s\", key);\n        if (reply == NULL || reply->type != REDIS_REPLY_INTEGER || \n            reply->integer != n+1) {\n            vrt_scnprintf(errmsg, LOG_MAX_LEN, \"incr %lld times error\", n+1);\n            goto error;\n        }\n        freeReplyObject(reply);\n        \n        n ++;\n    }\n\n    reply = redisCommand(vi->ctx, \"get %s\", key);\n    if (reply == NULL || reply->type != REDIS_REPLY_STRING) {\n        goto error;\n    } else {\n        long long value;\n        if (!string2ll(reply->str,reply->len,&value) || value != incr_times) {\n            vrt_scnprintf(errmsg, LOG_MAX_LEN, \"incr to %lld error, %s in fact\", \n                incr_times, reply->str);\n            goto error;\n        }\n    }\n    freeReplyObject(reply);\n\n    reply = redisCommand(vi->ctx, \"set %s %s\", key, \"a\");\n    if (reply == NULL || reply->type != REDIS_REPLY_STATUS || \n        reply->len != 2 || strcmp(reply->str,\"OK\")) {\n        goto error;\n    }\n    freeReplyObject(reply);\n\n    reply = redisCommand(vi->ctx, \"incr %s\", key);\n    if (reply == NULL || reply->type != REDIS_REPLY_ERROR) {\n        goto error;\n    }\n    freeReplyObject(reply);\n\n    show_test_result(VRT_TEST_OK,MESSAGE,errmsg);\n\n    return 1;\n\nerror:\n\n    if (reply) freeReplyObject(reply);\n\n    show_test_result(VRT_TEST_ERR,MESSAGE,errmsg);\n    errmsg[0] = '\\0';\n\n    return 0;\n}\n\nstatic int simple_test_cmd_decr(vire_instance *vi)\n{\n    char *key = \"test_cmd_decr-key\";\n    long long n = 0, decr_times = 100;\n    char *MESSAGE = \"DECR simple test\";\n    redisReply * reply = NULL;\n\n    reply = redisCommand(vi->ctx, \"del %s\", key);\n    if (reply == NULL || reply->type == REDIS_REPLY_ERROR) {\n        goto error;\n    }\n    freeReplyObject(reply);\n\n    while (n < decr_times) {\n        reply = redisCommand(vi->ctx, \"decr %s\", key);\n        if (reply == NULL || reply->type != REDIS_REPLY_INTEGER || \n            reply->integer + n != -1) {\n            vrt_scnprintf(errmsg, LOG_MAX_LEN, \"incr %lld times error\", n+1);\n            goto error;\n        }\n        freeReplyObject(reply);\n        \n        n ++;\n    }\n\n    reply = redisCommand(vi->ctx, \"get %s\", key);\n    if (reply == NULL || reply->type != REDIS_REPLY_STRING) {\n        goto error;\n    } else {\n        long long value;\n        if (!string2ll(reply->str,reply->len,&value) || value + decr_times != 0) {\n            vrt_scnprintf(errmsg, LOG_MAX_LEN, \"decr to -%lld error, %s in fact\", \n                decr_times, reply->str);\n            goto error;\n        }\n    }\n    freeReplyObject(reply);\n\n    reply = redisCommand(vi->ctx, \"set %s %s\", key, \"a\");\n    if (reply == NULL || reply->type != REDIS_REPLY_STATUS || \n        reply->len != 2 || strcmp(reply->str,\"OK\")) {\n        goto error;\n    }\n    freeReplyObject(reply);\n\n    reply = redisCommand(vi->ctx, \"incr %s\", key);\n    if (reply == NULL || reply->type != REDIS_REPLY_ERROR) {\n        goto error;\n    }\n    freeReplyObject(reply);\n\n    show_test_result(VRT_TEST_OK,MESSAGE,errmsg);\n\n    return 1;\n\nerror:\n\n    if (reply) freeReplyObject(reply);\n\n    show_test_result(VRT_TEST_ERR,MESSAGE,errmsg);\n    errmsg[0] = '\\0';\n\n    return 0;\n}\n\nstatic int simple_test_cmd_incrby(vire_instance *vi)\n{\n    char *key = \"test_cmd_incrby-key\";\n    long long n = 0, incrby_times = 100, incrby_step = 3;\n    char *MESSAGE = \"INCRBY simple test\";\n    redisReply * reply = NULL;\n\n    reply = redisCommand(vi->ctx, \"del %s\", key);\n    if (reply == NULL || reply->type == REDIS_REPLY_ERROR) {\n        goto error;\n    }\n    freeReplyObject(reply);\n\n    while (n < incrby_times) {\n        reply = redisCommand(vi->ctx, \"incrby %s %lld\", key, incrby_step);\n        if (reply == NULL || reply->type != REDIS_REPLY_INTEGER || \n            reply->integer != (n+1)*incrby_step) {\n            vrt_scnprintf(errmsg, LOG_MAX_LEN, \"incrby %lld %lld times error\", \n                incrby_step, n+1);\n            goto error;\n        }\n        freeReplyObject(reply);\n        \n        n ++;\n    }\n\n    reply = redisCommand(vi->ctx, \"get %s\", key);\n    if (reply == NULL || reply->type != REDIS_REPLY_STRING) {\n        goto error;\n    } else {\n        long long value;\n        if (!string2ll(reply->str,reply->len,&value) || \n            value != incrby_times*incrby_step) {\n            vrt_scnprintf(errmsg, LOG_MAX_LEN, \"incrby to %lld error, %s in fact\", \n                incrby_times*incrby_step, reply->str);\n            goto error;\n        }\n    }\n    freeReplyObject(reply);\n\n    reply = redisCommand(vi->ctx, \"set %s %s\", key, \"a\");\n    if (reply == NULL || reply->type != REDIS_REPLY_STATUS || \n        reply->len != 2 || strcmp(reply->str,\"OK\")) {\n        goto error;\n    }\n    freeReplyObject(reply);\n\n    reply = redisCommand(vi->ctx, \"incrby %s %lld\", key, incrby_step);\n    if (reply == NULL || reply->type != REDIS_REPLY_ERROR) {\n        goto error;\n    }\n    freeReplyObject(reply);\n\n    show_test_result(VRT_TEST_OK,MESSAGE,errmsg);\n\n    return 1;\n\nerror:\n\n    if (reply) freeReplyObject(reply);\n\n    show_test_result(VRT_TEST_ERR,MESSAGE,errmsg);\n    errmsg[0] = '\\0';\n\n    return 0;\n}\n\nstatic int simple_test_cmd_decrby(vire_instance *vi)\n{\n    char *key = \"test_cmd_decrby-key\";\n    long long n = 0, decrby_times = 100, decrby_step = 3;\n    char *MESSAGE = \"DECRBY simple test\";\n    redisReply * reply = NULL;\n\n    reply = redisCommand(vi->ctx, \"del %s\", key);\n    if (reply == NULL || reply->type == REDIS_REPLY_ERROR) {\n        goto error;\n    }\n    freeReplyObject(reply);\n\n    while (n < decrby_times) {\n        reply = redisCommand(vi->ctx, \"decrby %s %lld\", key, decrby_step);\n        if (reply == NULL || reply->type != REDIS_REPLY_INTEGER || \n            reply->integer + (n+1)*decrby_step != 0) {\n            vrt_scnprintf(errmsg, LOG_MAX_LEN, \"decrby %lld %lld times error\", \n                decrby_step, n+1);\n            goto error;\n        }\n        freeReplyObject(reply);\n        \n        n ++;\n    }\n\n    reply = redisCommand(vi->ctx, \"get %s\", key);\n    if (reply == NULL || reply->type != REDIS_REPLY_STRING) {\n        goto error;\n    } else {\n        long long value;\n        if (!string2ll(reply->str,reply->len,&value) || \n            value + decrby_times*decrby_step != 0) {\n            vrt_scnprintf(errmsg, LOG_MAX_LEN, \"decrby to -%lld error, %s in fact\", \n                decrby_times*decrby_step, reply->str);\n            goto error;\n        }\n    }\n    freeReplyObject(reply);\n\n    reply = redisCommand(vi->ctx, \"set %s %s\", key, \"a\");\n    if (reply == NULL || reply->type != REDIS_REPLY_STATUS || \n        reply->len != 2 || strcmp(reply->str,\"OK\")) {\n        goto error;\n    }\n    freeReplyObject(reply);\n\n    reply = redisCommand(vi->ctx, \"decrby %s %lld\", key, decrby_step);\n    if (reply == NULL || reply->type != REDIS_REPLY_ERROR) {\n        goto error;\n    }\n    freeReplyObject(reply);\n\n    show_test_result(VRT_TEST_OK,MESSAGE,errmsg);\n\n    return 1;\n\nerror:\n\n    if (reply) freeReplyObject(reply);\n\n    show_test_result(VRT_TEST_ERR,MESSAGE,errmsg);\n    errmsg[0] = '\\0';\n\n    return 0;\n}\n\nstatic int simple_test_cmd_append(vire_instance *vi)\n{\n    char *key = \"test_cmd_append-key\";\n    char *final_value = \"pqwpioqjqwoiuqiorueljsakhdflkqueuquewqwei[oqfiqpq-0ewrq0hdalkjz.zhjaidhfioahd\";\n    char *start = final_value, *pos = start, *end = final_value+strlen(final_value);\n    int step = 3, len;\n    char buf[20];\n    char *MESSAGE = \"APPEND simple test\";\n    redisReply * reply = NULL;\n\n    reply = redisCommand(vi->ctx, \"del %s\", key);\n    if (reply == NULL || reply->type == REDIS_REPLY_ERROR) {\n        goto error;\n    }\n    freeReplyObject(reply);\n\n    while (pos < end) {        \n        len = (end-pos >= step) ? step : (end-pos);\n        memcpy(buf,pos,len);\n        buf[len] = '\\0';\n        reply = redisCommand(vi->ctx, \"append %s %s\", key, buf);\n        if (reply == NULL || reply->type != REDIS_REPLY_INTEGER) {\n            goto error;\n        } else if (reply->integer != pos-start+len) {\n            vrt_scnprintf(errmsg, LOG_MAX_LEN, \"append %s %s error\", \n                key, buf);\n            goto error;\n        }\n        freeReplyObject(reply);\n        \n        pos += len;\n    }\n\n    reply = redisCommand(vi->ctx, \"get %s\", key);\n    if (reply == NULL || reply->type != REDIS_REPLY_STRING || \n        reply->len != strlen(final_value) || strcmp(reply->str,final_value)) {\n        goto error;\n    }\n    freeReplyObject(reply);\n\n    show_test_result(VRT_TEST_OK,MESSAGE,errmsg);\n\n    return 1;\n\nerror:\n\n    if (reply) freeReplyObject(reply);\n\n    show_test_result(VRT_TEST_ERR,MESSAGE,errmsg);\n    errmsg[0] = '\\0';\n\n    return 0;\n}\n\nstatic int simple_test_cmd_strlen(vire_instance *vi)\n{\n    char *key = \"test_cmd_strlen-key\";\n    char *value = \"test_cmd_strlen-value\";\n    char *MESSAGE = \"STRLEN simple test\";\n    redisReply * reply = NULL;\n    \n    reply = redisCommand(vi->ctx, \"set %s %s\", key, value);\n    if (reply == NULL || reply->type != REDIS_REPLY_STATUS || \n        reply->len != 2 || strcmp(reply->str,\"OK\")) {\n        goto error;\n    }\n    freeReplyObject(reply);\n\n    reply = redisCommand(vi->ctx, \"strlen %s\", key);\n    if (reply == NULL || reply->type != REDIS_REPLY_INTEGER || \n        reply->integer != strlen(value)) {\n        goto error;\n    }\n    freeReplyObject(reply);\n    reply = NULL;\n\n    show_test_result(VRT_TEST_OK,MESSAGE,errmsg);\n\n    return 1;\n\nerror:\n\n    if (reply) freeReplyObject(reply);\n\n    show_test_result(VRT_TEST_ERR,MESSAGE,errmsg);\n    errmsg[0] = '\\0';\n\n    return 0;\n}\n\nstatic int simple_test_cmd_getset(vire_instance *vi)\n{\n    char *key = \"test_cmd_getset-key\";\n    char *oldvalue = \"test_cmd_getset-oldvalue\";\n    char *newvalue = \"test_cmd_getset-newvalue\";\n    char *MESSAGE = \"GETSET simple test\";\n    redisReply * reply = NULL;\n    \n    reply = redisCommand(vi->ctx, \"set %s %s\", key, oldvalue);\n    if (reply == NULL || reply->type != REDIS_REPLY_STATUS || \n        reply->len != 2 || strcmp(reply->str,\"OK\")) {\n        goto error;\n    }\n    freeReplyObject(reply);\n\n    reply = redisCommand(vi->ctx, \"getset %s %s\", key, newvalue);\n    if (reply == NULL || reply->type != REDIS_REPLY_STRING || \n        reply->len != strlen(oldvalue) || strcmp(reply->str,oldvalue)) {\n        goto error;\n    }\n    freeReplyObject(reply);\n\n    reply = redisCommand(vi->ctx, \"get %s\", key);\n    if (reply == NULL || reply->type != REDIS_REPLY_STRING || \n        reply->len != strlen(newvalue) || strcmp(reply->str,newvalue)) {\n        goto error;\n    }\n    freeReplyObject(reply);\n\n    show_test_result(VRT_TEST_OK,MESSAGE,errmsg);\n\n    return 1;\n\nerror:\n\n    if (reply) freeReplyObject(reply);\n\n    show_test_result(VRT_TEST_ERR,MESSAGE,errmsg);\n    errmsg[0] = '\\0';\n\n    return 0;\n}\n\nstatic int simple_test_cmd_incrbyfloat(vire_instance *vi)\n{\n    char *key = \"test_cmd_incrbyfloat-key\";\n    char *final_value = \"314.00000000000000022\";\n    long long n = 0, incrby_times = 100;\n    float incrbyfloat_step = 3.14;\n    char *MESSAGE = \"INCRBYFLOAT simple test\";\n    redisReply * reply = NULL;\n\n    reply = redisCommand(vi->ctx, \"del %s\", key);\n    if (reply == NULL || reply->type == REDIS_REPLY_ERROR) {\n        goto error;\n    }\n    freeReplyObject(reply);\n\n    while (n < incrby_times) {\n        reply = redisCommand(vi->ctx, \"incrbyfloat %s %f\", key, incrbyfloat_step);\n        if (reply == NULL || reply->type != REDIS_REPLY_STRING) {\n            vrt_scnprintf(errmsg, LOG_MAX_LEN, \"incrbyfloat %f %lld times error\", \n                incrbyfloat_step, n+1);\n            goto error;\n        }\n        freeReplyObject(reply);\n        \n        n ++;\n    }\n\n    reply = redisCommand(vi->ctx, \"get %s\", key);\n    if (reply == NULL || reply->type != REDIS_REPLY_STRING || \n        strcmp(reply->str,final_value)) {\n        vrt_scnprintf(errmsg, LOG_MAX_LEN, \"incrbyfloat to %s error\", final_value);\n        goto error;\n    }\n    freeReplyObject(reply);\n\n    reply = redisCommand(vi->ctx, \"set %s %s\", key, \"a\");\n    if (reply == NULL || reply->type != REDIS_REPLY_STATUS || \n        reply->len != 2 || strcmp(reply->str,\"OK\")) {\n        goto error;\n    }\n    freeReplyObject(reply);\n\n    reply = redisCommand(vi->ctx, \"incrbyfloat %s %f\", key, incrbyfloat_step);\n    if (reply == NULL || reply->type != REDIS_REPLY_ERROR) {\n        goto error;\n    }\n    freeReplyObject(reply);\n\n    show_test_result(VRT_TEST_OK,MESSAGE,errmsg);\n\n    return 1;\n\nerror:\n\n    if (reply) freeReplyObject(reply);\n\n    show_test_result(VRT_TEST_ERR,MESSAGE,errmsg);\n    errmsg[0] = '\\0';\n\n    return 0;\n}\n\nstatic int simple_test_cmd_getbit_setbit_bitcount(vire_instance *vi)\n{\n    char *key = \"test_cmd_getbit_setbit_bitcount-key\";\n    char *MESSAGE = \"GETBIT/SETBIT/BITCOUNT simple test\";\n    int begin = 11, step = 3, times = 79, n;\n    redisReply * reply = NULL;\n\n    n = 0;\n    while(n < times) {\n        reply = redisCommand(vi->ctx, \"setbit %s %d 1\", key, begin+n*step);\n        if (reply == NULL || reply->type != REDIS_REPLY_INTEGER || \n            reply->integer != 0) {\n            goto error;\n        }\n        freeReplyObject(reply);\n\n        n ++;\n    }\n\n    n = 0;\n    while(n < times) {\n        reply = redisCommand(vi->ctx, \"getbit %s %d\", key, begin+n*step);\n        if (reply == NULL || reply->type != REDIS_REPLY_INTEGER || \n            reply->integer != 1) {\n            goto error;\n        }\n        freeReplyObject(reply);\n\n        n ++;\n    }\n\n    reply = redisCommand(vi->ctx, \"bitcount %s\", key);\n    if (reply == NULL || reply->type != REDIS_REPLY_INTEGER || \n        reply->integer != times) {\n        goto error;\n    }\n    freeReplyObject(reply);\n\n    show_test_result(VRT_TEST_OK,MESSAGE,errmsg);\n\n    return 1;\n\nerror:\n\n    if (reply) freeReplyObject(reply);\n\n    show_test_result(VRT_TEST_ERR,MESSAGE,errmsg);\n    errmsg[0] = '\\0';\n\n    return 0;\n}\n\nstatic int simple_test_cmd_getrange_setrange(vire_instance *vi)\n{\n    char *key = \"test_cmd_getrange_setrange-key\";\n    char *MESSAGE = \"GETRANGE/SETRANGE simple test\";\n    char *range_value = \"o090pl[]m,187h\";\n    int begin = 11, step = 53, times = 79, n;\n    redisReply * reply = NULL;\n\n    n = 0;\n    while(n < times) {\n        reply = redisCommand(vi->ctx, \"setrange %s %d %s\", \n            key, begin+n*step, range_value);\n        if (reply == NULL || reply->type != REDIS_REPLY_INTEGER || \n            reply->integer != begin+n*step+strlen(range_value)) {\n            vrt_scnprintf(errmsg, LOG_MAX_LEN, \"setrange %s %d %s error\", \n                key, begin+n*step, range_value);\n            goto error;\n        }\n        freeReplyObject(reply);\n\n        n ++;\n    }\n\n    n = 0;\n    while(n < times) {\n        reply = redisCommand(vi->ctx, \"getrange %s %d %d\", key, \n            begin+n*step, begin+n*step+strlen(range_value)-1);\n        if (reply == NULL || reply->type != REDIS_REPLY_STRING || \n            reply->len != strlen(range_value) || strcmp(reply->str, range_value)) {\n            vrt_scnprintf(errmsg, LOG_MAX_LEN, \"getrange %s %d %d error\", \n                key, begin+n*step, begin+n*step+strlen(range_value)-1);\n            goto error;\n        }\n        freeReplyObject(reply);\n\n        n ++;\n    }\n\n    show_test_result(VRT_TEST_OK,MESSAGE,errmsg);\n\n    return 1;\n\nerror:\n\n    if (reply) freeReplyObject(reply);\n\n    show_test_result(VRT_TEST_ERR,MESSAGE,errmsg);\n    errmsg[0] = '\\0';\n\n    return 0;\n}\n\nstatic int simple_test_cmd_bitpos(vire_instance *vi)\n{\n    char *key = \"test_cmd_bitpos-key\";\n    char *MESSAGE = \"BITPOS simple test\";\n    int pos = 11;\n    redisReply * reply = NULL;\n\n    reply = redisCommand(vi->ctx, \"del %s\", key);\n    if (reply == NULL || reply->type == REDIS_REPLY_ERROR) {\n        goto error;\n    }\n    freeReplyObject(reply);\n\n    reply = redisCommand(vi->ctx, \"bitpos %s 1\", key);\n    if (reply == NULL || reply->type != REDIS_REPLY_INTEGER || \n        reply->integer != -1) {\n        vrt_scnprintf(errmsg, LOG_MAX_LEN, \"bitpos %s 1 first time error\", \n            key);\n        goto error;\n    }\n    freeReplyObject(reply);\n\n    reply = redisCommand(vi->ctx, \"setbit %s 1 0\", key);\n    if (reply == NULL || reply->type != REDIS_REPLY_INTEGER || \n        reply->integer != 0) {\n        vrt_scnprintf(errmsg, LOG_MAX_LEN, \"setbit %s 1 0 error\", \n            key);\n        goto error;\n    }\n    freeReplyObject(reply);\n\n    reply = redisCommand(vi->ctx, \"bitpos %s 1\", key);\n    if (reply == NULL || reply->type != REDIS_REPLY_INTEGER || \n        reply->integer != -1) {\n        vrt_scnprintf(errmsg, LOG_MAX_LEN, \"bitpos %s 1 second time error\", \n            key);\n        goto error;\n    }\n    freeReplyObject(reply);\n\n    reply = redisCommand(vi->ctx, \"setbit %s %d 1\", key, pos);\n    if (reply == NULL || reply->type != REDIS_REPLY_INTEGER || \n        reply->integer != 0) {\n        vrt_scnprintf(errmsg, LOG_MAX_LEN, \"setbit %s %d 1 error\", \n            key, pos);\n        goto error;\n    }\n    freeReplyObject(reply);\n\n    reply = redisCommand(vi->ctx, \"bitpos %s 1\", key);\n    if (reply == NULL || reply->type != REDIS_REPLY_INTEGER || \n        reply->integer != pos) {\n        vrt_scnprintf(errmsg, LOG_MAX_LEN, \"bitpos %s 1 third time error\", \n            key);\n        goto error;\n    }\n    freeReplyObject(reply);\n\n    show_test_result(VRT_TEST_OK,MESSAGE,errmsg);\n\n    return 1;\n\nerror:\n\n    if (reply) freeReplyObject(reply);\n\n    show_test_result(VRT_TEST_ERR,MESSAGE,errmsg);\n    errmsg[0] = '\\0';\n\n    return 0;\n}\n\n#define MGET_MSET_KEYS_COUNT 333\nstatic int simple_test_cmd_mget_mset(vire_instance *vi)\n{\n    char *key = \"test_cmd_mget_mset-key\";\n    char *value = \"test_cmd_mget_mset-value\";\n    char *MESSAGE = \"MGET/MSET simple test\";\n    char keys[MGET_MSET_KEYS_COUNT][30];\n    char values[MGET_MSET_KEYS_COUNT][30];\n    char *argv[1+2*MGET_MSET_KEYS_COUNT];\n    size_t argvlen[1+2*MGET_MSET_KEYS_COUNT];\n    int j, idx;\n    redisReply *reply = NULL;\n\n    for (j = 0; j < MGET_MSET_KEYS_COUNT; j ++) {\n        vrt_scnprintf(keys[j], 30,\"%s%d\", key, j);\n        vrt_scnprintf(values[j], 30,\"%s%d\", value, j);\n    }\n    \n    argv[0] = \"mset\";\n    argvlen[0] = strlen(argv[0]);\n    idx = 1;\n    for (j = 0; j < MGET_MSET_KEYS_COUNT; j ++) {\n        argv[idx] = keys[j];\n        argvlen[idx++] = strlen(keys[j]);\n        argv[idx] = values[j];\n        argvlen[idx++] = strlen(values[j]);\n    }\n    \n    reply = redisCommandArgv(vi->ctx, 1+2*MGET_MSET_KEYS_COUNT, argv, argvlen);\n    if (reply == NULL || reply->type != REDIS_REPLY_STATUS || \n        reply->len != 2 || strcmp(reply->str,\"OK\")) {\n        vrt_scnprintf(errmsg, LOG_MAX_LEN, \"mset %d keys error\", \n            MGET_MSET_KEYS_COUNT);\n        goto error;\n    }\n    freeReplyObject(reply);\n\n    argv[0] = \"mget\";\n    argvlen[0] = strlen(argv[0]);\n    for (j = 1; j < 1+MGET_MSET_KEYS_COUNT; j ++) {\n        argv[j] = keys[j-1];\n        argvlen[j] = strlen(argv[j]);\n    }\n\n    reply = redisCommandArgv(vi->ctx, 1+MGET_MSET_KEYS_COUNT, argv, argvlen);\n    if (reply == NULL || reply->type != REDIS_REPLY_ARRAY || \n        reply->elements != MGET_MSET_KEYS_COUNT) {\n        vrt_scnprintf(errmsg, LOG_MAX_LEN, \"mget %d keys error\", \n            MGET_MSET_KEYS_COUNT);\n        goto error;\n    }\n    for (j = 0; j < MGET_MSET_KEYS_COUNT; j ++) {\n        redisReply *reply_sub = reply->element[j];\n        if (reply_sub == NULL ||\n            reply_sub->type != REDIS_REPLY_STRING || \n            reply_sub->len != strlen(values[j]) || \n            strcmp(reply_sub->str, values[j]))\n            goto error;\n    }\n    freeReplyObject(reply);\n    reply = NULL;\n\n    show_test_result(VRT_TEST_OK,MESSAGE,errmsg);\n\n    return 1;\n\nerror:\n\n    if (reply) freeReplyObject(reply);\n\n    show_test_result(VRT_TEST_ERR,MESSAGE,errmsg);\n    errmsg[0] = '\\0';\n\n    return 0;\n}\n\n#define TEST_HASH_ENCODED_ZIPLIST    0\n#define TEST_HASH_ENCODED_HT         1\n#define TEST_HASH_ENCODED_CAUSED_BY_FILED    0\n#define TEST_HASH_ENCODED_CAUSED_BY_VALUE    1\n#define TEST_HASH_ENCODED_CAUSED_BY_ALL      2\n#define TEST_HASH_ENCODED_ZIPLIST_FIELD_COUNT    56\n#define TEST_HASH_ENCODED_HT_FIELD_COUNT         678\n#define TEST_HASH_ENCODED_ZIPLIST_VALUE_LEN      21\n#define TEST_HASH_ENCODED_HT_VALUE_LEN           111\n\nstruct test_hash_member {\n    char *field;\n    char *value;\n};\n\nstatic int test_hash_member_length(struct test_hash_member **thms)\n{\n    int j = 0;\n    while (thms[j]) {\n        j ++;\n    }\n    return j;\n}\n\nstatic void test_hash_members_destroy(struct test_hash_member **thms)\n{\n    int j = 0;\n    while (thms[j]) {\n        free(thms[j]->field);\n        free(thms[j]->value);\n        free(thms[j]);\n        j ++;\n    }\n    free(thms);\n}\n\nstatic struct test_hash_member **simple_test_hash_init(vire_instance *vi, char *key, int hash_encode, int encode_cause)\n{\n    char *field = \"test_hash-field\";\n    char *value = \"test_hash-value\";\n    int field_count, value_len;\n    int j,n;\n    struct test_hash_member **thms = NULL;\n    redisReply *reply = NULL;\n\n    reply = redisCommand(vi->ctx, \"del %s\", key);\n    if (reply == NULL || reply->type == REDIS_REPLY_ERROR) {\n        goto error;\n    }\n    freeReplyObject(reply);\n\n    if (hash_encode == TEST_HASH_ENCODED_ZIPLIST) {\n        field_count = TEST_HASH_ENCODED_ZIPLIST_FIELD_COUNT;\n        value_len = TEST_HASH_ENCODED_ZIPLIST_VALUE_LEN;\n    } else if (encode_cause == TEST_HASH_ENCODED_CAUSED_BY_FILED) {\n        field_count = TEST_HASH_ENCODED_HT_FIELD_COUNT;\n        value_len = TEST_HASH_ENCODED_ZIPLIST_VALUE_LEN;\n    } else if (encode_cause == TEST_HASH_ENCODED_CAUSED_BY_VALUE) {\n        field_count = TEST_HASH_ENCODED_ZIPLIST_FIELD_COUNT;\n        value_len = TEST_HASH_ENCODED_HT_VALUE_LEN;\n    } else if (encode_cause == TEST_HASH_ENCODED_CAUSED_BY_ALL) {\n        field_count = TEST_HASH_ENCODED_HT_FIELD_COUNT;\n        value_len = TEST_HASH_ENCODED_HT_VALUE_LEN;\n    }\n    \n    thms = malloc((field_count+1)*sizeof(struct test_hash_member*));\n    for (j = 0; j < field_count; j ++) {\n        thms[j] = malloc(sizeof(struct test_hash_member));\n        thms[j]->field = malloc(30*sizeof(char));\n        thms[j]->value = malloc((value_len+1)*sizeof(char));\n        vrt_scnprintf(thms[j]->field, 30, \"%s%d\", field, j);\n        n = vrt_scnprintf(thms[j]->value, value_len, \"%s%d\", value, j);\n        if (n < value_len) {\n            memset(thms[j]->value,'x',value_len-n);\n            thms[j]->value[value_len] = '\\0';\n        }\n    }\n    thms[field_count] = NULL;\n\n    for (j = 0; j < field_count; j ++) { \n        reply = redisCommand(vi->ctx, \"hset %s %s %s\", \n            key, thms[j]->field, thms[j]->value);\n        if (reply == NULL || reply->type != REDIS_REPLY_INTEGER || \n            reply->integer != 1) {\n            goto error;\n        }\n        freeReplyObject(reply);\n    }\n\n    reply = redisCommand(vi->ctx, \"hlen %s\", key);\n    if (reply == NULL || reply->type != REDIS_REPLY_INTEGER || \n        reply->integer != field_count) {\n        goto error;\n    }\n    freeReplyObject(reply);\n\n    reply = redisCommand(vi->ctx, \"object encoding %s\", key);\n    if (reply == NULL || reply->type != REDIS_REPLY_STRING) {\n        goto error;\n    } else {\n        if (hash_encode == TEST_HASH_ENCODED_ZIPLIST) {\n            if(reply->len != 7 || strcmp(reply->str, \"ziplist\")) {\n                goto error;\n            }\n        } else {\n            if(reply->len != 9 || strcmp(reply->str, \"hashtable\")) {\n                goto error;\n            }\n        }\n    }\n    freeReplyObject(reply);\n    \n    return thms;\n\nerror:\n\n    if (thms) {\n        test_hash_members_destroy(thms);\n        thms = NULL;\n    }\n\n    if (reply) freeReplyObject(reply);\n\n    return NULL;\n}\n\nstatic int simple_test_hash_encode(vire_instance *vi)\n{\n    char *key = \"test_hash_encode\";\n    char *MESSAGE = \"HASH ENCODE simple test\";\n    struct test_hash_member **thms;\n    \n    thms = simple_test_hash_init(vi,key,TEST_HASH_ENCODED_ZIPLIST,TEST_HASH_ENCODED_CAUSED_BY_FILED);\n    if (thms == NULL) {\n        goto error;\n    }\n    test_hash_members_destroy(thms);\n    thms = simple_test_hash_init(vi,key,TEST_HASH_ENCODED_ZIPLIST,TEST_HASH_ENCODED_CAUSED_BY_VALUE);\n    if (thms == NULL) {\n        goto error;\n    }\n    test_hash_members_destroy(thms);\n    thms = simple_test_hash_init(vi,key,TEST_HASH_ENCODED_HT,TEST_HASH_ENCODED_CAUSED_BY_FILED);\n    if (thms == NULL) {\n        goto error;\n    }\n    test_hash_members_destroy(thms);\n    thms = simple_test_hash_init(vi,key,TEST_HASH_ENCODED_HT,TEST_HASH_ENCODED_CAUSED_BY_VALUE);\n    if (thms == NULL) {\n        goto error;\n    }\n    test_hash_members_destroy(thms);\n    thms = simple_test_hash_init(vi,key,TEST_HASH_ENCODED_HT,TEST_HASH_ENCODED_CAUSED_BY_ALL);\n    if (thms == NULL) {\n        goto error;\n    }\n    test_hash_members_destroy(thms);\n    \n    show_test_result(VRT_TEST_OK,MESSAGE,errmsg);\n\n    return 1;\n\nerror:\n\n    show_test_result(VRT_TEST_ERR,MESSAGE,errmsg);\n    errmsg[0] = '\\0';\n\n    return 0;\n}\n\nstatic int simple_test_cmd_hget_hset(vire_instance *vi)\n{\n    char *key = \"test_cmd_hget_hset-key\";\n    char *field = \"test_cmd_hget_hset-field\";\n    char *value = \"test_cmd_hget_hset-value\";\n    char *MESSAGE = \"HGET/HSET simple test\";\n    redisReply * reply = NULL;\n    struct test_hash_member **thms = NULL;\n    int idx;\n\n    reply = redisCommand(vi->ctx, \"del %s\", key);\n    if (reply == NULL || reply->type == REDIS_REPLY_ERROR) {\n        goto error;\n    }\n    freeReplyObject(reply);\n    \n    reply = redisCommand(vi->ctx, \"hset %s %s %s\", key, field, value);\n    if (reply == NULL || reply->type != REDIS_REPLY_INTEGER || \n        reply->integer != 1) {\n        goto error;\n    }\n    freeReplyObject(reply);\n\n    reply = redisCommand(vi->ctx, \"hget %s %s\", key, field);\n    if (reply == NULL || reply->type != REDIS_REPLY_STRING || \n        reply->len != strlen(value) || strcmp(reply->str,value)) {\n        goto error;\n    }\n    freeReplyObject(reply);\n\n    thms = simple_test_hash_init(vi,key,TEST_HASH_ENCODED_HT,TEST_HASH_ENCODED_CAUSED_BY_FILED);\n    if (thms == NULL) {\n        goto error;\n    }\n    reply = redisCommand(vi->ctx, \"hset %s %s %s\", key, field, value);\n    if (reply == NULL || reply->type != REDIS_REPLY_INTEGER || \n        reply->integer != 1) {\n        goto error;\n    }\n    freeReplyObject(reply);\n    reply = redisCommand(vi->ctx, \"hget %s %s\", key, field);\n    if (reply == NULL || reply->type != REDIS_REPLY_STRING || \n        reply->len != strlen(value) || strcmp(reply->str,value)) {\n        goto error;\n    }\n    freeReplyObject(reply);\n    idx = test_hash_member_length(thms)/2;\n    reply = redisCommand(vi->ctx, \"hget %s %s\", key, thms[idx]->field);\n    if (reply == NULL || reply->type != REDIS_REPLY_STRING || \n        reply->len != strlen(thms[idx]->value) || strcmp(reply->str,thms[idx]->value)) {\n        goto error;\n    }\n    freeReplyObject(reply);\n    test_hash_members_destroy(thms);\n\n    show_test_result(VRT_TEST_OK,MESSAGE,errmsg);\n\n    return 1;\n\nerror:\n\n    if (reply) freeReplyObject(reply);\n    if (thms) test_hash_members_destroy(thms);\n\n    show_test_result(VRT_TEST_ERR,MESSAGE,errmsg);\n    errmsg[0] = '\\0';\n\n    return 0;\n}\n\nstatic int simple_test_cmd_hlen(vire_instance *vi)\n{\n    char *key = \"test_cmd_hlen-key\";\n    char *field = \"test_cmd_hlen-field\";\n    char *value = \"test_cmd_hlen-value\";\n    char *MESSAGE = \"HLEN simple test\";\n    redisReply * reply = NULL;\n    int hash_len, j;\n\n    hash_len = 51;\n    reply = redisCommand(vi->ctx, \"del %s\", key);\n    if (reply == NULL || reply->type == REDIS_REPLY_ERROR) {\n        goto error;\n    }\n    freeReplyObject(reply);\n    for (j = 0; j < hash_len; j ++) {\n        reply = redisCommand(vi->ctx, \"hset %s %s%d %s\", key, field, j, value);\n        if (reply == NULL || reply->type != REDIS_REPLY_INTEGER || \n            reply->integer != 1) {\n            goto error;\n        }\n        freeReplyObject(reply);\n    }\n    reply = redisCommand(vi->ctx, \"hlen %s\", key);\n    if (reply == NULL || reply->type != REDIS_REPLY_INTEGER || \n        reply->integer != hash_len) {\n        goto error;\n    }\n    freeReplyObject(reply);\n\n    hash_len = 5111;\n    reply = redisCommand(vi->ctx, \"del %s\", key);\n    if (reply == NULL || reply->type == REDIS_REPLY_ERROR) {\n        goto error;\n    }\n    freeReplyObject(reply);\n    for (j = 0; j < hash_len; j ++) {\n        reply = redisCommand(vi->ctx, \"hset %s %s%d %s\", key, field, j, value);\n        if (reply == NULL || reply->type != REDIS_REPLY_INTEGER || \n            reply->integer != 1) {\n            goto error;\n        }\n        freeReplyObject(reply);\n    }\n    reply = redisCommand(vi->ctx, \"hlen %s\", key);\n    if (reply == NULL || reply->type != REDIS_REPLY_INTEGER || \n        reply->integer != hash_len) {\n        goto error;\n    }\n    freeReplyObject(reply);\n\n    show_test_result(VRT_TEST_OK,MESSAGE,errmsg);\n\n    return 1;\n\nerror:\n\n    if (reply) freeReplyObject(reply);\n\n    show_test_result(VRT_TEST_ERR,MESSAGE,errmsg);\n    errmsg[0] = '\\0';\n\n    return 0;\n}\n\nstatic int simple_test_cmd_hdel(vire_instance *vi)\n{\n    char *key = \"test_cmd_hdel-key\";\n    char *field = \"test_cmd_hdel-field\";\n    char *value = \"test_cmd_hdel-value\";\n    char *MESSAGE = \"HDEL simple test\";\n    redisReply * reply = NULL;\n    struct test_hash_member **thms = NULL;\n    int idx;\n\n    reply = redisCommand(vi->ctx, \"del %s\", key);\n    if (reply == NULL || reply->type == REDIS_REPLY_ERROR) {\n        goto error;\n    }\n    freeReplyObject(reply);\n    \n    reply = redisCommand(vi->ctx, \"hset %s %s%d %s\", key, field, 1, value);\n    if (reply == NULL || reply->type != REDIS_REPLY_INTEGER || \n        reply->integer != 1) {\n        goto error;\n    }\n    freeReplyObject(reply);\n    reply = redisCommand(vi->ctx, \"hset %s %s%d %s\", key, field, 2, value);\n    if (reply == NULL || reply->type != REDIS_REPLY_INTEGER || \n        reply->integer != 1) {\n        goto error;\n    }\n    freeReplyObject(reply);\n\n    reply = redisCommand(vi->ctx, \"hget %s %s%d\", key, field, 1);\n    if (reply == NULL || reply->type != REDIS_REPLY_STRING || \n        reply->len != strlen(value) || strcmp(reply->str, value)) {\n        goto error;\n    }\n    freeReplyObject(reply);\n    reply = redisCommand(vi->ctx, \"hdel %s %s%d\", key, field, 1);\n    if (reply == NULL || reply->type != REDIS_REPLY_INTEGER || \n        reply->integer != 1) {\n        goto error;\n    }\n    freeReplyObject(reply);\n    reply = redisCommand(vi->ctx, \"hget %s %s%d\", key, field, 1);\n    if (reply == NULL || reply->type != REDIS_REPLY_NIL) {\n        goto error;\n    }\n    freeReplyObject(reply);\n    \n    reply = redisCommand(vi->ctx, \"hdel %s %s%d\", key, field, 2);\n    if (reply == NULL || reply->type != REDIS_REPLY_INTEGER || \n        reply->integer != 1) {\n        goto error;\n    }\n    freeReplyObject(reply);\n    reply = redisCommand(vi->ctx, \"exists %s\", key);\n    if (reply == NULL || reply->type != REDIS_REPLY_INTEGER || \n        reply->integer != 0) {\n        goto error;\n    }\n    freeReplyObject(reply);\n    \n    thms = simple_test_hash_init(vi,key,TEST_HASH_ENCODED_HT,TEST_HASH_ENCODED_CAUSED_BY_FILED);\n    if (thms == NULL) {\n        goto error;\n    }\n    idx = test_hash_member_length(thms)/2;\n    reply = redisCommand(vi->ctx, \"hdel %s %s\", key, thms[idx]->field);\n    if (reply == NULL || reply->type != REDIS_REPLY_INTEGER || \n        reply->integer != 1) {\n        goto error;\n    }\n    freeReplyObject(reply);    \n    reply = redisCommand(vi->ctx, \"hget %s %s\", key, thms[idx]->field);\n    if (reply == NULL || reply->type != REDIS_REPLY_NIL) {\n        goto error;\n    }\n    freeReplyObject(reply);\n    test_hash_members_destroy(thms);\n\n    show_test_result(VRT_TEST_OK,MESSAGE,errmsg);\n\n    return 1;\n\nerror:\n\n    if (reply) freeReplyObject(reply);\n    if (thms) test_hash_members_destroy(thms);\n\n    show_test_result(VRT_TEST_ERR,MESSAGE,errmsg);\n    errmsg[0] = '\\0';\n\n    return 0;\n}\n\nstatic int simple_test_cmd_pfadd_pfcount(vire_instance *vi)\n{\n    char *key = \"test_cmd_pfadd_pfcount-key\";\n    char *value = \"test_cmd_pfadd_pfcount-value\";\n    char *MESSAGE = \"PFADD/PFCOUNT simple test\";\n    redisReply * reply = NULL;\n    int n = 0, count = 20329, repeat;\n\n    while (repeat < 2) {\n        int expect_count;\n        reply = redisCommand(vi->ctx, \"pfadd %s %s%d\", key, value, n++);\n        if (reply == NULL || reply->type != REDIS_REPLY_INTEGER) {\n            goto error;\n        }\n        freeReplyObject(reply);\n        if (n >= count) {\n            repeat++;\n            n = 0;\n        }\n\n        if (repeat == 0) {\n            expect_count = n;\n        } else {\n            expect_count = count;\n        }\n        \n        reply = redisCommand(vi->ctx, \"pfcount %s\", key);\n        if (reply == NULL || reply->type != REDIS_REPLY_INTEGER) {\n            goto error;\n        }\n        if (reply->integer != (long long)expect_count) {\n            float mistake = ((float)expect_count-(float)reply->integer)/(float)expect_count;\n            if (mistake < -0.02 || mistake > 0.02) {\n                vrt_scnprintf(errmsg, LOG_MAX_LEN, \"pfadd %d different elements is not approximated pfcount returned %lld, mistake %f\", \n                    expect_count, reply->integer, mistake);\n                goto error;\n            }\n        }\n        freeReplyObject(reply);\n    }\n\n    show_test_result(VRT_TEST_OK,MESSAGE,errmsg);\n\n    return 1;\n\nerror:\n\n    if (reply) freeReplyObject(reply);\n\n    show_test_result(VRT_TEST_ERR,MESSAGE,errmsg);\n    errmsg[0] = '\\0';\n\n    return 0;\n}\n\nint simple_test(void)\n{\n    vire_instance *vi;\n    int ok_count = 0, all_count = 0;\n    \n    vi = start_one_vire_instance();\n    if (vi == NULL) {\n        test_log_error(\"Run vire instance failed\");\n        return;\n    }\n\n    errmsg[0] = '\\0';\n\n    /* String */\n    ok_count+=simple_test_cmd_get_set(vi); all_count++;\n    ok_count+=simple_test_cmd_setnx(vi); all_count++;\n    ok_count+=simple_test_cmd_setex(vi); all_count++;\n    ok_count+=simple_test_cmd_psetex(vi); all_count++;\n    ok_count+=simple_test_cmd_incr(vi); all_count++;\n    ok_count+=simple_test_cmd_decr(vi); all_count++;\n    ok_count+=simple_test_cmd_incrby(vi); all_count++;\n    ok_count+=simple_test_cmd_decrby(vi); all_count++;\n    ok_count+=simple_test_cmd_append(vi); all_count++;\n    ok_count+=simple_test_cmd_strlen(vi); all_count++;\n    ok_count+=simple_test_cmd_getset(vi); all_count++;\n    ok_count+=simple_test_cmd_incrbyfloat(vi); all_count++;\n    ok_count+=simple_test_cmd_getbit_setbit_bitcount(vi); all_count++;\n    ok_count+=simple_test_cmd_getrange_setrange(vi); all_count++;\n    ok_count+=simple_test_cmd_bitpos(vi); all_count++;\n    ok_count+=simple_test_cmd_mget_mset(vi); all_count++;\n    /* Hash */\n    ok_count+=simple_test_hash_encode(vi); all_count++;\n    ok_count+=simple_test_cmd_hget_hset(vi); all_count++;\n    ok_count+=simple_test_cmd_hlen(vi); all_count++;\n    ok_count+=simple_test_cmd_hdel(vi); all_count++;\n    /* HyperLogLog */\n    ok_count+=simple_test_cmd_pfadd_pfcount(vi); all_count++;\n    \n    vire_instance_destroy(vi);\n\n    return ok_count==all_count?1:0;\n}\n"
  },
  {
    "path": "tests/vrt_simple.h",
    "content": "#ifndef _VRT_SIMPLE_H_\n#define _VRT_SIMPLE_H_\n\nint simple_test(void);\n\n\n#endif\n"
  },
  {
    "path": "tests/vrt_util.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <stdint.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <unistd.h>\n#include <dirent.h>\n#include <string.h>\n#include <ctype.h>\n#include <limits.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n\n#include <hiredis.h>\n\n#include <vrt_util.h>\n\nvoid\nvrt_assert(const char *cond, const char *file, int line, int panic)\n{\n    test_log_error(\"assert '%s' failed @ (%s, %d)\", cond, file, line);\n    if (panic) {\n        abort();\n    }\n}\n\nint\nvrt_vscnprintf(char *buf, size_t size, const char *fmt, va_list args)\n{\n    int n;\n\n    n = vsnprintf(buf, size, fmt, args);\n\n    /*\n     * The return value is the number of characters which would be written\n     * into buf not including the trailing '\\0'. If size is == 0 the\n     * function returns 0.\n     *\n     * On error, the function also returns 0. This is to allow idiom such\n     * as len += _vscnprintf(...)\n     *\n     * See: http://lwn.net/Articles/69419/\n     */\n    if (n <= 0) {\n        return 0;\n    }\n\n    if (n < (int) size) {\n        return n;\n    }\n\n    return (int)(size - 1);\n}\n\nint\nvrt_scnprintf(char *buf, size_t size, const char *fmt, ...)\n{\n    va_list args;\n    int n;\n\n    va_start(args, fmt);\n    n = vrt_vscnprintf(buf, size, fmt, args);\n    va_end(args);\n\n    return n;\n}\n\nvoid\n_test_log_error(const char *file, int line, const char *fmt, ...)\n{\n    int len, size, errno_save;\n    char buf[LOG_MAX_LEN];\n    va_list args;\n    \n    errno_save = errno;\n    len = 0;            /* length of output buffer */\n    size = LOG_MAX_LEN; /* size of output buffer */\n    \n    len += vrt_scnprintf(buf + len, size - len, \"%s:%d \", file, line);\n\n    va_start(args, fmt);\n    len += vsnprintf(buf + len, size - len, fmt, args);\n    va_end(args);\n\n    buf[len++] = '\\n';\n\n    write(STDERR_FILENO, buf, len);\n    \n    errno = errno_save;\n}\n\nvoid\n_test_log_out(const char *fmt, ...)\n{\n    int len, size, errno_save;\n    char buf[LOG_MAX_LEN];\n    va_list args;\n    \n    errno_save = errno;\n    len = 0;            /* length of output buffer */\n    size = LOG_MAX_LEN; /* size of output buffer */\n\n    va_start(args, fmt);\n    len += vsnprintf(buf + len, size - len, fmt, args);\n    va_end(args);\n\n    buf[len++] = '\\n';\n\n    write(STDOUT_FILENO, buf, len);\n    \n    errno = errno_save;\n}\n\n/*\n * Return the current time in microseconds since Epoch\n */\nint64_t\nvrt_usec_now(void)\n{\n    struct timeval now;\n    int64_t usec;\n    int status;\n\n    status = gettimeofday(&now, NULL);\n    if (status < 0) {\n        return -1;\n    }\n\n    usec = (int64_t)now.tv_sec * 1000000LL + (int64_t)now.tv_usec;\n\n    return usec;\n}\n\n/*\n * Return the current time in milliseconds since Epoch\n */\nint64_t\nvrt_msec_now(void)\n{\n    return vrt_usec_now() / 1000LL;\n}\n\n/*\n * Return the current time in seconds since Epoch\n */\nint64_t\nvrt_sec_now(void)\n{\n    return vrt_usec_now() / 1000000LL;\n}\n\n/* Given the filename, return the absolute path as an SDS string, or NULL\n * if it fails for some reason. Note that \"filename\" may be an absolute path\n * already, this will be detected and handled correctly.\n *\n * The function does not try to normalize everything, but only the obvious\n * case of one or more \"../\" appearning at the start of \"filename\"\n * relative path. */\nsds getAbsolutePath(char *filename) {\n    char cwd[1024];\n    sds abspath;\n    sds relpath = sdsnew(filename);\n\n    sdstrim(relpath,\" \\r\\n\\t\");\n    if (relpath[0] == '/') return relpath; /* Path is already absolute. */\n\n    /* If path is relative, join cwd and relative path. */\n    if (getcwd(cwd,sizeof(cwd)) == NULL) {\n        sdsfree(relpath);\n        return NULL;\n    }\n    abspath = sdsnew(cwd);\n    if (sdslen(abspath) && abspath[sdslen(abspath)-1] != '/')\n        abspath = sdscat(abspath,\"/\");\n\n    /* At this point we have the current path always ending with \"/\", and\n     * the trimmed relative path. Try to normalize the obvious case of\n     * trailing ../ elements at the start of the path.\n     *\n     * For every \"../\" we find in the filename, we remove it and also remove\n     * the last element of the cwd, unless the current cwd is \"/\". */\n    while (sdslen(relpath) >= 3 &&\n           relpath[0] == '.' && relpath[1] == '.' && relpath[2] == '/')\n    {\n        sdsrange(relpath,3,-1);\n        if (sdslen(abspath) > 1) {\n            char *p = abspath + sdslen(abspath)-2;\n            int trimlen = 1;\n\n            while(*p != '/') {\n                p--;\n                trimlen++;\n            }\n            sdsrange(abspath,0,-(trimlen+1));\n        }\n    }\n\n    /* Finally glue the two parts together. */\n    abspath = sdscatsds(abspath,relpath);\n    sdsfree(relpath);\n    return abspath;\n}\n\n/* Return the number of digits of 'v' when converted to string in radix 10.\n * See ll2string() for more information. */\nuint32_t digits10(uint64_t v) {\n    if (v < 10) return 1;\n    if (v < 100) return 2;\n    if (v < 1000) return 3;\n    if (v < 1000000000000UL) {\n        if (v < 100000000UL) {\n            if (v < 1000000) {\n                if (v < 10000) return 4;\n                return 5 + (v >= 100000);\n            }\n            return 7 + (v >= 10000000UL);\n        }\n        if (v < 10000000000UL) {\n            return 9 + (v >= 1000000000UL);\n        }\n        return 11 + (v >= 100000000000UL);\n    }\n    return 12 + digits10(v / 1000000000000UL);\n}\n\n/* Like digits10() but for signed values. */\nuint32_t sdigits10(int64_t v) {\n    if (v < 0) {\n        /* Abs value of LLONG_MIN requires special handling. */\n        uint64_t uv = (v != LLONG_MIN) ?\n                      (uint64_t)-v : ((uint64_t) LLONG_MAX)+1;\n        return digits10(uv)+1; /* +1 for the minus. */\n    } else {\n        return digits10(v);\n    }\n}\n\n/* Convert a long long into a string. Returns the number of\n * characters needed to represent the number.\n * If the buffer is not big enough to store the string, 0 is returned.\n *\n * Based on the following article (that apparently does not provide a\n * novel approach but only publicizes an already used technique):\n *\n * https://www.facebook.com/notes/facebook-engineering/three-optimization-tips-for-c/10151361643253920\n *\n * Modified in order to handle signed integers since the original code was\n * designed for unsigned integers. */\nint ll2string(char* dst, size_t dstlen, long long svalue) {\n    static const char digits[201] =\n        \"0001020304050607080910111213141516171819\"\n        \"2021222324252627282930313233343536373839\"\n        \"4041424344454647484950515253545556575859\"\n        \"6061626364656667686970717273747576777879\"\n        \"8081828384858687888990919293949596979899\";\n    int negative;\n    unsigned long long value;\n\n    /* The main loop works with 64bit unsigned integers for simplicity, so\n     * we convert the number here and remember if it is negative. */\n    if (svalue < 0) {\n        if (svalue != LLONG_MIN) {\n            value = -svalue;\n        } else {\n            value = ((unsigned long long) LLONG_MAX)+1;\n        }\n        negative = 1;\n    } else {\n        value = svalue;\n        negative = 0;\n    }\n\n    /* Check length. */\n    uint32_t const length = digits10(value)+negative;\n    if (length >= dstlen) return 0;\n\n    /* Null term. */\n    uint32_t next = length;\n    dst[next] = '\\0';\n    next--;\n    while (value >= 100) {\n        int const i = (value % 100) * 2;\n        value /= 100;\n        dst[next] = digits[i + 1];\n        dst[next - 1] = digits[i];\n        next -= 2;\n    }\n\n    /* Handle last 1-2 digits. */\n    if (value < 10) {\n        dst[next] = '0' + (uint32_t) value;\n    } else {\n        int i = (uint32_t) value * 2;\n        dst[next] = digits[i + 1];\n        dst[next - 1] = digits[i];\n    }\n\n    /* Add sign. */\n    if (negative) dst[0] = '-';\n    return length;\n}\n\n/* Convert a string into a long long. Returns 1 if the string could be parsed\n * into a (non-overflowing) long long, 0 otherwise. The value will be set to\n * the parsed value when appropriate. */\nint string2ll(const char *s, size_t slen, long long *value) {\n    const char *p = s;\n    size_t plen = 0;\n    int negative = 0;\n    unsigned long long v;\n\n    if (plen == slen)\n        return 0;\n\n    /* Special case: first and only digit is 0. */\n    if (slen == 1 && p[0] == '0') {\n        if (value != NULL) *value = 0;\n        return 1;\n    }\n\n    if (p[0] == '-') {\n        negative = 1;\n        p++; plen++;\n\n        /* Abort on only a negative sign. */\n        if (plen == slen)\n            return 0;\n    }\n\n    /* First digit should be 1-9, otherwise the string should just be 0. */\n    if (p[0] >= '1' && p[0] <= '9') {\n        v = p[0]-'0';\n        p++; plen++;\n    } else if (p[0] == '0' && slen == 1) {\n        *value = 0;\n        return 1;\n    } else {\n        return 0;\n    }\n\n    while (plen < slen && p[0] >= '0' && p[0] <= '9') {\n        if (v > (ULLONG_MAX / 10)) /* Overflow. */\n            return 0;\n        v *= 10;\n\n        if (v > (ULLONG_MAX - (p[0]-'0'))) /* Overflow. */\n            return 0;\n        v += p[0]-'0';\n\n        p++; plen++;\n    }\n\n    /* Return if not all bytes were used. */\n    if (plen < slen)\n        return 0;\n\n    if (negative) {\n        if (v > ((unsigned long long)(-(LLONG_MIN+1))+1)) /* Overflow. */\n            return 0;\n        if (value != NULL) *value = -v;\n    } else {\n        if (v > LLONG_MAX) /* Overflow. */\n            return 0;\n        if (value != NULL) *value = v;\n    }\n    return 1;\n}\n\n/* Convert a string into a long. Returns 1 if the string could be parsed into a\n * (non-overflowing) long, 0 otherwise. The value will be set to the parsed\n * value when appropriate. */\nint string2l(const char *s, size_t slen, long *lval) {\n    long long llval;\n\n    if (!string2ll(s,slen,&llval))\n        return 0;\n\n    if (llval < LONG_MIN || llval > LONG_MAX)\n        return 0;\n\n    *lval = (long)llval;\n    return 1;\n}\n\n/* Convert a double to a string representation. Returns the number of bytes\n * required. The representation should always be parsable by strtod(3). */\nint d2string(char *buf, size_t len, double value) {\n    if (isnan(value)) {\n        len = snprintf(buf,len,\"nan\");\n    } else if (isinf(value)) {\n        if (value < 0)\n            len = snprintf(buf,len,\"-inf\");\n        else\n            len = snprintf(buf,len,\"inf\");\n    } else if (value == 0) {\n        /* See: http://en.wikipedia.org/wiki/Signed_zero, \"Comparisons\". */\n        if (1.0/value < 0)\n            len = snprintf(buf,len,\"-0\");\n        else\n            len = snprintf(buf,len,\"0\");\n    } else {\n#if (DBL_MANT_DIG >= 52) && (LLONG_MAX == 0x7fffffffffffffffLL)\n        /* Check if the float is in a safe range to be casted into a\n         * long long. We are assuming that long long is 64 bit here.\n         * Also we are assuming that there are no implementations around where\n         * double has precision < 52 bit.\n         *\n         * Under this assumptions we test if a double is inside an interval\n         * where casting to long long is safe. Then using two castings we\n         * make sure the decimal part is zero. If all this is true we use\n         * integer printing function that is much faster. */\n        double min = -4503599627370495; /* (2^52)-1 */\n        double max = 4503599627370496; /* -(2^52) */\n        if (value > min && value < max && value == ((double)((long long)value)))\n            len = ll2string(buf,len,(long long)value);\n        else\n#endif\n            len = snprintf(buf,len,\"%.17g\",value);\n    }\n\n    return len;\n}\n\nint create_dir(char *path)\n{\n    if (mkdir(path,0755) < 0) {\n        return VRT_ERROR;\n    }\n\n    return VRT_OK;\n}\n\nint destroy_dir(char *path)\n{  \n    DIR *dp;\n    struct dirent *entry;\n    struct stat statbuf;\n    char cwd[1024];\n\n    if (getcwd(cwd,sizeof(cwd)) == NULL) {\n        return VRT_ERROR;\n    }\n    \n    if ((dp = opendir(path)) == NULL) {  \n        test_log_error(\"Can't open dir: %s\", path);  \n        return VRT_ERROR;  \n    }\n    \n    chdir (path);\n    while ((entry = readdir(dp)) != NULL) {\n        lstat(entry->d_name, &statbuf);\n        if (S_IFDIR & statbuf.st_mode) {\n            if (strcmp(\".\", entry->d_name) == 0 || strcmp(\"..\", entry->d_name) == 0)\n                continue;\n\n            destroy_dir(entry->d_name);\n        } else {\n            remove(entry->d_name);\n        }\n    }\n    \n    chdir(cwd);\n    closedir(dp);\n\n    remove(path);\n    return VRT_OK;\n}\n\nint get_pid_from_reply(redisContext *redisctx, char *host, int port)\n{\n    redisContext *ctx = redisctx;\n    redisReply * reply;\n    sds *lines;\n    size_t line_len;\n    int count, j;\n    int pid = -1;\n    \n    if (ctx == NULL) {\n        ctx = redisConnect(host,port);\n    }\n\n    if (ctx == NULL) {\n        test_log_error(\"Get pid from instance failed: can't connect to %s:%d\",host,port);\n        return -1;\n    }\n\n    reply = redisCommand(ctx, \"info server\");\n    if (reply == NULL) {\n        test_log_error(\"Execute 'info server' command on vire failed: %s\\n\",\n            ctx->err?ctx->errstr:\"\");\n        if (redisctx == NULL) redisFree(ctx);\n        return -1;\n    }\n\n    if (reply->type != REDIS_REPLY_STRING) {\n        test_log_error(\"Reply for 'info server' command from vire type %d is error\",\n            reply->type);\n        if (redisctx == NULL) redisFree(ctx);\n        freeReplyObject(reply);\n        return -1;\n    }\n\n    lines = sdssplitlen(reply->str,reply->len,\"\\r\\n\",2,&count);\n    if (lines == NULL) {\n        test_log_error(\"Reply for 'info server' command from vire is error\");\n        if (redisctx == NULL) redisFree(ctx);\n        freeReplyObject(reply);\n        return -1;\n    }\n\n    for (j = 0; j < count; j ++) {\n        line_len = sdslen(lines[j]);\n        if (line_len > 11 && !strncmp(\"process_id\", lines[j], 10)) {\n            if (string2l(lines[j]+11,line_len-11,&pid) == 0) {\n                test_log_error(\"Convert pid string %.*s to long failed\",\n                    line_len-11,lines[j]+11);\n                sdsfreesplitres(lines,count);\n                if (redisctx == NULL) redisFree(ctx);\n                freeReplyObject(reply);\n                return -1;\n            }\n            break;\n        }\n    }\n\n    sdsfreesplitres(lines,count);\n    if (redisctx == NULL) redisFree(ctx);\n    freeReplyObject(reply);\n\n    return pid;\n}\n\n/* Range is like 0-100 or just 10. \n  * So the count must be 1 or 2. */\nlong long *get_range_from_string(char *str, size_t len, int *count)\n{\n    int elem_count;\n    sds *elems;\n    long long value;\n    long long *range;\n    \n    elems = sdssplitlen(optarg,strlen(optarg),\"-\",1,&elem_count);\n    if (elems == NULL) {\n        goto error;\n    } else if (elem_count <= 0 || elem_count >= 3) {\n        sdsfreesplitres(elems,elem_count);\n        goto error;\n    }\n\n    if (elem_count == 1) {\n        if (string2ll(elems[0],sdslen(elems[0]),&value) != 1) {\n            sdsfreesplitres(elems,elem_count);\n            goto error;\n        }\n\n        range = malloc(1*sizeof(*range));\n        range[0] = value;\n        *count = 1;\n    } else if (elem_count == 2) {\n        if (string2ll(elems[0],sdslen(elems[0]),&value) != 1) {\n            sdsfreesplitres(elems,elem_count);\n            goto error;\n        }\n        \n        range = malloc(2*sizeof(*range));\n        range[0] = value;\n\n        if (string2ll(elems[1],sdslen(elems[1]),&value) != 1) {\n            sdsfreesplitres(elems,elem_count);\n            free(range);\n            goto error;\n        }\n\n        range[1] = value;\n        *count = 2;\n\n        if (range[0] > range[1]) {\n            sdsfreesplitres(elems,elem_count);\n            free(range);\n            goto error;\n        }\n    }\n\n    sdsfreesplitres(elems,elem_count);\n\n    return range;\n\nerror:\n\n    *count = -1;\n    return NULL;\n}\n\nsds get_host_port_from_address_string(char *address, int *port)\n{\n    sds *host_port;\n    int count = 0;\n    sds host;\n    long value;\n\n    *port = 0;\n    \n    host_port = sdssplitlen(address,strlen(address),\":\",1,&count);\n    if (host_port == NULL) {\n        return NULL;\n    } else if (count != 2) {\n        sdsfreesplitres(host_port,count);\n        return NULL;\n    }\n\n    if (string2l(host_port[1],sdslen(host_port[1]),&value) != 1 || \n        value <= 0 || value >= 65535) {\n        sdsfreesplitres(host_port,count);\n        return NULL;\n    }\n\n    *port = (int)value;\n    host = host_port[0];\n    host_port[0] = NULL;\n    sdsfreesplitres(host_port,count);\n    \n    return host;\n}\n"
  },
  {
    "path": "tests/vrt_util.h",
    "content": "#ifndef _VRT_UTIL_H_\n#define _VRT_UTIL_H_\n\n#ifdef HAVE_CONFIG_H\n# include <config.h>\n#endif\n\n#ifdef HAVE_DEBUG_LOG\n# define VRT_DEBUG_LOG 1\n#endif\n\n#include <dspecialconfig.h>\n\n#include <sds.h>\n\n#define VRT_OK        0\n#define VRT_ERROR    -1\n\n#define VRT_UINT8_MAXLEN     (3 + 1)\n#define VRT_UINT16_MAXLEN    (5 + 1)\n#define VRT_UINT32_MAXLEN    (10 + 1)\n#define VRT_UINT64_MAXLEN    (20 + 1)\n#define VRT_UINTMAX_MAXLEN   VRT_UINT64_MAXLEN\n\n#define VRT_MAXHOSTNAMELEN   256\n\n#define LF                  (uint8_t) 10\n#define CR                  (uint8_t) 13\n#define CRLF                \"\\x0d\\x0a\"\n#define CRLF_LEN            (sizeof(\"\\x0d\\x0a\") - 1)\n\n#define LOG_MAX_LEN 256 /* max length of log message */\n\nvoid _test_log_error(const char *file, int line, const char *fmt, ...);\nvoid _test_log_out(const char *fmt, ...);\n#if defined(VRT_DEBUG_LOG)\n#define test_log_debug(...) do {                              \\\n    _test_log_error(__FILE__, __LINE__, __VA_ARGS__);         \\\n} while (0)\n#else\n#define test_log_debug(...)\n#endif\n#define test_log_error(...) do {                              \\\n    _test_log_error(__FILE__, __LINE__, __VA_ARGS__);         \\\n} while (0)\n#define test_log_out(...) do {                                \\\n    _test_log_out(__VA_ARGS__);                               \\\n} while (0)\n\nvoid vrt_assert(const char *cond, const char *file, int line, int panic);\n\n#define ASSERT(_x) do {                         \\\n    if (!(_x)) {                                \\\n        vrt_assert(#_x, __FILE__, __LINE__, 1);  \\\n    }                                           \\\n} while (0)\n\nint vrt_scnprintf(char *buf, size_t size, const char *fmt, ...);\n\nint64_t vrt_usec_now(void);\nint64_t vrt_msec_now(void);\nint64_t vrt_sec_now(void);\n\nsds getAbsolutePath(char *filename);\n\nint ll2string(char* dst, size_t dstlen, long long svalue);\nint string2ll(const char *s, size_t slen, long long *value);\nint string2l(const char *s, size_t slen, long *lval);\nint d2string(char *buf, size_t len, double value);\n\nint create_dir(char *path);\nint destroy_dir(char *dir);\n\nint get_pid_from_reply(struct redisContext *redisctx, char *host, int port);\n\nlong long *get_range_from_string(char *str, size_t len, int *count);\n\nsds get_host_port_from_address_string(char *address, int *port);\n\n#endif\n"
  },
  {
    "path": "tests/vrtest.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <getopt.h>\n#include <fcntl.h>\n#include <errno.h>\n#include <signal.h>\n#include <sys/stat.h>\n#include <sys/utsname.h>\n\n#include <hiredis.h>\n\n#include <vrt_util.h>\n#include <vrt_public.h>\n#include <vrt_simple.h>\n\nstruct config {\n    char *pid_filename;\n\n    int pidfile;\n    int pid;\n};\n\nstatic struct config config;\n\nstatic int show_help;\nstatic int show_version;\n\nstatic struct option long_options[] = {\n    { \"help\",           no_argument,        NULL,   'h' },\n    { \"version\",        no_argument,        NULL,   'V' },\n    { \"execute-file\",   required_argument,  NULL,   'e' },\n    { \"pid-file\",       required_argument,  NULL,   'p' },\n    { NULL,             0,                  NULL,    0  }\n};\n\nstatic char short_options[] = \"hVe:p:\";\n\nstatic void\nvr_show_usage(void)\n{\n    printf(\n        \"Usage: viretest [-?hV] [-e execute-file] [-p pid-file]\" CRLF\n        \"\" CRLF);\n    printf(\n        \"Options:\" CRLF\n        \"  -h, --help             : this help\" CRLF\n        \"  -V, --version          : show version and exit\" CRLF\n        \"  -e, --execute-file     : vire execute file, default is src/vire\" CRLF\n        \"  -p, --pid-file         : pid file\" CRLF\n        \"\" CRLF);\n}\n\nstatic void\nvr_set_default_options(void)\n{\n    config.pid_filename = NULL;\n}\n\nstatic int\nvr_get_options(int argc, char **argv)\n{\n    int c;\n\n    opterr = 0;\n\n    for (;;) {\n        c = getopt_long(argc, argv, short_options, long_options, NULL);\n        if (c == -1) {\n            /* no more options */\n            break;\n        }\n\n        switch (c) {\n        case 'h':\n            show_version = 1;\n            show_help = 1;\n            break;\n\n        case 'V':\n            show_version = 1;\n            break;\n\n        case 'e':\n            set_execute_file(optarg);\n            break;\n\n        case 'p':\n            config.pid_filename = optarg;\n            break;\n\n        case '?':\n            switch (optopt) {\n            case 'e':\n            case 'p':\n                test_log_error(\"vire: option -%c requires string\",\n                           optopt);\n                break;\n\n            default:\n                test_log_error(\"vire: invalid option -- '%c'\", optopt);\n                break;\n            }\n            return VRT_ERROR;\n\n        default:\n            test_log_error(\"vire: invalid option -- '%c'\", optopt);\n            return VRT_ERROR;\n\n        }\n    }\n\n    return VRT_OK;\n}\n\nint\nmain(int argc, char **argv)\n{\n    int ret;\n    int ok_count = 0, all_count = 0;\n\n    vr_set_default_options();\n\n    ret = vr_get_options(argc, argv);\n    if (ret != VRT_OK) {\n        vr_show_usage();\n        exit(1);\n    }\n\n    if (show_version) {\n        test_log_out(\"This is viretest-%s\", VR_VERSION_STRING);\n        if (show_help) {\n            vr_show_usage();\n        }\n        exit(0);\n    }\n\n    create_work_dir();\n\n    test_log_out(\"Testing Vire version %s \\n\", VR_VERSION_STRING);\n    \n    ok_count+=simple_test(); all_count++;\n    \nclean:\n    destroy_work_dir();\n\n    if (ok_count == all_count)\n        test_log_out(\"\\n\\\\o/ \\033[32;1mAll tests passed without errors!\\033[0m\\n\");\n    return VRT_OK;\n}\n"
  },
  {
    "path": "tools/.gitignore",
    "content": "*.pyc\n*.out\n*.log\n"
  }
]