[
  {
    "path": ".gitignore",
    "content": ".*.swp\n*.o\n*.log\ndump.rdb\nredis-benchmark\nredis-check-aof\nredis-check-dump\nredis-cli\nredis-sentinel\nredis-server\ndoc-tools\nrelease\nmisc/*\nsrc/release.h\nappendonly.aof\nSHORT_TERM_TODO\nrelease.h\nsrc/transfer.sh\nsrc/configs\nredis.ds\nsrc/redis.conf\ndeps/lua/src/lua\ndeps/lua/src/luac\ndeps/lua/src/liblua.a\n.make-*\n.prerequisites\n*.dSYM\n"
  },
  {
    "path": "00-RELEASENOTES",
    "content": "Hello! This file is just a placeholder, since this is the \"unstable\" branch\nof Redis, the place where all the development happens.\n\nThere is no release notes for this branch, it gets forked into another branch\nevery time there is a partial feature freeze in order to eventually create\na new stable release.\n\nUsually \"unstable\" is stable enough for you to use it in development enviromnets\nhowever you should never use it in production environments. It is possible\nto download the latest stable release here:\n\n    http://download.redis.io/releases/redis-stable.tar.gz\n\nMore information is available at http://redis.io\n\nHappy hacking!\n"
  },
  {
    "path": "BUGS",
    "content": "Plese check https://github.com/antirez/redis/issues\n"
  },
  {
    "path": "CONTRIBUTING",
    "content": "Note: by contributing code to the Redis project in any form, including sending\na pull request via Github, a code fragment or patch via private email or\npublic discussion groups, you agree to release your code under the terms\nof the BSD license that you can find in the COPYING file included in the Redis\nsource distribution. You will include BSD license in the COPYING file within\neach source file that you contribute.\n\n# IMPORTANT: HOW TO USE REDIS GITHUB ISSUES\n\n* Github issues SHOULD ONLY BE USED to report bugs, and for DETAILED feature\n  requests. Everything else belongs to the Redis Google Group.\n\n  PLEASE DO NOT POST GENERAL QUESTIONS that are not about bugs or suspected\n  bugs in the Github issues system. We'll be very happy to help you and provide\n  all the support in the Redis Google Group.\n\n  Redis Google Group address:\n  \n      https://groups.google.com/forum/?fromgroups#!forum/redis-db\n\n# How to provide a patch for a new feature\n\n1. Drop a message to the Redis Google Group with a proposal of semantics/API.\n\n2. If in step 1 you get an acknowledge from the project leaders, use the\n   following procedure to submit a patch:\n\n    a. Fork Redis on github ( http://help.github.com/fork-a-repo/ )\n    b. Create a topic branch (git checkout -b my_branch)\n    c. Push to your branch (git push origin my_branch)\n    d. Initiate a pull request on github ( http://help.github.com/send-pull-requests/ )\n    e. Done :)\n\nThanks!\n"
  },
  {
    "path": "COPYING",
    "content": "Copyright (c) 2006-2014, Salvatore Sanfilippo\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n\n    * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n    * Neither the name of Redis nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "INSTALL",
    "content": "See README\n"
  },
  {
    "path": "MANIFESTO",
    "content": "[Note: this is the Redis manifesto, for general information about\n       installing and running Redis read the README file instead.]\n\nRedis Manifesto\n===============\n\n1 - A DSL for Abstract Data Types. Redis is a DSL (Domain Specific Language)\n    that manipulates abstract data types and implemented as a TCP daemon.\n    Commands manipulate a key space where keys are binary-safe strings and\n    values are different kinds of abstract data types. Every data type\n    represents an abstract version of a fundamental data structure. For instance\n    Redis Lists are an abstract representation of linked lists. In Redis, the\n    essence of a data type isn't just the kind of operations that the data types\n    support, but also the space and time complexity of the data type and the\n    operations performed upon it.\n\n2 - Memory storage is #1. The Redis data set, composed of defined key-value\n    pairs, is primarily stored in the computer's memory. The amount of memory in\n    all kinds of computers, including entry-level servers, is increasing\n    significantly each year. Memory is fast, and allows Redis to have very\n    predictable performance. Datasets composed of 10k or 40 millions keys will\n    perform similarly. Complex data types like Redis Sorted Sets are easy to\n    implement and manipulate in memory with good performance, making Redis very\n    simple. Redis will continue to explore alternative options (where data can\n    be optionally stored on disk, say) but the main goal of the project remains\n    the development of an in-memory database.\n\n3 - Fundamental data structures for a fundamental API. The Redis API is a direct\n    consequence of fundamental data structures. APIs can often be arbitrary but\n    not an API that resembles the nature of fundamental data structures. If we\n    ever meet intelligent life forms from another part of the universe, they'll\n    likely know, understand and recognize the same basic data structures we have\n    in our computer science books. Redis will avoid intermediate layers in API,\n    so that the complexity is obvious and more complex operations can be\n    performed as the sum of the basic operations.\n\n4 - Code is like a poem; it's not just something we write to reach some\n    practical result. Sometimes people that are far from the Redis philosophy\n    suggest using other code written by other authors (frequently in other\n    languages) in order to implement something Redis currently lacks. But to us\n    this is like if Shakespeare decided to end Enrico IV using the Paradiso from\n    the Divina Commedia. Is using any external code a bad idea? Not at all. Like\n    in \"One Thousand and One Nights\" smaller self contained stories are embedded\n    in a bigger story, we'll be happy to use beautiful self contained libraries\n    when needed. At the same time, when writing the Redis story we're trying to\n    write smaller stories that will fit in to other code.\n\n5 - We're against complexity. We believe designing systems is a fight against\n    complexity. We'll accept to fight the complexity when it's worthwhile but\n    we'll try hard to recognize when a small feature is not worth 1000s of lines\n    of code. Most of the time the best way to fight complexity is by not\n    creating it at all.\n\n6 - Two levels of API. The Redis API has two levels: 1) a subset of the API fits\n    naturally into a distributed version of Redis and 2) a more complex API that\n    supports multi-key operations. Both are useful if used judiciously but\n    there's no way to make the more complex multi-keys API distributed in an\n    opaque way without violating our other principles. We don't want to provide\n    the illusion of something that will work magically when actually it can't in\n    all cases. Instead we'll provide commands to quickly migrate keys from one\n    instance to another to perform multi-key operations and expose the tradeoffs\n    to the user.\n\n7 - We optimize for joy. We believe writing code is a lot of hard work, and the\n    only way it can be worth is by enjoying it. When there is no longer joy in\n    writing code, the best thing to do is stop. To prevent this, we'll avoid\n    taking paths that will make Redis less of a joy to develop.\n"
  },
  {
    "path": "Makefile",
    "content": "# Top level makefile, the real shit is at src/Makefile\n\ndefault: all\n\n.DEFAULT:\n\tcd src && $(MAKE) $@\n\ninstall:\n\tcd src && $(MAKE) $@\n\n.PHONY: install\n"
  },
  {
    "path": "README",
    "content": "Where to find complete Redis documentation?\n-------------------------------------------\n\nThis README is just a fast \"quick start\" document. You can find more detailed\ndocumentation at http://redis.io\n\nBuilding Redis\n--------------\n\nRedis can be compiled and used on Linux, OSX, OpenBSD, NetBSD, FreeBSD.\nWe support big endian and little endian architectures.\n\nIt may compile on Solaris derived systems (for instance SmartOS) but our\nsupport for this platform is \"best effort\" and Redis is not guaranteed to\nwork as well as in Linux, OSX, and *BSD there.\n\nIt is as simple as:\n\n    % make\n\nYou can run a 32 bit Redis binary using:\n\n    % make 32bit\n\nAfter building Redis is a good idea to test it, using:\n\n    % make test\n\nFixing problems building 32 bit binaries\n---------\n\nIf after building Redis with a 32 bit target you need to rebuild it\nwith a 64 bit target, or the other way around, you need to perform a\n\"make distclean\" in the root directory of the Redis distribution.\n\nIn case of build errors when trying to build a 32 bit binary of Redis, try\nthe following steps:\n\n* Install the packages libc6-dev-i386 (also try g++-multilib).\n* Try using the following command line instead of \"make 32bit\":\n\n    make CFLAGS=\"-m32 -march=native\" LDFLAGS=\"-m32\"\n\nAllocator\n---------\n\nSelecting a non-default memory allocator when building Redis is done by setting\nthe `MALLOC` environment variable. Redis is compiled and linked against libc\nmalloc by default, with the exception of jemalloc being the default on Linux\nsystems. This default was picked because jemalloc has proven to have fewer\nfragmentation problems than libc malloc.\n\nTo force compiling against libc malloc, use:\n\n    % make MALLOC=libc\n\nTo compile against jemalloc on Mac OS X systems, use:\n\n    % make MALLOC=jemalloc\n\nVerbose build\n-------------\n\nRedis will build with a user friendly colorized output by default.\nIf you want to see a more verbose output use the following:\n\n    % make V=1\n\nRunning Redis\n-------------\n\nTo run Redis with the default configuration just type:\n\n    % cd src\n    % ./redis-server\n    \nIf you want to provide your redis.conf, you have to run it using an additional\nparameter (the path of the configuration file):\n\n    % cd src\n    % ./redis-server /path/to/redis.conf\n\nIt is possible to alter the Redis configuration passing parameters directly\nas options using the command line. Examples:\n\n    % ./redis-server --port 9999 --slaveof 127.0.0.1 6379\n    % ./redis-server /etc/redis/6379.conf --loglevel debug\n\nAll the options in redis.conf are also supported as options using the command\nline, with exactly the same name.\n\nPlaying with Redis\n------------------\n\nYou can use redis-cli to play with Redis. Start a redis-server instance,\nthen in another terminal try the following:\n\n    % cd src\n    % ./redis-cli\n    redis> ping\n    PONG\n    redis> set foo bar\n    OK\n    redis> get foo\n    \"bar\"\n    redis> incr mycounter\n    (integer) 1\n    redis> incr mycounter\n    (integer) 2\n    redis> \n\nYou can find the list of all the available commands here:\n\n    http://redis.io/commands\n\nInstalling Redis\n-----------------\n\nIn order to install Redis binaries into /usr/local/bin just use:\n\n    % make install\n\nYou can use \"make PREFIX=/some/other/directory install\" if you wish to use a\ndifferent destination.\n\nMake install will just install binaries in your system, but will not configure\ninit scripts and configuration files in the appropriate place. This is not\nneeded if you want just to play a bit with Redis, but if you are installing\nit the proper way for a production system, we have a script doing this\nfor Ubuntu and Debian systems:\n\n    % cd utils\n    % ./install_server.sh\n\nThe script will ask you a few questions and will setup everything you need\nto run Redis properly as a background daemon that will start again on\nsystem reboots.\n\nYou'll be able to stop and start Redis using the script named\n/etc/init.d/redis_<portnumber>, for instance /etc/init.d/redis_6379.\n\nCode contributions\n---\n\nNote: by contributing code to the Redis project in any form, including sending\na pull request via Github, a code fragment or patch via private email or\npublic discussion groups, you agree to release your code under the terms\nof the BSD license that you can find in the COPYING file included in the Redis\nsource distribution.\n\nPlease see the CONTRIBUTING file in this source distribution for more\ninformation.\n\nEnjoy!\n"
  },
  {
    "path": "README.rst",
    "content": "Redis 3.0 源码注释\n============================\n\n本项目是注释版的 Redis 3.0 源码，\n原始代码来自： https://github.com/antirez/redis 。\n\n这份注释是我在创作新版《Redis 设计与实现》期间，\n为了了解 Redis 的内部实现而制作的，\n所有在书中有介绍的内容，\n在源码中都进行了相应的注释。\n\n在注释的过程中，\n除了少量空格和空行方面的调整外，\n没有对原始代码进行任何其他改动，\n最大程度地保证了代码的“原汁原味”。\n\n希望这份注释源码能给大家学习和了解 Redis 带来一点帮助。\n\n另外，\n`新版《Redis 设计与实现》 <http://RedisBook.com>`_\\ 正在各大网店发售中，\n希望大家可以多多支持这本书。\n\nHave fun!\n\n| 黄健宏（huangz）\n| 2014 年 6 月 28 日\n\n\n附录：各个源码文件的作用简介\n------------------------------\n\n+-------------------------------------------------------------------+-------------------------------------------------------------------+\n| 文件                                                              | 作用                                                              |\n+===================================================================+===================================================================+\n| ``adlist.c`` 、 ``adlist.h``                                      | 双端链表数据结构的实现。                                          |\n+-------------------------------------------------------------------+-------------------------------------------------------------------+\n| ``ae.c`` 、 ``ae.h`` 、 ``ae_epoll.c`` 、 ``ae_evport.c`` 、      | 事件处理器，以及各个具体实现。                                    |\n| ``ae_kqueue.c`` 、 ``ae_select.c``                                |                                                                   |\n+-------------------------------------------------------------------+-------------------------------------------------------------------+\n| ``anet.c`` 、 ``anet.h``                                          | Redis 的异步网络框架，内容主要为对 socket 库的包装。              |\n+-------------------------------------------------------------------+-------------------------------------------------------------------+\n| ``aof.c``                                                         | AOF 功能的实现。                                                  |\n+-------------------------------------------------------------------+-------------------------------------------------------------------+\n| ``asciilogo.h``                                                   | 保存了 Redis 的 ASCII LOGO 。                                     |\n+-------------------------------------------------------------------+-------------------------------------------------------------------+\n| ``bio.c`` 、 ``bio.h``                                            | Redis 的后台 I/O 程序，用于将 I/O 操作放到子线程里面执行，        |\n|                                                                   | 减少 I/O 操作对主线程的阻塞。                                     |\n+-------------------------------------------------------------------+-------------------------------------------------------------------+\n| ``bitops.c``                                                      | 二进制位操作命令的实现文件。                                      |\n+-------------------------------------------------------------------+-------------------------------------------------------------------+\n| ``blocked.c``                                                     | 用于实现 BLPOP 命令和 WAIT 命令的阻塞效果。                       |\n+-------------------------------------------------------------------+-------------------------------------------------------------------+\n| ``cluster.c`` 、 ``cluster.h``                                    | Redis 的集群实现。                                                |\n+-------------------------------------------------------------------+-------------------------------------------------------------------+\n| ``config.c`` 、 ``config.h``                                      | Redis 的配置管理实现，负责读取并分析配置文件，                    |\n|                                                                   | 然后根据这些配置修改 Redis 服务器的各个选项。                     |\n+-------------------------------------------------------------------+-------------------------------------------------------------------+\n| ``crc16.c`` 、 ``crc64.c`` 、 ``crc64.h``                         | 计算 CRC 校验和。                                                 |\n+-------------------------------------------------------------------+-------------------------------------------------------------------+\n| ``db.c``                                                          | 数据库实现。                                                      |\n+-------------------------------------------------------------------+-------------------------------------------------------------------+\n| ``debug.c``                                                       | 调试实现。                                                        |\n+-------------------------------------------------------------------+-------------------------------------------------------------------+\n| ``dict.c`` 、 ``dict.h``                                          | 字典数据结构的实现。                                              |\n+-------------------------------------------------------------------+-------------------------------------------------------------------+\n| ``endianconv.c`` 、 ``endianconv.h``                              | 二进制的大端、小端转换函数。                                      |\n+-------------------------------------------------------------------+-------------------------------------------------------------------+\n| ``fmacros.h``                                                     | 一些移植性方面的宏。                                              |\n+-------------------------------------------------------------------+-------------------------------------------------------------------+\n| ``help.h``                                                        | ``utils/generate-command-help.rb`` 程序自动生成的命令帮助信息。   |\n+-------------------------------------------------------------------+-------------------------------------------------------------------+\n| ``hyperloglog.c``                                                 | HyperLogLog 数据结构的实现。                                      |\n+-------------------------------------------------------------------+-------------------------------------------------------------------+\n| ``intset.c`` 、 ``intset.h``                                      | 整数集合数据结构的实现，用于优化 SET 类型。                       |\n+-------------------------------------------------------------------+-------------------------------------------------------------------+\n| ``lzf_c.c`` 、 ``lzf_d.c`` 、 ``lzf.h`` 、 ``lzfP.h``             | Redis 对字符串和 RDB 文件进行压缩时使用的 LZF 压缩算法的实现。    |\n+-------------------------------------------------------------------+-------------------------------------------------------------------+\n| ``Makefile`` 、 ``Makefile.dep``                                  | 构建文件。                                                        |\n+-------------------------------------------------------------------+-------------------------------------------------------------------+\n| ``memtest.c``                                                     | 内存测试。                                                        |\n+-------------------------------------------------------------------+-------------------------------------------------------------------+\n| ``mkreleasehdr.sh``                                               | 用于生成释出信息的脚本。                                          |\n+-------------------------------------------------------------------+-------------------------------------------------------------------+\n| ``multi.c``                                                       | Redis 的事务实现。                                                |\n+-------------------------------------------------------------------+-------------------------------------------------------------------+\n| ``networking.c``                                                  | Redis 的客户端网络操作库，                                        |\n|                                                                   | 用于实现命令请求接收、发送命令回复等工作，                        |\n|                                                                   | 文件中的函数大多为 write 、 read 、 close 等函数的包装，          |\n|                                                                   | 以及各种协议的分析和构建函数。                                    |\n+-------------------------------------------------------------------+-------------------------------------------------------------------+\n| ``notify.c``                                                      | Redis 的数据库通知实现。                                          |\n+-------------------------------------------------------------------+-------------------------------------------------------------------+\n| ``object.c``                                                      | Redis 的对象系统实现。                                            |\n+-------------------------------------------------------------------+-------------------------------------------------------------------+\n| ``pqsort.c`` 、 ``pqsort.h``                                      | 快速排序（QuickSort）算法的实现。                                 |\n+-------------------------------------------------------------------+-------------------------------------------------------------------+\n| ``pubsub.c``                                                      | 发布与订阅功能的实现。                                            |\n+-------------------------------------------------------------------+-------------------------------------------------------------------+\n| ``rand.c`` 、 ``rand.h``                                          | 伪随机数生成器。                                                  |\n+-------------------------------------------------------------------+-------------------------------------------------------------------+\n| ``rdb.c`` 、 ``rdb.h``                                            | RDB 持久化功能的实现。                                            |\n+-------------------------------------------------------------------+-------------------------------------------------------------------+\n| ``redisassert.h``                                                 | Redis 自建的断言系统。                                            |\n+-------------------------------------------------------------------+-------------------------------------------------------------------+\n| ``redis-benchmark.c``                                             | Redis 的性能测试程序。                                            |\n+-------------------------------------------------------------------+-------------------------------------------------------------------+\n| ``redis.c``                                                       | 负责服务器的启动、维护和关闭等事项。                              |\n+-------------------------------------------------------------------+-------------------------------------------------------------------+\n| ``redis-check-aof.c`` 、 ``redis-check-dump.c``                   | RDB 文件和 AOF 文件的合法性检查程序。                             |\n+-------------------------------------------------------------------+-------------------------------------------------------------------+\n| ``redis-cli.c``                                                   | Redis 客户端的实现。                                              |\n+-------------------------------------------------------------------+-------------------------------------------------------------------+\n| ``redis.h``                                                       | Redis 的主要头文件，记录了 Redis 中的大部分数据结构，             |\n|                                                                   | 包括服务器状态和客户端状态。                                      |\n+-------------------------------------------------------------------+-------------------------------------------------------------------+\n| ``redis-trib.rb``                                                 | Redis 集群的管理程序。                                            |\n+-------------------------------------------------------------------+-------------------------------------------------------------------+\n| ``release.c`` 、 ``release.h``                                    | 记录和生成 Redis 的释出版本信息。                                 |\n+-------------------------------------------------------------------+-------------------------------------------------------------------+\n| ``replication.c``                                                 | 复制功能的实现。                                                  |\n+-------------------------------------------------------------------+-------------------------------------------------------------------+\n| ``rio.c`` 、 ``rio.h``                                            | Redis 对文件 I/O 函数的包装，                                     |\n|                                                                   | 在普通 I/O 函数的基础上增加了显式缓存、以及计算校验和等功能。     |\n+-------------------------------------------------------------------+-------------------------------------------------------------------+\n| ``scripting.c``                                                   | 脚本功能的实现。                                                  |\n+-------------------------------------------------------------------+-------------------------------------------------------------------+\n| ``sds.c`` 、 ``sds.h``                                            | SDS 数据结构的实现，SDS 为 Redis 的默认字符串表示。               |\n+-------------------------------------------------------------------+-------------------------------------------------------------------+\n| ``sentinel.c``                                                    | Redis Sentinel 的实现。                                           |\n+-------------------------------------------------------------------+-------------------------------------------------------------------+\n| ``setproctitle.c``                                                | 进程环境设置函数。                                                |\n+-------------------------------------------------------------------+-------------------------------------------------------------------+\n| ``sha1.c`` 、 ``sha1.h``                                          | SHA1 校验和计算函数。                                             |\n+-------------------------------------------------------------------+-------------------------------------------------------------------+\n| ``slowlog.c`` 、 ``slowlog.h``                                    | 慢查询功能的实现。                                                |\n+-------------------------------------------------------------------+-------------------------------------------------------------------+\n| ``solarisfixes.h``                                                | 针对 Solaris 系统的补丁。                                         |\n+-------------------------------------------------------------------+-------------------------------------------------------------------+\n| ``sort.c``                                                        | SORT 命令的实现。                                                 |\n+-------------------------------------------------------------------+-------------------------------------------------------------------+\n| ``syncio.c``                                                      | 同步 I/O 操作。                                                   |\n+-------------------------------------------------------------------+-------------------------------------------------------------------+\n| ``testhelp.h``                                                    | 测试辅助宏。                                                      |\n+-------------------------------------------------------------------+-------------------------------------------------------------------+\n| ``t_hash.c`` 、 ``t_list.c`` 、 ``t_set.c`` 、 ``t_string.c`` 、  | 定义了 Redis 的各种数据类型，以及这些数据类型的命令。             |\n| ``t_zset.c``                                                      |                                                                   |\n+-------------------------------------------------------------------+-------------------------------------------------------------------+\n| ``util.c`` 、 ``util.h``                                          | 各种辅助函数。                                                    |\n+-------------------------------------------------------------------+-------------------------------------------------------------------+\n| ``valgrind.sup``                                                  | valgrind 的suppression文件。                                      |\n+-------------------------------------------------------------------+-------------------------------------------------------------------+\n| ``version.h``                                                     | 记录了 Redis 的版本号。                                           |\n+-------------------------------------------------------------------+-------------------------------------------------------------------+\n| ``ziplist.c`` 、 ``ziplist.h``                                    | ZIPLIST 数据结构的实现，用于优化 LIST 类型。                      |\n+-------------------------------------------------------------------+-------------------------------------------------------------------+\n| ``zipmap.c`` 、 ``zipmap.h``                                      | ZIPMAP 数据结构的实现，在 Redis 2.6 以前用与优化 HASH 类型，      |\n|                                                                   | Redis 2.6 开始已经废弃。                                          |\n+-------------------------------------------------------------------+-------------------------------------------------------------------+\n| ``zmalloc.c`` 、 ``zmalloc.h``                                    | 内存管理程序。                                                    |\n+-------------------------------------------------------------------+-------------------------------------------------------------------+\n"
  },
  {
    "path": "deps/Makefile",
    "content": "# Redis dependency Makefile\n\nuname_S:= $(shell sh -c 'uname -s 2>/dev/null || echo not')\n\nCCCOLOR=\"\\033[34m\"\nLINKCOLOR=\"\\033[34;1m\"\nSRCCOLOR=\"\\033[33m\"\nBINCOLOR=\"\\033[37;1m\"\nMAKECOLOR=\"\\033[32;1m\"\nENDCOLOR=\"\\033[0m\"\n\ndefault:\n\t@echo \"Explicit target required\"\n\n.PHONY: default\n\n# Prerequisites target\n.make-prerequisites:\n\t@touch $@\n\n# Clean everything when CFLAGS is different\nifneq ($(shell sh -c '[ -f .make-cflags ] && cat .make-cflags || echo none'), $(CFLAGS))\n.make-cflags: distclean\n\t-(echo \"$(CFLAGS)\" > .make-cflags)\n.make-prerequisites: .make-cflags\nendif\n\n# Clean everything when LDFLAGS is different\nifneq ($(shell sh -c '[ -f .make-ldflags ] && cat .make-ldflags || echo none'), $(LDFLAGS))\n.make-ldflags: distclean\n\t-(echo \"$(LDFLAGS)\" > .make-ldflags)\n.make-prerequisites: .make-ldflags\nendif\n\ndistclean:\n\t-(cd hiredis && $(MAKE) clean) > /dev/null || true\n\t-(cd linenoise && $(MAKE) clean) > /dev/null || true\n\t-(cd lua && $(MAKE) clean) > /dev/null || true\n\t-(cd jemalloc && [ -f Makefile ] && $(MAKE) distclean) > /dev/null || true\n\t-(rm -f .make-*)\n\n.PHONY: distclean\n\nhiredis: .make-prerequisites\n\t@printf '%b %b\\n' $(MAKECOLOR)MAKE$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR)\n\tcd hiredis && $(MAKE) static\n\n.PHONY: hiredis\n\nlinenoise: .make-prerequisites\n\t@printf '%b %b\\n' $(MAKECOLOR)MAKE$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR)\n\tcd linenoise && $(MAKE)\n\n.PHONY: linenoise\n\nifeq ($(uname_S),SunOS)\n\t# Make isinf() available\n\tLUA_CFLAGS= -D__C99FEATURES__=1\nendif\n\nLUA_CFLAGS+= -O2 -Wall -DLUA_ANSI $(CFLAGS)\nLUA_LDFLAGS+= $(LDFLAGS)\n\nlua: .make-prerequisites\n\t@printf '%b %b\\n' $(MAKECOLOR)MAKE$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR)\n\tcd lua/src && $(MAKE) all CFLAGS=\"$(LUA_CFLAGS)\" MYLDFLAGS=\"$(LUA_LDFLAGS)\"\n\n.PHONY: lua\n\nJEMALLOC_CFLAGS= -std=gnu99 -Wall -pipe -g3 -O3 -funroll-loops $(CFLAGS)\nJEMALLOC_LDFLAGS= $(LDFLAGS)\n\njemalloc: .make-prerequisites\n\t@printf '%b %b\\n' $(MAKECOLOR)MAKE$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR)\n\tcd jemalloc && ./configure --with-jemalloc-prefix=je_ --enable-cc-silence CFLAGS=\"$(JEMALLOC_CFLAGS)\" LDFLAGS=\"$(JEMALLOC_LDFLAGS)\"\n\tcd jemalloc && $(MAKE) CFLAGS=\"$(JEMALLOC_CFLAGS)\" LDFLAGS=\"$(JEMALLOC_LDFLAGS)\" lib/libjemalloc.a\n\n.PHONY: jemalloc\n"
  },
  {
    "path": "deps/hiredis/.gitignore",
    "content": "/hiredis-test\n/hiredis-example*\n/*.o\n/*.so\n/*.dylib\n/*.a\n"
  },
  {
    "path": "deps/hiredis/CHANGELOG.md",
    "content": "### 0.10.1\n\n* Makefile overhaul. Important to check out if you override one or more\n  variables using environment variables or via arguments to the \"make\" tool.\n\n* Issue #45: Fix potential memory leak for a multi bulk reply with 0 elements\n  being created by the default reply object functions.\n\n* Issue #43: Don't crash in an asynchronous context when Redis returns an error\n  reply after the connection has been made (this happens when the maximum\n  number of connections is reached).\n\n### 0.10.0\n\n* See commit log.\n\n"
  },
  {
    "path": "deps/hiredis/COPYING",
    "content": "Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>\nCopyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>\n\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, 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\n* Redistributions in binary form must reproduce the above copyright notice,\n  this list of conditions and the following disclaimer in the documentation\n  and/or other materials provided with the distribution.\n\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 specific\n  prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\nANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\nWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\nANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\nLOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\nANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\nSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "deps/hiredis/Makefile",
    "content": "# Hiredis Makefile\n# Copyright (C) 2010-2011 Salvatore Sanfilippo <antirez at gmail dot com>\n# Copyright (C) 2010-2011 Pieter Noordhuis <pcnoordhuis at gmail dot com>\n# This file is released under the BSD license, see the COPYING file\n\nOBJ=net.o hiredis.o sds.o async.o\nBINS=hiredis-example hiredis-test\nLIBNAME=libhiredis\n\nHIREDIS_MAJOR=0\nHIREDIS_MINOR=10\n\n# Fallback to gcc when $CC is not in $PATH.\nCC:=$(shell sh -c 'type $(CC) >/dev/null 2>/dev/null && echo $(CC) || echo gcc')\nOPTIMIZATION?=-O3\nWARNINGS=-Wall -W -Wstrict-prototypes -Wwrite-strings\nDEBUG?= -g -ggdb\nREAL_CFLAGS=$(OPTIMIZATION) -fPIC $(CFLAGS) $(WARNINGS) $(DEBUG) $(ARCH)\nREAL_LDFLAGS=$(LDFLAGS) $(ARCH)\n\nDYLIBSUFFIX=so\nSTLIBSUFFIX=a\nDYLIB_MINOR_NAME=$(LIBNAME).$(DYLIBSUFFIX).$(HIREDIS_MAJOR).$(HIREDIS_MINOR)\nDYLIB_MAJOR_NAME=$(LIBNAME).$(DYLIBSUFFIX).$(HIREDIS_MAJOR)\nDYLIBNAME=$(LIBNAME).$(DYLIBSUFFIX)\nDYLIB_MAKE_CMD=$(CC) -shared -Wl,-soname,$(DYLIB_MINOR_NAME) -o $(DYLIBNAME) $(LDFLAGS)\nSTLIBNAME=$(LIBNAME).$(STLIBSUFFIX)\nSTLIB_MAKE_CMD=ar rcs $(STLIBNAME)\n\n# Platform-specific overrides\nuname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')\nifeq ($(uname_S),SunOS)\n  REAL_LDFLAGS+= -ldl -lnsl -lsocket\n  DYLIB_MAKE_CMD=$(CC) -G -o $(DYLIBNAME) -h $(DYLIB_MINOR_NAME) $(LDFLAGS)\n  INSTALL= cp -r\nendif\nifeq ($(uname_S),Darwin)\n  DYLIBSUFFIX=dylib\n  DYLIB_MINOR_NAME=$(LIBNAME).$(HIREDIS_MAJOR).$(HIREDIS_MINOR).$(DYLIBSUFFIX)\n  DYLIB_MAJOR_NAME=$(LIBNAME).$(HIREDIS_MAJOR).$(DYLIBSUFFIX)\n  DYLIB_MAKE_CMD=$(CC) -shared -Wl,-install_name,$(DYLIB_MINOR_NAME) -o $(DYLIBNAME) $(LDFLAGS)\nendif\n\nall: $(DYLIBNAME) $(BINS)\n\n# Deps (use make dep to generate this)\nnet.o: net.c fmacros.h net.h hiredis.h\nasync.o: async.c async.h hiredis.h sds.h dict.c dict.h\nexample.o: example.c hiredis.h\nhiredis.o: hiredis.c fmacros.h hiredis.h net.h sds.h\nsds.o: sds.c sds.h\ntest.o: test.c hiredis.h\n\n$(DYLIBNAME): $(OBJ)\n\t$(DYLIB_MAKE_CMD) $(OBJ)\n\n$(STLIBNAME): $(OBJ)\n\t$(STLIB_MAKE_CMD) $(OBJ)\n\ndynamic: $(DYLIBNAME)\nstatic: $(STLIBNAME)\n\n# Binaries:\nhiredis-example-libevent: example-libevent.c adapters/libevent.h $(STLIBNAME)\n\t$(CC) -o $@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -levent example-libevent.c $(STLIBNAME)\n\nhiredis-example-libev: example-libev.c adapters/libev.h $(STLIBNAME)\n\t$(CC) -o $@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -lev example-libev.c $(STLIBNAME)\n\nifndef AE_DIR\nhiredis-example-ae:\n\t@echo \"Please specify AE_DIR (e.g. <redis repository>/src)\"\n\t@false\nelse\nhiredis-example-ae: example-ae.c adapters/ae.h $(STLIBNAME)\n\t$(CC) -o $@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I$(AE_DIR) $(AE_DIR)/ae.o $(AE_DIR)/zmalloc.o example-ae.c $(STLIBNAME)\nendif\n\nhiredis-%: %.o $(STLIBNAME)\n\t$(CC) -o $@ $(REAL_LDFLAGS) $< $(STLIBNAME)\n\ntest: hiredis-test\n\t./hiredis-test\n\ncheck: hiredis-test\n\techo \\\n\t\t\"daemonize yes\\n\" \\\n\t\t\"pidfile /tmp/hiredis-test-redis.pid\\n\" \\\n\t\t\"port 56379\\n\" \\\n\t\t\"bind 127.0.0.1\\n\" \\\n\t\t\"unixsocket /tmp/hiredis-test-redis.sock\" \\\n\t\t\t| redis-server -\n\t./hiredis-test -h 127.0.0.1 -p 56379 -s /tmp/hiredis-test-redis.sock || \\\n\t\t\t( kill `cat /tmp/hiredis-test-redis.pid` && false )\n\tkill `cat /tmp/hiredis-test-redis.pid`\n\n.c.o:\n\t$(CC) -std=c99 -pedantic -c $(REAL_CFLAGS) $<\n\nclean:\n\trm -rf $(DYLIBNAME) $(STLIBNAME) $(BINS) hiredis-example* *.o *.gcda *.gcno *.gcov\n\ndep:\n\t$(CC) -MM *.c\n\n# Installation related variables and target\nPREFIX?=/usr/local\nINCLUDE_PATH?=include/hiredis\nLIBRARY_PATH?=lib\nINSTALL_INCLUDE_PATH= $(PREFIX)/$(INCLUDE_PATH)\nINSTALL_LIBRARY_PATH= $(PREFIX)/$(LIBRARY_PATH)\n\nifeq ($(uname_S),SunOS)\n  INSTALL?= cp -r\nendif\n\nINSTALL?= cp -a\n\ninstall: $(DYLIBNAME) $(STLIBNAME)\n\tmkdir -p $(INSTALL_INCLUDE_PATH) $(INSTALL_LIBRARY_PATH)\n\t$(INSTALL) hiredis.h async.h adapters $(INSTALL_INCLUDE_PATH)\n\t$(INSTALL) $(DYLIBNAME) $(INSTALL_LIBRARY_PATH)/$(DYLIB_MINOR_NAME)\n\tcd $(INSTALL_LIBRARY_PATH) && ln -sf $(DYLIB_MINOR_NAME) $(DYLIB_MAJOR_NAME)\n\tcd $(INSTALL_LIBRARY_PATH) && ln -sf $(DYLIB_MAJOR_NAME) $(DYLIBNAME)\n\t$(INSTALL) $(STLIBNAME) $(INSTALL_LIBRARY_PATH)\n\n32bit:\n\t@echo \"\"\n\t@echo \"WARNING: if this fails under Linux you probably need to install libc6-dev-i386\"\n\t@echo \"\"\n\t$(MAKE) CFLAGS=\"-m32\" LDFLAGS=\"-m32\"\n\ngprof:\n\t$(MAKE) CFLAGS=\"-pg\" LDFLAGS=\"-pg\"\n\ngcov:\n\t$(MAKE) CFLAGS=\"-fprofile-arcs -ftest-coverage\" LDFLAGS=\"-fprofile-arcs\"\n\ncoverage: gcov\n\tmake check\n\tmkdir -p tmp/lcov\n\tlcov -d . -c -o tmp/lcov/hiredis.info\n\tgenhtml --legend -o tmp/lcov/report tmp/lcov/hiredis.info\n\nnoopt:\n\t$(MAKE) OPTIMIZATION=\"\"\n\n.PHONY: all test check clean dep install 32bit gprof gcov noopt\n"
  },
  {
    "path": "deps/hiredis/README.md",
    "content": "# HIREDIS\n\nHiredis is a minimalistic C client library for the [Redis](http://redis.io/) database.\n\nIt is minimalistic because it just adds minimal support for the protocol, but\nat the same time it uses an high level printf-alike API in order to make it\nmuch higher level than otherwise suggested by its minimal code base and the\nlack of explicit bindings for every Redis command.\n\nApart from supporting sending commands and receiving replies, it comes with\na reply parser that is decoupled from the I/O layer. It\nis a stream parser designed for easy reusability, which can for instance be used\nin higher level language bindings for efficient reply parsing.\n\nHiredis only supports the binary-safe Redis protocol, so you can use it with any\nRedis version >= 1.2.0.\n\nThe library comes with multiple APIs. There is the\n*synchronous API*, the *asynchronous API* and the *reply parsing API*.\n\n## UPGRADING\n\nVersion 0.9.0 is a major overhaul of hiredis in every aspect. However, upgrading existing\ncode using hiredis should not be a big pain. The key thing to keep in mind when\nupgrading is that hiredis >= 0.9.0 uses a `redisContext*` to keep state, in contrast to\nthe stateless 0.0.1 that only has a file descriptor to work with.\n\n## Synchronous API\n\nTo consume the synchronous API, there are only a few function calls that need to be introduced:\n\n    redisContext *redisConnect(const char *ip, int port);\n    void *redisCommand(redisContext *c, const char *format, ...);\n    void freeReplyObject(void *reply);\n\n### Connecting\n\nThe function `redisConnect` is used to create a so-called `redisContext`. The\ncontext is where Hiredis holds state for a connection. The `redisContext`\nstruct has an integer `err` field that is non-zero when an the connection is in\nan error state. The field `errstr` will contain a string with a description of\nthe error. More information on errors can be found in the **Errors** section.\nAfter trying to connect to Redis using `redisConnect` you should\ncheck the `err` field to see if establishing the connection was successful:\n\n    redisContext *c = redisConnect(\"127.0.0.1\", 6379);\n    if (c->err) {\n        printf(\"Error: %s\\n\", c->errstr);\n        // handle error\n    }\n\n### Sending commands\n\nThere are several ways to issue commands to Redis. The first that will be introduced is\n`redisCommand`. This function takes a format similar to printf. In the simplest form,\nit is used like this:\n\n    reply = redisCommand(context, \"SET foo bar\");\n\nThe specifier `%s` interpolates a string in the command, and uses `strlen` to\ndetermine the length of the string:\n\n    reply = redisCommand(context, \"SET foo %s\", value);\n\nWhen you need to pass binary safe strings in a command, the `%b` specifier can be\nused. Together with a pointer to the string, it requires a `size_t` length argument\nof the string:\n\n    reply = redisCommand(context, \"SET foo %b\", value, valuelen);\n\nInternally, Hiredis splits the command in different arguments and will\nconvert it to the protocol used to communicate with Redis.\nOne or more spaces separates arguments, so you can use the specifiers\nanywhere in an argument:\n\n    reply = redisCommand(context, \"SET key:%s %s\", myid, value);\n\n### Using replies\n\nThe return value of `redisCommand` holds a reply when the command was\nsuccessfully executed. When an error occurs, the return value is `NULL` and\nthe `err` field in the context will be set (see section on **Errors**).\nOnce an error is returned the context cannot be reused and you should set up\na new connection.\n\nThe standard replies that `redisCommand` are of the type `redisReply`. The\n`type` field in the `redisReply` should be used to test what kind of reply\nwas received:\n\n* **`REDIS_REPLY_STATUS`**:\n    * The command replied with a status reply. The status string can be accessed using `reply->str`.\n      The length of this string can be accessed using `reply->len`.\n\n* **`REDIS_REPLY_ERROR`**:\n    *  The command replied with an error. The error string can be accessed identical to `REDIS_REPLY_STATUS`.\n\n* **`REDIS_REPLY_INTEGER`**:\n    * The command replied with an integer. The integer value can be accessed using the\n      `reply->integer` field of type `long long`.\n\n* **`REDIS_REPLY_NIL`**:\n    * The command replied with a **nil** object. There is no data to access.\n\n* **`REDIS_REPLY_STRING`**:\n    * A bulk (string) reply. The value of the reply can be accessed using `reply->str`.\n      The length of this string can be accessed using `reply->len`.\n\n* **`REDIS_REPLY_ARRAY`**:\n    * A multi bulk reply. The number of elements in the multi bulk reply is stored in\n      `reply->elements`. Every element in the multi bulk reply is a `redisReply` object as well\n      and can be accessed via `reply->element[..index..]`.\n      Redis may reply with nested arrays but this is fully supported.\n\nReplies should be freed using the `freeReplyObject()` function.\nNote that this function will take care of freeing sub-replies objects\ncontained in arrays and nested arrays, so there is no need for the user to\nfree the sub replies (it is actually harmful and will corrupt the memory).\n\n**Important:** the current version of hiredis (0.10.0) free's replies when the\nasynchronous API is used. This means you should not call `freeReplyObject` when\nyou use this API. The reply is cleaned up by hiredis _after_ the callback\nreturns. This behavior will probably change in future releases, so make sure to\nkeep an eye on the changelog when upgrading (see issue #39).\n\n### Cleaning up\n\nTo disconnect and free the context the following function can be used:\n\n    void redisFree(redisContext *c);\n\nThis function immediately closes the socket and then free's the allocations done in\ncreating the context.\n\n### Sending commands (cont'd)\n\nTogether with `redisCommand`, the function `redisCommandArgv` can be used to issue commands.\nIt has the following prototype:\n\n    void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);\n\nIt takes the number of arguments `argc`, an array of strings `argv` and the lengths of the\narguments `argvlen`. For convenience, `argvlen` may be set to `NULL` and the function will\nuse `strlen(3)` on every argument to determine its length. Obviously, when any of the arguments\nneed to be binary safe, the entire array of lengths `argvlen` should be provided.\n\nThe return value has the same semantic as `redisCommand`.\n\n### Pipelining\n\nTo explain how Hiredis supports pipelining in a blocking connection, there needs to be\nunderstanding of the internal execution flow.\n\nWhen any of the functions in the `redisCommand` family is called, Hiredis first formats the\ncommand according to the Redis protocol. The formatted command is then put in the output buffer\nof the context. This output buffer is dynamic, so it can hold any number of commands.\nAfter the command is put in the output buffer, `redisGetReply` is called. This function has the\nfollowing two execution paths:\n\n1. The input buffer is non-empty:\n    * Try to parse a single reply from the input buffer and return it\n    * If no reply could be parsed, continue at *2*\n2. The input buffer is empty:\n    * Write the **entire** output buffer to the socket\n    * Read from the socket until a single reply could be parsed\n\nThe function `redisGetReply` is exported as part of the Hiredis API and can be used when a reply\nis expected on the socket. To pipeline commands, the only things that needs to be done is\nfilling up the output buffer. For this cause, two commands can be used that are identical\nto the `redisCommand` family, apart from not returning a reply:\n\n    void redisAppendCommand(redisContext *c, const char *format, ...);\n    void redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);\n\nAfter calling either function one or more times, `redisGetReply` can be used to receive the\nsubsequent replies. The return value for this function is either `REDIS_OK` or `REDIS_ERR`, where\nthe latter means an error occurred while reading a reply. Just as with the other commands,\nthe `err` field in the context can be used to find out what the cause of this error is.\n\nThe following examples shows a simple pipeline (resulting in only a single call to `write(2)` and\na single call to `read(2)`):\n\n    redisReply *reply;\n    redisAppendCommand(context,\"SET foo bar\");\n    redisAppendCommand(context,\"GET foo\");\n    redisGetReply(context,&reply); // reply for SET\n    freeReplyObject(reply);\n    redisGetReply(context,&reply); // reply for GET\n    freeReplyObject(reply);\n\nThis API can also be used to implement a blocking subscriber:\n\n    reply = redisCommand(context,\"SUBSCRIBE foo\");\n    freeReplyObject(reply);\n    while(redisGetReply(context,&reply) == REDIS_OK) {\n        // consume message\n        freeReplyObject(reply);\n    }\n\n### Errors\n\nWhen a function call is not successful, depending on the function either `NULL` or `REDIS_ERR` is\nreturned. The `err` field inside the context will be non-zero and set to one of the\nfollowing constants:\n\n* **`REDIS_ERR_IO`**:\n    There was an I/O error while creating the connection, trying to write\n    to the socket or read from the socket. If you included `errno.h` in your\n    application, you can use the global `errno` variable to find out what is\n    wrong.\n\n* **`REDIS_ERR_EOF`**:\n    The server closed the connection which resulted in an empty read.\n\n* **`REDIS_ERR_PROTOCOL`**:\n    There was an error while parsing the protocol.\n\n* **`REDIS_ERR_OTHER`**:\n    Any other error. Currently, it is only used when a specified hostname to connect\n    to cannot be resolved.\n\nIn every case, the `errstr` field in the context will be set to hold a string representation\nof the error.\n\n## Asynchronous API\n\nHiredis comes with an asynchronous API that works easily with any event library.\nExamples are bundled that show using Hiredis with [libev](http://software.schmorp.de/pkg/libev.html)\nand [libevent](http://monkey.org/~provos/libevent/).\n\n### Connecting\n\nThe function `redisAsyncConnect` can be used to establish a non-blocking connection to\nRedis. It returns a pointer to the newly created `redisAsyncContext` struct. The `err` field\nshould be checked after creation to see if there were errors creating the connection.\nBecause the connection that will be created is non-blocking, the kernel is not able to\ninstantly return if the specified host and port is able to accept a connection.\n\n    redisAsyncContext *c = redisAsyncConnect(\"127.0.0.1\", 6379);\n    if (c->err) {\n        printf(\"Error: %s\\n\", c->errstr);\n        // handle error\n    }\n\nThe asynchronous context can hold a disconnect callback function that is called when the\nconnection is disconnected (either because of an error or per user request). This function should\nhave the following prototype:\n\n    void(const redisAsyncContext *c, int status);\n\nOn a disconnect, the `status` argument is set to `REDIS_OK` when disconnection was initiated by the\nuser, or `REDIS_ERR` when the disconnection was caused by an error. When it is `REDIS_ERR`, the `err`\nfield in the context can be accessed to find out the cause of the error.\n\nThe context object is always free'd after the disconnect callback fired. When a reconnect is needed,\nthe disconnect callback is a good point to do so.\n\nSetting the disconnect callback can only be done once per context. For subsequent calls it will\nreturn `REDIS_ERR`. The function to set the disconnect callback has the following prototype:\n\n    int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn);\n\n### Sending commands and their callbacks\n\nIn an asynchronous context, commands are automatically pipelined due to the nature of an event loop.\nTherefore, unlike the synchronous API, there is only a single way to send commands.\nBecause commands are sent to Redis asynchronously, issuing a command requires a callback function\nthat is called when the reply is received. Reply callbacks should have the following prototype:\n\n    void(redisAsyncContext *c, void *reply, void *privdata);\n\nThe `privdata` argument can be used to curry arbitrary data to the callback from the point where\nthe command is initially queued for execution.\n\nThe functions that can be used to issue commands in an asynchronous context are:\n\n    int redisAsyncCommand(\n      redisAsyncContext *ac, redisCallbackFn *fn, void *privdata,\n      const char *format, ...);\n    int redisAsyncCommandArgv(\n      redisAsyncContext *ac, redisCallbackFn *fn, void *privdata,\n      int argc, const char **argv, const size_t *argvlen);\n\nBoth functions work like their blocking counterparts. The return value is `REDIS_OK` when the command\nwas successfully added to the output buffer and `REDIS_ERR` otherwise. Example: when the connection\nis being disconnected per user-request, no new commands may be added to the output buffer and `REDIS_ERR` is\nreturned on calls to the `redisAsyncCommand` family.\n\nIf the reply for a command with a `NULL` callback is read, it is immediately free'd. When the callback\nfor a command is non-`NULL`, the memory is free'd immediately following the callback: the reply is only\nvalid for the duration of the callback.\n\nAll pending callbacks are called with a `NULL` reply when the context encountered an error.\n\n### Disconnecting\n\nAn asynchronous connection can be terminated using:\n\n    void redisAsyncDisconnect(redisAsyncContext *ac);\n\nWhen this function is called, the connection is **not** immediately terminated. Instead, new\ncommands are no longer accepted and the connection is only terminated when all pending commands\nhave been written to the socket, their respective replies have been read and their respective\ncallbacks have been executed. After this, the disconnection callback is executed with the\n`REDIS_OK` status and the context object is free'd.\n\n### Hooking it up to event library *X*\n\nThere are a few hooks that need to be set on the context object after it is created.\nSee the `adapters/` directory for bindings to *libev* and *libevent*.\n\n## Reply parsing API\n\nHiredis comes with a reply parsing API that makes it easy for writing higher\nlevel language bindings.\n\nThe reply parsing API consists of the following functions:\n\n    redisReader *redisReaderCreate(void);\n    void redisReaderFree(redisReader *reader);\n    int redisReaderFeed(redisReader *reader, const char *buf, size_t len);\n    int redisReaderGetReply(redisReader *reader, void **reply);\n\nThe same set of functions are used internally by hiredis when creating a\nnormal Redis context, the above API just exposes it to the user for a direct\nusage.\n\n### Usage\n\nThe function `redisReaderCreate` creates a `redisReader` structure that holds a\nbuffer with unparsed data and state for the protocol parser.\n\nIncoming data -- most likely from a socket -- can be placed in the internal\nbuffer of the `redisReader` using `redisReaderFeed`. This function will make a\ncopy of the buffer pointed to by `buf` for `len` bytes. This data is parsed\nwhen `redisReaderGetReply` is called. This function returns an integer status\nand a reply object (as described above) via `void **reply`. The returned status\ncan be either `REDIS_OK` or `REDIS_ERR`, where the latter means something went\nwrong (either a protocol error, or an out of memory error).\n\n### Customizing replies\n\nThe function `redisReaderGetReply` creates `redisReply` and makes the function\nargument `reply` point to the created `redisReply` variable. For instance, if\nthe response of type `REDIS_REPLY_STATUS` then the `str` field of `redisReply`\nwill hold the status as a vanilla C string. However, the functions that are\nresponsible for creating instances of the `redisReply` can be customized by\nsetting the `fn` field on the `redisReader` struct. This should be done\nimmediately after creating the `redisReader`.\n\nFor example, [hiredis-rb](https://github.com/pietern/hiredis-rb/blob/master/ext/hiredis_ext/reader.c)\nuses customized reply object functions to create Ruby objects.\n\n### Reader max buffer\n\nBoth when using the Reader API directly or when using it indirectly via a\nnormal Redis context, the redisReader structure uses a buffer in order to\naccumulate data from the server.\nUsually this buffer is destroyed when it is empty and is larger than 16\nkb in order to avoid wasting memory in unused buffers\n\nHowever when working with very big payloads destroying the buffer may slow\ndown performances considerably, so it is possible to modify the max size of\nan idle buffer changing the value of the `maxbuf` field of the reader structure\nto the desired value. The special value of 0 means that there is no maximum\nvalue for an idle buffer, so the buffer will never get freed.\n\nFor instance if you have a normal Redis context you can set the maximum idle\nbuffer to zero (unlimited) just with:\n\n    context->reader->maxbuf = 0;\n\nThis should be done only in order to maximize performances when working with\nlarge payloads. The context should be set back to `REDIS_READER_MAX_BUF` again\nas soon as possible in order to prevent allocation of useless memory.\n\n## AUTHORS\n\nHiredis was written by Salvatore Sanfilippo (antirez at gmail) and\nPieter Noordhuis (pcnoordhuis at gmail) and is released under the BSD license.\n"
  },
  {
    "path": "deps/hiredis/adapters/ae.h",
    "content": "/*\n * Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>\n *\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 __HIREDIS_AE_H__\n#define __HIREDIS_AE_H__\n#include <sys/types.h>\n#include <ae.h>\n#include \"../hiredis.h\"\n#include \"../async.h\"\n\ntypedef struct redisAeEvents {\n    redisAsyncContext *context;\n    aeEventLoop *loop;\n    int fd;\n    int reading, writing;\n} redisAeEvents;\n\nstatic void redisAeReadEvent(aeEventLoop *el, int fd, void *privdata, int mask) {\n    ((void)el); ((void)fd); ((void)mask);\n\n    redisAeEvents *e = (redisAeEvents*)privdata;\n    redisAsyncHandleRead(e->context);\n}\n\nstatic void redisAeWriteEvent(aeEventLoop *el, int fd, void *privdata, int mask) {\n    ((void)el); ((void)fd); ((void)mask);\n\n    redisAeEvents *e = (redisAeEvents*)privdata;\n    redisAsyncHandleWrite(e->context);\n}\n\nstatic void redisAeAddRead(void *privdata) {\n    redisAeEvents *e = (redisAeEvents*)privdata;\n    aeEventLoop *loop = e->loop;\n    if (!e->reading) {\n        e->reading = 1;\n        aeCreateFileEvent(loop,e->fd,AE_READABLE,redisAeReadEvent,e);\n    }\n}\n\nstatic void redisAeDelRead(void *privdata) {\n    redisAeEvents *e = (redisAeEvents*)privdata;\n    aeEventLoop *loop = e->loop;\n    if (e->reading) {\n        e->reading = 0;\n        aeDeleteFileEvent(loop,e->fd,AE_READABLE);\n    }\n}\n\nstatic void redisAeAddWrite(void *privdata) {\n    redisAeEvents *e = (redisAeEvents*)privdata;\n    aeEventLoop *loop = e->loop;\n    if (!e->writing) {\n        e->writing = 1;\n        aeCreateFileEvent(loop,e->fd,AE_WRITABLE,redisAeWriteEvent,e);\n    }\n}\n\nstatic void redisAeDelWrite(void *privdata) {\n    redisAeEvents *e = (redisAeEvents*)privdata;\n    aeEventLoop *loop = e->loop;\n    if (e->writing) {\n        e->writing = 0;\n        aeDeleteFileEvent(loop,e->fd,AE_WRITABLE);\n    }\n}\n\nstatic void redisAeCleanup(void *privdata) {\n    redisAeEvents *e = (redisAeEvents*)privdata;\n    redisAeDelRead(privdata);\n    redisAeDelWrite(privdata);\n    free(e);\n}\n\nstatic int redisAeAttach(aeEventLoop *loop, redisAsyncContext *ac) {\n    redisContext *c = &(ac->c);\n    redisAeEvents *e;\n\n    /* Nothing should be attached when something is already attached */\n    if (ac->ev.data != NULL)\n        return REDIS_ERR;\n\n    /* Create container for context and r/w events */\n    e = (redisAeEvents*)malloc(sizeof(*e));\n    e->context = ac;\n    e->loop = loop;\n    e->fd = c->fd;\n    e->reading = e->writing = 0;\n\n    /* Register functions to start/stop listening for events */\n    ac->ev.addRead = redisAeAddRead;\n    ac->ev.delRead = redisAeDelRead;\n    ac->ev.addWrite = redisAeAddWrite;\n    ac->ev.delWrite = redisAeDelWrite;\n    ac->ev.cleanup = redisAeCleanup;\n    ac->ev.data = e;\n\n    return REDIS_OK;\n}\n#endif\n"
  },
  {
    "path": "deps/hiredis/adapters/libev.h",
    "content": "/*\n * Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>\n *\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 __HIREDIS_LIBEV_H__\n#define __HIREDIS_LIBEV_H__\n#include <stdlib.h>\n#include <sys/types.h>\n#include <ev.h>\n#include \"../hiredis.h\"\n#include \"../async.h\"\n\ntypedef struct redisLibevEvents {\n    redisAsyncContext *context;\n    struct ev_loop *loop;\n    int reading, writing;\n    ev_io rev, wev;\n} redisLibevEvents;\n\nstatic void redisLibevReadEvent(EV_P_ ev_io *watcher, int revents) {\n#if EV_MULTIPLICITY\n    ((void)loop);\n#endif\n    ((void)revents);\n\n    redisLibevEvents *e = (redisLibevEvents*)watcher->data;\n    redisAsyncHandleRead(e->context);\n}\n\nstatic void redisLibevWriteEvent(EV_P_ ev_io *watcher, int revents) {\n#if EV_MULTIPLICITY\n    ((void)loop);\n#endif\n    ((void)revents);\n\n    redisLibevEvents *e = (redisLibevEvents*)watcher->data;\n    redisAsyncHandleWrite(e->context);\n}\n\nstatic void redisLibevAddRead(void *privdata) {\n    redisLibevEvents *e = (redisLibevEvents*)privdata;\n    struct ev_loop *loop = e->loop;\n    ((void)loop);\n    if (!e->reading) {\n        e->reading = 1;\n        ev_io_start(EV_A_ &e->rev);\n    }\n}\n\nstatic void redisLibevDelRead(void *privdata) {\n    redisLibevEvents *e = (redisLibevEvents*)privdata;\n    struct ev_loop *loop = e->loop;\n    ((void)loop);\n    if (e->reading) {\n        e->reading = 0;\n        ev_io_stop(EV_A_ &e->rev);\n    }\n}\n\nstatic void redisLibevAddWrite(void *privdata) {\n    redisLibevEvents *e = (redisLibevEvents*)privdata;\n    struct ev_loop *loop = e->loop;\n    ((void)loop);\n    if (!e->writing) {\n        e->writing = 1;\n        ev_io_start(EV_A_ &e->wev);\n    }\n}\n\nstatic void redisLibevDelWrite(void *privdata) {\n    redisLibevEvents *e = (redisLibevEvents*)privdata;\n    struct ev_loop *loop = e->loop;\n    ((void)loop);\n    if (e->writing) {\n        e->writing = 0;\n        ev_io_stop(EV_A_ &e->wev);\n    }\n}\n\nstatic void redisLibevCleanup(void *privdata) {\n    redisLibevEvents *e = (redisLibevEvents*)privdata;\n    redisLibevDelRead(privdata);\n    redisLibevDelWrite(privdata);\n    free(e);\n}\n\nstatic int redisLibevAttach(EV_P_ redisAsyncContext *ac) {\n    redisContext *c = &(ac->c);\n    redisLibevEvents *e;\n\n    /* Nothing should be attached when something is already attached */\n    if (ac->ev.data != NULL)\n        return REDIS_ERR;\n\n    /* Create container for context and r/w events */\n    e = (redisLibevEvents*)malloc(sizeof(*e));\n    e->context = ac;\n#if EV_MULTIPLICITY\n    e->loop = loop;\n#else\n    e->loop = NULL;\n#endif\n    e->reading = e->writing = 0;\n    e->rev.data = e;\n    e->wev.data = e;\n\n    /* Register functions to start/stop listening for events */\n    ac->ev.addRead = redisLibevAddRead;\n    ac->ev.delRead = redisLibevDelRead;\n    ac->ev.addWrite = redisLibevAddWrite;\n    ac->ev.delWrite = redisLibevDelWrite;\n    ac->ev.cleanup = redisLibevCleanup;\n    ac->ev.data = e;\n\n    /* Initialize read/write events */\n    ev_io_init(&e->rev,redisLibevReadEvent,c->fd,EV_READ);\n    ev_io_init(&e->wev,redisLibevWriteEvent,c->fd,EV_WRITE);\n    return REDIS_OK;\n}\n\n#endif\n"
  },
  {
    "path": "deps/hiredis/adapters/libevent.h",
    "content": "/*\n * Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>\n *\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 __HIREDIS_LIBEVENT_H__\n#define __HIREDIS_LIBEVENT_H__\n#include <event.h>\n#include \"../hiredis.h\"\n#include \"../async.h\"\n\ntypedef struct redisLibeventEvents {\n    redisAsyncContext *context;\n    struct event rev, wev;\n} redisLibeventEvents;\n\nstatic void redisLibeventReadEvent(int fd, short event, void *arg) {\n    ((void)fd); ((void)event);\n    redisLibeventEvents *e = (redisLibeventEvents*)arg;\n    redisAsyncHandleRead(e->context);\n}\n\nstatic void redisLibeventWriteEvent(int fd, short event, void *arg) {\n    ((void)fd); ((void)event);\n    redisLibeventEvents *e = (redisLibeventEvents*)arg;\n    redisAsyncHandleWrite(e->context);\n}\n\nstatic void redisLibeventAddRead(void *privdata) {\n    redisLibeventEvents *e = (redisLibeventEvents*)privdata;\n    event_add(&e->rev,NULL);\n}\n\nstatic void redisLibeventDelRead(void *privdata) {\n    redisLibeventEvents *e = (redisLibeventEvents*)privdata;\n    event_del(&e->rev);\n}\n\nstatic void redisLibeventAddWrite(void *privdata) {\n    redisLibeventEvents *e = (redisLibeventEvents*)privdata;\n    event_add(&e->wev,NULL);\n}\n\nstatic void redisLibeventDelWrite(void *privdata) {\n    redisLibeventEvents *e = (redisLibeventEvents*)privdata;\n    event_del(&e->wev);\n}\n\nstatic void redisLibeventCleanup(void *privdata) {\n    redisLibeventEvents *e = (redisLibeventEvents*)privdata;\n    event_del(&e->rev);\n    event_del(&e->wev);\n    free(e);\n}\n\nstatic int redisLibeventAttach(redisAsyncContext *ac, struct event_base *base) {\n    redisContext *c = &(ac->c);\n    redisLibeventEvents *e;\n\n    /* Nothing should be attached when something is already attached */\n    if (ac->ev.data != NULL)\n        return REDIS_ERR;\n\n    /* Create container for context and r/w events */\n    e = (redisLibeventEvents*)malloc(sizeof(*e));\n    e->context = ac;\n\n    /* Register functions to start/stop listening for events */\n    ac->ev.addRead = redisLibeventAddRead;\n    ac->ev.delRead = redisLibeventDelRead;\n    ac->ev.addWrite = redisLibeventAddWrite;\n    ac->ev.delWrite = redisLibeventDelWrite;\n    ac->ev.cleanup = redisLibeventCleanup;\n    ac->ev.data = e;\n\n    /* Initialize and install read/write events */\n    event_set(&e->rev,c->fd,EV_READ,redisLibeventReadEvent,e);\n    event_set(&e->wev,c->fd,EV_WRITE,redisLibeventWriteEvent,e);\n    event_base_set(base,&e->rev);\n    event_base_set(base,&e->wev);\n    return REDIS_OK;\n}\n#endif\n"
  },
  {
    "path": "deps/hiredis/async.c",
    "content": "/*\n * Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>\n * Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>\n *\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 \"fmacros.h\"\n#include <stdlib.h>\n#include <string.h>\n#include <strings.h>\n#include <assert.h>\n#include <ctype.h>\n#include <errno.h>\n#include \"async.h\"\n#include \"net.h\"\n#include \"dict.c\"\n#include \"sds.h\"\n\n#define _EL_ADD_READ(ctx) do { \\\n        if ((ctx)->ev.addRead) (ctx)->ev.addRead((ctx)->ev.data); \\\n    } while(0)\n#define _EL_DEL_READ(ctx) do { \\\n        if ((ctx)->ev.delRead) (ctx)->ev.delRead((ctx)->ev.data); \\\n    } while(0)\n#define _EL_ADD_WRITE(ctx) do { \\\n        if ((ctx)->ev.addWrite) (ctx)->ev.addWrite((ctx)->ev.data); \\\n    } while(0)\n#define _EL_DEL_WRITE(ctx) do { \\\n        if ((ctx)->ev.delWrite) (ctx)->ev.delWrite((ctx)->ev.data); \\\n    } while(0)\n#define _EL_CLEANUP(ctx) do { \\\n        if ((ctx)->ev.cleanup) (ctx)->ev.cleanup((ctx)->ev.data); \\\n    } while(0);\n\n/* Forward declaration of function in hiredis.c */\nvoid __redisAppendCommand(redisContext *c, char *cmd, size_t len);\n\n/* Functions managing dictionary of callbacks for pub/sub. */\nstatic unsigned int callbackHash(const void *key) {\n    return dictGenHashFunction((unsigned char*)key,sdslen((char*)key));\n}\n\nstatic void *callbackValDup(void *privdata, const void *src) {\n    ((void) privdata);\n    redisCallback *dup = malloc(sizeof(*dup));\n    memcpy(dup,src,sizeof(*dup));\n    return dup;\n}\n\nstatic int callbackKeyCompare(void *privdata, const void *key1, const void *key2) {\n    int l1, l2;\n    ((void) 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\nstatic void callbackKeyDestructor(void *privdata, void *key) {\n    ((void) privdata);\n    sdsfree((sds)key);\n}\n\nstatic void callbackValDestructor(void *privdata, void *val) {\n    ((void) privdata);\n    free(val);\n}\n\nstatic dictType callbackDict = {\n    callbackHash,\n    NULL,\n    callbackValDup,\n    callbackKeyCompare,\n    callbackKeyDestructor,\n    callbackValDestructor\n};\n\nstatic redisAsyncContext *redisAsyncInitialize(redisContext *c) {\n    redisAsyncContext *ac = realloc(c,sizeof(redisAsyncContext));\n    c = &(ac->c);\n\n    /* The regular connect functions will always set the flag REDIS_CONNECTED.\n     * For the async API, we want to wait until the first write event is\n     * received up before setting this flag, so reset it here. */\n    c->flags &= ~REDIS_CONNECTED;\n\n    ac->err = 0;\n    ac->errstr = NULL;\n    ac->data = NULL;\n\n    ac->ev.data = NULL;\n    ac->ev.addRead = NULL;\n    ac->ev.delRead = NULL;\n    ac->ev.addWrite = NULL;\n    ac->ev.delWrite = NULL;\n    ac->ev.cleanup = NULL;\n\n    ac->onConnect = NULL;\n    ac->onDisconnect = NULL;\n\n    ac->replies.head = NULL;\n    ac->replies.tail = NULL;\n    ac->sub.invalid.head = NULL;\n    ac->sub.invalid.tail = NULL;\n    ac->sub.channels = dictCreate(&callbackDict,NULL);\n    ac->sub.patterns = dictCreate(&callbackDict,NULL);\n    return ac;\n}\n\n/* We want the error field to be accessible directly instead of requiring\n * an indirection to the redisContext struct. */\nstatic void __redisAsyncCopyError(redisAsyncContext *ac) {\n    redisContext *c = &(ac->c);\n    ac->err = c->err;\n    ac->errstr = c->errstr;\n}\n\nredisAsyncContext *redisAsyncConnect(const char *ip, int port) {\n    redisContext *c = redisConnectNonBlock(ip,port);\n    redisAsyncContext *ac = redisAsyncInitialize(c);\n    __redisAsyncCopyError(ac);\n    return ac;\n}\n\nredisAsyncContext *redisAsyncConnectUnix(const char *path) {\n    redisContext *c = redisConnectUnixNonBlock(path);\n    redisAsyncContext *ac = redisAsyncInitialize(c);\n    __redisAsyncCopyError(ac);\n    return ac;\n}\n\nint redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn) {\n    if (ac->onConnect == NULL) {\n        ac->onConnect = fn;\n\n        /* The common way to detect an established connection is to wait for\n         * the first write event to be fired. This assumes the related event\n         * library functions are already set. */\n        _EL_ADD_WRITE(ac);\n        return REDIS_OK;\n    }\n    return REDIS_ERR;\n}\n\nint redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn) {\n    if (ac->onDisconnect == NULL) {\n        ac->onDisconnect = fn;\n        return REDIS_OK;\n    }\n    return REDIS_ERR;\n}\n\n/* Helper functions to push/shift callbacks */\nstatic int __redisPushCallback(redisCallbackList *list, redisCallback *source) {\n    redisCallback *cb;\n\n    /* Copy callback from stack to heap */\n    cb = malloc(sizeof(*cb));\n    if (source != NULL) {\n        memcpy(cb,source,sizeof(*cb));\n        cb->next = NULL;\n    }\n\n    /* Store callback in list */\n    if (list->head == NULL)\n        list->head = cb;\n    if (list->tail != NULL)\n        list->tail->next = cb;\n    list->tail = cb;\n    return REDIS_OK;\n}\n\nstatic int __redisShiftCallback(redisCallbackList *list, redisCallback *target) {\n    redisCallback *cb = list->head;\n    if (cb != NULL) {\n        list->head = cb->next;\n        if (cb == list->tail)\n            list->tail = NULL;\n\n        /* Copy callback from heap to stack */\n        if (target != NULL)\n            memcpy(target,cb,sizeof(*cb));\n        free(cb);\n        return REDIS_OK;\n    }\n    return REDIS_ERR;\n}\n\nstatic void __redisRunCallback(redisAsyncContext *ac, redisCallback *cb, redisReply *reply) {\n    redisContext *c = &(ac->c);\n    if (cb->fn != NULL) {\n        c->flags |= REDIS_IN_CALLBACK;\n        cb->fn(ac,reply,cb->privdata);\n        c->flags &= ~REDIS_IN_CALLBACK;\n    }\n}\n\n/* Helper function to free the context. */\nstatic void __redisAsyncFree(redisAsyncContext *ac) {\n    redisContext *c = &(ac->c);\n    redisCallback cb;\n    dictIterator *it;\n    dictEntry *de;\n\n    /* Execute pending callbacks with NULL reply. */\n    while (__redisShiftCallback(&ac->replies,&cb) == REDIS_OK)\n        __redisRunCallback(ac,&cb,NULL);\n\n    /* Execute callbacks for invalid commands */\n    while (__redisShiftCallback(&ac->sub.invalid,&cb) == REDIS_OK)\n        __redisRunCallback(ac,&cb,NULL);\n\n    /* Run subscription callbacks callbacks with NULL reply */\n    it = dictGetIterator(ac->sub.channels);\n    while ((de = dictNext(it)) != NULL)\n        __redisRunCallback(ac,dictGetEntryVal(de),NULL);\n    dictReleaseIterator(it);\n    dictRelease(ac->sub.channels);\n\n    it = dictGetIterator(ac->sub.patterns);\n    while ((de = dictNext(it)) != NULL)\n        __redisRunCallback(ac,dictGetEntryVal(de),NULL);\n    dictReleaseIterator(it);\n    dictRelease(ac->sub.patterns);\n\n    /* Signal event lib to clean up */\n    _EL_CLEANUP(ac);\n\n    /* Execute disconnect callback. When redisAsyncFree() initiated destroying\n     * this context, the status will always be REDIS_OK. */\n    if (ac->onDisconnect && (c->flags & REDIS_CONNECTED)) {\n        if (c->flags & REDIS_FREEING) {\n            ac->onDisconnect(ac,REDIS_OK);\n        } else {\n            ac->onDisconnect(ac,(ac->err == 0) ? REDIS_OK : REDIS_ERR);\n        }\n    }\n\n    /* Cleanup self */\n    redisFree(c);\n}\n\n/* Free the async context. When this function is called from a callback,\n * control needs to be returned to redisProcessCallbacks() before actual\n * free'ing. To do so, a flag is set on the context which is picked up by\n * redisProcessCallbacks(). Otherwise, the context is immediately free'd. */\nvoid redisAsyncFree(redisAsyncContext *ac) {\n    redisContext *c = &(ac->c);\n    c->flags |= REDIS_FREEING;\n    if (!(c->flags & REDIS_IN_CALLBACK))\n        __redisAsyncFree(ac);\n}\n\n/* Helper function to make the disconnect happen and clean up. */\nstatic void __redisAsyncDisconnect(redisAsyncContext *ac) {\n    redisContext *c = &(ac->c);\n\n    /* Make sure error is accessible if there is any */\n    __redisAsyncCopyError(ac);\n\n    if (ac->err == 0) {\n        /* For clean disconnects, there should be no pending callbacks. */\n        assert(__redisShiftCallback(&ac->replies,NULL) == REDIS_ERR);\n    } else {\n        /* Disconnection is caused by an error, make sure that pending\n         * callbacks cannot call new commands. */\n        c->flags |= REDIS_DISCONNECTING;\n    }\n\n    /* For non-clean disconnects, __redisAsyncFree() will execute pending\n     * callbacks with a NULL-reply. */\n    __redisAsyncFree(ac);\n}\n\n/* Tries to do a clean disconnect from Redis, meaning it stops new commands\n * from being issued, but tries to flush the output buffer and execute\n * callbacks for all remaining replies. When this function is called from a\n * callback, there might be more replies and we can safely defer disconnecting\n * to redisProcessCallbacks(). Otherwise, we can only disconnect immediately\n * when there are no pending callbacks. */\nvoid redisAsyncDisconnect(redisAsyncContext *ac) {\n    redisContext *c = &(ac->c);\n    c->flags |= REDIS_DISCONNECTING;\n    if (!(c->flags & REDIS_IN_CALLBACK) && ac->replies.head == NULL)\n        __redisAsyncDisconnect(ac);\n}\n\nstatic int __redisGetSubscribeCallback(redisAsyncContext *ac, redisReply *reply, redisCallback *dstcb) {\n    redisContext *c = &(ac->c);\n    dict *callbacks;\n    dictEntry *de;\n    int pvariant;\n    char *stype;\n    sds sname;\n\n    /* Custom reply functions are not supported for pub/sub. This will fail\n     * very hard when they are used... */\n    if (reply->type == REDIS_REPLY_ARRAY) {\n        assert(reply->elements >= 2);\n        assert(reply->element[0]->type == REDIS_REPLY_STRING);\n        stype = reply->element[0]->str;\n        pvariant = (tolower(stype[0]) == 'p') ? 1 : 0;\n\n        if (pvariant)\n            callbacks = ac->sub.patterns;\n        else\n            callbacks = ac->sub.channels;\n\n        /* Locate the right callback */\n        assert(reply->element[1]->type == REDIS_REPLY_STRING);\n        sname = sdsnewlen(reply->element[1]->str,reply->element[1]->len);\n        de = dictFind(callbacks,sname);\n        if (de != NULL) {\n            memcpy(dstcb,dictGetEntryVal(de),sizeof(*dstcb));\n\n            /* If this is an unsubscribe message, remove it. */\n            if (strcasecmp(stype+pvariant,\"unsubscribe\") == 0) {\n                dictDelete(callbacks,sname);\n\n                /* If this was the last unsubscribe message, revert to\n                 * non-subscribe mode. */\n                assert(reply->element[2]->type == REDIS_REPLY_INTEGER);\n                if (reply->element[2]->integer == 0)\n                    c->flags &= ~REDIS_SUBSCRIBED;\n            }\n        }\n        sdsfree(sname);\n    } else {\n        /* Shift callback for invalid commands. */\n        __redisShiftCallback(&ac->sub.invalid,dstcb);\n    }\n    return REDIS_OK;\n}\n\nvoid redisProcessCallbacks(redisAsyncContext *ac) {\n    redisContext *c = &(ac->c);\n    redisCallback cb;\n    void *reply = NULL;\n    int status;\n\n    while((status = redisGetReply(c,&reply)) == REDIS_OK) {\n        if (reply == NULL) {\n            /* When the connection is being disconnected and there are\n             * no more replies, this is the cue to really disconnect. */\n            if (c->flags & REDIS_DISCONNECTING && sdslen(c->obuf) == 0) {\n                __redisAsyncDisconnect(ac);\n                return;\n            }\n            \n            /* If monitor mode, repush callback */\n            if(c->flags & REDIS_MONITORING) {\n                __redisPushCallback(&ac->replies,&cb);\n            }\n\n            /* When the connection is not being disconnected, simply stop\n             * trying to get replies and wait for the next loop tick. */\n            break;\n        }\n\n        /* Even if the context is subscribed, pending regular callbacks will\n         * get a reply before pub/sub messages arrive. */\n        if (__redisShiftCallback(&ac->replies,&cb) != REDIS_OK) {\n            /*\n             * A spontaneous reply in a not-subscribed context can be the error\n             * reply that is sent when a new connection exceeds the maximum\n             * number of allowed connections on the server side.\n             *\n             * This is seen as an error instead of a regular reply because the\n             * server closes the connection after sending it.\n             *\n             * To prevent the error from being overwritten by an EOF error the\n             * connection is closed here. See issue #43.\n             *\n             * Another possibility is that the server is loading its dataset.\n             * In this case we also want to close the connection, and have the\n             * user wait until the server is ready to take our request.\n             */\n            if (((redisReply*)reply)->type == REDIS_REPLY_ERROR) {\n                c->err = REDIS_ERR_OTHER;\n                snprintf(c->errstr,sizeof(c->errstr),\"%s\",((redisReply*)reply)->str);\n                __redisAsyncDisconnect(ac);\n                return;\n            }\n            /* No more regular callbacks and no errors, the context *must* be subscribed or monitoring. */\n            assert((c->flags & REDIS_SUBSCRIBED || c->flags & REDIS_MONITORING));\n            if(c->flags & REDIS_SUBSCRIBED)\n                __redisGetSubscribeCallback(ac,reply,&cb);\n        }\n\n        if (cb.fn != NULL) {\n            __redisRunCallback(ac,&cb,reply);\n            c->reader->fn->freeObject(reply);\n\n            /* Proceed with free'ing when redisAsyncFree() was called. */\n            if (c->flags & REDIS_FREEING) {\n                __redisAsyncFree(ac);\n                return;\n            }\n        } else {\n            /* No callback for this reply. This can either be a NULL callback,\n             * or there were no callbacks to begin with. Either way, don't\n             * abort with an error, but simply ignore it because the client\n             * doesn't know what the server will spit out over the wire. */\n            c->reader->fn->freeObject(reply);\n        }\n    }\n\n    /* Disconnect when there was an error reading the reply */\n    if (status != REDIS_OK)\n        __redisAsyncDisconnect(ac);\n}\n\n/* Internal helper function to detect socket status the first time a read or\n * write event fires. When connecting was not succesful, the connect callback\n * is called with a REDIS_ERR status and the context is free'd. */\nstatic int __redisAsyncHandleConnect(redisAsyncContext *ac) {\n    redisContext *c = &(ac->c);\n\n    if (redisCheckSocketError(c,c->fd) == REDIS_ERR) {\n        /* Try again later when connect(2) is still in progress. */\n        if (errno == EINPROGRESS)\n            return REDIS_OK;\n\n        if (ac->onConnect) ac->onConnect(ac,REDIS_ERR);\n        __redisAsyncDisconnect(ac);\n        return REDIS_ERR;\n    }\n\n    /* Mark context as connected. */\n    c->flags |= REDIS_CONNECTED;\n    if (ac->onConnect) ac->onConnect(ac,REDIS_OK);\n    return REDIS_OK;\n}\n\n/* This function should be called when the socket is readable.\n * It processes all replies that can be read and executes their callbacks.\n */\nvoid redisAsyncHandleRead(redisAsyncContext *ac) {\n    redisContext *c = &(ac->c);\n\n    if (!(c->flags & REDIS_CONNECTED)) {\n        /* Abort connect was not successful. */\n        if (__redisAsyncHandleConnect(ac) != REDIS_OK)\n            return;\n        /* Try again later when the context is still not connected. */\n        if (!(c->flags & REDIS_CONNECTED))\n            return;\n    }\n\n    if (redisBufferRead(c) == REDIS_ERR) {\n        __redisAsyncDisconnect(ac);\n    } else {\n        /* Always re-schedule reads */\n        _EL_ADD_READ(ac);\n        redisProcessCallbacks(ac);\n    }\n}\n\nvoid redisAsyncHandleWrite(redisAsyncContext *ac) {\n    redisContext *c = &(ac->c);\n    int done = 0;\n\n    if (!(c->flags & REDIS_CONNECTED)) {\n        /* Abort connect was not successful. */\n        if (__redisAsyncHandleConnect(ac) != REDIS_OK)\n            return;\n        /* Try again later when the context is still not connected. */\n        if (!(c->flags & REDIS_CONNECTED))\n            return;\n    }\n\n    if (redisBufferWrite(c,&done) == REDIS_ERR) {\n        __redisAsyncDisconnect(ac);\n    } else {\n        /* Continue writing when not done, stop writing otherwise */\n        if (!done)\n            _EL_ADD_WRITE(ac);\n        else\n            _EL_DEL_WRITE(ac);\n\n        /* Always schedule reads after writes */\n        _EL_ADD_READ(ac);\n    }\n}\n\n/* Sets a pointer to the first argument and its length starting at p. Returns\n * the number of bytes to skip to get to the following argument. */\nstatic char *nextArgument(char *start, char **str, size_t *len) {\n    char *p = start;\n    if (p[0] != '$') {\n        p = strchr(p,'$');\n        if (p == NULL) return NULL;\n    }\n\n    *len = (int)strtol(p+1,NULL,10);\n    p = strchr(p,'\\r');\n    assert(p);\n    *str = p+2;\n    return p+2+(*len)+2;\n}\n\n/* Helper function for the redisAsyncCommand* family of functions. Writes a\n * formatted command to the output buffer and registers the provided callback\n * function with the context. */\nstatic int __redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, char *cmd, size_t len) {\n    redisContext *c = &(ac->c);\n    redisCallback cb;\n    int pvariant, hasnext;\n    char *cstr, *astr;\n    size_t clen, alen;\n    char *p;\n    sds sname;\n\n    /* Don't accept new commands when the connection is about to be closed. */\n    if (c->flags & (REDIS_DISCONNECTING | REDIS_FREEING)) return REDIS_ERR;\n\n    /* Setup callback */\n    cb.fn = fn;\n    cb.privdata = privdata;\n\n    /* Find out which command will be appended. */\n    p = nextArgument(cmd,&cstr,&clen);\n    assert(p != NULL);\n    hasnext = (p[0] == '$');\n    pvariant = (tolower(cstr[0]) == 'p') ? 1 : 0;\n    cstr += pvariant;\n    clen -= pvariant;\n\n    if (hasnext && strncasecmp(cstr,\"subscribe\\r\\n\",11) == 0) {\n        c->flags |= REDIS_SUBSCRIBED;\n\n        /* Add every channel/pattern to the list of subscription callbacks. */\n        while ((p = nextArgument(p,&astr,&alen)) != NULL) {\n            sname = sdsnewlen(astr,alen);\n            if (pvariant)\n                dictReplace(ac->sub.patterns,sname,&cb);\n            else\n                dictReplace(ac->sub.channels,sname,&cb);\n        }\n    } else if (strncasecmp(cstr,\"unsubscribe\\r\\n\",13) == 0) {\n        /* It is only useful to call (P)UNSUBSCRIBE when the context is\n         * subscribed to one or more channels or patterns. */\n        if (!(c->flags & REDIS_SUBSCRIBED)) return REDIS_ERR;\n\n        /* (P)UNSUBSCRIBE does not have its own response: every channel or\n         * pattern that is unsubscribed will receive a message. This means we\n         * should not append a callback function for this command. */\n     } else if(strncasecmp(cstr,\"monitor\\r\\n\",9) == 0) {\n         /* Set monitor flag and push callback */\n         c->flags |= REDIS_MONITORING;\n         __redisPushCallback(&ac->replies,&cb);\n    } else {\n        if (c->flags & REDIS_SUBSCRIBED)\n            /* This will likely result in an error reply, but it needs to be\n             * received and passed to the callback. */\n            __redisPushCallback(&ac->sub.invalid,&cb);\n        else\n            __redisPushCallback(&ac->replies,&cb);\n    }\n\n    __redisAppendCommand(c,cmd,len);\n\n    /* Always schedule a write when the write buffer is non-empty */\n    _EL_ADD_WRITE(ac);\n\n    return REDIS_OK;\n}\n\nint redisvAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, va_list ap) {\n    char *cmd;\n    int len;\n    int status;\n    len = redisvFormatCommand(&cmd,format,ap);\n    status = __redisAsyncCommand(ac,fn,privdata,cmd,len);\n    free(cmd);\n    return status;\n}\n\nint redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, ...) {\n    va_list ap;\n    int status;\n    va_start(ap,format);\n    status = redisvAsyncCommand(ac,fn,privdata,format,ap);\n    va_end(ap);\n    return status;\n}\n\nint redisAsyncCommandArgv(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, int argc, const char **argv, const size_t *argvlen) {\n    char *cmd;\n    int len;\n    int status;\n    len = redisFormatCommandArgv(&cmd,argc,argv,argvlen);\n    status = __redisAsyncCommand(ac,fn,privdata,cmd,len);\n    free(cmd);\n    return status;\n}\n"
  },
  {
    "path": "deps/hiredis/async.h",
    "content": "/*\n * Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>\n * Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>\n *\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 __HIREDIS_ASYNC_H\n#define __HIREDIS_ASYNC_H\n#include \"hiredis.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nstruct redisAsyncContext; /* need forward declaration of redisAsyncContext */\nstruct dict; /* dictionary header is included in async.c */\n\n/* Reply callback prototype and container */\ntypedef void (redisCallbackFn)(struct redisAsyncContext*, void*, void*);\ntypedef struct redisCallback {\n    struct redisCallback *next; /* simple singly linked list */\n    redisCallbackFn *fn;\n    void *privdata;\n} redisCallback;\n\n/* List of callbacks for either regular replies or pub/sub */\ntypedef struct redisCallbackList {\n    redisCallback *head, *tail;\n} redisCallbackList;\n\n/* Connection callback prototypes */\ntypedef void (redisDisconnectCallback)(const struct redisAsyncContext*, int status);\ntypedef void (redisConnectCallback)(const struct redisAsyncContext*, int status);\n\n/* Context for an async connection to Redis */\ntypedef struct redisAsyncContext {\n    /* Hold the regular context, so it can be realloc'ed. */\n    redisContext c;\n\n    /* Setup error flags so they can be used directly. */\n    int err;\n    char *errstr;\n\n    /* Not used by hiredis */\n    void *data;\n\n    /* Event library data and hooks */\n    struct {\n        void *data;\n\n        /* Hooks that are called when the library expects to start\n         * reading/writing. These functions should be idempotent. */\n        void (*addRead)(void *privdata);\n        void (*delRead)(void *privdata);\n        void (*addWrite)(void *privdata);\n        void (*delWrite)(void *privdata);\n        void (*cleanup)(void *privdata);\n    } ev;\n\n    /* Called when either the connection is terminated due to an error or per\n     * user request. The status is set accordingly (REDIS_OK, REDIS_ERR). */\n    redisDisconnectCallback *onDisconnect;\n\n    /* Called when the first write event was received. */\n    redisConnectCallback *onConnect;\n\n    /* Regular command callbacks */\n    redisCallbackList replies;\n\n    /* Subscription callbacks */\n    struct {\n        redisCallbackList invalid;\n        struct dict *channels;\n        struct dict *patterns;\n    } sub;\n} redisAsyncContext;\n\n/* Functions that proxy to hiredis */\nredisAsyncContext *redisAsyncConnect(const char *ip, int port);\nredisAsyncContext *redisAsyncConnectUnix(const char *path);\nint redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn);\nint redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn);\nvoid redisAsyncDisconnect(redisAsyncContext *ac);\nvoid redisAsyncFree(redisAsyncContext *ac);\n\n/* Handle read/write events */\nvoid redisAsyncHandleRead(redisAsyncContext *ac);\nvoid redisAsyncHandleWrite(redisAsyncContext *ac);\n\n/* Command functions for an async context. Write the command to the\n * output buffer and register the provided callback. */\nint redisvAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, va_list ap);\nint redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, ...);\nint redisAsyncCommandArgv(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, int argc, const char **argv, const size_t *argvlen);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "deps/hiredis/dict.c",
    "content": "/* Hash table implementation.\n *\n * This file implements in memory hash tables with insert/del/replace/find/\n * get-random-element operations. Hash tables will auto resize if needed\n * tables of power of two in size are used, collisions are handled by\n * chaining. See the source code for more information... :)\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 \"fmacros.h\"\n#include <stdlib.h>\n#include <assert.h>\n#include <limits.h>\n#include \"dict.h\"\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/* Generic hash function (a popular one from Bernstein).\n * I tested a few and this was the best. */\nstatic unsigned int dictGenHashFunction(const unsigned char *buf, int len) {\n    unsigned int hash = 5381;\n\n    while (len--)\n        hash = ((hash << 5) + hash) + (*buf++); /* hash * 33 + c */\n    return hash;\n}\n\n/* ----------------------------- API implementation ------------------------- */\n\n/* Reset an hashtable already initialized with ht_init().\n * NOTE: This function should only called by ht_destroy(). */\nstatic void _dictReset(dict *ht) {\n    ht->table = NULL;\n    ht->size = 0;\n    ht->sizemask = 0;\n    ht->used = 0;\n}\n\n/* Create a new hash table */\nstatic dict *dictCreate(dictType *type, void *privDataPtr) {\n    dict *ht = malloc(sizeof(*ht));\n    _dictInit(ht,type,privDataPtr);\n    return ht;\n}\n\n/* Initialize the hash table */\nstatic int _dictInit(dict *ht, dictType *type, void *privDataPtr) {\n    _dictReset(ht);\n    ht->type = type;\n    ht->privdata = privDataPtr;\n    return DICT_OK;\n}\n\n/* Expand or create the hashtable */\nstatic int dictExpand(dict *ht, unsigned long size) {\n    dict n; /* the new hashtable */\n    unsigned long realsize = _dictNextPower(size), i;\n\n    /* the size is invalid if it is smaller than the number of\n     * elements already inside the hashtable */\n    if (ht->used > size)\n        return DICT_ERR;\n\n    _dictInit(&n, ht->type, ht->privdata);\n    n.size = realsize;\n    n.sizemask = realsize-1;\n    n.table = calloc(realsize,sizeof(dictEntry*));\n\n    /* Copy all the elements from the old to the new table:\n     * note that if the old hash table is empty ht->size is zero,\n     * so dictExpand just creates an hash table. */\n    n.used = ht->used;\n    for (i = 0; i < ht->size && ht->used > 0; i++) {\n        dictEntry *he, *nextHe;\n\n        if (ht->table[i] == NULL) continue;\n\n        /* For each hash entry on this slot... */\n        he = ht->table[i];\n        while(he) {\n            unsigned int h;\n\n            nextHe = he->next;\n            /* Get the new element index */\n            h = dictHashKey(ht, he->key) & n.sizemask;\n            he->next = n.table[h];\n            n.table[h] = he;\n            ht->used--;\n            /* Pass to the next element */\n            he = nextHe;\n        }\n    }\n    assert(ht->used == 0);\n    free(ht->table);\n\n    /* Remap the new hashtable in the old */\n    *ht = n;\n    return DICT_OK;\n}\n\n/* Add an element to the target hash table */\nstatic int dictAdd(dict *ht, void *key, void *val) {\n    int index;\n    dictEntry *entry;\n\n    /* Get the index of the new element, or -1 if\n     * the element already exists. */\n    if ((index = _dictKeyIndex(ht, key)) == -1)\n        return DICT_ERR;\n\n    /* Allocates the memory and stores key */\n    entry = malloc(sizeof(*entry));\n    entry->next = ht->table[index];\n    ht->table[index] = entry;\n\n    /* Set the hash entry fields. */\n    dictSetHashKey(ht, entry, key);\n    dictSetHashVal(ht, entry, val);\n    ht->used++;\n    return DICT_OK;\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. */\nstatic int dictReplace(dict *ht, void *key, void *val) {\n    dictEntry *entry, auxentry;\n\n    /* Try to add the element. If the key\n     * does not exists dictAdd will suceed. */\n    if (dictAdd(ht, key, val) == DICT_OK)\n        return 1;\n    /* It already exists, get the entry */\n    entry = dictFind(ht, key);\n    /* Free the old value and set the new one */\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    dictSetHashVal(ht, entry, val);\n    dictFreeEntryVal(ht, &auxentry);\n    return 0;\n}\n\n/* Search and remove an element */\nstatic int dictDelete(dict *ht, const void *key) {\n    unsigned int h;\n    dictEntry *de, *prevde;\n\n    if (ht->size == 0)\n        return DICT_ERR;\n    h = dictHashKey(ht, key) & ht->sizemask;\n    de = ht->table[h];\n\n    prevde = NULL;\n    while(de) {\n        if (dictCompareHashKeys(ht,key,de->key)) {\n            /* Unlink the element from the list */\n            if (prevde)\n                prevde->next = de->next;\n            else\n                ht->table[h] = de->next;\n\n            dictFreeEntryKey(ht,de);\n            dictFreeEntryVal(ht,de);\n            free(de);\n            ht->used--;\n            return DICT_OK;\n        }\n        prevde = de;\n        de = de->next;\n    }\n    return DICT_ERR; /* not found */\n}\n\n/* Destroy an entire hash table */\nstatic int _dictClear(dict *ht) {\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 ((he = ht->table[i]) == NULL) continue;\n        while(he) {\n            nextHe = he->next;\n            dictFreeEntryKey(ht, he);\n            dictFreeEntryVal(ht, he);\n            free(he);\n            ht->used--;\n            he = nextHe;\n        }\n    }\n    /* Free the table and the allocated cache structure */\n    free(ht->table);\n    /* Re-initialize the table */\n    _dictReset(ht);\n    return DICT_OK; /* never fails */\n}\n\n/* Clear & Release the hash table */\nstatic void dictRelease(dict *ht) {\n    _dictClear(ht);\n    free(ht);\n}\n\nstatic dictEntry *dictFind(dict *ht, const void *key) {\n    dictEntry *he;\n    unsigned int h;\n\n    if (ht->size == 0) return NULL;\n    h = dictHashKey(ht, key) & ht->sizemask;\n    he = ht->table[h];\n    while(he) {\n        if (dictCompareHashKeys(ht, key, he->key))\n            return he;\n        he = he->next;\n    }\n    return NULL;\n}\n\nstatic dictIterator *dictGetIterator(dict *ht) {\n    dictIterator *iter = malloc(sizeof(*iter));\n\n    iter->ht = ht;\n    iter->index = -1;\n    iter->entry = NULL;\n    iter->nextEntry = NULL;\n    return iter;\n}\n\nstatic dictEntry *dictNext(dictIterator *iter) {\n    while (1) {\n        if (iter->entry == NULL) {\n            iter->index++;\n            if (iter->index >=\n                    (signed)iter->ht->size) break;\n            iter->entry = iter->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\nstatic void dictReleaseIterator(dictIterator *iter) {\n    free(iter);\n}\n\n/* ------------------------- private functions ------------------------------ */\n\n/* Expand the hash table if needed */\nstatic int _dictExpandIfNeeded(dict *ht) {\n    /* If the hash table is empty expand it to the intial size,\n     * if the table is \"full\" dobule its size. */\n    if (ht->size == 0)\n        return dictExpand(ht, DICT_HT_INITIAL_SIZE);\n    if (ht->used == ht->size)\n        return dictExpand(ht, ht->size*2);\n    return DICT_OK;\n}\n\n/* Our hash table capability is a power of two */\nstatic unsigned long _dictNextPower(unsigned long size) {\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 * an hash entry for the given 'key'.\n * If the key already exists, -1 is returned. */\nstatic int _dictKeyIndex(dict *ht, const void *key) {\n    unsigned int h;\n    dictEntry *he;\n\n    /* Expand the hashtable if needed */\n    if (_dictExpandIfNeeded(ht) == DICT_ERR)\n        return -1;\n    /* Compute the key hash value */\n    h = dictHashKey(ht, key) & ht->sizemask;\n    /* Search if this slot does not already contain the given key */\n    he = ht->table[h];\n    while(he) {\n        if (dictCompareHashKeys(ht, key, he->key))\n            return -1;\n        he = he->next;\n    }\n    return h;\n}\n\n"
  },
  {
    "path": "deps/hiredis/dict.h",
    "content": "/* Hash table implementation.\n *\n * This file implements in memory hash tables with insert/del/replace/find/\n * get-random-element operations. Hash tables will auto resize if needed\n * tables of power of two in size are used, collisions are handled by\n * chaining. See the source code for more information... :)\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#ifndef __DICT_H\n#define __DICT_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    void *val;\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\ntypedef struct dict {\n    dictEntry **table;\n    dictType *type;\n    unsigned long size;\n    unsigned long sizemask;\n    unsigned long used;\n    void *privdata;\n} dict;\n\ntypedef struct dictIterator {\n    dict *ht;\n    int index;\n    dictEntry *entry, *nextEntry;\n} dictIterator;\n\n/* This is the initial size of every hash table */\n#define DICT_HT_INITIAL_SIZE     4\n\n/* ------------------------------- Macros ------------------------------------*/\n#define dictFreeEntryVal(ht, entry) \\\n    if ((ht)->type->valDestructor) \\\n        (ht)->type->valDestructor((ht)->privdata, (entry)->val)\n\n#define dictSetHashVal(ht, entry, _val_) do { \\\n    if ((ht)->type->valDup) \\\n        entry->val = (ht)->type->valDup((ht)->privdata, _val_); \\\n    else \\\n        entry->val = (_val_); \\\n} while(0)\n\n#define dictFreeEntryKey(ht, entry) \\\n    if ((ht)->type->keyDestructor) \\\n        (ht)->type->keyDestructor((ht)->privdata, (entry)->key)\n\n#define dictSetHashKey(ht, entry, _key_) do { \\\n    if ((ht)->type->keyDup) \\\n        entry->key = (ht)->type->keyDup((ht)->privdata, _key_); \\\n    else \\\n        entry->key = (_key_); \\\n} while(0)\n\n#define dictCompareHashKeys(ht, key1, key2) \\\n    (((ht)->type->keyCompare) ? \\\n        (ht)->type->keyCompare((ht)->privdata, key1, key2) : \\\n        (key1) == (key2))\n\n#define dictHashKey(ht, key) (ht)->type->hashFunction(key)\n\n#define dictGetEntryKey(he) ((he)->key)\n#define dictGetEntryVal(he) ((he)->val)\n#define dictSlots(ht) ((ht)->size)\n#define dictSize(ht) ((ht)->used)\n\n/* API */\nstatic unsigned int dictGenHashFunction(const unsigned char *buf, int len);\nstatic dict *dictCreate(dictType *type, void *privDataPtr);\nstatic int dictExpand(dict *ht, unsigned long size);\nstatic int dictAdd(dict *ht, void *key, void *val);\nstatic int dictReplace(dict *ht, void *key, void *val);\nstatic int dictDelete(dict *ht, const void *key);\nstatic void dictRelease(dict *ht);\nstatic dictEntry * dictFind(dict *ht, const void *key);\nstatic dictIterator *dictGetIterator(dict *ht);\nstatic dictEntry *dictNext(dictIterator *iter);\nstatic void dictReleaseIterator(dictIterator *iter);\n\n#endif /* __DICT_H */\n"
  },
  {
    "path": "deps/hiredis/example-ae.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <signal.h>\n#include \"hiredis.h\"\n#include \"async.h\"\n#include \"adapters/ae.h\"\n\n/* Put event loop in the global scope, so it can be explicitly stopped */\nstatic aeEventLoop *loop;\n\nvoid getCallback(redisAsyncContext *c, void *r, void *privdata) {\n    redisReply *reply = r;\n    if (reply == NULL) return;\n    printf(\"argv[%s]: %s\\n\", (char*)privdata, reply->str);\n\n    /* Disconnect after receiving the reply to GET */\n    redisAsyncDisconnect(c);\n}\n\nvoid connectCallback(const redisAsyncContext *c, int status) {\n    if (status != REDIS_OK) {\n        printf(\"Error: %s\\n\", c->errstr);\n        return;\n    }\n    printf(\"Connected...\\n\");\n}\n\nvoid disconnectCallback(const redisAsyncContext *c, int status) {\n    if (status != REDIS_OK) {\n        printf(\"Error: %s\\n\", c->errstr);\n        return;\n    }\n    printf(\"Disconnected...\\n\");\n}\n\nint main (int argc, char **argv) {\n    signal(SIGPIPE, SIG_IGN);\n\n    redisAsyncContext *c = redisAsyncConnect(\"127.0.0.1\", 6379);\n    if (c->err) {\n        /* Let *c leak for now... */\n        printf(\"Error: %s\\n\", c->errstr);\n        return 1;\n    }\n\n    loop = aeCreateEventLoop();\n    redisAeAttach(loop, c);\n    redisAsyncSetConnectCallback(c,connectCallback);\n    redisAsyncSetDisconnectCallback(c,disconnectCallback);\n    redisAsyncCommand(c, NULL, NULL, \"SET key %b\", argv[argc-1], strlen(argv[argc-1]));\n    redisAsyncCommand(c, getCallback, (char*)\"end-1\", \"GET key\");\n    aeMain(loop);\n    return 0;\n}\n\n"
  },
  {
    "path": "deps/hiredis/example-libev.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <signal.h>\n#include \"hiredis.h\"\n#include \"async.h\"\n#include \"adapters/libev.h\"\n\nvoid getCallback(redisAsyncContext *c, void *r, void *privdata) {\n    redisReply *reply = r;\n    if (reply == NULL) return;\n    printf(\"argv[%s]: %s\\n\", (char*)privdata, reply->str);\n\n    /* Disconnect after receiving the reply to GET */\n    redisAsyncDisconnect(c);\n}\n\nvoid connectCallback(const redisAsyncContext *c, int status) {\n    if (status != REDIS_OK) {\n        printf(\"Error: %s\\n\", c->errstr);\n        return;\n    }\n    printf(\"Connected...\\n\");\n}\n\nvoid disconnectCallback(const redisAsyncContext *c, int status) {\n    if (status != REDIS_OK) {\n        printf(\"Error: %s\\n\", c->errstr);\n        return;\n    }\n    printf(\"Disconnected...\\n\");\n}\n\nint main (int argc, char **argv) {\n    signal(SIGPIPE, SIG_IGN);\n\n    redisAsyncContext *c = redisAsyncConnect(\"127.0.0.1\", 6379);\n    if (c->err) {\n        /* Let *c leak for now... */\n        printf(\"Error: %s\\n\", c->errstr);\n        return 1;\n    }\n\n    redisLibevAttach(EV_DEFAULT_ c);\n    redisAsyncSetConnectCallback(c,connectCallback);\n    redisAsyncSetDisconnectCallback(c,disconnectCallback);\n    redisAsyncCommand(c, NULL, NULL, \"SET key %b\", argv[argc-1], strlen(argv[argc-1]));\n    redisAsyncCommand(c, getCallback, (char*)\"end-1\", \"GET key\");\n    ev_loop(EV_DEFAULT_ 0);\n    return 0;\n}\n"
  },
  {
    "path": "deps/hiredis/example-libevent.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <signal.h>\n#include \"hiredis.h\"\n#include \"async.h\"\n#include \"adapters/libevent.h\"\n\nvoid getCallback(redisAsyncContext *c, void *r, void *privdata) {\n    redisReply *reply = r;\n    if (reply == NULL) return;\n    printf(\"argv[%s]: %s\\n\", (char*)privdata, reply->str);\n\n    /* Disconnect after receiving the reply to GET */\n    redisAsyncDisconnect(c);\n}\n\nvoid connectCallback(const redisAsyncContext *c, int status) {\n    if (status != REDIS_OK) {\n        printf(\"Error: %s\\n\", c->errstr);\n        return;\n    }\n    printf(\"Connected...\\n\");\n}\n\nvoid disconnectCallback(const redisAsyncContext *c, int status) {\n    if (status != REDIS_OK) {\n        printf(\"Error: %s\\n\", c->errstr);\n        return;\n    }\n    printf(\"Disconnected...\\n\");\n}\n\nint main (int argc, char **argv) {\n    signal(SIGPIPE, SIG_IGN);\n    struct event_base *base = event_base_new();\n\n    redisAsyncContext *c = redisAsyncConnect(\"127.0.0.1\", 6379);\n    if (c->err) {\n        /* Let *c leak for now... */\n        printf(\"Error: %s\\n\", c->errstr);\n        return 1;\n    }\n\n    redisLibeventAttach(c,base);\n    redisAsyncSetConnectCallback(c,connectCallback);\n    redisAsyncSetDisconnectCallback(c,disconnectCallback);\n    redisAsyncCommand(c, NULL, NULL, \"SET key %b\", argv[argc-1], strlen(argv[argc-1]));\n    redisAsyncCommand(c, getCallback, (char*)\"end-1\", \"GET key\");\n    event_base_dispatch(base);\n    return 0;\n}\n"
  },
  {
    "path": "deps/hiredis/example.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include \"hiredis.h\"\n\nint main(void) {\n    unsigned int j;\n    redisContext *c;\n    redisReply *reply;\n\n    struct timeval timeout = { 1, 500000 }; // 1.5 seconds\n    c = redisConnectWithTimeout((char*)\"127.0.0.2\", 6379, timeout);\n    if (c->err) {\n        printf(\"Connection error: %s\\n\", c->errstr);\n        exit(1);\n    }\n\n    /* PING server */\n    reply = redisCommand(c,\"PING\");\n    printf(\"PING: %s\\n\", reply->str);\n    freeReplyObject(reply);\n\n    /* Set a key */\n    reply = redisCommand(c,\"SET %s %s\", \"foo\", \"hello world\");\n    printf(\"SET: %s\\n\", reply->str);\n    freeReplyObject(reply);\n\n    /* Set a key using binary safe API */\n    reply = redisCommand(c,\"SET %b %b\", \"bar\", 3, \"hello\", 5);\n    printf(\"SET (binary API): %s\\n\", reply->str);\n    freeReplyObject(reply);\n\n    /* Try a GET and two INCR */\n    reply = redisCommand(c,\"GET foo\");\n    printf(\"GET foo: %s\\n\", reply->str);\n    freeReplyObject(reply);\n\n    reply = redisCommand(c,\"INCR counter\");\n    printf(\"INCR counter: %lld\\n\", reply->integer);\n    freeReplyObject(reply);\n    /* again ... */\n    reply = redisCommand(c,\"INCR counter\");\n    printf(\"INCR counter: %lld\\n\", reply->integer);\n    freeReplyObject(reply);\n\n    /* Create a list of numbers, from 0 to 9 */\n    reply = redisCommand(c,\"DEL mylist\");\n    freeReplyObject(reply);\n    for (j = 0; j < 10; j++) {\n        char buf[64];\n\n        snprintf(buf,64,\"%d\",j);\n        reply = redisCommand(c,\"LPUSH mylist element-%s\", buf);\n        freeReplyObject(reply);\n    }\n\n    /* Let's check what we have inside the list */\n    reply = redisCommand(c,\"LRANGE mylist 0 -1\");\n    if (reply->type == REDIS_REPLY_ARRAY) {\n        for (j = 0; j < reply->elements; j++) {\n            printf(\"%u) %s\\n\", j, reply->element[j]->str);\n        }\n    }\n    freeReplyObject(reply);\n\n    return 0;\n}\n"
  },
  {
    "path": "deps/hiredis/fmacros.h",
    "content": "#ifndef __HIREDIS_FMACRO_H\n#define __HIREDIS_FMACRO_H\n\n#if !defined(_BSD_SOURCE)\n#define _BSD_SOURCE\n#endif\n\n#if defined(__sun__)\n#define _POSIX_C_SOURCE 200112L\n#elif defined(__linux__) || defined(__OpenBSD__) || defined(__NetBSD__)\n#define _XOPEN_SOURCE 600\n#else\n#define _XOPEN_SOURCE\n#endif\n\n#endif\n"
  },
  {
    "path": "deps/hiredis/hiredis.c",
    "content": "/*\n * Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>\n * Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>\n *\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 \"fmacros.h\"\n#include <string.h>\n#include <stdlib.h>\n#include <unistd.h>\n#include <assert.h>\n#include <errno.h>\n#include <ctype.h>\n\n#include \"hiredis.h\"\n#include \"net.h\"\n#include \"sds.h\"\n\nstatic redisReply *createReplyObject(int type);\nstatic void *createStringObject(const redisReadTask *task, char *str, size_t len);\nstatic void *createArrayObject(const redisReadTask *task, int elements);\nstatic void *createIntegerObject(const redisReadTask *task, long long value);\nstatic void *createNilObject(const redisReadTask *task);\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 redisReplyObjectFunctions defaultFunctions = {\n    createStringObject,\n    createArrayObject,\n    createIntegerObject,\n    createNilObject,\n    freeReplyObject\n};\n\n/* Create a reply object */\nstatic redisReply *createReplyObject(int type) {\n    redisReply *r = calloc(1,sizeof(*r));\n\n    if (r == NULL)\n        return NULL;\n\n    r->type = type;\n    return r;\n}\n\n/* Free a reply object */\nvoid freeReplyObject(void *reply) {\n    redisReply *r = reply;\n    size_t j;\n\n    switch(r->type) {\n    case REDIS_REPLY_INTEGER:\n        break; /* Nothing to free */\n    case REDIS_REPLY_ARRAY:\n        if (r->element != NULL) {\n            for (j = 0; j < r->elements; j++)\n                if (r->element[j] != NULL)\n                    freeReplyObject(r->element[j]);\n            free(r->element);\n        }\n        break;\n    case REDIS_REPLY_ERROR:\n    case REDIS_REPLY_STATUS:\n    case REDIS_REPLY_STRING:\n        if (r->str != NULL)\n            free(r->str);\n        break;\n    }\n    free(r);\n}\n\nstatic void *createStringObject(const redisReadTask *task, char *str, size_t len) {\n    redisReply *r, *parent;\n    char *buf;\n\n    r = createReplyObject(task->type);\n    if (r == NULL)\n        return NULL;\n\n    buf = malloc(len+1);\n    if (buf == NULL) {\n        freeReplyObject(r);\n        return NULL;\n    }\n\n    assert(task->type == REDIS_REPLY_ERROR  ||\n           task->type == REDIS_REPLY_STATUS ||\n           task->type == REDIS_REPLY_STRING);\n\n    /* Copy string value */\n    memcpy(buf,str,len);\n    buf[len] = '\\0';\n    r->str = buf;\n    r->len = len;\n\n    if (task->parent) {\n        parent = task->parent->obj;\n        assert(parent->type == REDIS_REPLY_ARRAY);\n        parent->element[task->idx] = r;\n    }\n    return r;\n}\n\nstatic void *createArrayObject(const redisReadTask *task, int elements) {\n    redisReply *r, *parent;\n\n    r = createReplyObject(REDIS_REPLY_ARRAY);\n    if (r == NULL)\n        return NULL;\n\n    if (elements > 0) {\n        r->element = calloc(elements,sizeof(redisReply*));\n        if (r->element == NULL) {\n            freeReplyObject(r);\n            return NULL;\n        }\n    }\n\n    r->elements = elements;\n\n    if (task->parent) {\n        parent = task->parent->obj;\n        assert(parent->type == REDIS_REPLY_ARRAY);\n        parent->element[task->idx] = r;\n    }\n    return r;\n}\n\nstatic void *createIntegerObject(const redisReadTask *task, long long value) {\n    redisReply *r, *parent;\n\n    r = createReplyObject(REDIS_REPLY_INTEGER);\n    if (r == NULL)\n        return NULL;\n\n    r->integer = value;\n\n    if (task->parent) {\n        parent = task->parent->obj;\n        assert(parent->type == REDIS_REPLY_ARRAY);\n        parent->element[task->idx] = r;\n    }\n    return r;\n}\n\nstatic void *createNilObject(const redisReadTask *task) {\n    redisReply *r, *parent;\n\n    r = createReplyObject(REDIS_REPLY_NIL);\n    if (r == NULL)\n        return NULL;\n\n    if (task->parent) {\n        parent = task->parent->obj;\n        assert(parent->type == REDIS_REPLY_ARRAY);\n        parent->element[task->idx] = r;\n    }\n    return r;\n}\n\nstatic void __redisReaderSetError(redisReader *r, int type, const char *str) {\n    size_t len;\n\n    if (r->reply != NULL && r->fn && r->fn->freeObject) {\n        r->fn->freeObject(r->reply);\n        r->reply = NULL;\n    }\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    /* Reset task stack. */\n    r->ridx = -1;\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    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 __redisReaderSetErrorProtocolByte(redisReader *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    __redisReaderSetError(r,REDIS_ERR_PROTOCOL,sbuf);\n}\n\nstatic void __redisReaderSetErrorOOM(redisReader *r) {\n    __redisReaderSetError(r,REDIS_ERR_OOM,\"Out of memory\");\n}\n\nstatic char *readBytes(redisReader *r, unsigned int bytes) {\n    char *p;\n    if (r->len-r->pos >= bytes) {\n        p = r->buf+r->pos;\n        r->pos += bytes;\n        return p;\n    }\n    return NULL;\n}\n\n/* Find pointer to \\r\\n. */\nstatic char *seekNewline(char *s, size_t len) {\n    int pos = 0;\n    int _len = len-1;\n\n    /* Position should be < len-1 because the character at \"pos\" should be\n     * followed by a \\n. Note that strchr cannot be used because it doesn't\n     * allow to search a limited length and the buffer that is being searched\n     * might not have a trailing NULL character. */\n    while (pos < _len) {\n        while(pos < _len && s[pos] != '\\r') pos++;\n        if (s[pos] != '\\r') {\n            /* Not found. */\n            return NULL;\n        } else {\n            if (s[pos+1] == '\\n') {\n                /* Found. */\n                return s+pos;\n            } else {\n                /* Continue searching. */\n                pos++;\n            }\n        }\n    }\n    return NULL;\n}\n\n/* Read a long long value starting at *s, under the assumption that it will be\n * terminated by \\r\\n. Ambiguously returns -1 for unexpected input. */\nstatic long long readLongLong(char *s) {\n    long long v = 0;\n    int dec, mult = 1;\n    char c;\n\n    if (*s == '-') {\n        mult = -1;\n        s++;\n    } else if (*s == '+') {\n        mult = 1;\n        s++;\n    }\n\n    while ((c = *(s++)) != '\\r') {\n        dec = c - '0';\n        if (dec >= 0 && dec < 10) {\n            v *= 10;\n            v += dec;\n        } else {\n            /* Should not happen... */\n            return -1;\n        }\n    }\n\n    return mult*v;\n}\n\nstatic char *readLine(redisReader *r, int *_len) {\n    char *p, *s;\n    int len;\n\n    p = r->buf+r->pos;\n    s = seekNewline(p,(r->len-r->pos));\n    if (s != NULL) {\n        len = s-(r->buf+r->pos);\n        r->pos += len+2; /* skip \\r\\n */\n        if (_len) *_len = len;\n        return p;\n    }\n    return NULL;\n}\n\nstatic void moveToNextTask(redisReader *r) {\n    redisReadTask *cur, *prv;\n    while (r->ridx >= 0) {\n        /* Return a.s.a.p. when the stack is now empty. */\n        if (r->ridx == 0) {\n            r->ridx--;\n            return;\n        }\n\n        cur = &(r->rstack[r->ridx]);\n        prv = &(r->rstack[r->ridx-1]);\n        assert(prv->type == REDIS_REPLY_ARRAY);\n        if (cur->idx == prv->elements-1) {\n            r->ridx--;\n        } else {\n            /* Reset the type because the next item can be anything */\n            assert(cur->idx < prv->elements);\n            cur->type = -1;\n            cur->elements = -1;\n            cur->idx++;\n            return;\n        }\n    }\n}\n\nstatic int processLineItem(redisReader *r) {\n    redisReadTask *cur = &(r->rstack[r->ridx]);\n    void *obj;\n    char *p;\n    int len;\n\n    if ((p = readLine(r,&len)) != NULL) {\n        if (cur->type == REDIS_REPLY_INTEGER) {\n            if (r->fn && r->fn->createInteger)\n                obj = r->fn->createInteger(cur,readLongLong(p));\n            else\n                obj = (void*)REDIS_REPLY_INTEGER;\n        } else {\n            /* Type will be error or status. */\n            if (r->fn && r->fn->createString)\n                obj = r->fn->createString(cur,p,len);\n            else\n                obj = (void*)(size_t)(cur->type);\n        }\n\n        if (obj == NULL) {\n            __redisReaderSetErrorOOM(r);\n            return REDIS_ERR;\n        }\n\n        /* Set reply if this is the root object. */\n        if (r->ridx == 0) r->reply = obj;\n        moveToNextTask(r);\n        return REDIS_OK;\n    }\n\n    return REDIS_ERR;\n}\n\nstatic int processBulkItem(redisReader *r) {\n    redisReadTask *cur = &(r->rstack[r->ridx]);\n    void *obj = NULL;\n    char *p, *s;\n    long len;\n    unsigned long bytelen;\n    int success = 0;\n\n    p = r->buf+r->pos;\n    s = seekNewline(p,r->len-r->pos);\n    if (s != NULL) {\n        p = r->buf+r->pos;\n        bytelen = s-(r->buf+r->pos)+2; /* include \\r\\n */\n        len = readLongLong(p);\n\n        if (len < 0) {\n            /* The nil object can always be created. */\n            if (r->fn && r->fn->createNil)\n                obj = r->fn->createNil(cur);\n            else\n                obj = (void*)REDIS_REPLY_NIL;\n            success = 1;\n        } else {\n            /* Only continue when the buffer contains the entire bulk item. */\n            bytelen += len+2; /* include \\r\\n */\n            if (r->pos+bytelen <= r->len) {\n                if (r->fn && r->fn->createString)\n                    obj = r->fn->createString(cur,s+2,len);\n                else\n                    obj = (void*)REDIS_REPLY_STRING;\n                success = 1;\n            }\n        }\n\n        /* Proceed when obj was created. */\n        if (success) {\n            if (obj == NULL) {\n                __redisReaderSetErrorOOM(r);\n                return REDIS_ERR;\n            }\n\n            r->pos += bytelen;\n\n            /* Set reply if this is the root object. */\n            if (r->ridx == 0) r->reply = obj;\n            moveToNextTask(r);\n            return REDIS_OK;\n        }\n    }\n\n    return REDIS_ERR;\n}\n\nstatic int processMultiBulkItem(redisReader *r) {\n    redisReadTask *cur = &(r->rstack[r->ridx]);\n    void *obj;\n    char *p;\n    long elements;\n    int root = 0;\n\n    /* Set error for nested multi bulks with depth > 7 */\n    if (r->ridx == 8) {\n        __redisReaderSetError(r,REDIS_ERR_PROTOCOL,\n            \"No support for nested multi bulk replies with depth > 7\");\n        return REDIS_ERR;\n    }\n\n    if ((p = readLine(r,NULL)) != NULL) {\n        elements = readLongLong(p);\n        root = (r->ridx == 0);\n\n        if (elements == -1) {\n            if (r->fn && r->fn->createNil)\n                obj = r->fn->createNil(cur);\n            else\n                obj = (void*)REDIS_REPLY_NIL;\n\n            if (obj == NULL) {\n                __redisReaderSetErrorOOM(r);\n                return REDIS_ERR;\n            }\n\n            moveToNextTask(r);\n        } else {\n            if (r->fn && r->fn->createArray)\n                obj = r->fn->createArray(cur,elements);\n            else\n                obj = (void*)REDIS_REPLY_ARRAY;\n\n            if (obj == NULL) {\n                __redisReaderSetErrorOOM(r);\n                return REDIS_ERR;\n            }\n\n            /* Modify task stack when there are more than 0 elements. */\n            if (elements > 0) {\n                cur->elements = elements;\n                cur->obj = obj;\n                r->ridx++;\n                r->rstack[r->ridx].type = -1;\n                r->rstack[r->ridx].elements = -1;\n                r->rstack[r->ridx].idx = 0;\n                r->rstack[r->ridx].obj = NULL;\n                r->rstack[r->ridx].parent = cur;\n                r->rstack[r->ridx].privdata = r->privdata;\n            } else {\n                moveToNextTask(r);\n            }\n        }\n\n        /* Set reply if this is the root object. */\n        if (root) r->reply = obj;\n        return REDIS_OK;\n    }\n\n    return REDIS_ERR;\n}\n\nstatic int processItem(redisReader *r) {\n    redisReadTask *cur = &(r->rstack[r->ridx]);\n    char *p;\n\n    /* check if we need to read type */\n    if (cur->type < 0) {\n        if ((p = readBytes(r,1)) != NULL) {\n            switch (p[0]) {\n            case '-':\n                cur->type = REDIS_REPLY_ERROR;\n                break;\n            case '+':\n                cur->type = REDIS_REPLY_STATUS;\n                break;\n            case ':':\n                cur->type = REDIS_REPLY_INTEGER;\n                break;\n            case '$':\n                cur->type = REDIS_REPLY_STRING;\n                break;\n            case '*':\n                cur->type = REDIS_REPLY_ARRAY;\n                break;\n            default:\n                __redisReaderSetErrorProtocolByte(r,*p);\n                return REDIS_ERR;\n            }\n        } else {\n            /* could not consume 1 byte */\n            return REDIS_ERR;\n        }\n    }\n\n    /* process typed item */\n    switch(cur->type) {\n    case REDIS_REPLY_ERROR:\n    case REDIS_REPLY_STATUS:\n    case REDIS_REPLY_INTEGER:\n        return processLineItem(r);\n    case REDIS_REPLY_STRING:\n        return processBulkItem(r);\n    case REDIS_REPLY_ARRAY:\n        return processMultiBulkItem(r);\n    default:\n        assert(NULL);\n        return REDIS_ERR; /* Avoid warning. */\n    }\n}\n\nredisReader *redisReaderCreate(void) {\n    redisReader *r;\n\n    r = calloc(sizeof(redisReader),1);\n    if (r == NULL)\n        return NULL;\n\n    r->err = 0;\n    r->errstr[0] = '\\0';\n    r->fn = &defaultFunctions;\n    r->buf = sdsempty();\n    r->maxbuf = REDIS_READER_MAX_BUF;\n    if (r->buf == NULL) {\n        free(r);\n        return NULL;\n    }\n\n    r->ridx = -1;\n    return r;\n}\n\nvoid redisReaderFree(redisReader *r) {\n    if (r->reply != NULL && r->fn && r->fn->freeObject)\n        r->fn->freeObject(r->reply);\n    if (r->buf != NULL)\n        sdsfree(r->buf);\n    free(r);\n}\n\nint redisReaderFeed(redisReader *r, const char *buf, size_t len) {\n    sds newbuf;\n\n    /* Return early when this reader is in an erroneous state. */\n    if (r->err)\n        return REDIS_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            __redisReaderSetErrorOOM(r);\n            return REDIS_ERR;\n        }\n\n        r->buf = newbuf;\n        r->len = sdslen(r->buf);\n    }\n\n    return REDIS_OK;\n}\n\nint redisReaderGetReply(redisReader *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 REDIS_ERR;\n\n    /* When the buffer is empty, there will never be a reply. */\n    if (r->len == 0)\n        return REDIS_OK;\n\n    /* Set first item to process when the stack is empty. */\n    if (r->ridx == -1) {\n        r->rstack[0].type = -1;\n        r->rstack[0].elements = -1;\n        r->rstack[0].idx = -1;\n        r->rstack[0].obj = NULL;\n        r->rstack[0].parent = NULL;\n        r->rstack[0].privdata = r->privdata;\n        r->ridx = 0;\n    }\n\n    /* Process items in reply. */\n    while (r->ridx >= 0)\n        if (processItem(r) != REDIS_OK)\n            break;\n\n    /* Return ASAP when an error occurred. */\n    if (r->err)\n        return REDIS_ERR;\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) {\n        sdsrange(r->buf,r->pos,-1);\n        r->pos = 0;\n        r->len = sdslen(r->buf);\n    }\n\n    /* Emit a reply when there is one. */\n    if (r->ridx == -1) {\n        if (reply != NULL)\n            *reply = r->reply;\n        r->reply = NULL;\n    }\n    return REDIS_OK;\n}\n\n/* Calculate the number of bytes needed to represent an integer as string. */\nstatic int intlen(int i) {\n    int len = 0;\n    if (i < 0) {\n        len++;\n        i = -i;\n    }\n    do {\n        len++;\n        i /= 10;\n    } while(i);\n    return len;\n}\n\n/* Helper that calculates the bulk length given a certain string length. */\nstatic size_t bulklen(size_t len) {\n    return 1+intlen(len)+2+len+2;\n}\n\nint redisvFormatCommand(char **target, const char *format, va_list ap) {\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 = 0;\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 err;\n                    curargv = newargv;\n                    curargv[argc++] = curarg;\n                    totlen += bulklen(sdslen(curarg));\n\n                    /* curarg is put in argv so it can be overwritten. */\n                    curarg = sdsempty();\n                    if (curarg == NULL) goto err;\n                    touched = 0;\n                }\n            } else {\n                newarg = sdscatlen(curarg,c,1);\n                if (newarg == NULL) goto 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                    char _format[16];\n                    const char *_p = c+1;\n                    size_t _l = 0;\n                    va_list _cpy;\n\n                    /* Flags */\n                    if (*_p != '\\0' && *_p == '#') _p++;\n                    if (*_p != '\\0' && *_p == '0') _p++;\n                    if (*_p != '\\0' && *_p == '-') _p++;\n                    if (*_p != '\\0' && *_p == ' ') _p++;\n                    if (*_p != '\\0' && *_p == '+') _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 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 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 err;\n        curargv = newargv;\n        curargv[argc++] = curarg;\n        totlen += bulklen(sdslen(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    /* Add bytes needed to hold multi bulk count */\n    totlen += 1+intlen(argc)+2;\n\n    /* Build the command at protocol level */\n    cmd = malloc(totlen+1);\n    if (cmd == NULL) goto err;\n\n    pos = sprintf(cmd,\"*%d\\r\\n\",argc);\n    for (j = 0; j < argc; j++) {\n        pos += sprintf(cmd+pos,\"$%zu\\r\\n\",sdslen(curargv[j]));\n        memcpy(cmd+pos,curargv[j],sdslen(curargv[j]));\n        pos += sdslen(curargv[j]);\n        sdsfree(curargv[j]);\n        cmd[pos++] = '\\r';\n        cmd[pos++] = '\\n';\n    }\n    assert(pos == totlen);\n    cmd[pos] = '\\0';\n\n    free(curargv);\n    *target = cmd;\n    return totlen;\n\nerr:\n    while(argc--)\n        sdsfree(curargv[argc]);\n    free(curargv);\n\n    if (curarg != NULL)\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 -1;\n}\n\n/* Format a command according to the Redis 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. Examples:\n *\n * len = redisFormatCommand(target, \"GET %s\", mykey);\n * len = redisFormatCommand(target, \"SET %s %b\", mykey, myval, myvallen);\n */\nint redisFormatCommand(char **target, const char *format, ...) {\n    va_list ap;\n    int len;\n    va_start(ap,format);\n    len = redisvFormatCommand(target,format,ap);\n    va_end(ap);\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 redisFormatCommandArgv(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    size_t len;\n    int totlen, j;\n\n    /* Calculate number of bytes needed for the command */\n    totlen = 1+intlen(argc)+2;\n    for (j = 0; j < argc; j++) {\n        len = argvlen ? argvlen[j] : strlen(argv[j]);\n        totlen += bulklen(len);\n    }\n\n    /* Build the command at protocol level */\n    cmd = malloc(totlen+1);\n    if (cmd == NULL)\n        return -1;\n\n    pos = sprintf(cmd,\"*%d\\r\\n\",argc);\n    for (j = 0; j < argc; j++) {\n        len = argvlen ? argvlen[j] : strlen(argv[j]);\n        pos += sprintf(cmd+pos,\"$%zu\\r\\n\",len);\n        memcpy(cmd+pos,argv[j],len);\n        pos += len;\n        cmd[pos++] = '\\r';\n        cmd[pos++] = '\\n';\n    }\n    assert(pos == totlen);\n    cmd[pos] = '\\0';\n\n    *target = cmd;\n    return totlen;\n}\n\nvoid __redisSetError(redisContext *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 == REDIS_ERR_IO);\n        strerror_r(errno,c->errstr,sizeof(c->errstr));\n    }\n}\n\nstatic redisContext *redisContextInit(void) {\n    redisContext *c;\n\n    c = calloc(1,sizeof(redisContext));\n    if (c == NULL)\n        return NULL;\n\n    c->err = 0;\n    c->errstr[0] = '\\0';\n    c->obuf = sdsempty();\n    c->reader = redisReaderCreate();\n    return c;\n}\n\nvoid redisFree(redisContext *c) {\n    if (c->fd > 0)\n        close(c->fd);\n    if (c->obuf != NULL)\n        sdsfree(c->obuf);\n    if (c->reader != NULL)\n        redisReaderFree(c->reader);\n    free(c);\n}\n\n/* Connect to a Redis instance. On error the field error in the returned\n * context will be set to the return value of the error function.\n * When no set of reply functions is given, the default set will be used. */\nredisContext *redisConnect(const char *ip, int port) {\n    redisContext *c = redisContextInit();\n    c->flags |= REDIS_BLOCK;\n    redisContextConnectTcp(c,ip,port,NULL);\n    return c;\n}\n\nredisContext *redisConnectWithTimeout(const char *ip, int port, struct timeval tv) {\n    redisContext *c = redisContextInit();\n    c->flags |= REDIS_BLOCK;\n    redisContextConnectTcp(c,ip,port,&tv);\n    return c;\n}\n\nredisContext *redisConnectNonBlock(const char *ip, int port) {\n    redisContext *c = redisContextInit();\n    c->flags &= ~REDIS_BLOCK;\n    redisContextConnectTcp(c,ip,port,NULL);\n    return c;\n}\n\nredisContext *redisConnectUnix(const char *path) {\n    redisContext *c = redisContextInit();\n    c->flags |= REDIS_BLOCK;\n    redisContextConnectUnix(c,path,NULL);\n    return c;\n}\n\nredisContext *redisConnectUnixWithTimeout(const char *path, struct timeval tv) {\n    redisContext *c = redisContextInit();\n    c->flags |= REDIS_BLOCK;\n    redisContextConnectUnix(c,path,&tv);\n    return c;\n}\n\nredisContext *redisConnectUnixNonBlock(const char *path) {\n    redisContext *c = redisContextInit();\n    c->flags &= ~REDIS_BLOCK;\n    redisContextConnectUnix(c,path,NULL);\n    return c;\n}\n\n/* Set read/write timeout on a blocking socket. */\nint redisSetTimeout(redisContext *c, struct timeval tv) {\n    if (c->flags & REDIS_BLOCK)\n        return redisContextSetTimeout(c,tv);\n    return REDIS_ERR;\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 redisContextReadReply to\n * see if there is a reply available. */\nint redisBufferRead(redisContext *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 REDIS_ERR;\n\n    nread = read(c->fd,buf,sizeof(buf));\n    if (nread == -1) {\n        if (errno == EAGAIN && !(c->flags & REDIS_BLOCK)) {\n            /* Try again later */\n        } else {\n            __redisSetError(c,REDIS_ERR_IO,NULL);\n            return REDIS_ERR;\n        }\n    } else if (nread == 0) {\n        __redisSetError(c,REDIS_ERR_EOF,\"Server closed the connection\");\n        return REDIS_ERR;\n    } else {\n        if (redisReaderFeed(c->reader,buf,nread) != REDIS_OK) {\n            __redisSetError(c,c->reader->err,c->reader->errstr);\n            return REDIS_ERR;\n        }\n    }\n    return REDIS_OK;\n}\n\n/* Write the output buffer to the socket.\n *\n * Returns REDIS_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 REDIS_ERR if an error occured trying to write and sets\n * c->errstr to hold the appropriate error string.\n */\nint redisBufferWrite(redisContext *c, int *done) {\n    int nwritten;\n\n    /* Return early when the context has seen an error. */\n    if (c->err)\n        return REDIS_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 & REDIS_BLOCK)) {\n                /* Try again later */\n            } else {\n                __redisSetError(c,REDIS_ERR_IO,NULL);\n                return REDIS_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 REDIS_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 redisGetReplyFromReader(redisContext *c, void **reply) {\n    if (redisReaderGetReply(c->reader,reply) == REDIS_ERR) {\n        __redisSetError(c,c->reader->err,c->reader->errstr);\n        return REDIS_ERR;\n    }\n    return REDIS_OK;\n}\n\nint redisGetReply(redisContext *c, void **reply) {\n    int wdone = 0;\n    void *aux = NULL;\n\n    /* Try to read pending replies */\n    if (redisGetReplyFromReader(c,&aux) == REDIS_ERR)\n        return REDIS_ERR;\n\n    /* For the blocking context, flush output buffer and read reply */\n    if (aux == NULL && c->flags & REDIS_BLOCK) {\n        /* Write until done */\n        do {\n            if (redisBufferWrite(c,&wdone) == REDIS_ERR)\n                return REDIS_ERR;\n        } while (!wdone);\n\n        /* Read until there is a reply */\n        do {\n            if (redisBufferRead(c) == REDIS_ERR)\n                return REDIS_ERR;\n            if (redisGetReplyFromReader(c,&aux) == REDIS_ERR)\n                return REDIS_ERR;\n        } while (aux == NULL);\n    }\n\n    /* Set reply object */\n    if (reply != NULL) *reply = aux;\n    return REDIS_OK;\n}\n\n\n/* Helper function for the redisAppendCommand* family of functions.\n *\n * Write a formatted command to the output buffer. When this family\n * is used, you need to call redisGetReply yourself to retrieve\n * the reply (or replies in pub/sub).\n */\nint __redisAppendCommand(redisContext *c, char *cmd, size_t len) {\n    sds newbuf;\n\n    newbuf = sdscatlen(c->obuf,cmd,len);\n    if (newbuf == NULL) {\n        __redisSetError(c,REDIS_ERR_OOM,\"Out of memory\");\n        return REDIS_ERR;\n    }\n\n    c->obuf = newbuf;\n    return REDIS_OK;\n}\n\nint redisvAppendCommand(redisContext *c, const char *format, va_list ap) {\n    char *cmd;\n    int len;\n\n    len = redisvFormatCommand(&cmd,format,ap);\n    if (len == -1) {\n        __redisSetError(c,REDIS_ERR_OOM,\"Out of memory\");\n        return REDIS_ERR;\n    }\n\n    if (__redisAppendCommand(c,cmd,len) != REDIS_OK) {\n        free(cmd);\n        return REDIS_ERR;\n    }\n\n    free(cmd);\n    return REDIS_OK;\n}\n\nint redisAppendCommand(redisContext *c, const char *format, ...) {\n    va_list ap;\n    int ret;\n\n    va_start(ap,format);\n    ret = redisvAppendCommand(c,format,ap);\n    va_end(ap);\n    return ret;\n}\n\nint redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen) {\n    char *cmd;\n    int len;\n\n    len = redisFormatCommandArgv(&cmd,argc,argv,argvlen);\n    if (len == -1) {\n        __redisSetError(c,REDIS_ERR_OOM,\"Out of memory\");\n        return REDIS_ERR;\n    }\n\n    if (__redisAppendCommand(c,cmd,len) != REDIS_OK) {\n        free(cmd);\n        return REDIS_ERR;\n    }\n\n    free(cmd);\n    return REDIS_OK;\n}\n\n/* Helper function for the redisCommand* family of functions.\n *\n * Write a formatted command to the output buffer. If the given context is\n * blocking, immediately read the reply into the \"reply\" pointer. When the\n * context is non-blocking, the \"reply\" pointer will not be used and the\n * command is simply appended to the write buffer.\n *\n * Returns the reply when a reply was succesfully retrieved. Returns NULL\n * otherwise. When NULL is returned in a blocking context, the error field\n * in the context will be set.\n */\nstatic void *__redisBlockForReply(redisContext *c) {\n    void *reply;\n\n    if (c->flags & REDIS_BLOCK) {\n        if (redisGetReply(c,&reply) != REDIS_OK)\n            return NULL;\n        return reply;\n    }\n    return NULL;\n}\n\nvoid *redisvCommand(redisContext *c, const char *format, va_list ap) {\n    if (redisvAppendCommand(c,format,ap) != REDIS_OK)\n        return NULL;\n    return __redisBlockForReply(c);\n}\n\nvoid *redisCommand(redisContext *c, const char *format, ...) {\n    va_list ap;\n    void *reply = NULL;\n    va_start(ap,format);\n    reply = redisvCommand(c,format,ap);\n    va_end(ap);\n    return reply;\n}\n\nvoid *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen) {\n    if (redisAppendCommandArgv(c,argc,argv,argvlen) != REDIS_OK)\n        return NULL;\n    return __redisBlockForReply(c);\n}\n"
  },
  {
    "path": "deps/hiredis/hiredis.h",
    "content": "/*\n * Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>\n * Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>\n *\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 __HIREDIS_H\n#define __HIREDIS_H\n#include <stdio.h> /* for size_t */\n#include <stdarg.h> /* for va_list */\n#include <sys/time.h> /* for struct timeval */\n\n#define HIREDIS_MAJOR 0\n#define HIREDIS_MINOR 10\n#define HIREDIS_PATCH 1\n\n#define REDIS_ERR -1\n#define REDIS_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 REDIS_ERR_IO 1 /* Error in read or write */\n#define REDIS_ERR_EOF 3 /* End of file */\n#define REDIS_ERR_PROTOCOL 4 /* Protocol error */\n#define REDIS_ERR_OOM 5 /* Out of memory */\n#define REDIS_ERR_OTHER 2 /* Everything else... */\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 REDIS_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 REDIS_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 REDIS_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 REDIS_FREEING 0x8\n\n/* Flag that is set when an async callback is executed. */\n#define REDIS_IN_CALLBACK 0x10\n\n/* Flag that is set when the async context has one or more subscriptions. */\n#define REDIS_SUBSCRIBED 0x20\n\n/* Flag that is set when monitor mode is active */\n#define REDIS_MONITORING 0x40\n\n#define REDIS_REPLY_STRING 1\n#define REDIS_REPLY_ARRAY 2\n#define REDIS_REPLY_INTEGER 3\n#define REDIS_REPLY_NIL 4\n#define REDIS_REPLY_STATUS 5\n#define REDIS_REPLY_ERROR 6\n\n#define REDIS_READER_MAX_BUF (1024*16)  /* Default max unused reader buffer. */\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/* This is the reply object returned by redisCommand() */\ntypedef struct redisReply {\n    int type; /* REDIS_REPLY_* */\n    long long integer; /* The integer when type is REDIS_REPLY_INTEGER */\n    int len; /* Length of string */\n    char *str; /* Used for both REDIS_REPLY_ERROR and REDIS_REPLY_STRING */\n    size_t elements; /* number of elements, for REDIS_REPLY_ARRAY */\n    struct redisReply **element; /* elements vector for REDIS_REPLY_ARRAY */\n} redisReply;\n\ntypedef struct redisReadTask {\n    int type;\n    int elements; /* number of elements in multibulk container */\n    int idx; /* index in parent (array) object */\n    void *obj; /* holds user-generated value for a read task */\n    struct redisReadTask *parent; /* parent task */\n    void *privdata; /* user-settable arbitrary field */\n} redisReadTask;\n\ntypedef struct redisReplyObjectFunctions {\n    void *(*createString)(const redisReadTask*, char*, size_t);\n    void *(*createArray)(const redisReadTask*, int);\n    void *(*createInteger)(const redisReadTask*, long long);\n    void *(*createNil)(const redisReadTask*);\n    void (*freeObject)(void*);\n} redisReplyObjectFunctions;\n\n/* State for the protocol parser */\ntypedef struct redisReader {\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    redisReadTask rstack[9];\n    int ridx; /* Index of current read task */\n    void *reply; /* Temporary reply pointer */\n\n    redisReplyObjectFunctions *fn;\n    void *privdata;\n} redisReader;\n\n/* Public API for the protocol parser. */\nredisReader *redisReaderCreate(void);\nvoid redisReaderFree(redisReader *r);\nint redisReaderFeed(redisReader *r, const char *buf, size_t len);\nint redisReaderGetReply(redisReader *r, void **reply);\n\n/* Backwards compatibility, can be removed on big version bump. */\n#define redisReplyReaderCreate redisReaderCreate\n#define redisReplyReaderFree redisReaderFree\n#define redisReplyReaderFeed redisReaderFeed\n#define redisReplyReaderGetReply redisReaderGetReply\n#define redisReplyReaderSetPrivdata(_r, _p) (int)(((redisReader*)(_r))->privdata = (_p))\n#define redisReplyReaderGetObject(_r) (((redisReader*)(_r))->reply)\n#define redisReplyReaderGetError(_r) (((redisReader*)(_r))->errstr)\n\n/* Function to free the reply objects hiredis returns by default. */\nvoid freeReplyObject(void *reply);\n\n/* Functions to format a command according to the protocol. */\nint redisvFormatCommand(char **target, const char *format, va_list ap);\nint redisFormatCommand(char **target, const char *format, ...);\nint redisFormatCommandArgv(char **target, int argc, const char **argv, const size_t *argvlen);\n\n/* Context for a connection to Redis */\ntypedef struct redisContext {\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    redisReader *reader; /* Protocol reader */\n} redisContext;\n\nredisContext *redisConnect(const char *ip, int port);\nredisContext *redisConnectWithTimeout(const char *ip, int port, struct timeval tv);\nredisContext *redisConnectNonBlock(const char *ip, int port);\nredisContext *redisConnectUnix(const char *path);\nredisContext *redisConnectUnixWithTimeout(const char *path, struct timeval tv);\nredisContext *redisConnectUnixNonBlock(const char *path);\nint redisSetTimeout(redisContext *c, struct timeval tv);\nvoid redisFree(redisContext *c);\nint redisBufferRead(redisContext *c);\nint redisBufferWrite(redisContext *c, int *done);\n\n/* In a blocking context, this function first checks if there are unconsumed\n * replies to return and returns one if so. Otherwise, it flushes the output\n * buffer to the socket and reads until it has a reply. In a non-blocking\n * context, it will return unconsumed replies until there are no more. */\nint redisGetReply(redisContext *c, void **reply);\nint redisGetReplyFromReader(redisContext *c, void **reply);\n\n/* Write a command to the output buffer. Use these functions in blocking mode\n * to get a pipeline of commands. */\nint redisvAppendCommand(redisContext *c, const char *format, va_list ap);\nint redisAppendCommand(redisContext *c, const char *format, ...);\nint redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);\n\n/* Issue a command to Redis. In a blocking context, it is identical to calling\n * redisAppendCommand, followed by redisGetReply. The function will return\n * NULL if there was an error in performing the request, otherwise it will\n * return the reply. In a non-blocking context, it is identical to calling\n * only redisAppendCommand and will always return NULL. */\nvoid *redisvCommand(redisContext *c, const char *format, va_list ap);\nvoid *redisCommand(redisContext *c, const char *format, ...);\nvoid *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "deps/hiredis/net.c",
    "content": "/* Extracted from anet.c to work properly with Hiredis error reporting.\n *\n * Copyright (c) 2006-2011, Salvatore Sanfilippo <antirez at gmail dot com>\n * Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>\n *\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 \"fmacros.h\"\n#include <sys/types.h>\n#include <sys/socket.h>\n#include <sys/select.h>\n#include <sys/un.h>\n#include <netinet/in.h>\n#include <netinet/tcp.h>\n#include <arpa/inet.h>\n#include <unistd.h>\n#include <fcntl.h>\n#include <string.h>\n#include <netdb.h>\n#include <errno.h>\n#include <stdarg.h>\n#include <stdio.h>\n#include <poll.h>\n#include <limits.h>\n\n#include \"net.h\"\n#include \"sds.h\"\n\n/* Defined in hiredis.c */\nvoid __redisSetError(redisContext *c, int type, const char *str);\n\nstatic void __redisSetErrorFromErrno(redisContext *c, int type, const char *prefix) {\n    char buf[128];\n    size_t len = 0;\n\n    if (prefix != NULL)\n        len = snprintf(buf,sizeof(buf),\"%s: \",prefix);\n    strerror_r(errno,buf+len,sizeof(buf)-len);\n    __redisSetError(c,type,buf);\n}\n\nstatic int redisSetReuseAddr(redisContext *c, int fd) {\n    int on = 1;\n    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {\n        __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);\n        close(fd);\n        return REDIS_ERR;\n    }\n    return REDIS_OK;\n}\n\nstatic int redisCreateSocket(redisContext *c, int type) {\n    int s;\n    if ((s = socket(type, SOCK_STREAM, 0)) == -1) {\n        __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);\n        return REDIS_ERR;\n    }\n    if (type == AF_INET) {\n        if (redisSetReuseAddr(c,s) == REDIS_ERR) {\n            return REDIS_ERR;\n        }\n    }\n    return s;\n}\n\nstatic int redisSetBlocking(redisContext *c, int fd, int blocking) {\n    int flags;\n\n    /* Set the socket nonblocking.\n     * Note that fcntl(2) for F_GETFL and F_SETFL can't be\n     * interrupted by a signal. */\n    if ((flags = fcntl(fd, F_GETFL)) == -1) {\n        __redisSetErrorFromErrno(c,REDIS_ERR_IO,\"fcntl(F_GETFL)\");\n        close(fd);\n        return REDIS_ERR;\n    }\n\n    if (blocking)\n        flags &= ~O_NONBLOCK;\n    else\n        flags |= O_NONBLOCK;\n\n    if (fcntl(fd, F_SETFL, flags) == -1) {\n        __redisSetErrorFromErrno(c,REDIS_ERR_IO,\"fcntl(F_SETFL)\");\n        close(fd);\n        return REDIS_ERR;\n    }\n    return REDIS_OK;\n}\n\nstatic int redisSetTcpNoDelay(redisContext *c, int fd) {\n    int yes = 1;\n    if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)) == -1) {\n        __redisSetErrorFromErrno(c,REDIS_ERR_IO,\"setsockopt(TCP_NODELAY)\");\n        close(fd);\n        return REDIS_ERR;\n    }\n    return REDIS_OK;\n}\n\n#define __MAX_MSEC (((LONG_MAX) - 999) / 1000)\n\nstatic int redisContextWaitReady(redisContext *c, int fd, const struct timeval *timeout) {\n    struct pollfd   wfd[1];\n    long msec;\n\n    msec          = -1;\n    wfd[0].fd     = fd;\n    wfd[0].events = POLLOUT;\n\n    /* Only use timeout when not NULL. */\n    if (timeout != NULL) {\n        if (timeout->tv_usec > 1000000 || timeout->tv_sec > __MAX_MSEC) {\n            close(fd);\n            return REDIS_ERR;\n        }\n\n        msec = (timeout->tv_sec * 1000) + ((timeout->tv_usec + 999) / 1000);\n\n        if (msec < 0 || msec > INT_MAX) {\n            msec = INT_MAX;\n        }\n    }\n\n    if (errno == EINPROGRESS) {\n        int res;\n\n        if ((res = poll(wfd, 1, msec)) == -1) {\n            __redisSetErrorFromErrno(c, REDIS_ERR_IO, \"poll(2)\");\n            close(fd);\n            return REDIS_ERR;\n        } else if (res == 0) {\n            errno = ETIMEDOUT;\n            __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);\n            close(fd);\n            return REDIS_ERR;\n        }\n\n        if (redisCheckSocketError(c, fd) != REDIS_OK)\n            return REDIS_ERR;\n\n        return REDIS_OK;\n    }\n\n    __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);\n    close(fd);\n    return REDIS_ERR;\n}\n\nint redisCheckSocketError(redisContext *c, int fd) {\n    int err = 0;\n    socklen_t errlen = sizeof(err);\n\n    if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &errlen) == -1) {\n        __redisSetErrorFromErrno(c,REDIS_ERR_IO,\"getsockopt(SO_ERROR)\");\n        close(fd);\n        return REDIS_ERR;\n    }\n\n    if (err) {\n        errno = err;\n        __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);\n        close(fd);\n        return REDIS_ERR;\n    }\n\n    return REDIS_OK;\n}\n\nint redisContextSetTimeout(redisContext *c, struct timeval tv) {\n    if (setsockopt(c->fd,SOL_SOCKET,SO_RCVTIMEO,&tv,sizeof(tv)) == -1) {\n        __redisSetErrorFromErrno(c,REDIS_ERR_IO,\"setsockopt(SO_RCVTIMEO)\");\n        return REDIS_ERR;\n    }\n    if (setsockopt(c->fd,SOL_SOCKET,SO_SNDTIMEO,&tv,sizeof(tv)) == -1) {\n        __redisSetErrorFromErrno(c,REDIS_ERR_IO,\"setsockopt(SO_SNDTIMEO)\");\n        return REDIS_ERR;\n    }\n    return REDIS_OK;\n}\n\nint redisContextConnectTcp(redisContext *c, const char *addr, int port, struct timeval *timeout) {\n    int s, rv;\n    char _port[6];  /* strlen(\"65535\"); */\n    struct addrinfo hints, *servinfo, *p;\n    int blocking = (c->flags & REDIS_BLOCK);\n\n    snprintf(_port, 6, \"%d\", port);\n    memset(&hints,0,sizeof(hints));\n    hints.ai_family = AF_INET;\n    hints.ai_socktype = SOCK_STREAM;\n\n    /* Try with IPv6 if no IPv4 address was found. We do it in this order since\n     * in a Redis client you can't afford to test if you have IPv6 connectivity\n     * as this would add latency to every connect. Otherwise a more sensible\n     * route could be: Use IPv6 if both addresses are available and there is IPv6\n     * connectivity. */\n    if ((rv = getaddrinfo(addr,_port,&hints,&servinfo)) != 0) {\n         hints.ai_family = AF_INET6;\n         if ((rv = getaddrinfo(addr,_port,&hints,&servinfo)) != 0) {\n            __redisSetError(c,REDIS_ERR_OTHER,gai_strerror(rv));\n            return REDIS_ERR;\n        }\n    }\n    for (p = servinfo; p != NULL; p = p->ai_next) {\n        if ((s = socket(p->ai_family,p->ai_socktype,p->ai_protocol)) == -1)\n            continue;\n\n        if (redisSetBlocking(c,s,0) != REDIS_OK)\n            goto error;\n        if (connect(s,p->ai_addr,p->ai_addrlen) == -1) {\n            if (errno == EHOSTUNREACH) {\n                close(s);\n                continue;\n            } else if (errno == EINPROGRESS && !blocking) {\n                /* This is ok. */\n            } else {\n                if (redisContextWaitReady(c,s,timeout) != REDIS_OK)\n                    goto error;\n            }\n        }\n        if (blocking && redisSetBlocking(c,s,1) != REDIS_OK)\n            goto error;\n        if (redisSetTcpNoDelay(c,s) != REDIS_OK)\n            goto error;\n\n        c->fd = s;\n        c->flags |= REDIS_CONNECTED;\n        rv = REDIS_OK;\n        goto end;\n    }\n    if (p == NULL) {\n        char buf[128];\n        snprintf(buf,sizeof(buf),\"Can't create socket: %s\",strerror(errno));\n        __redisSetError(c,REDIS_ERR_OTHER,buf);\n        goto error;\n    }\n\nerror:\n    rv = REDIS_ERR;\nend:\n    freeaddrinfo(servinfo);\n    return rv;  // Need to return REDIS_OK if alright\n}\n\nint redisContextConnectUnix(redisContext *c, const char *path, struct timeval *timeout) {\n    int s;\n    int blocking = (c->flags & REDIS_BLOCK);\n    struct sockaddr_un sa;\n\n    if ((s = redisCreateSocket(c,AF_LOCAL)) < 0)\n        return REDIS_ERR;\n    if (redisSetBlocking(c,s,0) != REDIS_OK)\n        return REDIS_ERR;\n\n    sa.sun_family = AF_LOCAL;\n    strncpy(sa.sun_path,path,sizeof(sa.sun_path)-1);\n    if (connect(s, (struct sockaddr*)&sa, sizeof(sa)) == -1) {\n        if (errno == EINPROGRESS && !blocking) {\n            /* This is ok. */\n        } else {\n            if (redisContextWaitReady(c,s,timeout) != REDIS_OK)\n                return REDIS_ERR;\n        }\n    }\n\n    /* Reset socket to be blocking after connect(2). */\n    if (blocking && redisSetBlocking(c,s,1) != REDIS_OK)\n        return REDIS_ERR;\n\n    c->fd = s;\n    c->flags |= REDIS_CONNECTED;\n    return REDIS_OK;\n}\n"
  },
  {
    "path": "deps/hiredis/net.h",
    "content": "/* Extracted from anet.c to work properly with Hiredis error reporting.\n *\n * Copyright (c) 2006-2011, Salvatore Sanfilippo <antirez at gmail dot com>\n * Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>\n *\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 __NET_H\n#define __NET_H\n\n#include \"hiredis.h\"\n\n#if defined(__sun)\n#define AF_LOCAL AF_UNIX\n#endif\n\nint redisCheckSocketError(redisContext *c, int fd);\nint redisContextSetTimeout(redisContext *c, struct timeval tv);\nint redisContextConnectTcp(redisContext *c, const char *addr, int port, struct timeval *timeout);\nint redisContextConnectUnix(redisContext *c, const char *path, struct timeval *timeout);\n\n#endif\n"
  },
  {
    "path": "deps/hiredis/sds.c",
    "content": "/* SDSLib, A C dynamic strings library\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#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <ctype.h>\n#include <assert.h>\n#include \"sds.h\"\n#include \"zmalloc.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 = zmalloc(sizeof(struct sdshdr)+initlen+1);\n    } else {\n        sh = zcalloc(sizeof(struct sdshdr)+initlen+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    zfree(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(struct sdshdr)));\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(struct sdshdr)));\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(struct sdshdr)));\n    newlen = (len+addlen);\n    if (newlen < SDS_MAX_PREALLOC)\n        newlen *= 2;\n    else\n        newlen += SDS_MAX_PREALLOC;\n    newsh = zrealloc(sh, sizeof(struct sdshdr)+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(struct sdshdr)));\n    sh = zrealloc(sh, sizeof(struct sdshdr)+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(struct sdshdr)));\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(struct sdshdr)));\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(struct sdshdr)));\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(struct sdshdr)));\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(struct sdshdr)));\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(struct sdshdr)));\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(struct sdshdr)));\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/* 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 = zmalloc(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            zfree(buf);\n            buflen *= 2;\n            continue;\n        }\n        break;\n    }\n    t = sdscat(s, buf);\n    zfree(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 = sdsempty(\"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/* 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 */\nsds sdstrim(sds s, const char *cset) {\n    struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));\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    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 * sdstrim(s,1,-1); => \"ello Worl\"\n */\nvoid sdsrange(sds s, int start, int end) {\n    struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));\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 = zmalloc(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 = zrealloc(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        zfree(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    zfree(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 an 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 = zrealloc(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 = zmalloc(sizeof(void*));\n            return vector;\n        }\n    }\n\nerr:\n    while((*argc)--)\n        sdsfree(vector[*argc]);\n    zfree(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#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 = sdstrim(sdsnew(\"xxciaoyyy\"),\"xy\");\n        test_cond(\"sdstrim() correctly trims characters\",\n            sdslen(x) == 4 && memcmp(x,\"ciao\\0\",5) == 0)\n\n        y = sdsrange(sdsdup(x),1,1);\n        test_cond(\"sdsrange(...,1,1)\",\n            sdslen(y) == 1 && memcmp(y,\"i\\0\",2) == 0)\n\n        sdsfree(y);\n        y = sdsrange(sdsdup(x),1,-1);\n        test_cond(\"sdsrange(...,1,-1)\",\n            sdslen(y) == 3 && memcmp(y,\"iao\\0\",4) == 0)\n\n        sdsfree(y);\n        y = sdsrange(sdsdup(x),-2,-1);\n        test_cond(\"sdsrange(...,-2,-1)\",\n            sdslen(y) == 2 && memcmp(y,\"ao\\0\",3) == 0)\n\n        sdsfree(y);\n        y = sdsrange(sdsdup(x),2,1);\n        test_cond(\"sdsrange(...,2,1)\",\n            sdslen(y) == 0 && memcmp(y,\"\\0\",1) == 0)\n\n        sdsfree(y);\n        y = sdsrange(sdsdup(x),1,100);\n        test_cond(\"sdsrange(...,1,100)\",\n            sdslen(y) == 3 && memcmp(y,\"iao\\0\",4) == 0)\n\n        sdsfree(y);\n        y = sdsrange(sdsdup(x),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        {\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": "deps/hiredis/sds.h",
    "content": "/* SDSLib, A C dynamic strings library\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#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\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 = (void*)(s-(sizeof(struct sdshdr)));\n    return sh->len;\n}\n\nstatic inline size_t sdsavail(const sds s) {\n    struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr)));\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 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);\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": "deps/hiredis/test.c",
    "content": "#include \"fmacros.h\"\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <strings.h>\n#include <sys/time.h>\n#include <assert.h>\n#include <unistd.h>\n#include <signal.h>\n#include <errno.h>\n\n#include \"hiredis.h\"\n\nenum connection_type {\n    CONN_TCP,\n    CONN_UNIX\n};\n\nstruct config {\n    enum connection_type type;\n\n    struct {\n        const char *host;\n        int port;\n    } tcp;\n\n    struct {\n        const char *path;\n    } unix;\n};\n\n/* The following lines make up our testing \"framework\" :) */\nstatic int tests = 0, fails = 0;\n#define test(_s) { printf(\"#%02d \", ++tests); printf(_s); }\n#define test_cond(_c) if(_c) printf(\"\\033[0;32mPASSED\\033[0;0m\\n\"); else {printf(\"\\033[0;31mFAILED\\033[0;0m\\n\"); fails++;}\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 redisContext *select_database(redisContext *c) {\n    redisReply *reply;\n\n    /* Switch to DB 9 for testing, now that we know we can chat. */\n    reply = redisCommand(c,\"SELECT 9\");\n    assert(reply != NULL);\n    freeReplyObject(reply);\n\n    /* Make sure the DB is emtpy */\n    reply = redisCommand(c,\"DBSIZE\");\n    assert(reply != NULL);\n    if (reply->type == REDIS_REPLY_INTEGER && reply->integer == 0) {\n        /* Awesome, DB 9 is empty and we can continue. */\n        freeReplyObject(reply);\n    } else {\n        printf(\"Database #9 is not empty, test can not continue\\n\");\n        exit(1);\n    }\n\n    return c;\n}\n\nstatic void disconnect(redisContext *c) {\n    redisReply *reply;\n\n    /* Make sure we're on DB 9. */\n    reply = redisCommand(c,\"SELECT 9\");\n    assert(reply != NULL);\n    freeReplyObject(reply);\n    reply = redisCommand(c,\"FLUSHDB\");\n    assert(reply != NULL);\n    freeReplyObject(reply);\n\n    /* Free the context as well. */\n    redisFree(c);\n}\n\nstatic redisContext *connect(struct config config) {\n    redisContext *c = NULL;\n\n    if (config.type == CONN_TCP) {\n        c = redisConnect(config.tcp.host, config.tcp.port);\n    } else if (config.type == CONN_UNIX) {\n        c = redisConnectUnix(config.unix.path);\n    } else {\n        assert(NULL);\n    }\n\n    if (c->err) {\n        printf(\"Connection error: %s\\n\", c->errstr);\n        exit(1);\n    }\n\n    return select_database(c);\n}\n\nstatic void test_format_commands(void) {\n    char *cmd;\n    int len;\n\n    test(\"Format command without interpolation: \");\n    len = redisFormatCommand(&cmd,\"SET foo bar\");\n    test_cond(strncmp(cmd,\"*3\\r\\n$3\\r\\nSET\\r\\n$3\\r\\nfoo\\r\\n$3\\r\\nbar\\r\\n\",len) == 0 &&\n        len == 4+4+(3+2)+4+(3+2)+4+(3+2));\n    free(cmd);\n\n    test(\"Format command with %%s string interpolation: \");\n    len = redisFormatCommand(&cmd,\"SET %s %s\",\"foo\",\"bar\");\n    test_cond(strncmp(cmd,\"*3\\r\\n$3\\r\\nSET\\r\\n$3\\r\\nfoo\\r\\n$3\\r\\nbar\\r\\n\",len) == 0 &&\n        len == 4+4+(3+2)+4+(3+2)+4+(3+2));\n    free(cmd);\n\n    test(\"Format command with %%s and an empty string: \");\n    len = redisFormatCommand(&cmd,\"SET %s %s\",\"foo\",\"\");\n    test_cond(strncmp(cmd,\"*3\\r\\n$3\\r\\nSET\\r\\n$3\\r\\nfoo\\r\\n$0\\r\\n\\r\\n\",len) == 0 &&\n        len == 4+4+(3+2)+4+(3+2)+4+(0+2));\n    free(cmd);\n\n    test(\"Format command with an empty string in between proper interpolations: \");\n    len = redisFormatCommand(&cmd,\"SET %s %s\",\"\",\"foo\");\n    test_cond(strncmp(cmd,\"*3\\r\\n$3\\r\\nSET\\r\\n$0\\r\\n\\r\\n$3\\r\\nfoo\\r\\n\",len) == 0 &&\n        len == 4+4+(3+2)+4+(0+2)+4+(3+2));\n    free(cmd);\n\n    test(\"Format command with %%b string interpolation: \");\n    len = redisFormatCommand(&cmd,\"SET %b %b\",\"foo\",3,\"b\\0r\",3);\n    test_cond(strncmp(cmd,\"*3\\r\\n$3\\r\\nSET\\r\\n$3\\r\\nfoo\\r\\n$3\\r\\nb\\0r\\r\\n\",len) == 0 &&\n        len == 4+4+(3+2)+4+(3+2)+4+(3+2));\n    free(cmd);\n\n    test(\"Format command with %%b and an empty string: \");\n    len = redisFormatCommand(&cmd,\"SET %b %b\",\"foo\",3,\"\",0);\n    test_cond(strncmp(cmd,\"*3\\r\\n$3\\r\\nSET\\r\\n$3\\r\\nfoo\\r\\n$0\\r\\n\\r\\n\",len) == 0 &&\n        len == 4+4+(3+2)+4+(3+2)+4+(0+2));\n    free(cmd);\n\n    test(\"Format command with literal %%: \");\n    len = redisFormatCommand(&cmd,\"SET %% %%\");\n    test_cond(strncmp(cmd,\"*3\\r\\n$3\\r\\nSET\\r\\n$1\\r\\n%\\r\\n$1\\r\\n%\\r\\n\",len) == 0 &&\n        len == 4+4+(3+2)+4+(1+2)+4+(1+2));\n    free(cmd);\n\n    /* Vararg width depends on the type. These tests make sure that the\n     * width is correctly determined using the format and subsequent varargs\n     * can correctly be interpolated. */\n#define INTEGER_WIDTH_TEST(fmt, type) do {                                                \\\n    type value = 123;                                                                     \\\n    test(\"Format command with printf-delegation (\" #type \"): \");                          \\\n    len = redisFormatCommand(&cmd,\"key:%08\" fmt \" str:%s\", value, \"hello\");               \\\n    test_cond(strncmp(cmd,\"*2\\r\\n$12\\r\\nkey:00000123\\r\\n$9\\r\\nstr:hello\\r\\n\",len) == 0 && \\\n        len == 4+5+(12+2)+4+(9+2));                                                       \\\n    free(cmd);                                                                            \\\n} while(0)\n\n#define FLOAT_WIDTH_TEST(type) do {                                                       \\\n    type value = 123.0;                                                                   \\\n    test(\"Format command with printf-delegation (\" #type \"): \");                          \\\n    len = redisFormatCommand(&cmd,\"key:%08.3f str:%s\", value, \"hello\");                   \\\n    test_cond(strncmp(cmd,\"*2\\r\\n$12\\r\\nkey:0123.000\\r\\n$9\\r\\nstr:hello\\r\\n\",len) == 0 && \\\n        len == 4+5+(12+2)+4+(9+2));                                                       \\\n    free(cmd);                                                                            \\\n} while(0)\n\n    INTEGER_WIDTH_TEST(\"d\", int);\n    INTEGER_WIDTH_TEST(\"hhd\", char);\n    INTEGER_WIDTH_TEST(\"hd\", short);\n    INTEGER_WIDTH_TEST(\"ld\", long);\n    INTEGER_WIDTH_TEST(\"lld\", long long);\n    INTEGER_WIDTH_TEST(\"u\", unsigned int);\n    INTEGER_WIDTH_TEST(\"hhu\", unsigned char);\n    INTEGER_WIDTH_TEST(\"hu\", unsigned short);\n    INTEGER_WIDTH_TEST(\"lu\", unsigned long);\n    INTEGER_WIDTH_TEST(\"llu\", unsigned long long);\n    FLOAT_WIDTH_TEST(float);\n    FLOAT_WIDTH_TEST(double);\n\n    test(\"Format command with invalid printf format: \");\n    len = redisFormatCommand(&cmd,\"key:%08p %b\",(void*)1234,\"foo\",3);\n    test_cond(len == -1);\n\n    const char *argv[3];\n    argv[0] = \"SET\";\n    argv[1] = \"foo\\0xxx\";\n    argv[2] = \"bar\";\n    size_t lens[3] = { 3, 7, 3 };\n    int argc = 3;\n\n    test(\"Format command by passing argc/argv without lengths: \");\n    len = redisFormatCommandArgv(&cmd,argc,argv,NULL);\n    test_cond(strncmp(cmd,\"*3\\r\\n$3\\r\\nSET\\r\\n$3\\r\\nfoo\\r\\n$3\\r\\nbar\\r\\n\",len) == 0 &&\n        len == 4+4+(3+2)+4+(3+2)+4+(3+2));\n    free(cmd);\n\n    test(\"Format command by passing argc/argv with lengths: \");\n    len = redisFormatCommandArgv(&cmd,argc,argv,lens);\n    test_cond(strncmp(cmd,\"*3\\r\\n$3\\r\\nSET\\r\\n$7\\r\\nfoo\\0xxx\\r\\n$3\\r\\nbar\\r\\n\",len) == 0 &&\n        len == 4+4+(3+2)+4+(7+2)+4+(3+2));\n    free(cmd);\n}\n\nstatic void test_reply_reader(void) {\n    redisReader *reader;\n    void *reply;\n    int ret;\n\n    test(\"Error handling in reply parser: \");\n    reader = redisReaderCreate();\n    redisReaderFeed(reader,(char*)\"@foo\\r\\n\",6);\n    ret = redisReaderGetReply(reader,NULL);\n    test_cond(ret == REDIS_ERR &&\n              strcasecmp(reader->errstr,\"Protocol error, got \\\"@\\\" as reply type byte\") == 0);\n    redisReaderFree(reader);\n\n    /* when the reply already contains multiple items, they must be free'd\n     * on an error. valgrind will bark when this doesn't happen. */\n    test(\"Memory cleanup in reply parser: \");\n    reader = redisReaderCreate();\n    redisReaderFeed(reader,(char*)\"*2\\r\\n\",4);\n    redisReaderFeed(reader,(char*)\"$5\\r\\nhello\\r\\n\",11);\n    redisReaderFeed(reader,(char*)\"@foo\\r\\n\",6);\n    ret = redisReaderGetReply(reader,NULL);\n    test_cond(ret == REDIS_ERR &&\n              strcasecmp(reader->errstr,\"Protocol error, got \\\"@\\\" as reply type byte\") == 0);\n    redisReaderFree(reader);\n\n    test(\"Set error on nested multi bulks with depth > 2: \");\n    reader = redisReaderCreate();\n    redisReaderFeed(reader,(char*)\"*1\\r\\n\",4);\n    redisReaderFeed(reader,(char*)\"*1\\r\\n\",4);\n    redisReaderFeed(reader,(char*)\"*1\\r\\n\",4);\n    redisReaderFeed(reader,(char*)\"*1\\r\\n\",4);\n    ret = redisReaderGetReply(reader,NULL);\n    test_cond(ret == REDIS_ERR &&\n              strncasecmp(reader->errstr,\"No support for\",14) == 0);\n    redisReaderFree(reader);\n\n    test(\"Works with NULL functions for reply: \");\n    reader = redisReaderCreate();\n    reader->fn = NULL;\n    redisReaderFeed(reader,(char*)\"+OK\\r\\n\",5);\n    ret = redisReaderGetReply(reader,&reply);\n    test_cond(ret == REDIS_OK && reply == (void*)REDIS_REPLY_STATUS);\n    redisReaderFree(reader);\n\n    test(\"Works when a single newline (\\\\r\\\\n) covers two calls to feed: \");\n    reader = redisReaderCreate();\n    reader->fn = NULL;\n    redisReaderFeed(reader,(char*)\"+OK\\r\",4);\n    ret = redisReaderGetReply(reader,&reply);\n    assert(ret == REDIS_OK && reply == NULL);\n    redisReaderFeed(reader,(char*)\"\\n\",1);\n    ret = redisReaderGetReply(reader,&reply);\n    test_cond(ret == REDIS_OK && reply == (void*)REDIS_REPLY_STATUS);\n    redisReaderFree(reader);\n\n    test(\"Don't reset state after protocol error: \");\n    reader = redisReaderCreate();\n    reader->fn = NULL;\n    redisReaderFeed(reader,(char*)\"x\",1);\n    ret = redisReaderGetReply(reader,&reply);\n    assert(ret == REDIS_ERR);\n    ret = redisReaderGetReply(reader,&reply);\n    test_cond(ret == REDIS_ERR && reply == NULL);\n    redisReaderFree(reader);\n\n    /* Regression test for issue #45 on GitHub. */\n    test(\"Don't do empty allocation for empty multi bulk: \");\n    reader = redisReaderCreate();\n    redisReaderFeed(reader,(char*)\"*0\\r\\n\",4);\n    ret = redisReaderGetReply(reader,&reply);\n    test_cond(ret == REDIS_OK &&\n        ((redisReply*)reply)->type == REDIS_REPLY_ARRAY &&\n        ((redisReply*)reply)->elements == 0);\n    freeReplyObject(reply);\n    redisReaderFree(reader);\n}\n\nstatic void test_blocking_connection_errors(void) {\n    redisContext *c;\n\n    test(\"Returns error when host cannot be resolved: \");\n    c = redisConnect((char*)\"idontexist.local\", 6379);\n    test_cond(c->err == REDIS_ERR_OTHER &&\n        (strcmp(c->errstr,\"Name or service not known\") == 0 ||\n         strcmp(c->errstr,\"Can't resolve: idontexist.local\") == 0));\n    redisFree(c);\n\n    test(\"Returns error when the port is not open: \");\n    c = redisConnect((char*)\"localhost\", 1);\n    test_cond(c->err == REDIS_ERR_IO &&\n        strcmp(c->errstr,\"Connection refused\") == 0);\n    redisFree(c);\n\n    test(\"Returns error when the unix socket path doesn't accept connections: \");\n    c = redisConnectUnix((char*)\"/tmp/idontexist.sock\");\n    test_cond(c->err == REDIS_ERR_IO); /* Don't care about the message... */\n    redisFree(c);\n}\n\nstatic void test_blocking_connection(struct config config) {\n    redisContext *c;\n    redisReply *reply;\n\n    c = connect(config);\n\n    test(\"Is able to deliver commands: \");\n    reply = redisCommand(c,\"PING\");\n    test_cond(reply->type == REDIS_REPLY_STATUS &&\n        strcasecmp(reply->str,\"pong\") == 0)\n    freeReplyObject(reply);\n\n    test(\"Is a able to send commands verbatim: \");\n    reply = redisCommand(c,\"SET foo bar\");\n    test_cond (reply->type == REDIS_REPLY_STATUS &&\n        strcasecmp(reply->str,\"ok\") == 0)\n    freeReplyObject(reply);\n\n    test(\"%%s String interpolation works: \");\n    reply = redisCommand(c,\"SET %s %s\",\"foo\",\"hello world\");\n    freeReplyObject(reply);\n    reply = redisCommand(c,\"GET foo\");\n    test_cond(reply->type == REDIS_REPLY_STRING &&\n        strcmp(reply->str,\"hello world\") == 0);\n    freeReplyObject(reply);\n\n    test(\"%%b String interpolation works: \");\n    reply = redisCommand(c,\"SET %b %b\",\"foo\",3,\"hello\\x00world\",11);\n    freeReplyObject(reply);\n    reply = redisCommand(c,\"GET foo\");\n    test_cond(reply->type == REDIS_REPLY_STRING &&\n        memcmp(reply->str,\"hello\\x00world\",11) == 0)\n\n    test(\"Binary reply length is correct: \");\n    test_cond(reply->len == 11)\n    freeReplyObject(reply);\n\n    test(\"Can parse nil replies: \");\n    reply = redisCommand(c,\"GET nokey\");\n    test_cond(reply->type == REDIS_REPLY_NIL)\n    freeReplyObject(reply);\n\n    /* test 7 */\n    test(\"Can parse integer replies: \");\n    reply = redisCommand(c,\"INCR mycounter\");\n    test_cond(reply->type == REDIS_REPLY_INTEGER && reply->integer == 1)\n    freeReplyObject(reply);\n\n    test(\"Can parse multi bulk replies: \");\n    freeReplyObject(redisCommand(c,\"LPUSH mylist foo\"));\n    freeReplyObject(redisCommand(c,\"LPUSH mylist bar\"));\n    reply = redisCommand(c,\"LRANGE mylist 0 -1\");\n    test_cond(reply->type == REDIS_REPLY_ARRAY &&\n              reply->elements == 2 &&\n              !memcmp(reply->element[0]->str,\"bar\",3) &&\n              !memcmp(reply->element[1]->str,\"foo\",3))\n    freeReplyObject(reply);\n\n    /* m/e with multi bulk reply *before* other reply.\n     * specifically test ordering of reply items to parse. */\n    test(\"Can handle nested multi bulk replies: \");\n    freeReplyObject(redisCommand(c,\"MULTI\"));\n    freeReplyObject(redisCommand(c,\"LRANGE mylist 0 -1\"));\n    freeReplyObject(redisCommand(c,\"PING\"));\n    reply = (redisCommand(c,\"EXEC\"));\n    test_cond(reply->type == REDIS_REPLY_ARRAY &&\n              reply->elements == 2 &&\n              reply->element[0]->type == REDIS_REPLY_ARRAY &&\n              reply->element[0]->elements == 2 &&\n              !memcmp(reply->element[0]->element[0]->str,\"bar\",3) &&\n              !memcmp(reply->element[0]->element[1]->str,\"foo\",3) &&\n              reply->element[1]->type == REDIS_REPLY_STATUS &&\n              strcasecmp(reply->element[1]->str,\"pong\") == 0);\n    freeReplyObject(reply);\n\n    disconnect(c);\n}\n\nstatic void test_blocking_io_errors(struct config config) {\n    redisContext *c;\n    redisReply *reply;\n    void *_reply;\n    int major, minor;\n\n    /* Connect to target given by config. */\n    c = connect(config);\n    {\n        /* Find out Redis version to determine the path for the next test */\n        const char *field = \"redis_version:\";\n        char *p, *eptr;\n\n        reply = redisCommand(c,\"INFO\");\n        p = strstr(reply->str,field);\n        major = strtol(p+strlen(field),&eptr,10);\n        p = eptr+1; /* char next to the first \".\" */\n        minor = strtol(p,&eptr,10);\n        freeReplyObject(reply);\n    }\n\n    test(\"Returns I/O error when the connection is lost: \");\n    reply = redisCommand(c,\"QUIT\");\n    if (major >= 2 && minor > 0) {\n        /* > 2.0 returns OK on QUIT and read() should be issued once more\n         * to know the descriptor is at EOF. */\n        test_cond(strcasecmp(reply->str,\"OK\") == 0 &&\n            redisGetReply(c,&_reply) == REDIS_ERR);\n        freeReplyObject(reply);\n    } else {\n        test_cond(reply == NULL);\n    }\n\n    /* On 2.0, QUIT will cause the connection to be closed immediately and\n     * the read(2) for the reply on QUIT will set the error to EOF.\n     * On >2.0, QUIT will return with OK and another read(2) needed to be\n     * issued to find out the socket was closed by the server. In both\n     * conditions, the error will be set to EOF. */\n    assert(c->err == REDIS_ERR_EOF &&\n        strcmp(c->errstr,\"Server closed the connection\") == 0);\n    redisFree(c);\n\n    c = connect(config);\n    test(\"Returns I/O error on socket timeout: \");\n    struct timeval tv = { 0, 1000 };\n    assert(redisSetTimeout(c,tv) == REDIS_OK);\n    test_cond(redisGetReply(c,&_reply) == REDIS_ERR &&\n        c->err == REDIS_ERR_IO && errno == EAGAIN);\n    redisFree(c);\n}\n\nstatic void test_throughput(struct config config) {\n    redisContext *c = connect(config);\n    redisReply **replies;\n    int i, num;\n    long long t1, t2;\n\n    test(\"Throughput:\\n\");\n    for (i = 0; i < 500; i++)\n        freeReplyObject(redisCommand(c,\"LPUSH mylist foo\"));\n\n    num = 1000;\n    replies = malloc(sizeof(redisReply*)*num);\n    t1 = usec();\n    for (i = 0; i < num; i++) {\n        replies[i] = redisCommand(c,\"PING\");\n        assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_STATUS);\n    }\n    t2 = usec();\n    for (i = 0; i < num; i++) freeReplyObject(replies[i]);\n    free(replies);\n    printf(\"\\t(%dx PING: %.3fs)\\n\", num, (t2-t1)/1000000.0);\n\n    replies = malloc(sizeof(redisReply*)*num);\n    t1 = usec();\n    for (i = 0; i < num; i++) {\n        replies[i] = redisCommand(c,\"LRANGE mylist 0 499\");\n        assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_ARRAY);\n        assert(replies[i] != NULL && replies[i]->elements == 500);\n    }\n    t2 = usec();\n    for (i = 0; i < num; i++) freeReplyObject(replies[i]);\n    free(replies);\n    printf(\"\\t(%dx LRANGE with 500 elements: %.3fs)\\n\", num, (t2-t1)/1000000.0);\n\n    num = 10000;\n    replies = malloc(sizeof(redisReply*)*num);\n    for (i = 0; i < num; i++)\n        redisAppendCommand(c,\"PING\");\n    t1 = usec();\n    for (i = 0; i < num; i++) {\n        assert(redisGetReply(c, (void*)&replies[i]) == REDIS_OK);\n        assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_STATUS);\n    }\n    t2 = usec();\n    for (i = 0; i < num; i++) freeReplyObject(replies[i]);\n    free(replies);\n    printf(\"\\t(%dx PING (pipelined): %.3fs)\\n\", num, (t2-t1)/1000000.0);\n\n    replies = malloc(sizeof(redisReply*)*num);\n    for (i = 0; i < num; i++)\n        redisAppendCommand(c,\"LRANGE mylist 0 499\");\n    t1 = usec();\n    for (i = 0; i < num; i++) {\n        assert(redisGetReply(c, (void*)&replies[i]) == REDIS_OK);\n        assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_ARRAY);\n        assert(replies[i] != NULL && replies[i]->elements == 500);\n    }\n    t2 = usec();\n    for (i = 0; i < num; i++) freeReplyObject(replies[i]);\n    free(replies);\n    printf(\"\\t(%dx LRANGE with 500 elements (pipelined): %.3fs)\\n\", num, (t2-t1)/1000000.0);\n\n    disconnect(c);\n}\n\n// static long __test_callback_flags = 0;\n// static void __test_callback(redisContext *c, void *privdata) {\n//     ((void)c);\n//     /* Shift to detect execution order */\n//     __test_callback_flags <<= 8;\n//     __test_callback_flags |= (long)privdata;\n// }\n//\n// static void __test_reply_callback(redisContext *c, redisReply *reply, void *privdata) {\n//     ((void)c);\n//     /* Shift to detect execution order */\n//     __test_callback_flags <<= 8;\n//     __test_callback_flags |= (long)privdata;\n//     if (reply) freeReplyObject(reply);\n// }\n//\n// static redisContext *__connect_nonblock() {\n//     /* Reset callback flags */\n//     __test_callback_flags = 0;\n//     return redisConnectNonBlock(\"127.0.0.1\", port, NULL);\n// }\n//\n// static void test_nonblocking_connection() {\n//     redisContext *c;\n//     int wdone = 0;\n//\n//     test(\"Calls command callback when command is issued: \");\n//     c = __connect_nonblock();\n//     redisSetCommandCallback(c,__test_callback,(void*)1);\n//     redisCommand(c,\"PING\");\n//     test_cond(__test_callback_flags == 1);\n//     redisFree(c);\n//\n//     test(\"Calls disconnect callback on redisDisconnect: \");\n//     c = __connect_nonblock();\n//     redisSetDisconnectCallback(c,__test_callback,(void*)2);\n//     redisDisconnect(c);\n//     test_cond(__test_callback_flags == 2);\n//     redisFree(c);\n//\n//     test(\"Calls disconnect callback and free callback on redisFree: \");\n//     c = __connect_nonblock();\n//     redisSetDisconnectCallback(c,__test_callback,(void*)2);\n//     redisSetFreeCallback(c,__test_callback,(void*)4);\n//     redisFree(c);\n//     test_cond(__test_callback_flags == ((2 << 8) | 4));\n//\n//     test(\"redisBufferWrite against empty write buffer: \");\n//     c = __connect_nonblock();\n//     test_cond(redisBufferWrite(c,&wdone) == REDIS_OK && wdone == 1);\n//     redisFree(c);\n//\n//     test(\"redisBufferWrite against not yet connected fd: \");\n//     c = __connect_nonblock();\n//     redisCommand(c,\"PING\");\n//     test_cond(redisBufferWrite(c,NULL) == REDIS_ERR &&\n//               strncmp(c->error,\"write:\",6) == 0);\n//     redisFree(c);\n//\n//     test(\"redisBufferWrite against closed fd: \");\n//     c = __connect_nonblock();\n//     redisCommand(c,\"PING\");\n//     redisDisconnect(c);\n//     test_cond(redisBufferWrite(c,NULL) == REDIS_ERR &&\n//               strncmp(c->error,\"write:\",6) == 0);\n//     redisFree(c);\n//\n//     test(\"Process callbacks in the right sequence: \");\n//     c = __connect_nonblock();\n//     redisCommandWithCallback(c,__test_reply_callback,(void*)1,\"PING\");\n//     redisCommandWithCallback(c,__test_reply_callback,(void*)2,\"PING\");\n//     redisCommandWithCallback(c,__test_reply_callback,(void*)3,\"PING\");\n//\n//     /* Write output buffer */\n//     wdone = 0;\n//     while(!wdone) {\n//         usleep(500);\n//         redisBufferWrite(c,&wdone);\n//     }\n//\n//     /* Read until at least one callback is executed (the 3 replies will\n//      * arrive in a single packet, causing all callbacks to be executed in\n//      * a single pass). */\n//     while(__test_callback_flags == 0) {\n//         assert(redisBufferRead(c) == REDIS_OK);\n//         redisProcessCallbacks(c);\n//     }\n//     test_cond(__test_callback_flags == 0x010203);\n//     redisFree(c);\n//\n//     test(\"redisDisconnect executes pending callbacks with NULL reply: \");\n//     c = __connect_nonblock();\n//     redisSetDisconnectCallback(c,__test_callback,(void*)1);\n//     redisCommandWithCallback(c,__test_reply_callback,(void*)2,\"PING\");\n//     redisDisconnect(c);\n//     test_cond(__test_callback_flags == 0x0201);\n//     redisFree(c);\n// }\n\nint main(int argc, char **argv) {\n    struct config cfg = {\n        .tcp = {\n            .host = \"127.0.0.1\",\n            .port = 6379\n        },\n        .unix = {\n            .path = \"/tmp/redis.sock\"\n        }\n    };\n    int throughput = 1;\n\n    /* Ignore broken pipe signal (for I/O error tests). */\n    signal(SIGPIPE, SIG_IGN);\n\n    /* Parse command line options. */\n    argv++; argc--;\n    while (argc) {\n        if (argc >= 2 && !strcmp(argv[0],\"-h\")) {\n            argv++; argc--;\n            cfg.tcp.host = argv[0];\n        } else if (argc >= 2 && !strcmp(argv[0],\"-p\")) {\n            argv++; argc--;\n            cfg.tcp.port = atoi(argv[0]);\n        } else if (argc >= 2 && !strcmp(argv[0],\"-s\")) {\n            argv++; argc--;\n            cfg.unix.path = argv[0];\n        } else if (argc >= 1 && !strcmp(argv[0],\"--skip-throughput\")) {\n            throughput = 0;\n        } else {\n            fprintf(stderr, \"Invalid argument: %s\\n\", argv[0]);\n            exit(1);\n        }\n        argv++; argc--;\n    }\n\n    test_format_commands();\n    test_reply_reader();\n    test_blocking_connection_errors();\n\n    printf(\"\\nTesting against TCP connection (%s:%d):\\n\", cfg.tcp.host, cfg.tcp.port);\n    cfg.type = CONN_TCP;\n    test_blocking_connection(cfg);\n    test_blocking_io_errors(cfg);\n    if (throughput) test_throughput(cfg);\n\n    printf(\"\\nTesting against Unix socket connection (%s):\\n\", cfg.unix.path);\n    cfg.type = CONN_UNIX;\n    test_blocking_connection(cfg);\n    test_blocking_io_errors(cfg);\n    if (throughput) test_throughput(cfg);\n\n    if (fails) {\n        printf(\"*** %d TESTS FAILED ***\\n\", fails);\n        return 1;\n    }\n\n    printf(\"ALL TESTS PASSED\\n\");\n    return 0;\n}\n"
  },
  {
    "path": "deps/hiredis/zmalloc.h",
    "content": "/* Drop in replacement for zmalloc.h in order to just use libc malloc without\n * any wrappering. */\n\n#ifndef ZMALLOC_H\n#define ZMALLOC_H\n\n#define zmalloc malloc\n#define zrealloc realloc\n#define zcalloc(x) calloc(x,1)\n#define zfree free\n#define zstrdup strdup\n\n#endif\n"
  },
  {
    "path": "deps/jemalloc/.gitignore",
    "content": "/autom4te.cache/\n/config.stamp\n/config.log\n/config.status\n/doc/html.xsl\n/doc/manpages.xsl\n/doc/jemalloc.xml\n/lib/\n/Makefile\n/include/jemalloc/internal/jemalloc_internal\\.h\n/include/jemalloc/internal/size_classes\\.h\n/include/jemalloc/jemalloc\\.h\n/include/jemalloc/jemalloc_defs\\.h\n/src/*.[od]\n/test/*.[od]\n/test/*.out\n/test/[a-zA-Z_]*\n!test/*.c\n!test/*.exp\n/bin/jemalloc.sh\n"
  },
  {
    "path": "deps/jemalloc/COPYING",
    "content": "Unless otherwise specified, files in the jemalloc source distribution are\nsubject to the following license:\n--------------------------------------------------------------------------------\nCopyright (C) 2002-2012 Jason Evans <jasone@canonware.com>.\nAll rights reserved.\nCopyright (C) 2007-2012 Mozilla Foundation.  All rights reserved.\nCopyright (C) 2009-2012 Facebook, Inc.  All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n1. Redistributions of source code must retain the above copyright notice(s),\n   this list of conditions and the following disclaimer.\n2. Redistributions in binary form must reproduce the above copyright notice(s),\n   this list of conditions and the following disclaimer in the documentation\n   and/or other materials provided with the distribution.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY EXPRESS\nOR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\nMERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO\nEVENT SHALL THE COPYRIGHT HOLDER(S) BE LIABLE FOR ANY DIRECT, INDIRECT,\nINCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\nPROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\nLIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE\nOR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\nADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n--------------------------------------------------------------------------------\n"
  },
  {
    "path": "deps/jemalloc/ChangeLog",
    "content": "Following are change highlights associated with official releases.  Important\nbug fixes are all mentioned, but internal enhancements are omitted here for\nbrevity (even though they are more fun to write about).  Much more detail can be\nfound in the git revision history:\n\n    http://www.canonware.com/cgi-bin/gitweb.cgi?p=jemalloc.git\n    git://canonware.com/jemalloc.git\n\n* 3.2.0 (November 9, 2012)\n\n  In addition to a couple of bug fixes, this version modifies page run\n  allocation and dirty page purging algorithms in order to better control\n  page-level virtual memory fragmentation.\n\n  Incompatible changes:\n  - Change the \"opt.lg_dirty_mult\" default from 5 to 3 (32:1 to 8:1).\n\n  Bug fixes:\n  - Fix dss/mmap allocation precedence code to use recyclable mmap memory only\n    after primary dss allocation fails.\n  - Fix deadlock in the \"arenas.purge\" mallctl.  This regression was introduced\n    in 3.1.0 by the addition of the \"arena.<i>.purge\" mallctl.\n\n* 3.1.0 (October 16, 2012)\n\n  New features:\n  - Auto-detect whether running inside Valgrind, thus removing the need to\n    manually specify MALLOC_CONF=valgrind:true.\n  - Add the \"arenas.extend\" mallctl, which allows applications to create\n    manually managed arenas.\n  - Add the ALLOCM_ARENA() flag for {,r,d}allocm().\n  - Add the \"opt.dss\", \"arena.<i>.dss\", and \"stats.arenas.<i>.dss\" mallctls,\n    which provide control over dss/mmap precedence.\n  - Add the \"arena.<i>.purge\" mallctl, which obsoletes \"arenas.purge\".\n  - Define LG_QUANTUM for hppa.\n\n  Incompatible changes:\n  - Disable tcache by default if running inside Valgrind, in order to avoid\n    making unallocated objects appear reachable to Valgrind.\n  - Drop const from malloc_usable_size() argument on Linux.\n\n  Bug fixes:\n  - Fix heap profiling crash if sampled object is freed via realloc(p, 0).\n  - Remove const from __*_hook variable declarations, so that glibc can modify\n    them during process forking.\n  - Fix mlockall(2)/madvise(2) interaction.\n  - Fix fork(2)-related deadlocks.\n  - Fix error return value for \"thread.tcache.enabled\" mallctl.\n\n* 3.0.0 (May 11, 2012)\n\n  Although this version adds some major new features, the primary focus is on\n  internal code cleanup that facilitates maintainability and portability, most\n  of which is not reflected in the ChangeLog.  This is the first release to\n  incorporate substantial contributions from numerous other developers, and the\n  result is a more broadly useful allocator (see the git revision history for\n  contribution details).  Note that the license has been unified, thanks to\n  Facebook granting a license under the same terms as the other copyright\n  holders (see COPYING).\n\n  New features:\n  - Implement Valgrind support, redzones, and quarantine.\n  - Add support for additional platforms:\n    + FreeBSD\n    + Mac OS X Lion\n    + MinGW\n    + Windows (no support yet for replacing the system malloc)\n  - Add support for additional architectures:\n    + MIPS\n    + SH4\n    + Tilera\n  - Add support for cross compiling.\n  - Add nallocm(), which rounds a request size up to the nearest size class\n    without actually allocating.\n  - Implement aligned_alloc() (blame C11).\n  - Add the \"thread.tcache.enabled\" mallctl.\n  - Add the \"opt.prof_final\" mallctl.\n  - Update pprof (from gperftools 2.0).\n  - Add the --with-mangling option.\n  - Add the --disable-experimental option.\n  - Add the --disable-munmap option, and make it the default on Linux.\n  - Add the --enable-mremap option, which disables use of mremap(2) by default.\n\n  Incompatible changes:\n  - Enable stats by default.\n  - Enable fill by default.\n  - Disable lazy locking by default.\n  - Rename the \"tcache.flush\" mallctl to \"thread.tcache.flush\".\n  - Rename the \"arenas.pagesize\" mallctl to \"arenas.page\".\n  - Change the \"opt.lg_prof_sample\" default from 0 to 19 (1 B to 512 KiB).\n  - Change the \"opt.prof_accum\" default from true to false.\n\n  Removed features:\n  - Remove the swap feature, including the \"config.swap\", \"swap.avail\",\n    \"swap.prezeroed\", \"swap.nfds\", and \"swap.fds\" mallctls.\n  - Remove highruns statistics, including the\n    \"stats.arenas.<i>.bins.<j>.highruns\" and\n    \"stats.arenas.<i>.lruns.<j>.highruns\" mallctls.\n  - As part of small size class refactoring, remove the \"opt.lg_[qc]space_max\",\n    \"arenas.cacheline\", \"arenas.subpage\", \"arenas.[tqcs]space_{min,max}\", and\n    \"arenas.[tqcs]bins\" mallctls.\n  - Remove the \"arenas.chunksize\" mallctl.\n  - Remove the \"opt.lg_prof_tcmax\" option.\n  - Remove the \"opt.lg_prof_bt_max\" option.\n  - Remove the \"opt.lg_tcache_gc_sweep\" option.\n  - Remove the --disable-tiny option, including the \"config.tiny\" mallctl.\n  - Remove the --enable-dynamic-page-shift configure option.\n  - Remove the --enable-sysv configure option.\n\n  Bug fixes:\n  - Fix a statistics-related bug in the \"thread.arena\" mallctl that could cause\n    invalid statistics and crashes.\n  - Work around TLS deallocation via free() on Linux.  This bug could cause\n    write-after-free memory corruption.\n  - Fix a potential deadlock that could occur during interval- and\n    growth-triggered heap profile dumps.\n  - Fix large calloc() zeroing bugs due to dropping chunk map unzeroed flags.\n  - Fix chunk_alloc_dss() to stop claiming memory is zeroed.  This bug could\n    cause memory corruption and crashes with --enable-dss specified.\n  - Fix fork-related bugs that could cause deadlock in children between fork\n    and exec.\n  - Fix malloc_stats_print() to honor 'b' and 'l' in the opts parameter.\n  - Fix realloc(p, 0) to act like free(p).\n  - Do not enforce minimum alignment in memalign().\n  - Check for NULL pointer in malloc_usable_size().\n  - Fix an off-by-one heap profile statistics bug that could be observed in\n    interval- and growth-triggered heap profiles.\n  - Fix the \"epoch\" mallctl to update cached stats even if the passed in epoch\n    is 0.\n  - Fix bin->runcur management to fix a layout policy bug.  This bug did not\n    affect correctness.\n  - Fix a bug in choose_arena_hard() that potentially caused more arenas to be\n    initialized than necessary.\n  - Add missing \"opt.lg_tcache_max\" mallctl implementation.\n  - Use glibc allocator hooks to make mixed allocator usage less likely.\n  - Fix build issues for --disable-tcache.\n  - Don't mangle pthread_create() when --with-private-namespace is specified.\n\n* 2.2.5 (November 14, 2011)\n\n  Bug fixes:\n  - Fix huge_ralloc() race when using mremap(2).  This is a serious bug that\n    could cause memory corruption and/or crashes.\n  - Fix huge_ralloc() to maintain chunk statistics.\n  - Fix malloc_stats_print(..., \"a\") output.\n\n* 2.2.4 (November 5, 2011)\n\n  Bug fixes:\n  - Initialize arenas_tsd before using it.  This bug existed for 2.2.[0-3], as\n    well as for --disable-tls builds in earlier releases.\n  - Do not assume a 4 KiB page size in test/rallocm.c.\n\n* 2.2.3 (August 31, 2011)\n\n  This version fixes numerous bugs related to heap profiling.\n\n  Bug fixes:\n  - Fix a prof-related race condition.  This bug could cause memory corruption,\n    but only occurred in non-default configurations (prof_accum:false).\n  - Fix off-by-one backtracing issues (make sure that prof_alloc_prep() is\n    excluded from backtraces).\n  - Fix a prof-related bug in realloc() (only triggered by OOM errors).\n  - Fix prof-related bugs in allocm() and rallocm().\n  - Fix prof_tdata_cleanup() for --disable-tls builds.\n  - Fix a relative include path, to fix objdir builds.\n\n* 2.2.2 (July 30, 2011)\n\n  Bug fixes:\n  - Fix a build error for --disable-tcache.\n  - Fix assertions in arena_purge() (for real this time).\n  - Add the --with-private-namespace option.  This is a workaround for symbol\n    conflicts that can inadvertently arise when using static libraries.\n\n* 2.2.1 (March 30, 2011)\n\n  Bug fixes:\n  - Implement atomic operations for x86/x64.  This fixes compilation failures\n    for versions of gcc that are still in wide use.\n  - Fix an assertion in arena_purge().\n\n* 2.2.0 (March 22, 2011)\n\n  This version incorporates several improvements to algorithms and data\n  structures that tend to reduce fragmentation and increase speed.\n\n  New features:\n  - Add the \"stats.cactive\" mallctl.\n  - Update pprof (from google-perftools 1.7).\n  - Improve backtracing-related configuration logic, and add the\n    --disable-prof-libgcc option.\n\n  Bug fixes:\n  - Change default symbol visibility from \"internal\", to \"hidden\", which\n    decreases the overhead of library-internal function calls.\n  - Fix symbol visibility so that it is also set on OS X.\n  - Fix a build dependency regression caused by the introduction of the .pic.o\n    suffix for PIC object files.\n  - Add missing checks for mutex initialization failures.\n  - Don't use libgcc-based backtracing except on x64, where it is known to work.\n  - Fix deadlocks on OS X that were due to memory allocation in\n    pthread_mutex_lock().\n  - Heap profiling-specific fixes:\n    + Fix memory corruption due to integer overflow in small region index\n      computation, when using a small enough sample interval that profiling\n      context pointers are stored in small run headers.\n    + Fix a bootstrap ordering bug that only occurred with TLS disabled.\n    + Fix a rallocm() rsize bug.\n    + Fix error detection bugs for aligned memory allocation.\n\n* 2.1.3 (March 14, 2011)\n\n  Bug fixes:\n  - Fix a cpp logic regression (due to the \"thread.{de,}allocatedp\" mallctl fix\n    for OS X in 2.1.2).\n  - Fix a \"thread.arena\" mallctl bug.\n  - Fix a thread cache stats merging bug.\n\n* 2.1.2 (March 2, 2011)\n\n  Bug fixes:\n  - Fix \"thread.{de,}allocatedp\" mallctl for OS X.\n  - Add missing jemalloc.a to build system.\n\n* 2.1.1 (January 31, 2011)\n\n  Bug fixes:\n  - Fix aligned huge reallocation (affected allocm()).\n  - Fix the ALLOCM_LG_ALIGN macro definition.\n  - Fix a heap dumping deadlock.\n  - Fix a \"thread.arena\" mallctl bug.\n\n* 2.1.0 (December 3, 2010)\n\n  This version incorporates some optimizations that can't quite be considered\n  bug fixes.\n\n  New features:\n  - Use Linux's mremap(2) for huge object reallocation when possible.\n  - Avoid locking in mallctl*() when possible.\n  - Add the \"thread.[de]allocatedp\" mallctl's.\n  - Convert the manual page source from roff to DocBook, and generate both roff\n    and HTML manuals.\n\n  Bug fixes:\n  - Fix a crash due to incorrect bootstrap ordering.  This only impacted\n    --enable-debug --enable-dss configurations.\n  - Fix a minor statistics bug for mallctl(\"swap.avail\", ...).\n\n* 2.0.1 (October 29, 2010)\n\n  Bug fixes:\n  - Fix a race condition in heap profiling that could cause undefined behavior\n    if \"opt.prof_accum\" were disabled.\n  - Add missing mutex unlocks for some OOM error paths in the heap profiling\n    code.\n  - Fix a compilation error for non-C99 builds.\n\n* 2.0.0 (October 24, 2010)\n\n  This version focuses on the experimental *allocm() API, and on improved\n  run-time configuration/introspection.  Nonetheless, numerous performance\n  improvements are also included.\n\n  New features:\n  - Implement the experimental {,r,s,d}allocm() API, which provides a superset\n    of the functionality available via malloc(), calloc(), posix_memalign(),\n    realloc(), malloc_usable_size(), and free().  These functions can be used to\n    allocate/reallocate aligned zeroed memory, ask for optional extra memory\n    during reallocation, prevent object movement during reallocation, etc.\n  - Replace JEMALLOC_OPTIONS/JEMALLOC_PROF_PREFIX with MALLOC_CONF, which is\n    more human-readable, and more flexible.  For example:\n      JEMALLOC_OPTIONS=AJP\n    is now:\n      MALLOC_CONF=abort:true,fill:true,stats_print:true\n  - Port to Apple OS X.  Sponsored by Mozilla.\n  - Make it possible for the application to control thread-->arena mappings via\n    the \"thread.arena\" mallctl.\n  - Add compile-time support for all TLS-related functionality via pthreads TSD.\n    This is mainly of interest for OS X, which does not support TLS, but has a\n    TSD implementation with similar performance.\n  - Override memalign() and valloc() if they are provided by the system.\n  - Add the \"arenas.purge\" mallctl, which can be used to synchronously purge all\n    dirty unused pages.\n  - Make cumulative heap profiling data optional, so that it is possible to\n    limit the amount of memory consumed by heap profiling data structures.\n  - Add per thread allocation counters that can be accessed via the\n    \"thread.allocated\" and \"thread.deallocated\" mallctls.\n\n  Incompatible changes:\n  - Remove JEMALLOC_OPTIONS and malloc_options (see MALLOC_CONF above).\n  - Increase default backtrace depth from 4 to 128 for heap profiling.\n  - Disable interval-based profile dumps by default.\n\n  Bug fixes:\n  - Remove bad assertions in fork handler functions.  These assertions could\n    cause aborts for some combinations of configure settings.\n  - Fix strerror_r() usage to deal with non-standard semantics in GNU libc.\n  - Fix leak context reporting.  This bug tended to cause the number of contexts\n    to be underreported (though the reported number of objects and bytes were\n    correct).\n  - Fix a realloc() bug for large in-place growing reallocation.  This bug could\n    cause memory corruption, but it was hard to trigger.\n  - Fix an allocation bug for small allocations that could be triggered if\n    multiple threads raced to create a new run of backing pages.\n  - Enhance the heap profiler to trigger samples based on usable size, rather\n    than request size.\n  - Fix a heap profiling bug due to sometimes losing track of requested object\n    size for sampled objects.\n\n* 1.0.3 (August 12, 2010)\n\n  Bug fixes:\n  - Fix the libunwind-based implementation of stack backtracing (used for heap\n    profiling).  This bug could cause zero-length backtraces to be reported.\n  - Add a missing mutex unlock in library initialization code.  If multiple\n    threads raced to initialize malloc, some of them could end up permanently\n    blocked.\n\n* 1.0.2 (May 11, 2010)\n\n  Bug fixes:\n  - Fix junk filling of large objects, which could cause memory corruption.\n  - Add MAP_NORESERVE support for chunk mapping, because otherwise virtual\n    memory limits could cause swap file configuration to fail.  Contributed by\n    Jordan DeLong.\n\n* 1.0.1 (April 14, 2010)\n\n  Bug fixes:\n  - Fix compilation when --enable-fill is specified.\n  - Fix threads-related profiling bugs that affected accuracy and caused memory\n    to be leaked during thread exit.\n  - Fix dirty page purging race conditions that could cause crashes.\n  - Fix crash in tcache flushing code during thread destruction.\n\n* 1.0.0 (April 11, 2010)\n\n  This release focuses on speed and run-time introspection.  Numerous\n  algorithmic improvements make this release substantially faster than its\n  predecessors.\n\n  New features:\n  - Implement autoconf-based configuration system.\n  - Add mallctl*(), for the purposes of introspection and run-time\n    configuration.\n  - Make it possible for the application to manually flush a thread's cache, via\n    the \"tcache.flush\" mallctl.\n  - Base maximum dirty page count on proportion of active memory.\n  - Compute various addtional run-time statistics, including per size class\n    statistics for large objects.\n  - Expose malloc_stats_print(), which can be called repeatedly by the\n    application.\n  - Simplify the malloc_message() signature to only take one string argument,\n    and incorporate an opaque data pointer argument for use by the application\n    in combination with malloc_stats_print().\n  - Add support for allocation backed by one or more swap files, and allow the\n    application to disable over-commit if swap files are in use.\n  - Implement allocation profiling and leak checking.\n\n  Removed features:\n  - Remove the dynamic arena rebalancing code, since thread-specific caching\n    reduces its utility.\n\n  Bug fixes:\n  - Modify chunk allocation to work when address space layout randomization\n    (ASLR) is in use.\n  - Fix thread cleanup bugs related to TLS destruction.\n  - Handle 0-size allocation requests in posix_memalign().\n  - Fix a chunk leak.  The leaked chunks were never touched, so this impacted\n    virtual memory usage, but not physical memory usage.\n\n* linux_2008082[78]a (August 27/28, 2008)\n\n  These snapshot releases are the simple result of incorporating Linux-specific\n  support into the FreeBSD malloc sources.\n\n--------------------------------------------------------------------------------\nvim:filetype=text:textwidth=80\n"
  },
  {
    "path": "deps/jemalloc/INSTALL",
    "content": "Building and installing jemalloc can be as simple as typing the following while\nin the root directory of the source tree:\n\n    ./configure\n    make\n    make install\n\n=== Advanced configuration =====================================================\n\nThe 'configure' script supports numerous options that allow control of which\nfunctionality is enabled, where jemalloc is installed, etc.  Optionally, pass\nany of the following arguments (not a definitive list) to 'configure':\n\n--help\n    Print a definitive list of options.\n\n--prefix=<install-root-dir>\n    Set the base directory in which to install.  For example:\n\n        ./configure --prefix=/usr/local\n\n    will cause files to be installed into /usr/local/include, /usr/local/lib,\n    and /usr/local/man.\n\n--with-rpath=<colon-separated-rpath>\n    Embed one or more library paths, so that libjemalloc can find the libraries\n    it is linked to.  This works only on ELF-based systems.\n\n--with-mangling=<map>\n    Mangle public symbols specified in <map> which is a comma-separated list of\n    name:mangled pairs.\n\n    For example, to use ld's --wrap option as an alternative method for\n    overriding libc's malloc implementation, specify something like:\n\n      --with-mangling=malloc:__wrap_malloc,free:__wrap_free[...]\n\n    Note that mangling happens prior to application of the prefix specified by\n    --with-jemalloc-prefix, and mangled symbols are then ignored when applying\n    the prefix.\n\n--with-jemalloc-prefix=<prefix>\n    Prefix all public APIs with <prefix>.  For example, if <prefix> is\n    \"prefix_\", API changes like the following occur:\n\n      malloc()         --> prefix_malloc()\n      malloc_conf      --> prefix_malloc_conf\n      /etc/malloc.conf --> /etc/prefix_malloc.conf\n      MALLOC_CONF      --> PREFIX_MALLOC_CONF\n\n    This makes it possible to use jemalloc at the same time as the system\n    allocator, or even to use multiple copies of jemalloc simultaneously.\n\n    By default, the prefix is \"\", except on OS X, where it is \"je_\".  On OS X,\n    jemalloc overlays the default malloc zone, but makes no attempt to actually\n    replace the \"malloc\", \"calloc\", etc. symbols.\n\n--with-private-namespace=<prefix>\n    Prefix all library-private APIs with <prefix>.  For shared libraries,\n    symbol visibility mechanisms prevent these symbols from being exported, but\n    for static libraries, naming collisions are a real possibility.  By\n    default, the prefix is \"\" (empty string).\n\n--with-install-suffix=<suffix>\n    Append <suffix> to the base name of all installed files, such that multiple\n    versions of jemalloc can coexist in the same installation directory.  For\n    example, libjemalloc.so.0 becomes libjemalloc<suffix>.so.0.\n\n--enable-cc-silence\n    Enable code that silences non-useful compiler warnings.  This is helpful\n    when trying to tell serious warnings from those due to compiler\n    limitations, but it potentially incurs a performance penalty.\n\n--enable-debug\n    Enable assertions and validation code.  This incurs a substantial\n    performance hit, but is very useful during application development.\n\n--disable-stats\n    Disable statistics gathering functionality.  See the \"opt.stats_print\"\n    option documentation for usage details.\n\n--enable-prof\n    Enable heap profiling and leak detection functionality.  See the \"opt.prof\"\n    option documentation for usage details.  When enabled, there are several\n    approaches to backtracing, and the configure script chooses the first one\n    in the following list that appears to function correctly:\n\n    + libunwind      (requires --enable-prof-libunwind)\n    + libgcc         (unless --disable-prof-libgcc)\n    + gcc intrinsics (unless --disable-prof-gcc)\n\n--enable-prof-libunwind\n    Use the libunwind library (http://www.nongnu.org/libunwind/) for stack\n    backtracing.\n\n--disable-prof-libgcc\n    Disable the use of libgcc's backtracing functionality.\n\n--disable-prof-gcc\n    Disable the use of gcc intrinsics for backtracing.\n\n--with-static-libunwind=<libunwind.a>\n    Statically link against the specified libunwind.a rather than dynamically\n    linking with -lunwind.\n\n--disable-tcache\n    Disable thread-specific caches for small objects.  Objects are cached and\n    released in bulk, thus reducing the total number of mutex operations.  See\n    the \"opt.tcache\" option for usage details.\n\n--enable-mremap\n    Enable huge realloc() via mremap(2).  mremap() is disabled by default\n    because the flavor used is specific to Linux, which has a quirk in its\n    virtual memory allocation algorithm that causes semi-permanent VM map holes\n    under normal jemalloc operation.\n\n--disable-munmap\n    Disable virtual memory deallocation via munmap(2); instead keep track of\n    the virtual memory for later use.  munmap() is disabled by default (i.e.\n    --disable-munmap is implied) on Linux, which has a quirk in its virtual\n    memory allocation algorithm that causes semi-permanent VM map holes under\n    normal jemalloc operation.\n\n--enable-dss\n    Enable support for page allocation/deallocation via sbrk(2), in addition to\n    mmap(2).\n\n--disable-fill\n    Disable support for junk/zero filling of memory, quarantine, and redzones.\n    See the \"opt.junk\", \"opt.zero\", \"opt.quarantine\", and \"opt.redzone\" option\n    documentation for usage details.\n\n--disable-valgrind\n    Disable support for Valgrind.\n\n--disable-experimental\n    Disable support for the experimental API (*allocm()).\n\n--enable-utrace\n    Enable utrace(2)-based allocation tracing.  This feature is not broadly\n    portable (FreeBSD has it, but Linux and OS X do not).\n\n--enable-xmalloc\n    Enable support for optional immediate termination due to out-of-memory\n    errors, as is commonly implemented by \"xmalloc\" wrapper function for malloc.\n    See the \"opt.xmalloc\" option documentation for usage details.\n\n--enable-lazy-lock\n    Enable code that wraps pthread_create() to detect when an application\n    switches from single-threaded to multi-threaded mode, so that it can avoid\n    mutex locking/unlocking operations while in single-threaded mode.  In\n    practice, this feature usually has little impact on performance unless\n    thread-specific caching is disabled.\n\n--disable-tls\n    Disable thread-local storage (TLS), which allows for fast access to\n    thread-local variables via the __thread keyword.  If TLS is available,\n    jemalloc uses it for several purposes.\n\n--with-xslroot=<path>\n    Specify where to find DocBook XSL stylesheets when building the\n    documentation.\n\nThe following environment variables (not a definitive list) impact configure's\nbehavior:\n\nCFLAGS=\"?\"\n    Pass these flags to the compiler.  You probably shouldn't define this unless\n    you know what you are doing.  (Use EXTRA_CFLAGS instead.)\n\nEXTRA_CFLAGS=\"?\"\n    Append these flags to CFLAGS.  This makes it possible to add flags such as\n    -Werror, while allowing the configure script to determine what other flags\n    are appropriate for the specified configuration.\n\n    The configure script specifically checks whether an optimization flag (-O*)\n    is specified in EXTRA_CFLAGS, and refrains from specifying an optimization\n    level if it finds that one has already been specified.\n\nCPPFLAGS=\"?\"\n    Pass these flags to the C preprocessor.  Note that CFLAGS is not passed to\n    'cpp' when 'configure' is looking for include files, so you must use\n    CPPFLAGS instead if you need to help 'configure' find header files.\n\nLD_LIBRARY_PATH=\"?\"\n    'ld' uses this colon-separated list to find libraries.\n\nLDFLAGS=\"?\"\n    Pass these flags when linking.\n\nPATH=\"?\"\n    'configure' uses this to find programs.\n\n=== Advanced compilation =======================================================\n\nTo build only parts of jemalloc, use the following targets:\n\n    build_lib_shared\n    build_lib_static\n    build_lib\n    build_doc_html\n    build_doc_man\n    build_doc\n\nTo install only parts of jemalloc, use the following targets:\n\n    install_bin\n    install_include\n    install_lib_shared\n    install_lib_static\n    install_lib\n    install_doc_html\n    install_doc_man\n    install_doc\n\nTo clean up build results to varying degrees, use the following make targets:\n\n    clean\n    distclean\n    relclean\n\n=== Advanced installation ======================================================\n\nOptionally, define make variables when invoking make, including (not\nexclusively):\n\nINCLUDEDIR=\"?\"\n    Use this as the installation prefix for header files.\n\nLIBDIR=\"?\"\n    Use this as the installation prefix for libraries.\n\nMANDIR=\"?\"\n    Use this as the installation prefix for man pages.\n\nDESTDIR=\"?\"\n    Prepend DESTDIR to INCLUDEDIR, LIBDIR, DATADIR, and MANDIR.  This is useful\n    when installing to a different path than was specified via --prefix.\n\nCC=\"?\"\n    Use this to invoke the C compiler.\n\nCFLAGS=\"?\"\n    Pass these flags to the compiler.\n\nCPPFLAGS=\"?\"\n    Pass these flags to the C preprocessor.\n\nLDFLAGS=\"?\"\n    Pass these flags when linking.\n\nPATH=\"?\"\n    Use this to search for programs used during configuration and building.\n\n=== Development ================================================================\n\nIf you intend to make non-trivial changes to jemalloc, use the 'autogen.sh'\nscript rather than 'configure'.  This re-generates 'configure', enables\nconfiguration dependency rules, and enables re-generation of automatically\ngenerated source files.\n\nThe build system supports using an object directory separate from the source\ntree.  For example, you can create an 'obj' directory, and from within that\ndirectory, issue configuration and build commands:\n\n    autoconf\n    mkdir obj\n    cd obj\n    ../configure --enable-autogen\n    make\n\n=== Documentation ==============================================================\n\nThe manual page is generated in both html and roff formats.  Any web browser\ncan be used to view the html manual.  The roff manual page can be formatted\nprior to installation via the following command:\n\n    nroff -man -t doc/jemalloc.3\n"
  },
  {
    "path": "deps/jemalloc/Makefile.in",
    "content": "# Clear out all vpaths, then set just one (default vpath) for the main build\n# directory.\nvpath\nvpath % .\n\n# Clear the default suffixes, so that built-in rules are not used.\n.SUFFIXES :\n\nSHELL := /bin/sh\n\nCC := @CC@\n\n# Configuration parameters.\nDESTDIR =\nBINDIR := $(DESTDIR)@BINDIR@\nINCLUDEDIR := $(DESTDIR)@INCLUDEDIR@\nLIBDIR := $(DESTDIR)@LIBDIR@\nDATADIR := $(DESTDIR)@DATADIR@\nMANDIR := $(DESTDIR)@MANDIR@\nsrcroot := @srcroot@\nobjroot := @objroot@\nabs_srcroot := @abs_srcroot@\nabs_objroot := @abs_objroot@\n\n# Build parameters.\nCPPFLAGS := @CPPFLAGS@ -I$(srcroot)include -I$(objroot)include\nCFLAGS := @CFLAGS@\nLDFLAGS := @LDFLAGS@\nEXTRA_LDFLAGS := @EXTRA_LDFLAGS@\nLIBS := @LIBS@\nRPATH_EXTRA := @RPATH_EXTRA@\nSO := @so@\nIMPORTLIB := @importlib@\nO := @o@\nA := @a@\nEXE := @exe@\nLIBPREFIX := @libprefix@\nREV := @rev@\ninstall_suffix := @install_suffix@\nABI := @abi@\nXSLTPROC := @XSLTPROC@\nAUTOCONF := @AUTOCONF@\n_RPATH = @RPATH@\nRPATH = $(if $(1),$(call _RPATH,$(1)))\ncfghdrs_in := @cfghdrs_in@\ncfghdrs_out := @cfghdrs_out@\ncfgoutputs_in := @cfgoutputs_in@\ncfgoutputs_out := @cfgoutputs_out@\nenable_autogen := @enable_autogen@\nenable_experimental := @enable_experimental@\nDSO_LDFLAGS = @DSO_LDFLAGS@\nSOREV = @SOREV@\nPIC_CFLAGS = @PIC_CFLAGS@\nCTARGET = @CTARGET@\nLDTARGET = @LDTARGET@\nMKLIB = @MKLIB@\nCC_MM = @CC_MM@\n\nifeq (macho, $(ABI))\nTEST_LIBRARY_PATH := DYLD_FALLBACK_LIBRARY_PATH=\"$(objroot)lib\"\nelse\nifeq (pecoff, $(ABI))\nTEST_LIBRARY_PATH := PATH=\"$(PATH):$(objroot)lib\"\nelse\nTEST_LIBRARY_PATH :=\nendif\nendif\n\nLIBJEMALLOC := $(LIBPREFIX)jemalloc$(install_suffix)\n\n# Lists of files.\nBINS := $(srcroot)bin/pprof $(objroot)bin/jemalloc.sh\nCHDRS := $(objroot)include/jemalloc/jemalloc$(install_suffix).h \\\n\t$(objroot)include/jemalloc/jemalloc_defs$(install_suffix).h\nCSRCS := $(srcroot)src/jemalloc.c $(srcroot)src/arena.c $(srcroot)src/atomic.c \\\n\t$(srcroot)src/base.c $(srcroot)src/bitmap.c $(srcroot)src/chunk.c \\\n\t$(srcroot)src/chunk_dss.c $(srcroot)src/chunk_mmap.c \\\n\t$(srcroot)src/ckh.c $(srcroot)src/ctl.c $(srcroot)src/extent.c \\\n\t$(srcroot)src/hash.c $(srcroot)src/huge.c $(srcroot)src/mb.c \\\n\t$(srcroot)src/mutex.c $(srcroot)src/prof.c $(srcroot)src/quarantine.c \\\n\t$(srcroot)src/rtree.c $(srcroot)src/stats.c $(srcroot)src/tcache.c \\\n\t$(srcroot)src/util.c $(srcroot)src/tsd.c\nifeq (macho, $(ABI))\nCSRCS += $(srcroot)src/zone.c\nendif\nifeq ($(IMPORTLIB),$(SO))\nSTATIC_LIBS := $(objroot)lib/$(LIBJEMALLOC).$(A)\nendif\nifdef PIC_CFLAGS\nSTATIC_LIBS += $(objroot)lib/$(LIBJEMALLOC)_pic.$(A)\nelse\nSTATIC_LIBS += $(objroot)lib/$(LIBJEMALLOC)_s.$(A)\nendif\nDSOS := $(objroot)lib/$(LIBJEMALLOC).$(SOREV)\nifneq ($(SOREV),$(SO))\nDSOS += $(objroot)lib/$(LIBJEMALLOC).$(SO)\nendif\nMAN3 := $(objroot)doc/jemalloc$(install_suffix).3\nDOCS_XML := $(objroot)doc/jemalloc$(install_suffix).xml\nDOCS_HTML := $(DOCS_XML:$(objroot)%.xml=$(srcroot)%.html)\nDOCS_MAN3 := $(DOCS_XML:$(objroot)%.xml=$(srcroot)%.3)\nDOCS := $(DOCS_HTML) $(DOCS_MAN3)\nCTESTS := $(srcroot)test/aligned_alloc.c $(srcroot)test/allocated.c \\\n\t$(srcroot)test/ALLOCM_ARENA.c $(srcroot)test/bitmap.c \\\n\t$(srcroot)test/mremap.c $(srcroot)test/posix_memalign.c \\\n\t$(srcroot)test/thread_arena.c $(srcroot)test/thread_tcache_enabled.c\nifeq ($(enable_experimental), 1)\nCTESTS += $(srcroot)test/allocm.c $(srcroot)test/rallocm.c\nendif\n\nCOBJS := $(CSRCS:$(srcroot)%.c=$(objroot)%.$(O))\nCPICOBJS := $(CSRCS:$(srcroot)%.c=$(objroot)%.pic.$(O))\nCTESTOBJS := $(CTESTS:$(srcroot)%.c=$(objroot)%.$(O))\n\n.PHONY: all dist doc_html doc_man doc\n.PHONY: install_bin install_include install_lib\n.PHONY: install_html install_man install_doc install\n.PHONY: tests check clean distclean relclean\n\n.SECONDARY : $(CTESTOBJS)\n\n# Default target.\nall: build\n\ndist: build_doc\n\n$(srcroot)doc/%.html : $(objroot)doc/%.xml $(srcroot)doc/stylesheet.xsl $(objroot)doc/html.xsl\n\t$(XSLTPROC) -o $@ $(objroot)doc/html.xsl $<\n\n$(srcroot)doc/%.3 : $(objroot)doc/%.xml $(srcroot)doc/stylesheet.xsl $(objroot)doc/manpages.xsl\n\t$(XSLTPROC) -o $@ $(objroot)doc/manpages.xsl $<\n\nbuild_doc_html: $(DOCS_HTML)\nbuild_doc_man: $(DOCS_MAN3)\nbuild_doc: $(DOCS)\n\n#\n# Include generated dependency files.\n#\nifdef CC_MM\n-include $(COBJS:%.$(O)=%.d)\n-include $(CPICOBJS:%.$(O)=%.d)\n-include $(CTESTOBJS:%.$(O)=%.d)\nendif\n\n$(COBJS): $(objroot)src/%.$(O): $(srcroot)src/%.c\n$(CPICOBJS): $(objroot)src/%.pic.$(O): $(srcroot)src/%.c\n$(CPICOBJS): CFLAGS += $(PIC_CFLAGS)\n$(CTESTOBJS): $(objroot)test/%.$(O): $(srcroot)test/%.c\n$(CTESTOBJS): CPPFLAGS += -I$(objroot)test\nifneq ($(IMPORTLIB),$(SO))\n$(COBJS): CPPFLAGS += -DDLLEXPORT\nendif\n\nifndef CC_MM\n# Dependencies\nHEADER_DIRS = $(srcroot)include/jemalloc/internal \\\n\t$(objroot)include/jemalloc $(objroot)include/jemalloc/internal\nHEADERS = $(wildcard $(foreach dir,$(HEADER_DIRS),$(dir)/*.h))\n$(COBJS) $(CPICOBJS) $(CTESTOBJS): $(HEADERS)\n$(CTESTOBJS): $(objroot)test/jemalloc_test.h\nendif\n\n$(COBJS) $(CPICOBJS) $(CTESTOBJS): %.$(O):\n\t@mkdir -p $(@D)\n\t$(CC) $(CFLAGS) -c $(CPPFLAGS) $(CTARGET) $<\nifdef CC_MM\n\t@$(CC) -MM $(CPPFLAGS) -MT $@ -o $(@:%.$(O)=%.d) $<\nendif\n\nifneq ($(SOREV),$(SO))\n%.$(SO) : %.$(SOREV)\n\t@mkdir -p $(@D)\n\tln -sf $(<F) $@\nendif\n\n$(objroot)lib/$(LIBJEMALLOC).$(SOREV) : $(if $(PIC_CFLAGS),$(CPICOBJS),$(COBJS))\n\t@mkdir -p $(@D)\n\t$(CC) $(DSO_LDFLAGS) $(call RPATH,$(RPATH_EXTRA)) $(LDTARGET) $+ $(LDFLAGS) $(LIBS) $(EXTRA_LDFLAGS)\n\n$(objroot)lib/$(LIBJEMALLOC)_pic.$(A) : $(CPICOBJS)\n$(objroot)lib/$(LIBJEMALLOC).$(A) : $(COBJS)\n$(objroot)lib/$(LIBJEMALLOC)_s.$(A) : $(COBJS)\n\n$(STATIC_LIBS):\n\t@mkdir -p $(@D)\n\t$(MKLIB) $+\n\n$(objroot)test/bitmap$(EXE): $(objroot)src/bitmap.$(O)\n\n$(objroot)test/%$(EXE): $(objroot)test/%.$(O) $(objroot)src/util.$(O) $(DSOS)\n\t@mkdir -p $(@D)\n\t$(CC) $(LDTARGET) $(filter %.$(O),$^) $(call RPATH,$(objroot)lib) $(objroot)lib/$(LIBJEMALLOC).$(IMPORTLIB) $(filter -lpthread,$(LIBS)) $(EXTRA_LDFLAGS)\n\nbuild_lib_shared: $(DSOS)\nbuild_lib_static: $(STATIC_LIBS)\nbuild: build_lib_shared build_lib_static\n\ninstall_bin:\n\tinstall -d $(BINDIR)\n\t@for b in $(BINS); do \\\n\techo \"install -m 755 $$b $(BINDIR)\"; \\\n\tinstall -m 755 $$b $(BINDIR); \\\ndone\n\ninstall_include:\n\tinstall -d $(INCLUDEDIR)/jemalloc\n\t@for h in $(CHDRS); do \\\n\techo \"install -m 644 $$h $(INCLUDEDIR)/jemalloc\"; \\\n\tinstall -m 644 $$h $(INCLUDEDIR)/jemalloc; \\\ndone\n\ninstall_lib_shared: $(DSOS)\n\tinstall -d $(LIBDIR)\n\tinstall -m 755 $(objroot)lib/$(LIBJEMALLOC).$(SOREV) $(LIBDIR)\nifneq ($(SOREV),$(SO))\n\tln -sf $(LIBJEMALLOC).$(SOREV) $(LIBDIR)/$(LIBJEMALLOC).$(SO)\nendif\n\ninstall_lib_static: $(STATIC_LIBS)\n\tinstall -d $(LIBDIR)\n\t@for l in $(STATIC_LIBS); do \\\n\techo \"install -m 755 $$l $(LIBDIR)\"; \\\n\tinstall -m 755 $$l $(LIBDIR); \\\ndone\n\ninstall_lib: install_lib_shared install_lib_static\n\ninstall_doc_html:\n\tinstall -d $(DATADIR)/doc/jemalloc$(install_suffix)\n\t@for d in $(DOCS_HTML); do \\\n\techo \"install -m 644 $$d $(DATADIR)/doc/jemalloc$(install_suffix)\"; \\\n\tinstall -m 644 $$d $(DATADIR)/doc/jemalloc$(install_suffix); \\\ndone\n\ninstall_doc_man:\n\tinstall -d $(MANDIR)/man3\n\t@for d in $(DOCS_MAN3); do \\\n\techo \"install -m 644 $$d $(MANDIR)/man3\"; \\\n\tinstall -m 644 $$d $(MANDIR)/man3; \\\ndone\n\ninstall_doc: install_doc_html install_doc_man\n\ninstall: install_bin install_include install_lib install_doc\n\ntests: $(CTESTS:$(srcroot)%.c=$(objroot)%$(EXE))\n\ncheck: tests\n\t@mkdir -p $(objroot)test\n\t@$(SHELL) -c 'total=0; \\\n\t\tfailures=0; \\\n\t\techo \"=========================================\"; \\\n\t\tfor t in $(CTESTS:$(srcroot)%.c=$(objroot)%); do \\\n\t\t\ttotal=`expr $$total + 1`; \\\n\t\t\t/bin/echo -n \"$${t} ... \"; \\\n\t\t\t$(TEST_LIBRARY_PATH) $${t}$(EXE) $(abs_srcroot) \\\n\t\t\t  $(abs_objroot) > $(objroot)$${t}.out 2>&1; \\\n\t\t\tif test -e \"$(srcroot)$${t}.exp\"; then \\\n\t\t\t\tdiff -w -u $(srcroot)$${t}.exp \\\n\t\t\t\t  $(objroot)$${t}.out >/dev/null 2>&1; \\\n\t\t\t\tfail=$$?; \\\n\t\t\t\tif test \"$${fail}\" -eq \"1\" ; then \\\n\t\t\t\t\tfailures=`expr $${failures} + 1`; \\\n\t\t\t\t\techo \"*** FAIL ***\"; \\\n\t\t\t\telse \\\n\t\t\t\t\techo \"pass\"; \\\n\t\t\t\tfi; \\\n\t\t\telse \\\n\t\t\t\techo \"*** FAIL *** (.exp file is missing)\"; \\\n\t\t\t\tfailures=`expr $${failures} + 1`; \\\n\t\t\tfi; \\\n\t\tdone; \\\n\t\techo \"=========================================\"; \\\n\t\techo \"Failures: $${failures}/$${total}\"'\n\nclean:\n\trm -f $(COBJS)\n\trm -f $(CPICOBJS)\n\trm -f $(COBJS:%.$(O)=%.d)\n\trm -f $(CPICOBJS:%.$(O)=%.d)\n\trm -f $(CTESTOBJS:%.$(O)=%$(EXE))\n\trm -f $(CTESTOBJS)\n\trm -f $(CTESTOBJS:%.$(O)=%.d)\n\trm -f $(CTESTOBJS:%.$(O)=%.out)\n\trm -f $(DSOS) $(STATIC_LIBS)\n\ndistclean: clean\n\trm -rf $(objroot)autom4te.cache\n\trm -f $(objroot)config.log\n\trm -f $(objroot)config.status\n\trm -f $(objroot)config.stamp\n\trm -f $(cfghdrs_out)\n\trm -f $(cfgoutputs_out)\n\nrelclean: distclean\n\trm -f $(objroot)configure\n\trm -f $(srcroot)VERSION\n\trm -f $(DOCS_HTML)\n\trm -f $(DOCS_MAN3)\n\n#===============================================================================\n# Re-configuration rules.\n\nifeq ($(enable_autogen), 1)\n$(srcroot)configure : $(srcroot)configure.ac\n\tcd ./$(srcroot) && $(AUTOCONF)\n\n$(objroot)config.status : $(srcroot)configure\n\t./$(objroot)config.status --recheck\n\n$(srcroot)config.stamp.in : $(srcroot)configure.ac\n\techo stamp > $(srcroot)config.stamp.in\n\n$(objroot)config.stamp : $(cfgoutputs_in) $(cfghdrs_in) $(srcroot)configure\n\t./$(objroot)config.status\n\t@touch $@\n\n# There must be some action in order for make to re-read Makefile when it is\n# out of date.\n$(cfgoutputs_out) $(cfghdrs_out) : $(objroot)config.stamp\n\t@true\nendif\n"
  },
  {
    "path": "deps/jemalloc/README",
    "content": "jemalloc is a general-purpose scalable concurrent malloc(3) implementation.\nThis distribution is a \"portable\" implementation that currently targets\nFreeBSD, Linux, Apple OS X, and MinGW.  jemalloc is included as the default\nallocator in the FreeBSD and NetBSD operating systems, and it is used by the\nMozilla Firefox web browser on Microsoft Windows-related platforms.  Depending\non your needs, one of the other divergent versions may suit your needs better\nthan this distribution.\n\nThe COPYING file contains copyright and licensing information.\n\nThe INSTALL file contains information on how to configure, build, and install\njemalloc.\n\nThe ChangeLog file contains a brief summary of changes for each release.\n\nURL: http://www.canonware.com/jemalloc/\n"
  },
  {
    "path": "deps/jemalloc/VERSION",
    "content": "3.2.0-0-g87499f6748ebe4817571e817e9f680ccb5bf54a9\n"
  },
  {
    "path": "deps/jemalloc/autogen.sh",
    "content": "#!/bin/sh\n\nfor i in autoconf; do\n    echo \"$i\"\n    $i\n    if [ $? -ne 0 ]; then\n\techo \"Error $? in $i\"\n\texit 1\n    fi\ndone\n\necho \"./configure --enable-autogen $@\"\n./configure --enable-autogen $@\nif [ $? -ne 0 ]; then\n    echo \"Error $? in ./configure\"\n    exit 1\nfi\n"
  },
  {
    "path": "deps/jemalloc/bin/jemalloc.sh.in",
    "content": "#!/bin/sh\n\nprefix=@prefix@\nexec_prefix=@exec_prefix@\nlibdir=@libdir@\n\n@LD_PRELOAD_VAR@=${libdir}/libjemalloc.@SOREV@\nexport @LD_PRELOAD_VAR@\nexec \"$@\"\n"
  },
  {
    "path": "deps/jemalloc/bin/pprof",
    "content": "#! /usr/bin/env perl\n\n# Copyright (c) 1998-2007, Google 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\n# met:\n# \n#     * Redistributions of source code must retain the above copyright\n# notice, this list of conditions and the following disclaimer.\n#     * Redistributions in binary form must reproduce the above\n# copyright notice, this list of conditions and the following disclaimer\n# in the documentation and/or other materials provided with the\n# distribution.\n#     * Neither the name of Google Inc. nor the names of its\n# contributors may be used to endorse or promote products derived from\n# this software without specific prior written permission.\n# \n# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n# \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n# ---\n# Program for printing the profile generated by common/profiler.cc,\n# or by the heap profiler (common/debugallocation.cc)\n#\n# The profile contains a sequence of entries of the form:\n#       <count> <stack trace>\n# This program parses the profile, and generates user-readable\n# output.\n#\n# Examples:\n#\n# % tools/pprof \"program\" \"profile\"\n#   Enters \"interactive\" mode\n#\n# % tools/pprof --text \"program\" \"profile\"\n#   Generates one line per procedure\n#\n# % tools/pprof --gv \"program\" \"profile\"\n#   Generates annotated call-graph and displays via \"gv\"\n#\n# % tools/pprof --gv --focus=Mutex \"program\" \"profile\"\n#   Restrict to code paths that involve an entry that matches \"Mutex\"\n#\n# % tools/pprof --gv --focus=Mutex --ignore=string \"program\" \"profile\"\n#   Restrict to code paths that involve an entry that matches \"Mutex\"\n#   and does not match \"string\"\n#\n# % tools/pprof --list=IBF_CheckDocid \"program\" \"profile\"\n#   Generates disassembly listing of all routines with at least one\n#   sample that match the --list=<regexp> pattern.  The listing is\n#   annotated with the flat and cumulative sample counts at each line.\n#\n# % tools/pprof --disasm=IBF_CheckDocid \"program\" \"profile\"\n#   Generates disassembly listing of all routines with at least one\n#   sample that match the --disasm=<regexp> pattern.  The listing is\n#   annotated with the flat and cumulative sample counts at each PC value.\n#\n# TODO: Use color to indicate files?\n\nuse strict;\nuse warnings;\nuse Getopt::Long;\n\nmy $PPROF_VERSION = \"2.0\";\n\n# These are the object tools we use which can come from a\n# user-specified location using --tools, from the PPROF_TOOLS\n# environment variable, or from the environment.\nmy %obj_tool_map = (\n  \"objdump\" => \"objdump\",\n  \"nm\" => \"nm\",\n  \"addr2line\" => \"addr2line\",\n  \"c++filt\" => \"c++filt\",\n  ## ConfigureObjTools may add architecture-specific entries:\n  #\"nm_pdb\" => \"nm-pdb\",       # for reading windows (PDB-format) executables\n  #\"addr2line_pdb\" => \"addr2line-pdb\",                                # ditto\n  #\"otool\" => \"otool\",         # equivalent of objdump on OS X\n);\n# NOTE: these are lists, so you can put in commandline flags if you want.\nmy @DOT = (\"dot\");          # leave non-absolute, since it may be in /usr/local\nmy @GV = (\"gv\");\nmy @EVINCE = (\"evince\");    # could also be xpdf or perhaps acroread\nmy @KCACHEGRIND = (\"kcachegrind\");\nmy @PS2PDF = (\"ps2pdf\");\n# These are used for dynamic profiles\nmy @URL_FETCHER = (\"curl\", \"-s\");\n\n# These are the web pages that servers need to support for dynamic profiles\nmy $HEAP_PAGE = \"/pprof/heap\";\nmy $PROFILE_PAGE = \"/pprof/profile\";   # must support cgi-param \"?seconds=#\"\nmy $PMUPROFILE_PAGE = \"/pprof/pmuprofile(?:\\\\?.*)?\"; # must support cgi-param\n                                                # ?seconds=#&event=x&period=n\nmy $GROWTH_PAGE = \"/pprof/growth\";\nmy $CONTENTION_PAGE = \"/pprof/contention\";\nmy $WALL_PAGE = \"/pprof/wall(?:\\\\?.*)?\";  # accepts options like namefilter\nmy $FILTEREDPROFILE_PAGE = \"/pprof/filteredprofile(?:\\\\?.*)?\";\nmy $CENSUSPROFILE_PAGE = \"/pprof/censusprofile(?:\\\\?.*)?\"; # must support cgi-param\n                                                       # \"?seconds=#\",\n                                                       # \"?tags_regexp=#\" and\n                                                       # \"?type=#\".\nmy $SYMBOL_PAGE = \"/pprof/symbol\";     # must support symbol lookup via POST\nmy $PROGRAM_NAME_PAGE = \"/pprof/cmdline\";\n\n# These are the web pages that can be named on the command line.\n# All the alternatives must begin with /.\nmy $PROFILES = \"($HEAP_PAGE|$PROFILE_PAGE|$PMUPROFILE_PAGE|\" .\n               \"$GROWTH_PAGE|$CONTENTION_PAGE|$WALL_PAGE|\" .\n               \"$FILTEREDPROFILE_PAGE|$CENSUSPROFILE_PAGE)\";\n\n# default binary name\nmy $UNKNOWN_BINARY = \"(unknown)\";\n\n# There is a pervasive dependency on the length (in hex characters,\n# i.e., nibbles) of an address, distinguishing between 32-bit and\n# 64-bit profiles.  To err on the safe size, default to 64-bit here:\nmy $address_length = 16;\n\nmy $dev_null = \"/dev/null\";\nif (! -e $dev_null && $^O =~ /MSWin/) {    # $^O is the OS perl was built for\n  $dev_null = \"nul\";\n}\n\n# A list of paths to search for shared object files\nmy @prefix_list = ();\n\n# Special routine name that should not have any symbols.\n# Used as separator to parse \"addr2line -i\" output.\nmy $sep_symbol = '_fini';\nmy $sep_address = undef;\n\n##### Argument parsing #####\n\nsub usage_string {\n  return <<EOF;\nUsage:\npprof [options] <program> <profiles>\n   <profiles> is a space separated list of profile names.\npprof [options] <symbolized-profiles>\n   <symbolized-profiles> is a list of profile files where each file contains\n   the necessary symbol mappings  as well as profile data (likely generated\n   with --raw).\npprof [options] <profile>\n   <profile> is a remote form.  Symbols are obtained from host:port$SYMBOL_PAGE\n\n   Each name can be:\n   /path/to/profile        - a path to a profile file\n   host:port[/<service>]   - a location of a service to get profile from\n\n   The /<service> can be $HEAP_PAGE, $PROFILE_PAGE, /pprof/pmuprofile,\n                         $GROWTH_PAGE, $CONTENTION_PAGE, /pprof/wall,\n                         $CENSUSPROFILE_PAGE, or /pprof/filteredprofile.\n   For instance:\n     pprof http://myserver.com:80$HEAP_PAGE\n   If /<service> is omitted, the service defaults to $PROFILE_PAGE (cpu profiling).\npprof --symbols <program>\n   Maps addresses to symbol names.  In this mode, stdin should be a\n   list of library mappings, in the same format as is found in the heap-\n   and cpu-profile files (this loosely matches that of /proc/self/maps\n   on linux), followed by a list of hex addresses to map, one per line.\n\n   For more help with querying remote servers, including how to add the\n   necessary server-side support code, see this filename (or one like it):\n\n   /usr/doc/gperftools-$PPROF_VERSION/pprof_remote_servers.html\n\nOptions:\n   --cum               Sort by cumulative data\n   --base=<base>       Subtract <base> from <profile> before display\n   --interactive       Run in interactive mode (interactive \"help\" gives help) [default]\n   --seconds=<n>       Length of time for dynamic profiles [default=30 secs]\n   --add_lib=<file>    Read additional symbols and line info from the given library\n   --lib_prefix=<dir>  Comma separated list of library path prefixes\n\nReporting Granularity:\n   --addresses         Report at address level\n   --lines             Report at source line level\n   --functions         Report at function level [default]\n   --files             Report at source file level\n\nOutput type:\n   --text              Generate text report\n   --callgrind         Generate callgrind format to stdout\n   --gv                Generate Postscript and display\n   --evince            Generate PDF and display\n   --web               Generate SVG and display\n   --list=<regexp>     Generate source listing of matching routines\n   --disasm=<regexp>   Generate disassembly of matching routines\n   --symbols           Print demangled symbol names found at given addresses\n   --dot               Generate DOT file to stdout\n   --ps                Generate Postcript to stdout\n   --pdf               Generate PDF to stdout\n   --svg               Generate SVG to stdout\n   --gif               Generate GIF to stdout\n   --raw               Generate symbolized pprof data (useful with remote fetch)\n\nHeap-Profile Options:\n   --inuse_space       Display in-use (mega)bytes [default]\n   --inuse_objects     Display in-use objects\n   --alloc_space       Display allocated (mega)bytes\n   --alloc_objects     Display allocated objects\n   --show_bytes        Display space in bytes\n   --drop_negative     Ignore negative differences\n\nContention-profile options:\n   --total_delay       Display total delay at each region [default]\n   --contentions       Display number of delays at each region\n   --mean_delay        Display mean delay at each region\n\nCall-graph Options:\n   --nodecount=<n>     Show at most so many nodes [default=80]\n   --nodefraction=<f>  Hide nodes below <f>*total [default=.005]\n   --edgefraction=<f>  Hide edges below <f>*total [default=.001]\n   --maxdegree=<n>     Max incoming/outgoing edges per node [default=8]\n   --focus=<regexp>    Focus on nodes matching <regexp>\n   --ignore=<regexp>   Ignore nodes matching <regexp>\n   --scale=<n>         Set GV scaling [default=0]\n   --heapcheck         Make nodes with non-0 object counts\n                       (i.e. direct leak generators) more visible\n\nMiscellaneous:\n   --tools=<prefix or binary:fullpath>[,...]   \\$PATH for object tool pathnames\n   --test              Run unit tests\n   --help              This message\n   --version           Version information\n\nEnvironment Variables:\n   PPROF_TMPDIR        Profiles directory. Defaults to \\$HOME/pprof\n   PPROF_TOOLS         Prefix for object tools pathnames\n\nExamples:\n\npprof /bin/ls ls.prof\n                       Enters \"interactive\" mode\npprof --text /bin/ls ls.prof\n                       Outputs one line per procedure\npprof --web /bin/ls ls.prof\n                       Displays annotated call-graph in web browser\npprof --gv /bin/ls ls.prof\n                       Displays annotated call-graph via 'gv'\npprof --gv --focus=Mutex /bin/ls ls.prof\n                       Restricts to code paths including a .*Mutex.* entry\npprof --gv --focus=Mutex --ignore=string /bin/ls ls.prof\n                       Code paths including Mutex but not string\npprof --list=getdir /bin/ls ls.prof\n                       (Per-line) annotated source listing for getdir()\npprof --disasm=getdir /bin/ls ls.prof\n                       (Per-PC) annotated disassembly for getdir()\n\npprof http://localhost:1234/\n                       Enters \"interactive\" mode\npprof --text localhost:1234\n                       Outputs one line per procedure for localhost:1234\npprof --raw localhost:1234 > ./local.raw\npprof --text ./local.raw\n                       Fetches a remote profile for later analysis and then\n                       analyzes it in text mode.\nEOF\n}\n\nsub version_string {\n  return <<EOF\npprof (part of gperftools $PPROF_VERSION)\n\nCopyright 1998-2007 Google Inc.\n\nThis is BSD licensed software; see the source for copying conditions\nand license information.\nThere is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A\nPARTICULAR PURPOSE.\nEOF\n}\n\nsub usage {\n  my $msg = shift;\n  print STDERR \"$msg\\n\\n\";\n  print STDERR usage_string();\n  print STDERR \"\\nFATAL ERROR: $msg\\n\";    # just as a reminder\n  exit(1);\n}\n\nsub Init() {\n  # Setup tmp-file name and handler to clean it up.\n  # We do this in the very beginning so that we can use\n  # error() and cleanup() function anytime here after.\n  $main::tmpfile_sym = \"/tmp/pprof$$.sym\";\n  $main::tmpfile_ps = \"/tmp/pprof$$\";\n  $main::next_tmpfile = 0;\n  $SIG{'INT'} = \\&sighandler;\n\n  # Cache from filename/linenumber to source code\n  $main::source_cache = ();\n\n  $main::opt_help = 0;\n  $main::opt_version = 0;\n\n  $main::opt_cum = 0;\n  $main::opt_base = '';\n  $main::opt_addresses = 0;\n  $main::opt_lines = 0;\n  $main::opt_functions = 0;\n  $main::opt_files = 0;\n  $main::opt_lib_prefix = \"\";\n\n  $main::opt_text = 0;\n  $main::opt_callgrind = 0;\n  $main::opt_list = \"\";\n  $main::opt_disasm = \"\";\n  $main::opt_symbols = 0;\n  $main::opt_gv = 0;\n  $main::opt_evince = 0;\n  $main::opt_web = 0;\n  $main::opt_dot = 0;\n  $main::opt_ps = 0;\n  $main::opt_pdf = 0;\n  $main::opt_gif = 0;\n  $main::opt_svg = 0;\n  $main::opt_raw = 0;\n\n  $main::opt_nodecount = 80;\n  $main::opt_nodefraction = 0.005;\n  $main::opt_edgefraction = 0.001;\n  $main::opt_maxdegree = 8;\n  $main::opt_focus = '';\n  $main::opt_ignore = '';\n  $main::opt_scale = 0;\n  $main::opt_heapcheck = 0;\n  $main::opt_seconds = 30;\n  $main::opt_lib = \"\";\n\n  $main::opt_inuse_space   = 0;\n  $main::opt_inuse_objects = 0;\n  $main::opt_alloc_space   = 0;\n  $main::opt_alloc_objects = 0;\n  $main::opt_show_bytes    = 0;\n  $main::opt_drop_negative = 0;\n  $main::opt_interactive   = 0;\n\n  $main::opt_total_delay = 0;\n  $main::opt_contentions = 0;\n  $main::opt_mean_delay = 0;\n\n  $main::opt_tools   = \"\";\n  $main::opt_debug   = 0;\n  $main::opt_test    = 0;\n\n  # These are undocumented flags used only by unittests.\n  $main::opt_test_stride = 0;\n\n  # Are we using $SYMBOL_PAGE?\n  $main::use_symbol_page = 0;\n\n  # Files returned by TempName.\n  %main::tempnames = ();\n\n  # Type of profile we are dealing with\n  # Supported types:\n  #     cpu\n  #     heap\n  #     growth\n  #     contention\n  $main::profile_type = '';     # Empty type means \"unknown\"\n\n  GetOptions(\"help!\"          => \\$main::opt_help,\n             \"version!\"       => \\$main::opt_version,\n             \"cum!\"           => \\$main::opt_cum,\n             \"base=s\"         => \\$main::opt_base,\n             \"seconds=i\"      => \\$main::opt_seconds,\n             \"add_lib=s\"      => \\$main::opt_lib,\n             \"lib_prefix=s\"   => \\$main::opt_lib_prefix,\n             \"functions!\"     => \\$main::opt_functions,\n             \"lines!\"         => \\$main::opt_lines,\n             \"addresses!\"     => \\$main::opt_addresses,\n             \"files!\"         => \\$main::opt_files,\n             \"text!\"          => \\$main::opt_text,\n             \"callgrind!\"     => \\$main::opt_callgrind,\n             \"list=s\"         => \\$main::opt_list,\n             \"disasm=s\"       => \\$main::opt_disasm,\n             \"symbols!\"       => \\$main::opt_symbols,\n             \"gv!\"            => \\$main::opt_gv,\n             \"evince!\"        => \\$main::opt_evince,\n             \"web!\"           => \\$main::opt_web,\n             \"dot!\"           => \\$main::opt_dot,\n             \"ps!\"            => \\$main::opt_ps,\n             \"pdf!\"           => \\$main::opt_pdf,\n             \"svg!\"           => \\$main::opt_svg,\n             \"gif!\"           => \\$main::opt_gif,\n             \"raw!\"           => \\$main::opt_raw,\n             \"interactive!\"   => \\$main::opt_interactive,\n             \"nodecount=i\"    => \\$main::opt_nodecount,\n             \"nodefraction=f\" => \\$main::opt_nodefraction,\n             \"edgefraction=f\" => \\$main::opt_edgefraction,\n             \"maxdegree=i\"    => \\$main::opt_maxdegree,\n             \"focus=s\"        => \\$main::opt_focus,\n             \"ignore=s\"       => \\$main::opt_ignore,\n             \"scale=i\"        => \\$main::opt_scale,\n             \"heapcheck\"      => \\$main::opt_heapcheck,\n             \"inuse_space!\"   => \\$main::opt_inuse_space,\n             \"inuse_objects!\" => \\$main::opt_inuse_objects,\n             \"alloc_space!\"   => \\$main::opt_alloc_space,\n             \"alloc_objects!\" => \\$main::opt_alloc_objects,\n             \"show_bytes!\"    => \\$main::opt_show_bytes,\n             \"drop_negative!\" => \\$main::opt_drop_negative,\n             \"total_delay!\"   => \\$main::opt_total_delay,\n             \"contentions!\"   => \\$main::opt_contentions,\n             \"mean_delay!\"    => \\$main::opt_mean_delay,\n             \"tools=s\"        => \\$main::opt_tools,\n             \"test!\"          => \\$main::opt_test,\n             \"debug!\"         => \\$main::opt_debug,\n             # Undocumented flags used only by unittests:\n             \"test_stride=i\"  => \\$main::opt_test_stride,\n      ) || usage(\"Invalid option(s)\");\n\n  # Deal with the standard --help and --version\n  if ($main::opt_help) {\n    print usage_string();\n    exit(0);\n  }\n\n  if ($main::opt_version) {\n    print version_string();\n    exit(0);\n  }\n\n  # Disassembly/listing/symbols mode requires address-level info\n  if ($main::opt_disasm || $main::opt_list || $main::opt_symbols) {\n    $main::opt_functions = 0;\n    $main::opt_lines = 0;\n    $main::opt_addresses = 1;\n    $main::opt_files = 0;\n  }\n\n  # Check heap-profiling flags\n  if ($main::opt_inuse_space +\n      $main::opt_inuse_objects +\n      $main::opt_alloc_space +\n      $main::opt_alloc_objects > 1) {\n    usage(\"Specify at most on of --inuse/--alloc options\");\n  }\n\n  # Check output granularities\n  my $grains =\n      $main::opt_functions +\n      $main::opt_lines +\n      $main::opt_addresses +\n      $main::opt_files +\n      0;\n  if ($grains > 1) {\n    usage(\"Only specify one output granularity option\");\n  }\n  if ($grains == 0) {\n    $main::opt_functions = 1;\n  }\n\n  # Check output modes\n  my $modes =\n      $main::opt_text +\n      $main::opt_callgrind +\n      ($main::opt_list eq '' ? 0 : 1) +\n      ($main::opt_disasm eq '' ? 0 : 1) +\n      ($main::opt_symbols == 0 ? 0 : 1) +\n      $main::opt_gv +\n      $main::opt_evince +\n      $main::opt_web +\n      $main::opt_dot +\n      $main::opt_ps +\n      $main::opt_pdf +\n      $main::opt_svg +\n      $main::opt_gif +\n      $main::opt_raw +\n      $main::opt_interactive +\n      0;\n  if ($modes > 1) {\n    usage(\"Only specify one output mode\");\n  }\n  if ($modes == 0) {\n    if (-t STDOUT) {  # If STDOUT is a tty, activate interactive mode\n      $main::opt_interactive = 1;\n    } else {\n      $main::opt_text = 1;\n    }\n  }\n\n  if ($main::opt_test) {\n    RunUnitTests();\n    # Should not return\n    exit(1);\n  }\n\n  # Binary name and profile arguments list\n  $main::prog = \"\";\n  @main::pfile_args = ();\n\n  # Remote profiling without a binary (using $SYMBOL_PAGE instead)\n  if (@ARGV > 0) {\n    if (IsProfileURL($ARGV[0])) {\n      $main::use_symbol_page = 1;\n    } elsif (IsSymbolizedProfileFile($ARGV[0])) {\n      $main::use_symbolized_profile = 1;\n      $main::prog = $UNKNOWN_BINARY;  # will be set later from the profile file\n    }\n  }\n\n  if ($main::use_symbol_page || $main::use_symbolized_profile) {\n    # We don't need a binary!\n    my %disabled = ('--lines' => $main::opt_lines,\n                    '--disasm' => $main::opt_disasm);\n    for my $option (keys %disabled) {\n      usage(\"$option cannot be used without a binary\") if $disabled{$option};\n    }\n    # Set $main::prog later...\n    scalar(@ARGV) || usage(\"Did not specify profile file\");\n  } elsif ($main::opt_symbols) {\n    # --symbols needs a binary-name (to run nm on, etc) but not profiles\n    $main::prog = shift(@ARGV) || usage(\"Did not specify program\");\n  } else {\n    $main::prog = shift(@ARGV) || usage(\"Did not specify program\");\n    scalar(@ARGV) || usage(\"Did not specify profile file\");\n  }\n\n  # Parse profile file/location arguments\n  foreach my $farg (@ARGV) {\n    if ($farg =~ m/(.*)\\@([0-9]+)(|\\/.*)$/ ) {\n      my $machine = $1;\n      my $num_machines = $2;\n      my $path = $3;\n      for (my $i = 0; $i < $num_machines; $i++) {\n        unshift(@main::pfile_args, \"$i.$machine$path\");\n      }\n    } else {\n      unshift(@main::pfile_args, $farg);\n    }\n  }\n\n  if ($main::use_symbol_page) {\n    unless (IsProfileURL($main::pfile_args[0])) {\n      error(\"The first profile should be a remote form to use $SYMBOL_PAGE\\n\");\n    }\n    CheckSymbolPage();\n    $main::prog = FetchProgramName();\n  } elsif (!$main::use_symbolized_profile) {  # may not need objtools!\n    ConfigureObjTools($main::prog)\n  }\n\n  # Break the opt_lib_prefix into the prefix_list array\n  @prefix_list = split (',', $main::opt_lib_prefix);\n\n  # Remove trailing / from the prefixes, in the list to prevent\n  # searching things like /my/path//lib/mylib.so\n  foreach (@prefix_list) {\n    s|/+$||;\n  }\n}\n\nsub Main() {\n  Init();\n  $main::collected_profile = undef;\n  @main::profile_files = ();\n  $main::op_time = time();\n\n  # Printing symbols is special and requires a lot less info that most.\n  if ($main::opt_symbols) {\n    PrintSymbols(*STDIN);   # Get /proc/maps and symbols output from stdin\n    return;\n  }\n\n  # Fetch all profile data\n  FetchDynamicProfiles();\n\n  # this will hold symbols that we read from the profile files\n  my $symbol_map = {};\n\n  # Read one profile, pick the last item on the list\n  my $data = ReadProfile($main::prog, pop(@main::profile_files));\n  my $profile = $data->{profile};\n  my $pcs = $data->{pcs};\n  my $libs = $data->{libs};   # Info about main program and shared libraries\n  $symbol_map = MergeSymbols($symbol_map, $data->{symbols});\n\n  # Add additional profiles, if available.\n  if (scalar(@main::profile_files) > 0) {\n    foreach my $pname (@main::profile_files) {\n      my $data2 = ReadProfile($main::prog, $pname);\n      $profile = AddProfile($profile, $data2->{profile});\n      $pcs = AddPcs($pcs, $data2->{pcs});\n      $symbol_map = MergeSymbols($symbol_map, $data2->{symbols});\n    }\n  }\n\n  # Subtract base from profile, if specified\n  if ($main::opt_base ne '') {\n    my $base = ReadProfile($main::prog, $main::opt_base);\n    $profile = SubtractProfile($profile, $base->{profile});\n    $pcs = AddPcs($pcs, $base->{pcs});\n    $symbol_map = MergeSymbols($symbol_map, $base->{symbols});\n  }\n\n  # Get total data in profile\n  my $total = TotalProfile($profile);\n\n  # Collect symbols\n  my $symbols;\n  if ($main::use_symbolized_profile) {\n    $symbols = FetchSymbols($pcs, $symbol_map);\n  } elsif ($main::use_symbol_page) {\n    $symbols = FetchSymbols($pcs);\n  } else {\n    # TODO(csilvers): $libs uses the /proc/self/maps data from profile1,\n    # which may differ from the data from subsequent profiles, especially\n    # if they were run on different machines.  Use appropriate libs for\n    # each pc somehow.\n    $symbols = ExtractSymbols($libs, $pcs);\n  }\n\n  # Remove uniniteresting stack items\n  $profile = RemoveUninterestingFrames($symbols, $profile);\n\n  # Focus?\n  if ($main::opt_focus ne '') {\n    $profile = FocusProfile($symbols, $profile, $main::opt_focus);\n  }\n\n  # Ignore?\n  if ($main::opt_ignore ne '') {\n    $profile = IgnoreProfile($symbols, $profile, $main::opt_ignore);\n  }\n\n  my $calls = ExtractCalls($symbols, $profile);\n\n  # Reduce profiles to required output granularity, and also clean\n  # each stack trace so a given entry exists at most once.\n  my $reduced = ReduceProfile($symbols, $profile);\n\n  # Get derived profiles\n  my $flat = FlatProfile($reduced);\n  my $cumulative = CumulativeProfile($reduced);\n\n  # Print\n  if (!$main::opt_interactive) {\n    if ($main::opt_disasm) {\n      PrintDisassembly($libs, $flat, $cumulative, $main::opt_disasm);\n    } elsif ($main::opt_list) {\n      PrintListing($total, $libs, $flat, $cumulative, $main::opt_list, 0);\n    } elsif ($main::opt_text) {\n      # Make sure the output is empty when have nothing to report\n      # (only matters when --heapcheck is given but we must be\n      # compatible with old branches that did not pass --heapcheck always):\n      if ($total != 0) {\n        printf(\"Total: %s %s\\n\", Unparse($total), Units());\n      }\n      PrintText($symbols, $flat, $cumulative, -1);\n    } elsif ($main::opt_raw) {\n      PrintSymbolizedProfile($symbols, $profile, $main::prog);\n    } elsif ($main::opt_callgrind) {\n      PrintCallgrind($calls);\n    } else {\n      if (PrintDot($main::prog, $symbols, $profile, $flat, $cumulative, $total)) {\n        if ($main::opt_gv) {\n          RunGV(TempName($main::next_tmpfile, \"ps\"), \"\");\n        } elsif ($main::opt_evince) {\n          RunEvince(TempName($main::next_tmpfile, \"pdf\"), \"\");\n        } elsif ($main::opt_web) {\n          my $tmp = TempName($main::next_tmpfile, \"svg\");\n          RunWeb($tmp);\n          # The command we run might hand the file name off\n          # to an already running browser instance and then exit.\n          # Normally, we'd remove $tmp on exit (right now),\n          # but fork a child to remove $tmp a little later, so that the\n          # browser has time to load it first.\n          delete $main::tempnames{$tmp};\n          if (fork() == 0) {\n            sleep 5;\n            unlink($tmp);\n            exit(0);\n          }\n        }\n      } else {\n        cleanup();\n        exit(1);\n      }\n    }\n  } else {\n    InteractiveMode($profile, $symbols, $libs, $total);\n  }\n\n  cleanup();\n  exit(0);\n}\n\n##### Entry Point #####\n\nMain();\n\n# Temporary code to detect if we're running on a Goobuntu system.\n# These systems don't have the right stuff installed for the special\n# Readline libraries to work, so as a temporary workaround, we default\n# to using the normal stdio code, rather than the fancier readline-based\n# code\nsub ReadlineMightFail {\n  if (-e '/lib/libtermcap.so.2') {\n    return 0;  # libtermcap exists, so readline should be okay\n  } else {\n    return 1;\n  }\n}\n\nsub RunGV {\n  my $fname = shift;\n  my $bg = shift;       # \"\" or \" &\" if we should run in background\n  if (!system(ShellEscape(@GV, \"--version\") . \" >$dev_null 2>&1\")) {\n    # Options using double dash are supported by this gv version.\n    # Also, turn on noantialias to better handle bug in gv for\n    # postscript files with large dimensions.\n    # TODO: Maybe we should not pass the --noantialias flag\n    # if the gv version is known to work properly without the flag.\n    system(ShellEscape(@GV, \"--scale=$main::opt_scale\", \"--noantialias\", $fname)\n           . $bg);\n  } else {\n    # Old gv version - only supports options that use single dash.\n    print STDERR ShellEscape(@GV, \"-scale\", $main::opt_scale) . \"\\n\";\n    system(ShellEscape(@GV, \"-scale\", \"$main::opt_scale\", $fname) . $bg);\n  }\n}\n\nsub RunEvince {\n  my $fname = shift;\n  my $bg = shift;       # \"\" or \" &\" if we should run in background\n  system(ShellEscape(@EVINCE, $fname) . $bg);\n}\n\nsub RunWeb {\n  my $fname = shift;\n  print STDERR \"Loading web page file:///$fname\\n\";\n\n  if (`uname` =~ /Darwin/) {\n    # OS X: open will use standard preference for SVG files.\n    system(\"/usr/bin/open\", $fname);\n    return;\n  }\n\n  # Some kind of Unix; try generic symlinks, then specific browsers.\n  # (Stop once we find one.)\n  # Works best if the browser is already running.\n  my @alt = (\n    \"/etc/alternatives/gnome-www-browser\",\n    \"/etc/alternatives/x-www-browser\",\n    \"google-chrome\",\n    \"firefox\",\n  );\n  foreach my $b (@alt) {\n    if (system($b, $fname) == 0) {\n      return;\n    }\n  }\n\n  print STDERR \"Could not load web browser.\\n\";\n}\n\nsub RunKcachegrind {\n  my $fname = shift;\n  my $bg = shift;       # \"\" or \" &\" if we should run in background\n  print STDERR \"Starting '@KCACHEGRIND \" . $fname . $bg . \"'\\n\";\n  system(ShellEscape(@KCACHEGRIND, $fname) . $bg);\n}\n\n\n##### Interactive helper routines #####\n\nsub InteractiveMode {\n  $| = 1;  # Make output unbuffered for interactive mode\n  my ($orig_profile, $symbols, $libs, $total) = @_;\n\n  print STDERR \"Welcome to pprof!  For help, type 'help'.\\n\";\n\n  # Use ReadLine if it's installed and input comes from a console.\n  if ( -t STDIN &&\n       !ReadlineMightFail() &&\n       defined(eval {require Term::ReadLine}) ) {\n    my $term = new Term::ReadLine 'pprof';\n    while ( defined ($_ = $term->readline('(pprof) '))) {\n      $term->addhistory($_) if /\\S/;\n      if (!InteractiveCommand($orig_profile, $symbols, $libs, $total, $_)) {\n        last;    # exit when we get an interactive command to quit\n      }\n    }\n  } else {       # don't have readline\n    while (1) {\n      print STDERR \"(pprof) \";\n      $_ = <STDIN>;\n      last if ! defined $_ ;\n      s/\\r//g;         # turn windows-looking lines into unix-looking lines\n\n      # Save some flags that might be reset by InteractiveCommand()\n      my $save_opt_lines = $main::opt_lines;\n\n      if (!InteractiveCommand($orig_profile, $symbols, $libs, $total, $_)) {\n        last;    # exit when we get an interactive command to quit\n      }\n\n      # Restore flags\n      $main::opt_lines = $save_opt_lines;\n    }\n  }\n}\n\n# Takes two args: orig profile, and command to run.\n# Returns 1 if we should keep going, or 0 if we were asked to quit\nsub InteractiveCommand {\n  my($orig_profile, $symbols, $libs, $total, $command) = @_;\n  $_ = $command;                # just to make future m//'s easier\n  if (!defined($_)) {\n    print STDERR \"\\n\";\n    return 0;\n  }\n  if (m/^\\s*quit/) {\n    return 0;\n  }\n  if (m/^\\s*help/) {\n    InteractiveHelpMessage();\n    return 1;\n  }\n  # Clear all the mode options -- mode is controlled by \"$command\"\n  $main::opt_text = 0;\n  $main::opt_callgrind = 0;\n  $main::opt_disasm = 0;\n  $main::opt_list = 0;\n  $main::opt_gv = 0;\n  $main::opt_evince = 0;\n  $main::opt_cum = 0;\n\n  if (m/^\\s*(text|top)(\\d*)\\s*(.*)/) {\n    $main::opt_text = 1;\n\n    my $line_limit = ($2 ne \"\") ? int($2) : 10;\n\n    my $routine;\n    my $ignore;\n    ($routine, $ignore) = ParseInteractiveArgs($3);\n\n    my $profile = ProcessProfile($total, $orig_profile, $symbols, \"\", $ignore);\n    my $reduced = ReduceProfile($symbols, $profile);\n\n    # Get derived profiles\n    my $flat = FlatProfile($reduced);\n    my $cumulative = CumulativeProfile($reduced);\n\n    PrintText($symbols, $flat, $cumulative, $line_limit);\n    return 1;\n  }\n  if (m/^\\s*callgrind\\s*([^ \\n]*)/) {\n    $main::opt_callgrind = 1;\n\n    # Get derived profiles\n    my $calls = ExtractCalls($symbols, $orig_profile);\n    my $filename = $1;\n    if ( $1 eq '' ) {\n      $filename = TempName($main::next_tmpfile, \"callgrind\");\n    }\n    PrintCallgrind($calls, $filename);\n    if ( $1 eq '' ) {\n      RunKcachegrind($filename, \" & \");\n      $main::next_tmpfile++;\n    }\n\n    return 1;\n  }\n  if (m/^\\s*(web)?list\\s*(.+)/) {\n    my $html = (defined($1) && ($1 eq \"web\"));\n    $main::opt_list = 1;\n\n    my $routine;\n    my $ignore;\n    ($routine, $ignore) = ParseInteractiveArgs($2);\n\n    my $profile = ProcessProfile($total, $orig_profile, $symbols, \"\", $ignore);\n    my $reduced = ReduceProfile($symbols, $profile);\n\n    # Get derived profiles\n    my $flat = FlatProfile($reduced);\n    my $cumulative = CumulativeProfile($reduced);\n\n    PrintListing($total, $libs, $flat, $cumulative, $routine, $html);\n    return 1;\n  }\n  if (m/^\\s*disasm\\s*(.+)/) {\n    $main::opt_disasm = 1;\n\n    my $routine;\n    my $ignore;\n    ($routine, $ignore) = ParseInteractiveArgs($1);\n\n    # Process current profile to account for various settings\n    my $profile = ProcessProfile($total, $orig_profile, $symbols, \"\", $ignore);\n    my $reduced = ReduceProfile($symbols, $profile);\n\n    # Get derived profiles\n    my $flat = FlatProfile($reduced);\n    my $cumulative = CumulativeProfile($reduced);\n\n    PrintDisassembly($libs, $flat, $cumulative, $routine);\n    return 1;\n  }\n  if (m/^\\s*(gv|web|evince)\\s*(.*)/) {\n    $main::opt_gv = 0;\n    $main::opt_evince = 0;\n    $main::opt_web = 0;\n    if ($1 eq \"gv\") {\n      $main::opt_gv = 1;\n    } elsif ($1 eq \"evince\") {\n      $main::opt_evince = 1;\n    } elsif ($1 eq \"web\") {\n      $main::opt_web = 1;\n    }\n\n    my $focus;\n    my $ignore;\n    ($focus, $ignore) = ParseInteractiveArgs($2);\n\n    # Process current profile to account for various settings\n    my $profile = ProcessProfile($total, $orig_profile, $symbols,\n                                 $focus, $ignore);\n    my $reduced = ReduceProfile($symbols, $profile);\n\n    # Get derived profiles\n    my $flat = FlatProfile($reduced);\n    my $cumulative = CumulativeProfile($reduced);\n\n    if (PrintDot($main::prog, $symbols, $profile, $flat, $cumulative, $total)) {\n      if ($main::opt_gv) {\n        RunGV(TempName($main::next_tmpfile, \"ps\"), \" &\");\n      } elsif ($main::opt_evince) {\n        RunEvince(TempName($main::next_tmpfile, \"pdf\"), \" &\");\n      } elsif ($main::opt_web) {\n        RunWeb(TempName($main::next_tmpfile, \"svg\"));\n      }\n      $main::next_tmpfile++;\n    }\n    return 1;\n  }\n  if (m/^\\s*$/) {\n    return 1;\n  }\n  print STDERR \"Unknown command: try 'help'.\\n\";\n  return 1;\n}\n\n\nsub ProcessProfile {\n  my $total_count = shift;\n  my $orig_profile = shift;\n  my $symbols = shift;\n  my $focus = shift;\n  my $ignore = shift;\n\n  # Process current profile to account for various settings\n  my $profile = $orig_profile;\n  printf(\"Total: %s %s\\n\", Unparse($total_count), Units());\n  if ($focus ne '') {\n    $profile = FocusProfile($symbols, $profile, $focus);\n    my $focus_count = TotalProfile($profile);\n    printf(\"After focusing on '%s': %s %s of %s (%0.1f%%)\\n\",\n           $focus,\n           Unparse($focus_count), Units(),\n           Unparse($total_count), ($focus_count*100.0) / $total_count);\n  }\n  if ($ignore ne '') {\n    $profile = IgnoreProfile($symbols, $profile, $ignore);\n    my $ignore_count = TotalProfile($profile);\n    printf(\"After ignoring '%s': %s %s of %s (%0.1f%%)\\n\",\n           $ignore,\n           Unparse($ignore_count), Units(),\n           Unparse($total_count),\n           ($ignore_count*100.0) / $total_count);\n  }\n\n  return $profile;\n}\n\nsub InteractiveHelpMessage {\n  print STDERR <<ENDOFHELP;\nInteractive pprof mode\n\nCommands:\n  gv\n  gv [focus] [-ignore1] [-ignore2]\n      Show graphical hierarchical display of current profile.  Without\n      any arguments, shows all samples in the profile.  With the optional\n      \"focus\" argument, restricts the samples shown to just those where\n      the \"focus\" regular expression matches a routine name on the stack\n      trace.\n\n  web\n  web [focus] [-ignore1] [-ignore2]\n      Like GV, but displays profile in your web browser instead of using\n      Ghostview. Works best if your web browser is already running.\n      To change the browser that gets used:\n      On Linux, set the /etc/alternatives/gnome-www-browser symlink.\n      On OS X, change the Finder association for SVG files.\n\n  list [routine_regexp] [-ignore1] [-ignore2]\n      Show source listing of routines whose names match \"routine_regexp\"\n\n  weblist [routine_regexp] [-ignore1] [-ignore2]\n     Displays a source listing of routines whose names match \"routine_regexp\"\n     in a web browser.  You can click on source lines to view the\n     corresponding disassembly.\n\n  top [--cum] [-ignore1] [-ignore2]\n  top20 [--cum] [-ignore1] [-ignore2]\n  top37 [--cum] [-ignore1] [-ignore2]\n      Show top lines ordered by flat profile count, or cumulative count\n      if --cum is specified.  If a number is present after 'top', the\n      top K routines will be shown (defaults to showing the top 10)\n\n  disasm [routine_regexp] [-ignore1] [-ignore2]\n      Show disassembly of routines whose names match \"routine_regexp\",\n      annotated with sample counts.\n\n  callgrind\n  callgrind [filename]\n      Generates callgrind file. If no filename is given, kcachegrind is called.\n\n  help - This listing\n  quit or ^D - End pprof\n\nFor commands that accept optional -ignore tags, samples where any routine in\nthe stack trace matches the regular expression in any of the -ignore\nparameters will be ignored.\n\nFurther pprof details are available at this location (or one similar):\n\n /usr/doc/gperftools-$PPROF_VERSION/cpu_profiler.html\n /usr/doc/gperftools-$PPROF_VERSION/heap_profiler.html\n\nENDOFHELP\n}\nsub ParseInteractiveArgs {\n  my $args = shift;\n  my $focus = \"\";\n  my $ignore = \"\";\n  my @x = split(/ +/, $args);\n  foreach $a (@x) {\n    if ($a =~ m/^(--|-)lines$/) {\n      $main::opt_lines = 1;\n    } elsif ($a =~ m/^(--|-)cum$/) {\n      $main::opt_cum = 1;\n    } elsif ($a =~ m/^-(.*)/) {\n      $ignore .= (($ignore ne \"\") ? \"|\" : \"\" ) . $1;\n    } else {\n      $focus .= (($focus ne \"\") ? \"|\" : \"\" ) . $a;\n    }\n  }\n  if ($ignore ne \"\") {\n    print STDERR \"Ignoring samples in call stacks that match '$ignore'\\n\";\n  }\n  return ($focus, $ignore);\n}\n\n##### Output code #####\n\nsub TempName {\n  my $fnum = shift;\n  my $ext = shift;\n  my $file = \"$main::tmpfile_ps.$fnum.$ext\";\n  $main::tempnames{$file} = 1;\n  return $file;\n}\n\n# Print profile data in packed binary format (64-bit) to standard out\nsub PrintProfileData {\n  my $profile = shift;\n\n  # print header (64-bit style)\n  # (zero) (header-size) (version) (sample-period) (zero)\n  print pack('L*', 0, 0, 3, 0, 0, 0, 1, 0, 0, 0);\n\n  foreach my $k (keys(%{$profile})) {\n    my $count = $profile->{$k};\n    my @addrs = split(/\\n/, $k);\n    if ($#addrs >= 0) {\n      my $depth = $#addrs + 1;\n      # int(foo / 2**32) is the only reliable way to get rid of bottom\n      # 32 bits on both 32- and 64-bit systems.\n      print pack('L*', $count & 0xFFFFFFFF, int($count / 2**32));\n      print pack('L*', $depth & 0xFFFFFFFF, int($depth / 2**32));\n\n      foreach my $full_addr (@addrs) {\n        my $addr = $full_addr;\n        $addr =~ s/0x0*//;  # strip off leading 0x, zeroes\n        if (length($addr) > 16) {\n          print STDERR \"Invalid address in profile: $full_addr\\n\";\n          next;\n        }\n        my $low_addr = substr($addr, -8);       # get last 8 hex chars\n        my $high_addr = substr($addr, -16, 8);  # get up to 8 more hex chars\n        print pack('L*', hex('0x' . $low_addr), hex('0x' . $high_addr));\n      }\n    }\n  }\n}\n\n# Print symbols and profile data\nsub PrintSymbolizedProfile {\n  my $symbols = shift;\n  my $profile = shift;\n  my $prog = shift;\n\n  $SYMBOL_PAGE =~ m,[^/]+$,;    # matches everything after the last slash\n  my $symbol_marker = $&;\n\n  print '--- ', $symbol_marker, \"\\n\";\n  if (defined($prog)) {\n    print 'binary=', $prog, \"\\n\";\n  }\n  while (my ($pc, $name) = each(%{$symbols})) {\n    my $sep = ' ';\n    print '0x', $pc;\n    # We have a list of function names, which include the inlined\n    # calls.  They are separated (and terminated) by --, which is\n    # illegal in function names.\n    for (my $j = 2; $j <= $#{$name}; $j += 3) {\n      print $sep, $name->[$j];\n      $sep = '--';\n    }\n    print \"\\n\";\n  }\n  print '---', \"\\n\";\n\n  $PROFILE_PAGE =~ m,[^/]+$,;    # matches everything after the last slash\n  my $profile_marker = $&;\n  print '--- ', $profile_marker, \"\\n\";\n  if (defined($main::collected_profile)) {\n    # if used with remote fetch, simply dump the collected profile to output.\n    open(SRC, \"<$main::collected_profile\");\n    while (<SRC>) {\n      print $_;\n    }\n    close(SRC);\n  } else {\n    # dump a cpu-format profile to standard out\n    PrintProfileData($profile);\n  }\n}\n\n# Print text output\nsub PrintText {\n  my $symbols = shift;\n  my $flat = shift;\n  my $cumulative = shift;\n  my $line_limit = shift;\n\n  my $total = TotalProfile($flat);\n\n  # Which profile to sort by?\n  my $s = $main::opt_cum ? $cumulative : $flat;\n\n  my $running_sum = 0;\n  my $lines = 0;\n  foreach my $k (sort { GetEntry($s, $b) <=> GetEntry($s, $a) || $a cmp $b }\n                 keys(%{$cumulative})) {\n    my $f = GetEntry($flat, $k);\n    my $c = GetEntry($cumulative, $k);\n    $running_sum += $f;\n\n    my $sym = $k;\n    if (exists($symbols->{$k})) {\n      $sym = $symbols->{$k}->[0] . \" \" . $symbols->{$k}->[1];\n      if ($main::opt_addresses) {\n        $sym = $k . \" \" . $sym;\n      }\n    }\n\n    if ($f != 0 || $c != 0) {\n      printf(\"%8s %6s %6s %8s %6s %s\\n\",\n             Unparse($f),\n             Percent($f, $total),\n             Percent($running_sum, $total),\n             Unparse($c),\n             Percent($c, $total),\n             $sym);\n    }\n    $lines++;\n    last if ($line_limit >= 0 && $lines >= $line_limit);\n  }\n}\n\n# Callgrind format has a compression for repeated function and file\n# names.  You show the name the first time, and just use its number\n# subsequently.  This can cut down the file to about a third or a\n# quarter of its uncompressed size.  $key and $val are the key/value\n# pair that would normally be printed by callgrind; $map is a map from\n# value to number.\nsub CompressedCGName {\n  my($key, $val, $map) = @_;\n  my $idx = $map->{$val};\n  # For very short keys, providing an index hurts rather than helps.\n  if (length($val) <= 3) {\n    return \"$key=$val\\n\";\n  } elsif (defined($idx)) {\n    return \"$key=($idx)\\n\";\n  } else {\n    # scalar(keys $map) gives the number of items in the map.\n    $idx = scalar(keys(%{$map})) + 1;\n    $map->{$val} = $idx;\n    return \"$key=($idx) $val\\n\";\n  }\n}\n\n# Print the call graph in a way that's suiteable for callgrind.\nsub PrintCallgrind {\n  my $calls = shift;\n  my $filename;\n  my %filename_to_index_map;\n  my %fnname_to_index_map;\n\n  if ($main::opt_interactive) {\n    $filename = shift;\n    print STDERR \"Writing callgrind file to '$filename'.\\n\"\n  } else {\n    $filename = \"&STDOUT\";\n  }\n  open(CG, \">$filename\");\n  printf CG (\"events: Hits\\n\\n\");\n  foreach my $call ( map { $_->[0] }\n                     sort { $a->[1] cmp $b ->[1] ||\n                            $a->[2] <=> $b->[2] }\n                     map { /([^:]+):(\\d+):([^ ]+)( -> ([^:]+):(\\d+):(.+))?/;\n                           [$_, $1, $2] }\n                     keys %$calls ) {\n    my $count = int($calls->{$call});\n    $call =~ /([^:]+):(\\d+):([^ ]+)( -> ([^:]+):(\\d+):(.+))?/;\n    my ( $caller_file, $caller_line, $caller_function,\n         $callee_file, $callee_line, $callee_function ) =\n       ( $1, $2, $3, $5, $6, $7 );\n\n    # TODO(csilvers): for better compression, collect all the\n    # caller/callee_files and functions first, before printing\n    # anything, and only compress those referenced more than once.\n    printf CG CompressedCGName(\"fl\", $caller_file, \\%filename_to_index_map);\n    printf CG CompressedCGName(\"fn\", $caller_function, \\%fnname_to_index_map);\n    if (defined $6) {\n      printf CG CompressedCGName(\"cfl\", $callee_file, \\%filename_to_index_map);\n      printf CG CompressedCGName(\"cfn\", $callee_function, \\%fnname_to_index_map);\n      printf CG (\"calls=$count $callee_line\\n\");\n    }\n    printf CG (\"$caller_line $count\\n\\n\");\n  }\n}\n\n# Print disassembly for all all routines that match $main::opt_disasm\nsub PrintDisassembly {\n  my $libs = shift;\n  my $flat = shift;\n  my $cumulative = shift;\n  my $disasm_opts = shift;\n\n  my $total = TotalProfile($flat);\n\n  foreach my $lib (@{$libs}) {\n    my $symbol_table = GetProcedureBoundaries($lib->[0], $disasm_opts);\n    my $offset = AddressSub($lib->[1], $lib->[3]);\n    foreach my $routine (sort ByName keys(%{$symbol_table})) {\n      my $start_addr = $symbol_table->{$routine}->[0];\n      my $end_addr = $symbol_table->{$routine}->[1];\n      # See if there are any samples in this routine\n      my $length = hex(AddressSub($end_addr, $start_addr));\n      my $addr = AddressAdd($start_addr, $offset);\n      for (my $i = 0; $i < $length; $i++) {\n        if (defined($cumulative->{$addr})) {\n          PrintDisassembledFunction($lib->[0], $offset,\n                                    $routine, $flat, $cumulative,\n                                    $start_addr, $end_addr, $total);\n          last;\n        }\n        $addr = AddressInc($addr);\n      }\n    }\n  }\n}\n\n# Return reference to array of tuples of the form:\n#       [start_address, filename, linenumber, instruction, limit_address]\n# E.g.,\n#       [\"0x806c43d\", \"/foo/bar.cc\", 131, \"ret\", \"0x806c440\"]\nsub Disassemble {\n  my $prog = shift;\n  my $offset = shift;\n  my $start_addr = shift;\n  my $end_addr = shift;\n\n  my $objdump = $obj_tool_map{\"objdump\"};\n  my $cmd = ShellEscape($objdump, \"-C\", \"-d\", \"-l\", \"--no-show-raw-insn\",\n                        \"--start-address=0x$start_addr\",\n                        \"--stop-address=0x$end_addr\", $prog);\n  open(OBJDUMP, \"$cmd |\") || error(\"$cmd: $!\\n\");\n  my @result = ();\n  my $filename = \"\";\n  my $linenumber = -1;\n  my $last = [\"\", \"\", \"\", \"\"];\n  while (<OBJDUMP>) {\n    s/\\r//g;         # turn windows-looking lines into unix-looking lines\n    chop;\n    if (m|\\s*([^:\\s]+):(\\d+)\\s*$|) {\n      # Location line of the form:\n      #   <filename>:<linenumber>\n      $filename = $1;\n      $linenumber = $2;\n    } elsif (m/^ +([0-9a-f]+):\\s*(.*)/) {\n      # Disassembly line -- zero-extend address to full length\n      my $addr = HexExtend($1);\n      my $k = AddressAdd($addr, $offset);\n      $last->[4] = $k;   # Store ending address for previous instruction\n      $last = [$k, $filename, $linenumber, $2, $end_addr];\n      push(@result, $last);\n    }\n  }\n  close(OBJDUMP);\n  return @result;\n}\n\n# The input file should contain lines of the form /proc/maps-like\n# output (same format as expected from the profiles) or that looks\n# like hex addresses (like \"0xDEADBEEF\").  We will parse all\n# /proc/maps output, and for all the hex addresses, we will output\n# \"short\" symbol names, one per line, in the same order as the input.\nsub PrintSymbols {\n  my $maps_and_symbols_file = shift;\n\n  # ParseLibraries expects pcs to be in a set.  Fine by us...\n  my @pclist = ();   # pcs in sorted order\n  my $pcs = {};\n  my $map = \"\";\n  foreach my $line (<$maps_and_symbols_file>) {\n    $line =~ s/\\r//g;    # turn windows-looking lines into unix-looking lines\n    if ($line =~ /\\b(0x[0-9a-f]+)\\b/i) {\n      push(@pclist, HexExtend($1));\n      $pcs->{$pclist[-1]} = 1;\n    } else {\n      $map .= $line;\n    }\n  }\n\n  my $libs = ParseLibraries($main::prog, $map, $pcs);\n  my $symbols = ExtractSymbols($libs, $pcs);\n\n  foreach my $pc (@pclist) {\n    # ->[0] is the shortname, ->[2] is the full name\n    print(($symbols->{$pc}->[0] || \"??\") . \"\\n\");\n  }\n}\n\n\n# For sorting functions by name\nsub ByName {\n  return ShortFunctionName($a) cmp ShortFunctionName($b);\n}\n\n# Print source-listing for all all routines that match $list_opts\nsub PrintListing {\n  my $total = shift;\n  my $libs = shift;\n  my $flat = shift;\n  my $cumulative = shift;\n  my $list_opts = shift;\n  my $html = shift;\n\n  my $output = \\*STDOUT;\n  my $fname = \"\";\n\n  if ($html) {\n    # Arrange to write the output to a temporary file\n    $fname = TempName($main::next_tmpfile, \"html\");\n    $main::next_tmpfile++;\n    if (!open(TEMP, \">$fname\")) {\n      print STDERR \"$fname: $!\\n\";\n      return;\n    }\n    $output = \\*TEMP;\n    print $output HtmlListingHeader();\n    printf $output (\"<div class=\\\"legend\\\">%s<br>Total: %s %s</div>\\n\",\n                    $main::prog, Unparse($total), Units());\n  }\n\n  my $listed = 0;\n  foreach my $lib (@{$libs}) {\n    my $symbol_table = GetProcedureBoundaries($lib->[0], $list_opts);\n    my $offset = AddressSub($lib->[1], $lib->[3]);\n    foreach my $routine (sort ByName keys(%{$symbol_table})) {\n      # Print if there are any samples in this routine\n      my $start_addr = $symbol_table->{$routine}->[0];\n      my $end_addr = $symbol_table->{$routine}->[1];\n      my $length = hex(AddressSub($end_addr, $start_addr));\n      my $addr = AddressAdd($start_addr, $offset);\n      for (my $i = 0; $i < $length; $i++) {\n        if (defined($cumulative->{$addr})) {\n          $listed += PrintSource(\n            $lib->[0], $offset,\n            $routine, $flat, $cumulative,\n            $start_addr, $end_addr,\n            $html,\n            $output);\n          last;\n        }\n        $addr = AddressInc($addr);\n      }\n    }\n  }\n\n  if ($html) {\n    if ($listed > 0) {\n      print $output HtmlListingFooter();\n      close($output);\n      RunWeb($fname);\n    } else {\n      close($output);\n      unlink($fname);\n    }\n  }\n}\n\nsub HtmlListingHeader {\n  return <<'EOF';\n<DOCTYPE html>\n<html>\n<head>\n<title>Pprof listing</title>\n<style type=\"text/css\">\nbody {\n  font-family: sans-serif;\n}\nh1 {\n  font-size: 1.5em;\n  margin-bottom: 4px;\n}\n.legend {\n  font-size: 1.25em;\n}\n.line {\n  color: #aaaaaa;\n}\n.nop {\n  color: #aaaaaa;\n}\n.unimportant {\n  color: #cccccc;\n}\n.disasmloc {\n  color: #000000;\n}\n.deadsrc {\n  cursor: pointer;\n}\n.deadsrc:hover {\n  background-color: #eeeeee;\n}\n.livesrc {\n  color: #0000ff;\n  cursor: pointer;\n}\n.livesrc:hover {\n  background-color: #eeeeee;\n}\n.asm {\n  color: #008800;\n  display: none;\n}\n</style>\n<script type=\"text/javascript\">\nfunction pprof_toggle_asm(e) {\n  var target;\n  if (!e) e = window.event;\n  if (e.target) target = e.target;\n  else if (e.srcElement) target = e.srcElement;\n\n  if (target) {\n    var asm = target.nextSibling;\n    if (asm && asm.className == \"asm\") {\n      asm.style.display = (asm.style.display == \"block\" ? \"\" : \"block\");\n      e.preventDefault();\n      return false;\n    }\n  }\n}\n</script>\n</head>\n<body>\nEOF\n}\n\nsub HtmlListingFooter {\n  return <<'EOF';\n</body>\n</html>\nEOF\n}\n\nsub HtmlEscape {\n  my $text = shift;\n  $text =~ s/&/&amp;/g;\n  $text =~ s/</&lt;/g;\n  $text =~ s/>/&gt;/g;\n  return $text;\n}\n\n# Returns the indentation of the line, if it has any non-whitespace\n# characters.  Otherwise, returns -1.\nsub Indentation {\n  my $line = shift;\n  if (m/^(\\s*)\\S/) {\n    return length($1);\n  } else {\n    return -1;\n  }\n}\n\n# If the symbol table contains inlining info, Disassemble() may tag an\n# instruction with a location inside an inlined function.  But for\n# source listings, we prefer to use the location in the function we\n# are listing.  So use MapToSymbols() to fetch full location\n# information for each instruction and then pick out the first\n# location from a location list (location list contains callers before\n# callees in case of inlining).\n#\n# After this routine has run, each entry in $instructions contains:\n#   [0] start address\n#   [1] filename for function we are listing\n#   [2] line number for function we are listing\n#   [3] disassembly\n#   [4] limit address\n#   [5] most specific filename (may be different from [1] due to inlining)\n#   [6] most specific line number (may be different from [2] due to inlining)\nsub GetTopLevelLineNumbers {\n  my ($lib, $offset, $instructions) = @_;\n  my $pcs = [];\n  for (my $i = 0; $i <= $#{$instructions}; $i++) {\n    push(@{$pcs}, $instructions->[$i]->[0]);\n  }\n  my $symbols = {};\n  MapToSymbols($lib, $offset, $pcs, $symbols);\n  for (my $i = 0; $i <= $#{$instructions}; $i++) {\n    my $e = $instructions->[$i];\n    push(@{$e}, $e->[1]);\n    push(@{$e}, $e->[2]);\n    my $addr = $e->[0];\n    my $sym = $symbols->{$addr};\n    if (defined($sym)) {\n      if ($#{$sym} >= 2 && $sym->[1] =~ m/^(.*):(\\d+)$/) {\n        $e->[1] = $1;  # File name\n        $e->[2] = $2;  # Line number\n      }\n    }\n  }\n}\n\n# Print source-listing for one routine\nsub PrintSource {\n  my $prog = shift;\n  my $offset = shift;\n  my $routine = shift;\n  my $flat = shift;\n  my $cumulative = shift;\n  my $start_addr = shift;\n  my $end_addr = shift;\n  my $html = shift;\n  my $output = shift;\n\n  # Disassemble all instructions (just to get line numbers)\n  my @instructions = Disassemble($prog, $offset, $start_addr, $end_addr);\n  GetTopLevelLineNumbers($prog, $offset, \\@instructions);\n\n  # Hack 1: assume that the first source file encountered in the\n  # disassembly contains the routine\n  my $filename = undef;\n  for (my $i = 0; $i <= $#instructions; $i++) {\n    if ($instructions[$i]->[2] >= 0) {\n      $filename = $instructions[$i]->[1];\n      last;\n    }\n  }\n  if (!defined($filename)) {\n    print STDERR \"no filename found in $routine\\n\";\n    return 0;\n  }\n\n  # Hack 2: assume that the largest line number from $filename is the\n  # end of the procedure.  This is typically safe since if P1 contains\n  # an inlined call to P2, then P2 usually occurs earlier in the\n  # source file.  If this does not work, we might have to compute a\n  # density profile or just print all regions we find.\n  my $lastline = 0;\n  for (my $i = 0; $i <= $#instructions; $i++) {\n    my $f = $instructions[$i]->[1];\n    my $l = $instructions[$i]->[2];\n    if (($f eq $filename) && ($l > $lastline)) {\n      $lastline = $l;\n    }\n  }\n\n  # Hack 3: assume the first source location from \"filename\" is the start of\n  # the source code.\n  my $firstline = 1;\n  for (my $i = 0; $i <= $#instructions; $i++) {\n    if ($instructions[$i]->[1] eq $filename) {\n      $firstline = $instructions[$i]->[2];\n      last;\n    }\n  }\n\n  # Hack 4: Extend last line forward until its indentation is less than\n  # the indentation we saw on $firstline\n  my $oldlastline = $lastline;\n  {\n    if (!open(FILE, \"<$filename\")) {\n      print STDERR \"$filename: $!\\n\";\n      return 0;\n    }\n    my $l = 0;\n    my $first_indentation = -1;\n    while (<FILE>) {\n      s/\\r//g;         # turn windows-looking lines into unix-looking lines\n      $l++;\n      my $indent = Indentation($_);\n      if ($l >= $firstline) {\n        if ($first_indentation < 0 && $indent >= 0) {\n          $first_indentation = $indent;\n          last if ($first_indentation == 0);\n        }\n      }\n      if ($l >= $lastline && $indent >= 0) {\n        if ($indent >= $first_indentation) {\n          $lastline = $l+1;\n        } else {\n          last;\n        }\n      }\n    }\n    close(FILE);\n  }\n\n  # Assign all samples to the range $firstline,$lastline,\n  # Hack 4: If an instruction does not occur in the range, its samples\n  # are moved to the next instruction that occurs in the range.\n  my $samples1 = {};        # Map from line number to flat count\n  my $samples2 = {};        # Map from line number to cumulative count\n  my $running1 = 0;         # Unassigned flat counts\n  my $running2 = 0;         # Unassigned cumulative counts\n  my $total1 = 0;           # Total flat counts\n  my $total2 = 0;           # Total cumulative counts\n  my %disasm = ();          # Map from line number to disassembly\n  my $running_disasm = \"\";  # Unassigned disassembly\n  my $skip_marker = \"---\\n\";\n  if ($html) {\n    $skip_marker = \"\";\n    for (my $l = $firstline; $l <= $lastline; $l++) {\n      $disasm{$l} = \"\";\n    }\n  }\n  my $last_dis_filename = '';\n  my $last_dis_linenum = -1;\n  my $last_touched_line = -1;  # To detect gaps in disassembly for a line\n  foreach my $e (@instructions) {\n    # Add up counts for all address that fall inside this instruction\n    my $c1 = 0;\n    my $c2 = 0;\n    for (my $a = $e->[0]; $a lt $e->[4]; $a = AddressInc($a)) {\n      $c1 += GetEntry($flat, $a);\n      $c2 += GetEntry($cumulative, $a);\n    }\n\n    if ($html) {\n      my $dis = sprintf(\"      %6s %6s \\t\\t%8s: %s \",\n                        HtmlPrintNumber($c1),\n                        HtmlPrintNumber($c2),\n                        UnparseAddress($offset, $e->[0]),\n                        CleanDisassembly($e->[3]));\n      \n      # Append the most specific source line associated with this instruction\n      if (length($dis) < 80) { $dis .= (' ' x (80 - length($dis))) };\n      $dis = HtmlEscape($dis);\n      my $f = $e->[5];\n      my $l = $e->[6];\n      if ($f ne $last_dis_filename) {\n        $dis .= sprintf(\"<span class=disasmloc>%s:%d</span>\", \n                        HtmlEscape(CleanFileName($f)), $l);\n      } elsif ($l ne $last_dis_linenum) {\n        # De-emphasize the unchanged file name portion\n        $dis .= sprintf(\"<span class=unimportant>%s</span>\" .\n                        \"<span class=disasmloc>:%d</span>\", \n                        HtmlEscape(CleanFileName($f)), $l);\n      } else {\n        # De-emphasize the entire location\n        $dis .= sprintf(\"<span class=unimportant>%s:%d</span>\", \n                        HtmlEscape(CleanFileName($f)), $l);\n      }\n      $last_dis_filename = $f;\n      $last_dis_linenum = $l;\n      $running_disasm .= $dis;\n      $running_disasm .= \"\\n\";\n    }\n\n    $running1 += $c1;\n    $running2 += $c2;\n    $total1 += $c1;\n    $total2 += $c2;\n    my $file = $e->[1];\n    my $line = $e->[2];\n    if (($file eq $filename) &&\n        ($line >= $firstline) &&\n        ($line <= $lastline)) {\n      # Assign all accumulated samples to this line\n      AddEntry($samples1, $line, $running1);\n      AddEntry($samples2, $line, $running2);\n      $running1 = 0;\n      $running2 = 0;\n      if ($html) {\n        if ($line != $last_touched_line && $disasm{$line} ne '') {\n          $disasm{$line} .= \"\\n\";\n        }\n        $disasm{$line} .= $running_disasm;\n        $running_disasm = '';\n        $last_touched_line = $line;\n      }\n    }\n  }\n\n  # Assign any leftover samples to $lastline\n  AddEntry($samples1, $lastline, $running1);\n  AddEntry($samples2, $lastline, $running2);\n  if ($html) {\n    if ($lastline != $last_touched_line && $disasm{$lastline} ne '') {\n      $disasm{$lastline} .= \"\\n\";\n    }\n    $disasm{$lastline} .= $running_disasm;\n  }\n\n  if ($html) {\n    printf $output (\n      \"<h1>%s</h1>%s\\n<pre onClick=\\\"pprof_toggle_asm()\\\">\\n\" .\n      \"Total:%6s %6s (flat / cumulative %s)\\n\",\n      HtmlEscape(ShortFunctionName($routine)),\n      HtmlEscape(CleanFileName($filename)),\n      Unparse($total1),\n      Unparse($total2),\n      Units());\n  } else {\n    printf $output (\n      \"ROUTINE ====================== %s in %s\\n\" .\n      \"%6s %6s Total %s (flat / cumulative)\\n\",\n      ShortFunctionName($routine),\n      CleanFileName($filename),\n      Unparse($total1),\n      Unparse($total2),\n      Units());\n  }\n  if (!open(FILE, \"<$filename\")) {\n    print STDERR \"$filename: $!\\n\";\n    return 0;\n  }\n  my $l = 0;\n  while (<FILE>) {\n    s/\\r//g;         # turn windows-looking lines into unix-looking lines\n    $l++;\n    if ($l >= $firstline - 5 &&\n        (($l <= $oldlastline + 5) || ($l <= $lastline))) {\n      chop;\n      my $text = $_;\n      if ($l == $firstline) { print $output $skip_marker; }\n      my $n1 = GetEntry($samples1, $l);\n      my $n2 = GetEntry($samples2, $l);\n      if ($html) {\n        # Emit a span that has one of the following classes:\n        #    livesrc -- has samples\n        #    deadsrc -- has disassembly, but with no samples\n        #    nop     -- has no matching disasembly\n        # Also emit an optional span containing disassembly.\n        my $dis = $disasm{$l};\n        my $asm = \"\";\n        if (defined($dis) && $dis ne '') {\n          $asm = \"<span class=\\\"asm\\\">\" . $dis . \"</span>\";\n        }\n        my $source_class = (($n1 + $n2 > 0) \n                            ? \"livesrc\" \n                            : (($asm ne \"\") ? \"deadsrc\" : \"nop\"));\n        printf $output (\n          \"<span class=\\\"line\\\">%5d</span> \" .\n          \"<span class=\\\"%s\\\">%6s %6s %s</span>%s\\n\",\n          $l, $source_class,\n          HtmlPrintNumber($n1),\n          HtmlPrintNumber($n2),\n          HtmlEscape($text),\n          $asm);\n      } else {\n        printf $output(\n          \"%6s %6s %4d: %s\\n\",\n          UnparseAlt($n1),\n          UnparseAlt($n2),\n          $l,\n          $text);\n      }\n      if ($l == $lastline)  { print $output $skip_marker; }\n    };\n  }\n  close(FILE);\n  if ($html) {\n    print $output \"</pre>\\n\";\n  }\n  return 1;\n}\n\n# Return the source line for the specified file/linenumber.\n# Returns undef if not found.\nsub SourceLine {\n  my $file = shift;\n  my $line = shift;\n\n  # Look in cache\n  if (!defined($main::source_cache{$file})) {\n    if (100 < scalar keys(%main::source_cache)) {\n      # Clear the cache when it gets too big\n      $main::source_cache = ();\n    }\n\n    # Read all lines from the file\n    if (!open(FILE, \"<$file\")) {\n      print STDERR \"$file: $!\\n\";\n      $main::source_cache{$file} = [];  # Cache the negative result\n      return undef;\n    }\n    my $lines = [];\n    push(@{$lines}, \"\");        # So we can use 1-based line numbers as indices\n    while (<FILE>) {\n      push(@{$lines}, $_);\n    }\n    close(FILE);\n\n    # Save the lines in the cache\n    $main::source_cache{$file} = $lines;\n  }\n\n  my $lines = $main::source_cache{$file};\n  if (($line < 0) || ($line > $#{$lines})) {\n    return undef;\n  } else {\n    return $lines->[$line];\n  }\n}\n\n# Print disassembly for one routine with interspersed source if available\nsub PrintDisassembledFunction {\n  my $prog = shift;\n  my $offset = shift;\n  my $routine = shift;\n  my $flat = shift;\n  my $cumulative = shift;\n  my $start_addr = shift;\n  my $end_addr = shift;\n  my $total = shift;\n\n  # Disassemble all instructions\n  my @instructions = Disassemble($prog, $offset, $start_addr, $end_addr);\n\n  # Make array of counts per instruction\n  my @flat_count = ();\n  my @cum_count = ();\n  my $flat_total = 0;\n  my $cum_total = 0;\n  foreach my $e (@instructions) {\n    # Add up counts for all address that fall inside this instruction\n    my $c1 = 0;\n    my $c2 = 0;\n    for (my $a = $e->[0]; $a lt $e->[4]; $a = AddressInc($a)) {\n      $c1 += GetEntry($flat, $a);\n      $c2 += GetEntry($cumulative, $a);\n    }\n    push(@flat_count, $c1);\n    push(@cum_count, $c2);\n    $flat_total += $c1;\n    $cum_total += $c2;\n  }\n\n  # Print header with total counts\n  printf(\"ROUTINE ====================== %s\\n\" .\n         \"%6s %6s %s (flat, cumulative) %.1f%% of total\\n\",\n         ShortFunctionName($routine),\n         Unparse($flat_total),\n         Unparse($cum_total),\n         Units(),\n         ($cum_total * 100.0) / $total);\n\n  # Process instructions in order\n  my $current_file = \"\";\n  for (my $i = 0; $i <= $#instructions; ) {\n    my $e = $instructions[$i];\n\n    # Print the new file name whenever we switch files\n    if ($e->[1] ne $current_file) {\n      $current_file = $e->[1];\n      my $fname = $current_file;\n      $fname =~ s|^\\./||;   # Trim leading \"./\"\n\n      # Shorten long file names\n      if (length($fname) >= 58) {\n        $fname = \"...\" . substr($fname, -55);\n      }\n      printf(\"-------------------- %s\\n\", $fname);\n    }\n\n    # TODO: Compute range of lines to print together to deal with\n    # small reorderings.\n    my $first_line = $e->[2];\n    my $last_line = $first_line;\n    my %flat_sum = ();\n    my %cum_sum = ();\n    for (my $l = $first_line; $l <= $last_line; $l++) {\n      $flat_sum{$l} = 0;\n      $cum_sum{$l} = 0;\n    }\n\n    # Find run of instructions for this range of source lines\n    my $first_inst = $i;\n    while (($i <= $#instructions) &&\n           ($instructions[$i]->[2] >= $first_line) &&\n           ($instructions[$i]->[2] <= $last_line)) {\n      $e = $instructions[$i];\n      $flat_sum{$e->[2]} += $flat_count[$i];\n      $cum_sum{$e->[2]} += $cum_count[$i];\n      $i++;\n    }\n    my $last_inst = $i - 1;\n\n    # Print source lines\n    for (my $l = $first_line; $l <= $last_line; $l++) {\n      my $line = SourceLine($current_file, $l);\n      if (!defined($line)) {\n        $line = \"?\\n\";\n        next;\n      } else {\n        $line =~ s/^\\s+//;\n      }\n      printf(\"%6s %6s %5d: %s\",\n             UnparseAlt($flat_sum{$l}),\n             UnparseAlt($cum_sum{$l}),\n             $l,\n             $line);\n    }\n\n    # Print disassembly\n    for (my $x = $first_inst; $x <= $last_inst; $x++) {\n      my $e = $instructions[$x];\n      printf(\"%6s %6s    %8s: %6s\\n\",\n             UnparseAlt($flat_count[$x]),\n             UnparseAlt($cum_count[$x]),\n             UnparseAddress($offset, $e->[0]),\n             CleanDisassembly($e->[3]));\n    }\n  }\n}\n\n# Print DOT graph\nsub PrintDot {\n  my $prog = shift;\n  my $symbols = shift;\n  my $raw = shift;\n  my $flat = shift;\n  my $cumulative = shift;\n  my $overall_total = shift;\n\n  # Get total\n  my $local_total = TotalProfile($flat);\n  my $nodelimit = int($main::opt_nodefraction * $local_total);\n  my $edgelimit = int($main::opt_edgefraction * $local_total);\n  my $nodecount = $main::opt_nodecount;\n\n  # Find nodes to include\n  my @list = (sort { abs(GetEntry($cumulative, $b)) <=>\n                     abs(GetEntry($cumulative, $a))\n                     || $a cmp $b }\n              keys(%{$cumulative}));\n  my $last = $nodecount - 1;\n  if ($last > $#list) {\n    $last = $#list;\n  }\n  while (($last >= 0) &&\n         (abs(GetEntry($cumulative, $list[$last])) <= $nodelimit)) {\n    $last--;\n  }\n  if ($last < 0) {\n    print STDERR \"No nodes to print\\n\";\n    return 0;\n  }\n\n  if ($nodelimit > 0 || $edgelimit > 0) {\n    printf STDERR (\"Dropping nodes with <= %s %s; edges with <= %s abs(%s)\\n\",\n                   Unparse($nodelimit), Units(),\n                   Unparse($edgelimit), Units());\n  }\n\n  # Open DOT output file\n  my $output;\n  my $escaped_dot = ShellEscape(@DOT);\n  my $escaped_ps2pdf = ShellEscape(@PS2PDF);\n  if ($main::opt_gv) {\n    my $escaped_outfile = ShellEscape(TempName($main::next_tmpfile, \"ps\"));\n    $output = \"| $escaped_dot -Tps2 >$escaped_outfile\";\n  } elsif ($main::opt_evince) {\n    my $escaped_outfile = ShellEscape(TempName($main::next_tmpfile, \"pdf\"));\n    $output = \"| $escaped_dot -Tps2 | $escaped_ps2pdf - $escaped_outfile\";\n  } elsif ($main::opt_ps) {\n    $output = \"| $escaped_dot -Tps2\";\n  } elsif ($main::opt_pdf) {\n    $output = \"| $escaped_dot -Tps2 | $escaped_ps2pdf - -\";\n  } elsif ($main::opt_web || $main::opt_svg) {\n    # We need to post-process the SVG, so write to a temporary file always.\n    my $escaped_outfile = ShellEscape(TempName($main::next_tmpfile, \"svg\"));\n    $output = \"| $escaped_dot -Tsvg >$escaped_outfile\";\n  } elsif ($main::opt_gif) {\n    $output = \"| $escaped_dot -Tgif\";\n  } else {\n    $output = \">&STDOUT\";\n  }\n  open(DOT, $output) || error(\"$output: $!\\n\");\n\n  # Title\n  printf DOT (\"digraph \\\"%s; %s %s\\\" {\\n\",\n              $prog,\n              Unparse($overall_total),\n              Units());\n  if ($main::opt_pdf) {\n    # The output is more printable if we set the page size for dot.\n    printf DOT (\"size=\\\"8,11\\\"\\n\");\n  }\n  printf DOT (\"node [width=0.375,height=0.25];\\n\");\n\n  # Print legend\n  printf DOT (\"Legend [shape=box,fontsize=24,shape=plaintext,\" .\n              \"label=\\\"%s\\\\l%s\\\\l%s\\\\l%s\\\\l%s\\\\l\\\"];\\n\",\n              $prog,\n              sprintf(\"Total %s: %s\", Units(), Unparse($overall_total)),\n              sprintf(\"Focusing on: %s\", Unparse($local_total)),\n              sprintf(\"Dropped nodes with <= %s abs(%s)\",\n                      Unparse($nodelimit), Units()),\n              sprintf(\"Dropped edges with <= %s %s\",\n                      Unparse($edgelimit), Units())\n              );\n\n  # Print nodes\n  my %node = ();\n  my $nextnode = 1;\n  foreach my $a (@list[0..$last]) {\n    # Pick font size\n    my $f = GetEntry($flat, $a);\n    my $c = GetEntry($cumulative, $a);\n\n    my $fs = 8;\n    if ($local_total > 0) {\n      $fs = 8 + (50.0 * sqrt(abs($f * 1.0 / $local_total)));\n    }\n\n    $node{$a} = $nextnode++;\n    my $sym = $a;\n    $sym =~ s/\\s+/\\\\n/g;\n    $sym =~ s/::/\\\\n/g;\n\n    # Extra cumulative info to print for non-leaves\n    my $extra = \"\";\n    if ($f != $c) {\n      $extra = sprintf(\"\\\\rof %s (%s)\",\n                       Unparse($c),\n                       Percent($c, $local_total));\n    }\n    my $style = \"\";\n    if ($main::opt_heapcheck) {\n      if ($f > 0) {\n        # make leak-causing nodes more visible (add a background)\n        $style = \",style=filled,fillcolor=gray\"\n      } elsif ($f < 0) {\n        # make anti-leak-causing nodes (which almost never occur)\n        # stand out as well (triple border)\n        $style = \",peripheries=3\"\n      }\n    }\n\n    printf DOT (\"N%d [label=\\\"%s\\\\n%s (%s)%s\\\\r\" .\n                \"\\\",shape=box,fontsize=%.1f%s];\\n\",\n                $node{$a},\n                $sym,\n                Unparse($f),\n                Percent($f, $local_total),\n                $extra,\n                $fs,\n                $style,\n               );\n  }\n\n  # Get edges and counts per edge\n  my %edge = ();\n  my $n;\n  my $fullname_to_shortname_map = {};\n  FillFullnameToShortnameMap($symbols, $fullname_to_shortname_map);\n  foreach my $k (keys(%{$raw})) {\n    # TODO: omit low %age edges\n    $n = $raw->{$k};\n    my @translated = TranslateStack($symbols, $fullname_to_shortname_map, $k);\n    for (my $i = 1; $i <= $#translated; $i++) {\n      my $src = $translated[$i];\n      my $dst = $translated[$i-1];\n      #next if ($src eq $dst);  # Avoid self-edges?\n      if (exists($node{$src}) && exists($node{$dst})) {\n        my $edge_label = \"$src\\001$dst\";\n        if (!exists($edge{$edge_label})) {\n          $edge{$edge_label} = 0;\n        }\n        $edge{$edge_label} += $n;\n      }\n    }\n  }\n\n  # Print edges (process in order of decreasing counts)\n  my %indegree = ();   # Number of incoming edges added per node so far\n  my %outdegree = ();  # Number of outgoing edges added per node so far\n  foreach my $e (sort { $edge{$b} <=> $edge{$a} } keys(%edge)) {\n    my @x = split(/\\001/, $e);\n    $n = $edge{$e};\n\n    # Initialize degree of kept incoming and outgoing edges if necessary\n    my $src = $x[0];\n    my $dst = $x[1];\n    if (!exists($outdegree{$src})) { $outdegree{$src} = 0; }\n    if (!exists($indegree{$dst})) { $indegree{$dst} = 0; }\n\n    my $keep;\n    if ($indegree{$dst} == 0) {\n      # Keep edge if needed for reachability\n      $keep = 1;\n    } elsif (abs($n) <= $edgelimit) {\n      # Drop if we are below --edgefraction\n      $keep = 0;\n    } elsif ($outdegree{$src} >= $main::opt_maxdegree ||\n             $indegree{$dst} >= $main::opt_maxdegree) {\n      # Keep limited number of in/out edges per node\n      $keep = 0;\n    } else {\n      $keep = 1;\n    }\n\n    if ($keep) {\n      $outdegree{$src}++;\n      $indegree{$dst}++;\n\n      # Compute line width based on edge count\n      my $fraction = abs($local_total ? (3 * ($n / $local_total)) : 0);\n      if ($fraction > 1) { $fraction = 1; }\n      my $w = $fraction * 2;\n      if ($w < 1 && ($main::opt_web || $main::opt_svg)) {\n        # SVG output treats line widths < 1 poorly.\n        $w = 1;\n      }\n\n      # Dot sometimes segfaults if given edge weights that are too large, so\n      # we cap the weights at a large value\n      my $edgeweight = abs($n) ** 0.7;\n      if ($edgeweight > 100000) { $edgeweight = 100000; }\n      $edgeweight = int($edgeweight);\n\n      my $style = sprintf(\"setlinewidth(%f)\", $w);\n      if ($x[1] =~ m/\\(inline\\)/) {\n        $style .= \",dashed\";\n      }\n\n      # Use a slightly squashed function of the edge count as the weight\n      printf DOT (\"N%s -> N%s [label=%s, weight=%d, style=\\\"%s\\\"];\\n\",\n                  $node{$x[0]},\n                  $node{$x[1]},\n                  Unparse($n),\n                  $edgeweight,\n                  $style);\n    }\n  }\n\n  print DOT (\"}\\n\");\n  close(DOT);\n\n  if ($main::opt_web || $main::opt_svg) {\n    # Rewrite SVG to be more usable inside web browser.\n    RewriteSvg(TempName($main::next_tmpfile, \"svg\"));\n  }\n\n  return 1;\n}\n\nsub RewriteSvg {\n  my $svgfile = shift;\n\n  open(SVG, $svgfile) || die \"open temp svg: $!\";\n  my @svg = <SVG>;\n  close(SVG);\n  unlink $svgfile;\n  my $svg = join('', @svg);\n\n  # Dot's SVG output is\n  #\n  #    <svg width=\"___\" height=\"___\"\n  #     viewBox=\"___\" xmlns=...>\n  #    <g id=\"graph0\" transform=\"...\">\n  #    ...\n  #    </g>\n  #    </svg>\n  #\n  # Change it to\n  #\n  #    <svg width=\"100%\" height=\"100%\"\n  #     xmlns=...>\n  #    $svg_javascript\n  #    <g id=\"viewport\" transform=\"translate(0,0)\">\n  #    <g id=\"graph0\" transform=\"...\">\n  #    ...\n  #    </g>\n  #    </g>\n  #    </svg>\n\n  # Fix width, height; drop viewBox.\n  $svg =~ s/(?s)<svg width=\"[^\"]+\" height=\"[^\"]+\"(.*?)viewBox=\"[^\"]+\"/<svg width=\"100%\" height=\"100%\"$1/;\n\n  # Insert script, viewport <g> above first <g>\n  my $svg_javascript = SvgJavascript();\n  my $viewport = \"<g id=\\\"viewport\\\" transform=\\\"translate(0,0)\\\">\\n\";\n  $svg =~ s/<g id=\"graph\\d\"/$svg_javascript$viewport$&/;\n\n  # Insert final </g> above </svg>.\n  $svg =~ s/(.*)(<\\/svg>)/$1<\\/g>$2/;\n  $svg =~ s/<g id=\"graph\\d\"(.*?)/<g id=\"viewport\"$1/;\n\n  if ($main::opt_svg) {\n    # --svg: write to standard output.\n    print $svg;\n  } else {\n    # Write back to temporary file.\n    open(SVG, \">$svgfile\") || die \"open $svgfile: $!\";\n    print SVG $svg;\n    close(SVG);\n  }\n}\n\nsub SvgJavascript {\n  return <<'EOF';\n<script type=\"text/ecmascript\"><![CDATA[\n// SVGPan\n// http://www.cyberz.org/blog/2009/12/08/svgpan-a-javascript-svg-panzoomdrag-library/\n// Local modification: if(true || ...) below to force panning, never moving.\n\n/**\n *  SVGPan library 1.2\n * ====================\n *\n * Given an unique existing element with id \"viewport\", including the\n * the library into any SVG adds the following capabilities:\n *\n *  - Mouse panning\n *  - Mouse zooming (using the wheel)\n *  - Object dargging\n *\n * Known issues:\n *\n *  - Zooming (while panning) on Safari has still some issues\n *\n * Releases:\n *\n * 1.2, Sat Mar 20 08:42:50 GMT 2010, Zeng Xiaohui\n *\tFixed a bug with browser mouse handler interaction\n *\n * 1.1, Wed Feb  3 17:39:33 GMT 2010, Zeng Xiaohui\n *\tUpdated the zoom code to support the mouse wheel on Safari/Chrome\n *\n * 1.0, Andrea Leofreddi\n *\tFirst release\n *\n * This code is licensed under the following BSD license:\n *\n * Copyright 2009-2010 Andrea Leofreddi <a.leofreddi@itcharm.com>. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without modification, are\n * permitted provided that the following conditions are met:\n *\n *    1. Redistributions of source code must retain the above copyright notice, this list of\n *       conditions and the following disclaimer.\n *\n *    2. Redistributions in binary form must reproduce the above copyright notice, this list\n *       of conditions and the following disclaimer in the documentation and/or other materials\n *       provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY Andrea Leofreddi ``AS IS'' AND ANY EXPRESS OR IMPLIED\n * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND\n * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Andrea Leofreddi OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\n * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n * The views and conclusions contained in the software and documentation are those of the\n * authors and should not be interpreted as representing official policies, either expressed\n * or implied, of Andrea Leofreddi.\n */\n\nvar root = document.documentElement;\n\nvar state = 'none', stateTarget, stateOrigin, stateTf;\n\nsetupHandlers(root);\n\n/**\n * Register handlers\n */\nfunction setupHandlers(root){\n\tsetAttributes(root, {\n\t\t\"onmouseup\" : \"add(evt)\",\n\t\t\"onmousedown\" : \"handleMouseDown(evt)\",\n\t\t\"onmousemove\" : \"handleMouseMove(evt)\",\n\t\t\"onmouseup\" : \"handleMouseUp(evt)\",\n\t\t//\"onmouseout\" : \"handleMouseUp(evt)\", // Decomment this to stop the pan functionality when dragging out of the SVG element\n\t});\n\n\tif(navigator.userAgent.toLowerCase().indexOf('webkit') >= 0)\n\t\twindow.addEventListener('mousewheel', handleMouseWheel, false); // Chrome/Safari\n\telse\n\t\twindow.addEventListener('DOMMouseScroll', handleMouseWheel, false); // Others\n\n\tvar g = svgDoc.getElementById(\"svg\");\n\tg.width = \"100%\";\n\tg.height = \"100%\";\n}\n\n/**\n * Instance an SVGPoint object with given event coordinates.\n */\nfunction getEventPoint(evt) {\n\tvar p = root.createSVGPoint();\n\n\tp.x = evt.clientX;\n\tp.y = evt.clientY;\n\n\treturn p;\n}\n\n/**\n * Sets the current transform matrix of an element.\n */\nfunction setCTM(element, matrix) {\n\tvar s = \"matrix(\" + matrix.a + \",\" + matrix.b + \",\" + matrix.c + \",\" + matrix.d + \",\" + matrix.e + \",\" + matrix.f + \")\";\n\n\telement.setAttribute(\"transform\", s);\n}\n\n/**\n * Dumps a matrix to a string (useful for debug).\n */\nfunction dumpMatrix(matrix) {\n\tvar s = \"[ \" + matrix.a + \", \" + matrix.c + \", \" + matrix.e + \"\\n  \" + matrix.b + \", \" + matrix.d + \", \" + matrix.f + \"\\n  0, 0, 1 ]\";\n\n\treturn s;\n}\n\n/**\n * Sets attributes of an element.\n */\nfunction setAttributes(element, attributes){\n\tfor (i in attributes)\n\t\telement.setAttributeNS(null, i, attributes[i]);\n}\n\n/**\n * Handle mouse move event.\n */\nfunction handleMouseWheel(evt) {\n\tif(evt.preventDefault)\n\t\tevt.preventDefault();\n\n\tevt.returnValue = false;\n\n\tvar svgDoc = evt.target.ownerDocument;\n\n\tvar delta;\n\n\tif(evt.wheelDelta)\n\t\tdelta = evt.wheelDelta / 3600; // Chrome/Safari\n\telse\n\t\tdelta = evt.detail / -90; // Mozilla\n\n\tvar z = 1 + delta; // Zoom factor: 0.9/1.1\n\n\tvar g = svgDoc.getElementById(\"viewport\");\n\n\tvar p = getEventPoint(evt);\n\n\tp = p.matrixTransform(g.getCTM().inverse());\n\n\t// Compute new scale matrix in current mouse position\n\tvar k = root.createSVGMatrix().translate(p.x, p.y).scale(z).translate(-p.x, -p.y);\n\n        setCTM(g, g.getCTM().multiply(k));\n\n\tstateTf = stateTf.multiply(k.inverse());\n}\n\n/**\n * Handle mouse move event.\n */\nfunction handleMouseMove(evt) {\n\tif(evt.preventDefault)\n\t\tevt.preventDefault();\n\n\tevt.returnValue = false;\n\n\tvar svgDoc = evt.target.ownerDocument;\n\n\tvar g = svgDoc.getElementById(\"viewport\");\n\n\tif(state == 'pan') {\n\t\t// Pan mode\n\t\tvar p = getEventPoint(evt).matrixTransform(stateTf);\n\n\t\tsetCTM(g, stateTf.inverse().translate(p.x - stateOrigin.x, p.y - stateOrigin.y));\n\t} else if(state == 'move') {\n\t\t// Move mode\n\t\tvar p = getEventPoint(evt).matrixTransform(g.getCTM().inverse());\n\n\t\tsetCTM(stateTarget, root.createSVGMatrix().translate(p.x - stateOrigin.x, p.y - stateOrigin.y).multiply(g.getCTM().inverse()).multiply(stateTarget.getCTM()));\n\n\t\tstateOrigin = p;\n\t}\n}\n\n/**\n * Handle click event.\n */\nfunction handleMouseDown(evt) {\n\tif(evt.preventDefault)\n\t\tevt.preventDefault();\n\n\tevt.returnValue = false;\n\n\tvar svgDoc = evt.target.ownerDocument;\n\n\tvar g = svgDoc.getElementById(\"viewport\");\n\n\tif(true || evt.target.tagName == \"svg\") {\n\t\t// Pan mode\n\t\tstate = 'pan';\n\n\t\tstateTf = g.getCTM().inverse();\n\n\t\tstateOrigin = getEventPoint(evt).matrixTransform(stateTf);\n\t} else {\n\t\t// Move mode\n\t\tstate = 'move';\n\n\t\tstateTarget = evt.target;\n\n\t\tstateTf = g.getCTM().inverse();\n\n\t\tstateOrigin = getEventPoint(evt).matrixTransform(stateTf);\n\t}\n}\n\n/**\n * Handle mouse button release event.\n */\nfunction handleMouseUp(evt) {\n\tif(evt.preventDefault)\n\t\tevt.preventDefault();\n\n\tevt.returnValue = false;\n\n\tvar svgDoc = evt.target.ownerDocument;\n\n\tif(state == 'pan' || state == 'move') {\n\t\t// Quit pan mode\n\t\tstate = '';\n\t}\n}\n\n]]></script>\nEOF\n}\n\n# Provides a map from fullname to shortname for cases where the\n# shortname is ambiguous.  The symlist has both the fullname and\n# shortname for all symbols, which is usually fine, but sometimes --\n# such as overloaded functions -- two different fullnames can map to\n# the same shortname.  In that case, we use the address of the\n# function to disambiguate the two.  This function fills in a map that\n# maps fullnames to modified shortnames in such cases.  If a fullname\n# is not present in the map, the 'normal' shortname provided by the\n# symlist is the appropriate one to use.\nsub FillFullnameToShortnameMap {\n  my $symbols = shift;\n  my $fullname_to_shortname_map = shift;\n  my $shortnames_seen_once = {};\n  my $shortnames_seen_more_than_once = {};\n\n  foreach my $symlist (values(%{$symbols})) {\n    # TODO(csilvers): deal with inlined symbols too.\n    my $shortname = $symlist->[0];\n    my $fullname = $symlist->[2];\n    if ($fullname !~ /<[0-9a-fA-F]+>$/) {  # fullname doesn't end in an address\n      next;       # the only collisions we care about are when addresses differ\n    }\n    if (defined($shortnames_seen_once->{$shortname}) &&\n        $shortnames_seen_once->{$shortname} ne $fullname) {\n      $shortnames_seen_more_than_once->{$shortname} = 1;\n    } else {\n      $shortnames_seen_once->{$shortname} = $fullname;\n    }\n  }\n\n  foreach my $symlist (values(%{$symbols})) {\n    my $shortname = $symlist->[0];\n    my $fullname = $symlist->[2];\n    # TODO(csilvers): take in a list of addresses we care about, and only\n    # store in the map if $symlist->[1] is in that list.  Saves space.\n    next if defined($fullname_to_shortname_map->{$fullname});\n    if (defined($shortnames_seen_more_than_once->{$shortname})) {\n      if ($fullname =~ /<0*([^>]*)>$/) {   # fullname has address at end of it\n        $fullname_to_shortname_map->{$fullname} = \"$shortname\\@$1\";\n      }\n    }\n  }\n}\n\n# Return a small number that identifies the argument.\n# Multiple calls with the same argument will return the same number.\n# Calls with different arguments will return different numbers.\nsub ShortIdFor {\n  my $key = shift;\n  my $id = $main::uniqueid{$key};\n  if (!defined($id)) {\n    $id = keys(%main::uniqueid) + 1;\n    $main::uniqueid{$key} = $id;\n  }\n  return $id;\n}\n\n# Translate a stack of addresses into a stack of symbols\nsub TranslateStack {\n  my $symbols = shift;\n  my $fullname_to_shortname_map = shift;\n  my $k = shift;\n\n  my @addrs = split(/\\n/, $k);\n  my @result = ();\n  for (my $i = 0; $i <= $#addrs; $i++) {\n    my $a = $addrs[$i];\n\n    # Skip large addresses since they sometimes show up as fake entries on RH9\n    if (length($a) > 8 && $a gt \"7fffffffffffffff\") {\n      next;\n    }\n\n    if ($main::opt_disasm || $main::opt_list) {\n      # We want just the address for the key\n      push(@result, $a);\n      next;\n    }\n\n    my $symlist = $symbols->{$a};\n    if (!defined($symlist)) {\n      $symlist = [$a, \"\", $a];\n    }\n\n    # We can have a sequence of symbols for a particular entry\n    # (more than one symbol in the case of inlining).  Callers\n    # come before callees in symlist, so walk backwards since\n    # the translated stack should contain callees before callers.\n    for (my $j = $#{$symlist}; $j >= 2; $j -= 3) {\n      my $func = $symlist->[$j-2];\n      my $fileline = $symlist->[$j-1];\n      my $fullfunc = $symlist->[$j];\n      if (defined($fullname_to_shortname_map->{$fullfunc})) {\n        $func = $fullname_to_shortname_map->{$fullfunc};\n      }\n      if ($j > 2) {\n        $func = \"$func (inline)\";\n      }\n\n      # Do not merge nodes corresponding to Callback::Run since that\n      # causes confusing cycles in dot display.  Instead, we synthesize\n      # a unique name for this frame per caller.\n      if ($func =~ m/Callback.*::Run$/) {\n        my $caller = ($i > 0) ? $addrs[$i-1] : 0;\n        $func = \"Run#\" . ShortIdFor($caller);\n      }\n\n      if ($main::opt_addresses) {\n        push(@result, \"$a $func $fileline\");\n      } elsif ($main::opt_lines) {\n        if ($func eq '??' && $fileline eq '??:0') {\n          push(@result, \"$a\");\n        } else {\n          push(@result, \"$func $fileline\");\n        }\n      } elsif ($main::opt_functions) {\n        if ($func eq '??') {\n          push(@result, \"$a\");\n        } else {\n          push(@result, $func);\n        }\n      } elsif ($main::opt_files) {\n        if ($fileline eq '??:0' || $fileline eq '') {\n          push(@result, \"$a\");\n        } else {\n          my $f = $fileline;\n          $f =~ s/:\\d+$//;\n          push(@result, $f);\n        }\n      } else {\n        push(@result, $a);\n        last;  # Do not print inlined info\n      }\n    }\n  }\n\n  # print join(\",\", @addrs), \" => \", join(\",\", @result), \"\\n\";\n  return @result;\n}\n\n# Generate percent string for a number and a total\nsub Percent {\n  my $num = shift;\n  my $tot = shift;\n  if ($tot != 0) {\n    return sprintf(\"%.1f%%\", $num * 100.0 / $tot);\n  } else {\n    return ($num == 0) ? \"nan\" : (($num > 0) ? \"+inf\" : \"-inf\");\n  }\n}\n\n# Generate pretty-printed form of number\nsub Unparse {\n  my $num = shift;\n  if ($main::profile_type eq 'heap' || $main::profile_type eq 'growth') {\n    if ($main::opt_inuse_objects || $main::opt_alloc_objects) {\n      return sprintf(\"%d\", $num);\n    } else {\n      if ($main::opt_show_bytes) {\n        return sprintf(\"%d\", $num);\n      } else {\n        return sprintf(\"%.1f\", $num / 1048576.0);\n      }\n    }\n  } elsif ($main::profile_type eq 'contention' && !$main::opt_contentions) {\n    return sprintf(\"%.3f\", $num / 1e9); # Convert nanoseconds to seconds\n  } else {\n    return sprintf(\"%d\", $num);\n  }\n}\n\n# Alternate pretty-printed form: 0 maps to \".\"\nsub UnparseAlt {\n  my $num = shift;\n  if ($num == 0) {\n    return \".\";\n  } else {\n    return Unparse($num);\n  }\n}\n\n# Alternate pretty-printed form: 0 maps to \"\"\nsub HtmlPrintNumber {\n  my $num = shift;\n  if ($num == 0) {\n    return \"\";\n  } else {\n    return Unparse($num);\n  }\n}\n\n# Return output units\nsub Units {\n  if ($main::profile_type eq 'heap' || $main::profile_type eq 'growth') {\n    if ($main::opt_inuse_objects || $main::opt_alloc_objects) {\n      return \"objects\";\n    } else {\n      if ($main::opt_show_bytes) {\n        return \"B\";\n      } else {\n        return \"MB\";\n      }\n    }\n  } elsif ($main::profile_type eq 'contention' && !$main::opt_contentions) {\n    return \"seconds\";\n  } else {\n    return \"samples\";\n  }\n}\n\n##### Profile manipulation code #####\n\n# Generate flattened profile:\n# If count is charged to stack [a,b,c,d], in generated profile,\n# it will be charged to [a]\nsub FlatProfile {\n  my $profile = shift;\n  my $result = {};\n  foreach my $k (keys(%{$profile})) {\n    my $count = $profile->{$k};\n    my @addrs = split(/\\n/, $k);\n    if ($#addrs >= 0) {\n      AddEntry($result, $addrs[0], $count);\n    }\n  }\n  return $result;\n}\n\n# Generate cumulative profile:\n# If count is charged to stack [a,b,c,d], in generated profile,\n# it will be charged to [a], [b], [c], [d]\nsub CumulativeProfile {\n  my $profile = shift;\n  my $result = {};\n  foreach my $k (keys(%{$profile})) {\n    my $count = $profile->{$k};\n    my @addrs = split(/\\n/, $k);\n    foreach my $a (@addrs) {\n      AddEntry($result, $a, $count);\n    }\n  }\n  return $result;\n}\n\n# If the second-youngest PC on the stack is always the same, returns\n# that pc.  Otherwise, returns undef.\nsub IsSecondPcAlwaysTheSame {\n  my $profile = shift;\n\n  my $second_pc = undef;\n  foreach my $k (keys(%{$profile})) {\n    my @addrs = split(/\\n/, $k);\n    if ($#addrs < 1) {\n      return undef;\n    }\n    if (not defined $second_pc) {\n      $second_pc = $addrs[1];\n    } else {\n      if ($second_pc ne $addrs[1]) {\n        return undef;\n      }\n    }\n  }\n  return $second_pc;\n}\n\nsub ExtractSymbolLocation {\n  my $symbols = shift;\n  my $address = shift;\n  # 'addr2line' outputs \"??:0\" for unknown locations; we do the\n  # same to be consistent.\n  my $location = \"??:0:unknown\";\n  if (exists $symbols->{$address}) {\n    my $file = $symbols->{$address}->[1];\n    if ($file eq \"?\") {\n      $file = \"??:0\"\n    }\n    $location = $file . \":\" . $symbols->{$address}->[0];\n  }\n  return $location;\n}\n\n# Extracts a graph of calls.\nsub ExtractCalls {\n  my $symbols = shift;\n  my $profile = shift;\n\n  my $calls = {};\n  while( my ($stack_trace, $count) = each %$profile ) {\n    my @address = split(/\\n/, $stack_trace);\n    my $destination = ExtractSymbolLocation($symbols, $address[0]);\n    AddEntry($calls, $destination, $count);\n    for (my $i = 1; $i <= $#address; $i++) {\n      my $source = ExtractSymbolLocation($symbols, $address[$i]);\n      my $call = \"$source -> $destination\";\n      AddEntry($calls, $call, $count);\n      $destination = $source;\n    }\n  }\n\n  return $calls;\n}\n\nsub RemoveUninterestingFrames {\n  my $symbols = shift;\n  my $profile = shift;\n\n  # List of function names to skip\n  my %skip = ();\n  my $skip_regexp = 'NOMATCH';\n  if ($main::profile_type eq 'heap' || $main::profile_type eq 'growth') {\n    foreach my $name ('calloc',\n                      'cfree',\n                      'malloc',\n                      'free',\n                      'memalign',\n                      'posix_memalign',\n                      'pvalloc',\n                      'valloc',\n                      'realloc',\n                      'tc_calloc',\n                      'tc_cfree',\n                      'tc_malloc',\n                      'tc_free',\n                      'tc_memalign',\n                      'tc_posix_memalign',\n                      'tc_pvalloc',\n                      'tc_valloc',\n                      'tc_realloc',\n                      'tc_new',\n                      'tc_delete',\n                      'tc_newarray',\n                      'tc_deletearray',\n                      'tc_new_nothrow',\n                      'tc_newarray_nothrow',\n                      'do_malloc',\n                      '::do_malloc',   # new name -- got moved to an unnamed ns\n                      '::do_malloc_or_cpp_alloc',\n                      'DoSampledAllocation',\n                      'simple_alloc::allocate',\n                      '__malloc_alloc_template::allocate',\n                      '__builtin_delete',\n                      '__builtin_new',\n                      '__builtin_vec_delete',\n                      '__builtin_vec_new',\n                      'operator new',\n                      'operator new[]',\n                      # The entry to our memory-allocation routines on OS X\n                      'malloc_zone_malloc',\n                      'malloc_zone_calloc',\n                      'malloc_zone_valloc',\n                      'malloc_zone_realloc',\n                      'malloc_zone_memalign',\n                      'malloc_zone_free',\n                      # These mark the beginning/end of our custom sections\n                      '__start_google_malloc',\n                      '__stop_google_malloc',\n                      '__start_malloc_hook',\n                      '__stop_malloc_hook') {\n      $skip{$name} = 1;\n      $skip{\"_\" . $name} = 1;   # Mach (OS X) adds a _ prefix to everything\n    }\n    # TODO: Remove TCMalloc once everything has been\n    # moved into the tcmalloc:: namespace and we have flushed\n    # old code out of the system.\n    $skip_regexp = \"TCMalloc|^tcmalloc::\";\n  } elsif ($main::profile_type eq 'contention') {\n    foreach my $vname ('base::RecordLockProfileData',\n                       'base::SubmitMutexProfileData',\n                       'base::SubmitSpinLockProfileData',\n                       'Mutex::Unlock',\n                       'Mutex::UnlockSlow',\n                       'Mutex::ReaderUnlock',\n                       'MutexLock::~MutexLock',\n                       'SpinLock::Unlock',\n                       'SpinLock::SlowUnlock',\n                       'SpinLockHolder::~SpinLockHolder') {\n      $skip{$vname} = 1;\n    }\n  } elsif ($main::profile_type eq 'cpu') {\n    # Drop signal handlers used for CPU profile collection\n    # TODO(dpeng): this should not be necessary; it's taken\n    # care of by the general 2nd-pc mechanism below.\n    foreach my $name ('ProfileData::Add',           # historical\n                      'ProfileData::prof_handler',  # historical\n                      'CpuProfiler::prof_handler',\n                      '__FRAME_END__',\n                      '__pthread_sighandler',\n                      '__restore') {\n      $skip{$name} = 1;\n    }\n  } else {\n    # Nothing skipped for unknown types\n  }\n\n  if ($main::profile_type eq 'cpu') {\n    # If all the second-youngest program counters are the same,\n    # this STRONGLY suggests that it is an artifact of measurement,\n    # i.e., stack frames pushed by the CPU profiler signal handler.\n    # Hence, we delete them.\n    # (The topmost PC is read from the signal structure, not from\n    # the stack, so it does not get involved.)\n    while (my $second_pc = IsSecondPcAlwaysTheSame($profile)) {\n      my $result = {};\n      my $func = '';\n      if (exists($symbols->{$second_pc})) {\n        $second_pc = $symbols->{$second_pc}->[0];\n      }\n      print STDERR \"Removing $second_pc from all stack traces.\\n\";\n      foreach my $k (keys(%{$profile})) {\n        my $count = $profile->{$k};\n        my @addrs = split(/\\n/, $k);\n        splice @addrs, 1, 1;\n        my $reduced_path = join(\"\\n\", @addrs);\n        AddEntry($result, $reduced_path, $count);\n      }\n      $profile = $result;\n    }\n  }\n\n  my $result = {};\n  foreach my $k (keys(%{$profile})) {\n    my $count = $profile->{$k};\n    my @addrs = split(/\\n/, $k);\n    my @path = ();\n    foreach my $a (@addrs) {\n      if (exists($symbols->{$a})) {\n        my $func = $symbols->{$a}->[0];\n        if ($skip{$func} || ($func =~ m/$skip_regexp/)) {\n          next;\n        }\n      }\n      push(@path, $a);\n    }\n    my $reduced_path = join(\"\\n\", @path);\n    AddEntry($result, $reduced_path, $count);\n  }\n  return $result;\n}\n\n# Reduce profile to granularity given by user\nsub ReduceProfile {\n  my $symbols = shift;\n  my $profile = shift;\n  my $result = {};\n  my $fullname_to_shortname_map = {};\n  FillFullnameToShortnameMap($symbols, $fullname_to_shortname_map);\n  foreach my $k (keys(%{$profile})) {\n    my $count = $profile->{$k};\n    my @translated = TranslateStack($symbols, $fullname_to_shortname_map, $k);\n    my @path = ();\n    my %seen = ();\n    $seen{''} = 1;      # So that empty keys are skipped\n    foreach my $e (@translated) {\n      # To avoid double-counting due to recursion, skip a stack-trace\n      # entry if it has already been seen\n      if (!$seen{$e}) {\n        $seen{$e} = 1;\n        push(@path, $e);\n      }\n    }\n    my $reduced_path = join(\"\\n\", @path);\n    AddEntry($result, $reduced_path, $count);\n  }\n  return $result;\n}\n\n# Does the specified symbol array match the regexp?\nsub SymbolMatches {\n  my $sym = shift;\n  my $re = shift;\n  if (defined($sym)) {\n    for (my $i = 0; $i < $#{$sym}; $i += 3) {\n      if ($sym->[$i] =~ m/$re/ || $sym->[$i+1] =~ m/$re/) {\n        return 1;\n      }\n    }\n  }\n  return 0;\n}\n\n# Focus only on paths involving specified regexps\nsub FocusProfile {\n  my $symbols = shift;\n  my $profile = shift;\n  my $focus = shift;\n  my $result = {};\n  foreach my $k (keys(%{$profile})) {\n    my $count = $profile->{$k};\n    my @addrs = split(/\\n/, $k);\n    foreach my $a (@addrs) {\n      # Reply if it matches either the address/shortname/fileline\n      if (($a =~ m/$focus/) || SymbolMatches($symbols->{$a}, $focus)) {\n        AddEntry($result, $k, $count);\n        last;\n      }\n    }\n  }\n  return $result;\n}\n\n# Focus only on paths not involving specified regexps\nsub IgnoreProfile {\n  my $symbols = shift;\n  my $profile = shift;\n  my $ignore = shift;\n  my $result = {};\n  foreach my $k (keys(%{$profile})) {\n    my $count = $profile->{$k};\n    my @addrs = split(/\\n/, $k);\n    my $matched = 0;\n    foreach my $a (@addrs) {\n      # Reply if it matches either the address/shortname/fileline\n      if (($a =~ m/$ignore/) || SymbolMatches($symbols->{$a}, $ignore)) {\n        $matched = 1;\n        last;\n      }\n    }\n    if (!$matched) {\n      AddEntry($result, $k, $count);\n    }\n  }\n  return $result;\n}\n\n# Get total count in profile\nsub TotalProfile {\n  my $profile = shift;\n  my $result = 0;\n  foreach my $k (keys(%{$profile})) {\n    $result += $profile->{$k};\n  }\n  return $result;\n}\n\n# Add A to B\nsub AddProfile {\n  my $A = shift;\n  my $B = shift;\n\n  my $R = {};\n  # add all keys in A\n  foreach my $k (keys(%{$A})) {\n    my $v = $A->{$k};\n    AddEntry($R, $k, $v);\n  }\n  # add all keys in B\n  foreach my $k (keys(%{$B})) {\n    my $v = $B->{$k};\n    AddEntry($R, $k, $v);\n  }\n  return $R;\n}\n\n# Merges symbol maps\nsub MergeSymbols {\n  my $A = shift;\n  my $B = shift;\n\n  my $R = {};\n  foreach my $k (keys(%{$A})) {\n    $R->{$k} = $A->{$k};\n  }\n  if (defined($B)) {\n    foreach my $k (keys(%{$B})) {\n      $R->{$k} = $B->{$k};\n    }\n  }\n  return $R;\n}\n\n\n# Add A to B\nsub AddPcs {\n  my $A = shift;\n  my $B = shift;\n\n  my $R = {};\n  # add all keys in A\n  foreach my $k (keys(%{$A})) {\n    $R->{$k} = 1\n  }\n  # add all keys in B\n  foreach my $k (keys(%{$B})) {\n    $R->{$k} = 1\n  }\n  return $R;\n}\n\n# Subtract B from A\nsub SubtractProfile {\n  my $A = shift;\n  my $B = shift;\n\n  my $R = {};\n  foreach my $k (keys(%{$A})) {\n    my $v = $A->{$k} - GetEntry($B, $k);\n    if ($v < 0 && $main::opt_drop_negative) {\n      $v = 0;\n    }\n    AddEntry($R, $k, $v);\n  }\n  if (!$main::opt_drop_negative) {\n    # Take care of when subtracted profile has more entries\n    foreach my $k (keys(%{$B})) {\n      if (!exists($A->{$k})) {\n        AddEntry($R, $k, 0 - $B->{$k});\n      }\n    }\n  }\n  return $R;\n}\n\n# Get entry from profile; zero if not present\nsub GetEntry {\n  my $profile = shift;\n  my $k = shift;\n  if (exists($profile->{$k})) {\n    return $profile->{$k};\n  } else {\n    return 0;\n  }\n}\n\n# Add entry to specified profile\nsub AddEntry {\n  my $profile = shift;\n  my $k = shift;\n  my $n = shift;\n  if (!exists($profile->{$k})) {\n    $profile->{$k} = 0;\n  }\n  $profile->{$k} += $n;\n}\n\n# Add a stack of entries to specified profile, and add them to the $pcs\n# list.\nsub AddEntries {\n  my $profile = shift;\n  my $pcs = shift;\n  my $stack = shift;\n  my $count = shift;\n  my @k = ();\n\n  foreach my $e (split(/\\s+/, $stack)) {\n    my $pc = HexExtend($e);\n    $pcs->{$pc} = 1;\n    push @k, $pc;\n  }\n  AddEntry($profile, (join \"\\n\", @k), $count);\n}\n\n##### Code to profile a server dynamically #####\n\nsub CheckSymbolPage {\n  my $url = SymbolPageURL();\n  my $command = ShellEscape(@URL_FETCHER, $url);\n  open(SYMBOL, \"$command |\") or error($command);\n  my $line = <SYMBOL>;\n  $line =~ s/\\r//g;         # turn windows-looking lines into unix-looking lines\n  close(SYMBOL);\n  unless (defined($line)) {\n    error(\"$url doesn't exist\\n\");\n  }\n\n  if ($line =~ /^num_symbols:\\s+(\\d+)$/) {\n    if ($1 == 0) {\n      error(\"Stripped binary. No symbols available.\\n\");\n    }\n  } else {\n    error(\"Failed to get the number of symbols from $url\\n\");\n  }\n}\n\nsub IsProfileURL {\n  my $profile_name = shift;\n  if (-f $profile_name) {\n    printf STDERR \"Using local file $profile_name.\\n\";\n    return 0;\n  }\n  return 1;\n}\n\nsub ParseProfileURL {\n  my $profile_name = shift;\n\n  if (!defined($profile_name) || $profile_name eq \"\") {\n    return ();\n  }\n\n  # Split profile URL - matches all non-empty strings, so no test.\n  $profile_name =~ m,^(https?://)?([^/]+)(.*?)(/|$PROFILES)?$,;\n\n  my $proto = $1 || \"http://\";\n  my $hostport = $2;\n  my $prefix = $3;\n  my $profile = $4 || \"/\";\n\n  my $host = $hostport;\n  $host =~ s/:.*//;\n\n  my $baseurl = \"$proto$hostport$prefix\";\n  return ($host, $baseurl, $profile);\n}\n\n# We fetch symbols from the first profile argument.\nsub SymbolPageURL {\n  my ($host, $baseURL, $path) = ParseProfileURL($main::pfile_args[0]);\n  return \"$baseURL$SYMBOL_PAGE\";\n}\n\nsub FetchProgramName() {\n  my ($host, $baseURL, $path) = ParseProfileURL($main::pfile_args[0]);\n  my $url = \"$baseURL$PROGRAM_NAME_PAGE\";\n  my $command_line = ShellEscape(@URL_FETCHER, $url);\n  open(CMDLINE, \"$command_line |\") or error($command_line);\n  my $cmdline = <CMDLINE>;\n  $cmdline =~ s/\\r//g;   # turn windows-looking lines into unix-looking lines\n  close(CMDLINE);\n  error(\"Failed to get program name from $url\\n\") unless defined($cmdline);\n  $cmdline =~ s/\\x00.+//;  # Remove argv[1] and latters.\n  $cmdline =~ s!\\n!!g;  # Remove LFs.\n  return $cmdline;\n}\n\n# Gee, curl's -L (--location) option isn't reliable at least\n# with its 7.12.3 version.  Curl will forget to post data if\n# there is a redirection.  This function is a workaround for\n# curl.  Redirection happens on borg hosts.\nsub ResolveRedirectionForCurl {\n  my $url = shift;\n  my $command_line = ShellEscape(@URL_FETCHER, \"--head\", $url);\n  open(CMDLINE, \"$command_line |\") or error($command_line);\n  while (<CMDLINE>) {\n    s/\\r//g;         # turn windows-looking lines into unix-looking lines\n    if (/^Location: (.*)/) {\n      $url = $1;\n    }\n  }\n  close(CMDLINE);\n  return $url;\n}\n\n# Add a timeout flat to URL_FETCHER.  Returns a new list.\nsub AddFetchTimeout {\n  my $timeout = shift;\n  my @fetcher = shift;\n  if (defined($timeout)) {\n    if (join(\" \", @fetcher) =~ m/\\bcurl -s/) {\n      push(@fetcher, \"--max-time\", sprintf(\"%d\", $timeout));\n    } elsif (join(\" \", @fetcher) =~ m/\\brpcget\\b/) {\n      push(@fetcher, sprintf(\"--deadline=%d\", $timeout));\n    }\n  }\n  return @fetcher;\n}\n\n# Reads a symbol map from the file handle name given as $1, returning\n# the resulting symbol map.  Also processes variables relating to symbols.\n# Currently, the only variable processed is 'binary=<value>' which updates\n# $main::prog to have the correct program name.\nsub ReadSymbols {\n  my $in = shift;\n  my $map = {};\n  while (<$in>) {\n    s/\\r//g;         # turn windows-looking lines into unix-looking lines\n    # Removes all the leading zeroes from the symbols, see comment below.\n    if (m/^0x0*([0-9a-f]+)\\s+(.+)/) {\n      $map->{$1} = $2;\n    } elsif (m/^---/) {\n      last;\n    } elsif (m/^([a-z][^=]*)=(.*)$/ ) {\n      my ($variable, $value) = ($1, $2);\n      for ($variable, $value) {\n        s/^\\s+//;\n        s/\\s+$//;\n      }\n      if ($variable eq \"binary\") {\n        if ($main::prog ne $UNKNOWN_BINARY && $main::prog ne $value) {\n          printf STDERR (\"Warning: Mismatched binary name '%s', using '%s'.\\n\",\n                         $main::prog, $value);\n        }\n        $main::prog = $value;\n      } else {\n        printf STDERR (\"Ignoring unknown variable in symbols list: \" .\n            \"'%s' = '%s'\\n\", $variable, $value);\n      }\n    }\n  }\n  return $map;\n}\n\n# Fetches and processes symbols to prepare them for use in the profile output\n# code.  If the optional 'symbol_map' arg is not given, fetches symbols from\n# $SYMBOL_PAGE for all PC values found in profile.  Otherwise, the raw symbols\n# are assumed to have already been fetched into 'symbol_map' and are simply\n# extracted and processed.\nsub FetchSymbols {\n  my $pcset = shift;\n  my $symbol_map = shift;\n\n  my %seen = ();\n  my @pcs = grep { !$seen{$_}++ } keys(%$pcset);  # uniq\n\n  if (!defined($symbol_map)) {\n    my $post_data = join(\"+\", sort((map {\"0x\" . \"$_\"} @pcs)));\n\n    open(POSTFILE, \">$main::tmpfile_sym\");\n    print POSTFILE $post_data;\n    close(POSTFILE);\n\n    my $url = SymbolPageURL();\n\n    my $command_line;\n    if (join(\" \", @URL_FETCHER) =~ m/\\bcurl -s/) {\n      $url = ResolveRedirectionForCurl($url);\n      $command_line = ShellEscape(@URL_FETCHER, \"-d\", \"\\@$main::tmpfile_sym\",\n                                  $url);\n    } else {\n      $command_line = (ShellEscape(@URL_FETCHER, \"--post\", $url)\n                       . \" < \" . ShellEscape($main::tmpfile_sym));\n    }\n    # We use c++filt in case $SYMBOL_PAGE gives us mangled symbols.\n    my $escaped_cppfilt = ShellEscape($obj_tool_map{\"c++filt\"});\n    open(SYMBOL, \"$command_line | $escaped_cppfilt |\") or error($command_line);\n    $symbol_map = ReadSymbols(*SYMBOL{IO});\n    close(SYMBOL);\n  }\n\n  my $symbols = {};\n  foreach my $pc (@pcs) {\n    my $fullname;\n    # For 64 bits binaries, symbols are extracted with 8 leading zeroes.\n    # Then /symbol reads the long symbols in as uint64, and outputs\n    # the result with a \"0x%08llx\" format which get rid of the zeroes.\n    # By removing all the leading zeroes in both $pc and the symbols from\n    # /symbol, the symbols match and are retrievable from the map.\n    my $shortpc = $pc;\n    $shortpc =~ s/^0*//;\n    # Each line may have a list of names, which includes the function\n    # and also other functions it has inlined.  They are separated (in\n    # PrintSymbolizedProfile), by --, which is illegal in function names.\n    my $fullnames;\n    if (defined($symbol_map->{$shortpc})) {\n      $fullnames = $symbol_map->{$shortpc};\n    } else {\n      $fullnames = \"0x\" . $pc;  # Just use addresses\n    }\n    my $sym = [];\n    $symbols->{$pc} = $sym;\n    foreach my $fullname (split(\"--\", $fullnames)) {\n      my $name = ShortFunctionName($fullname);\n      push(@{$sym}, $name, \"?\", $fullname);\n    }\n  }\n  return $symbols;\n}\n\nsub BaseName {\n  my $file_name = shift;\n  $file_name =~ s!^.*/!!;  # Remove directory name\n  return $file_name;\n}\n\nsub MakeProfileBaseName {\n  my ($binary_name, $profile_name) = @_;\n  my ($host, $baseURL, $path) = ParseProfileURL($profile_name);\n  my $binary_shortname = BaseName($binary_name);\n  return sprintf(\"%s.%s.%s\",\n                 $binary_shortname, $main::op_time, $host);\n}\n\nsub FetchDynamicProfile {\n  my $binary_name = shift;\n  my $profile_name = shift;\n  my $fetch_name_only = shift;\n  my $encourage_patience = shift;\n\n  if (!IsProfileURL($profile_name)) {\n    return $profile_name;\n  } else {\n    my ($host, $baseURL, $path) = ParseProfileURL($profile_name);\n    if ($path eq \"\" || $path eq \"/\") {\n      # Missing type specifier defaults to cpu-profile\n      $path = $PROFILE_PAGE;\n    }\n\n    my $profile_file = MakeProfileBaseName($binary_name, $profile_name);\n\n    my $url = \"$baseURL$path\";\n    my $fetch_timeout = undef;\n    if ($path =~ m/$PROFILE_PAGE|$PMUPROFILE_PAGE/) {\n      if ($path =~ m/[?]/) {\n        $url .= \"&\";\n      } else {\n        $url .= \"?\";\n      }\n      $url .= sprintf(\"seconds=%d\", $main::opt_seconds);\n      $fetch_timeout = $main::opt_seconds * 1.01 + 60;\n    } else {\n      # For non-CPU profiles, we add a type-extension to\n      # the target profile file name.\n      my $suffix = $path;\n      $suffix =~ s,/,.,g;\n      $profile_file .= $suffix;\n    }\n\n    my $profile_dir = $ENV{\"PPROF_TMPDIR\"} || ($ENV{HOME} . \"/pprof\");\n    if (! -d $profile_dir) {\n      mkdir($profile_dir)\n          || die(\"Unable to create profile directory $profile_dir: $!\\n\");\n    }\n    my $tmp_profile = \"$profile_dir/.tmp.$profile_file\";\n    my $real_profile = \"$profile_dir/$profile_file\";\n\n    if ($fetch_name_only > 0) {\n      return $real_profile;\n    }\n\n    my @fetcher = AddFetchTimeout($fetch_timeout, @URL_FETCHER);\n    my $cmd = ShellEscape(@fetcher, $url) . \" > \" . ShellEscape($tmp_profile);\n    if ($path =~ m/$PROFILE_PAGE|$PMUPROFILE_PAGE|$CENSUSPROFILE_PAGE/){\n      print STDERR \"Gathering CPU profile from $url for $main::opt_seconds seconds to\\n  ${real_profile}\\n\";\n      if ($encourage_patience) {\n        print STDERR \"Be patient...\\n\";\n      }\n    } else {\n      print STDERR \"Fetching $path profile from $url to\\n  ${real_profile}\\n\";\n    }\n\n    (system($cmd) == 0) || error(\"Failed to get profile: $cmd: $!\\n\");\n    (system(\"mv\", $tmp_profile, $real_profile) == 0) || error(\"Unable to rename profile\\n\");\n    print STDERR \"Wrote profile to $real_profile\\n\";\n    $main::collected_profile = $real_profile;\n    return $main::collected_profile;\n  }\n}\n\n# Collect profiles in parallel\nsub FetchDynamicProfiles {\n  my $items = scalar(@main::pfile_args);\n  my $levels = log($items) / log(2);\n\n  if ($items == 1) {\n    $main::profile_files[0] = FetchDynamicProfile($main::prog, $main::pfile_args[0], 0, 1);\n  } else {\n    # math rounding issues\n    if ((2 ** $levels) < $items) {\n     $levels++;\n    }\n    my $count = scalar(@main::pfile_args);\n    for (my $i = 0; $i < $count; $i++) {\n      $main::profile_files[$i] = FetchDynamicProfile($main::prog, $main::pfile_args[$i], 1, 0);\n    }\n    print STDERR \"Fetching $count profiles, Be patient...\\n\";\n    FetchDynamicProfilesRecurse($levels, 0, 0);\n    $main::collected_profile = join(\" \\\\\\n    \", @main::profile_files);\n  }\n}\n\n# Recursively fork a process to get enough processes\n# collecting profiles\nsub FetchDynamicProfilesRecurse {\n  my $maxlevel = shift;\n  my $level = shift;\n  my $position = shift;\n\n  if (my $pid = fork()) {\n    $position = 0 | ($position << 1);\n    TryCollectProfile($maxlevel, $level, $position);\n    wait;\n  } else {\n    $position = 1 | ($position << 1);\n    TryCollectProfile($maxlevel, $level, $position);\n    cleanup();\n    exit(0);\n  }\n}\n\n# Collect a single profile\nsub TryCollectProfile {\n  my $maxlevel = shift;\n  my $level = shift;\n  my $position = shift;\n\n  if ($level >= ($maxlevel - 1)) {\n    if ($position < scalar(@main::pfile_args)) {\n      FetchDynamicProfile($main::prog, $main::pfile_args[$position], 0, 0);\n    }\n  } else {\n    FetchDynamicProfilesRecurse($maxlevel, $level+1, $position);\n  }\n}\n\n##### Parsing code #####\n\n# Provide a small streaming-read module to handle very large\n# cpu-profile files.  Stream in chunks along a sliding window.\n# Provides an interface to get one 'slot', correctly handling\n# endian-ness differences.  A slot is one 32-bit or 64-bit word\n# (depending on the input profile).  We tell endianness and bit-size\n# for the profile by looking at the first 8 bytes: in cpu profiles,\n# the second slot is always 3 (we'll accept anything that's not 0).\nBEGIN {\n  package CpuProfileStream;\n\n  sub new {\n    my ($class, $file, $fname) = @_;\n    my $self = { file        => $file,\n                 base        => 0,\n                 stride      => 512 * 1024,   # must be a multiple of bitsize/8\n                 slots       => [],\n                 unpack_code => \"\",           # N for big-endian, V for little\n                 perl_is_64bit => 1,          # matters if profile is 64-bit\n    };\n    bless $self, $class;\n    # Let unittests adjust the stride\n    if ($main::opt_test_stride > 0) {\n      $self->{stride} = $main::opt_test_stride;\n    }\n    # Read the first two slots to figure out bitsize and endianness.\n    my $slots = $self->{slots};\n    my $str;\n    read($self->{file}, $str, 8);\n    # Set the global $address_length based on what we see here.\n    # 8 is 32-bit (8 hexadecimal chars); 16 is 64-bit (16 hexadecimal chars).\n    $address_length = ($str eq (chr(0)x8)) ? 16 : 8;\n    if ($address_length == 8) {\n      if (substr($str, 6, 2) eq chr(0)x2) {\n        $self->{unpack_code} = 'V';  # Little-endian.\n      } elsif (substr($str, 4, 2) eq chr(0)x2) {\n        $self->{unpack_code} = 'N';  # Big-endian\n      } else {\n        ::error(\"$fname: header size >= 2**16\\n\");\n      }\n      @$slots = unpack($self->{unpack_code} . \"*\", $str);\n    } else {\n      # If we're a 64-bit profile, check if we're a 64-bit-capable\n      # perl.  Otherwise, each slot will be represented as a float\n      # instead of an int64, losing precision and making all the\n      # 64-bit addresses wrong.  We won't complain yet, but will\n      # later if we ever see a value that doesn't fit in 32 bits.\n      my $has_q = 0;\n      eval { $has_q = pack(\"Q\", \"1\") ? 1 : 1; };\n      if (!$has_q) {\n        $self->{perl_is_64bit} = 0;\n      }\n      read($self->{file}, $str, 8);\n      if (substr($str, 4, 4) eq chr(0)x4) {\n        # We'd love to use 'Q', but it's a) not universal, b) not endian-proof.\n        $self->{unpack_code} = 'V';  # Little-endian.\n      } elsif (substr($str, 0, 4) eq chr(0)x4) {\n        $self->{unpack_code} = 'N';  # Big-endian\n      } else {\n        ::error(\"$fname: header size >= 2**32\\n\");\n      }\n      my @pair = unpack($self->{unpack_code} . \"*\", $str);\n      # Since we know one of the pair is 0, it's fine to just add them.\n      @$slots = (0, $pair[0] + $pair[1]);\n    }\n    return $self;\n  }\n\n  # Load more data when we access slots->get(X) which is not yet in memory.\n  sub overflow {\n    my ($self) = @_;\n    my $slots = $self->{slots};\n    $self->{base} += $#$slots + 1;   # skip over data we're replacing\n    my $str;\n    read($self->{file}, $str, $self->{stride});\n    if ($address_length == 8) {      # the 32-bit case\n      # This is the easy case: unpack provides 32-bit unpacking primitives.\n      @$slots = unpack($self->{unpack_code} . \"*\", $str);\n    } else {\n      # We need to unpack 32 bits at a time and combine.\n      my @b32_values = unpack($self->{unpack_code} . \"*\", $str);\n      my @b64_values = ();\n      for (my $i = 0; $i < $#b32_values; $i += 2) {\n        # TODO(csilvers): if this is a 32-bit perl, the math below\n        #    could end up in a too-large int, which perl will promote\n        #    to a double, losing necessary precision.  Deal with that.\n        #    Right now, we just die.\n        my ($lo, $hi) = ($b32_values[$i], $b32_values[$i+1]);\n        if ($self->{unpack_code} eq 'N') {    # big-endian\n          ($lo, $hi) = ($hi, $lo);\n        }\n        my $value = $lo + $hi * (2**32);\n        if (!$self->{perl_is_64bit} &&   # check value is exactly represented\n            (($value % (2**32)) != $lo || int($value / (2**32)) != $hi)) {\n          ::error(\"Need a 64-bit perl to process this 64-bit profile.\\n\");\n        }\n        push(@b64_values, $value);\n      }\n      @$slots = @b64_values;\n    }\n  }\n\n  # Access the i-th long in the file (logically), or -1 at EOF.\n  sub get {\n    my ($self, $idx) = @_;\n    my $slots = $self->{slots};\n    while ($#$slots >= 0) {\n      if ($idx < $self->{base}) {\n        # The only time we expect a reference to $slots[$i - something]\n        # after referencing $slots[$i] is reading the very first header.\n        # Since $stride > |header|, that shouldn't cause any lookback\n        # errors.  And everything after the header is sequential.\n        print STDERR \"Unexpected look-back reading CPU profile\";\n        return -1;   # shrug, don't know what better to return\n      } elsif ($idx > $self->{base} + $#$slots) {\n        $self->overflow();\n      } else {\n        return $slots->[$idx - $self->{base}];\n      }\n    }\n    # If we get here, $slots is [], which means we've reached EOF\n    return -1;  # unique since slots is supposed to hold unsigned numbers\n  }\n}\n\n# Reads the top, 'header' section of a profile, and returns the last\n# line of the header, commonly called a 'header line'.  The header\n# section of a profile consists of zero or more 'command' lines that\n# are instructions to pprof, which pprof executes when reading the\n# header.  All 'command' lines start with a %.  After the command\n# lines is the 'header line', which is a profile-specific line that\n# indicates what type of profile it is, and perhaps other global\n# information about the profile.  For instance, here's a header line\n# for a heap profile:\n#   heap profile:     53:    38236 [  5525:  1284029] @ heapprofile\n# For historical reasons, the CPU profile does not contain a text-\n# readable header line.  If the profile looks like a CPU profile,\n# this function returns \"\".  If no header line could be found, this\n# function returns undef.\n#\n# The following commands are recognized:\n#   %warn -- emit the rest of this line to stderr, prefixed by 'WARNING:'\n#\n# The input file should be in binmode.\nsub ReadProfileHeader {\n  local *PROFILE = shift;\n  my $firstchar = \"\";\n  my $line = \"\";\n  read(PROFILE, $firstchar, 1);\n  seek(PROFILE, -1, 1);                    # unread the firstchar\n  if ($firstchar !~ /[[:print:]]/) {       # is not a text character\n    return \"\";\n  }\n  while (defined($line = <PROFILE>)) {\n    $line =~ s/\\r//g;   # turn windows-looking lines into unix-looking lines\n    if ($line =~ /^%warn\\s+(.*)/) {        # 'warn' command\n      # Note this matches both '%warn blah\\n' and '%warn\\n'.\n      print STDERR \"WARNING: $1\\n\";        # print the rest of the line\n    } elsif ($line =~ /^%/) {\n      print STDERR \"Ignoring unknown command from profile header: $line\";\n    } else {\n      # End of commands, must be the header line.\n      return $line;\n    }\n  }\n  return undef;     # got to EOF without seeing a header line\n}\n\nsub IsSymbolizedProfileFile {\n  my $file_name = shift;\n  if (!(-e $file_name) || !(-r $file_name)) {\n    return 0;\n  }\n  # Check if the file contains a symbol-section marker.\n  open(TFILE, \"<$file_name\");\n  binmode TFILE;\n  my $firstline = ReadProfileHeader(*TFILE);\n  close(TFILE);\n  if (!$firstline) {\n    return 0;\n  }\n  $SYMBOL_PAGE =~ m,[^/]+$,;    # matches everything after the last slash\n  my $symbol_marker = $&;\n  return $firstline =~ /^--- *$symbol_marker/;\n}\n\n# Parse profile generated by common/profiler.cc and return a reference\n# to a map:\n#      $result->{version}     Version number of profile file\n#      $result->{period}      Sampling period (in microseconds)\n#      $result->{profile}     Profile object\n#      $result->{map}         Memory map info from profile\n#      $result->{pcs}         Hash of all PC values seen, key is hex address\nsub ReadProfile {\n  my $prog = shift;\n  my $fname = shift;\n  my $result;            # return value\n\n  $CONTENTION_PAGE =~ m,[^/]+$,;    # matches everything after the last slash\n  my $contention_marker = $&;\n  $GROWTH_PAGE  =~ m,[^/]+$,;    # matches everything after the last slash\n  my $growth_marker = $&;\n  $SYMBOL_PAGE =~ m,[^/]+$,;    # matches everything after the last slash\n  my $symbol_marker = $&;\n  $PROFILE_PAGE =~ m,[^/]+$,;    # matches everything after the last slash\n  my $profile_marker = $&;\n\n  # Look at first line to see if it is a heap or a CPU profile.\n  # CPU profile may start with no header at all, and just binary data\n  # (starting with \\0\\0\\0\\0) -- in that case, don't try to read the\n  # whole firstline, since it may be gigabytes(!) of data.\n  open(PROFILE, \"<$fname\") || error(\"$fname: $!\\n\");\n  binmode PROFILE;      # New perls do UTF-8 processing\n  my $header = ReadProfileHeader(*PROFILE);\n  if (!defined($header)) {   # means \"at EOF\"\n    error(\"Profile is empty.\\n\");\n  }\n\n  my $symbols;\n  if ($header =~ m/^--- *$symbol_marker/o) {\n    # Verify that the user asked for a symbolized profile\n    if (!$main::use_symbolized_profile) {\n      # we have both a binary and symbolized profiles, abort\n      error(\"FATAL ERROR: Symbolized profile\\n   $fname\\ncannot be used with \" .\n            \"a binary arg. Try again without passing\\n   $prog\\n\");\n    }\n    # Read the symbol section of the symbolized profile file.\n    $symbols = ReadSymbols(*PROFILE{IO});\n    # Read the next line to get the header for the remaining profile.\n    $header = ReadProfileHeader(*PROFILE) || \"\";\n  }\n\n  $main::profile_type = '';\n  if ($header =~ m/^heap profile:.*$growth_marker/o) {\n    $main::profile_type = 'growth';\n    $result =  ReadHeapProfile($prog, *PROFILE, $header);\n  } elsif ($header =~ m/^heap profile:/) {\n    $main::profile_type = 'heap';\n    $result =  ReadHeapProfile($prog, *PROFILE, $header);\n  } elsif ($header =~ m/^--- *$contention_marker/o) {\n    $main::profile_type = 'contention';\n    $result = ReadSynchProfile($prog, *PROFILE);\n  } elsif ($header =~ m/^--- *Stacks:/) {\n    print STDERR\n      \"Old format contention profile: mistakenly reports \" .\n      \"condition variable signals as lock contentions.\\n\";\n    $main::profile_type = 'contention';\n    $result = ReadSynchProfile($prog, *PROFILE);\n  } elsif ($header =~ m/^--- *$profile_marker/) {\n    # the binary cpu profile data starts immediately after this line\n    $main::profile_type = 'cpu';\n    $result = ReadCPUProfile($prog, $fname, *PROFILE);\n  } else {\n    if (defined($symbols)) {\n      # a symbolized profile contains a format we don't recognize, bail out\n      error(\"$fname: Cannot recognize profile section after symbols.\\n\");\n    }\n    # no ascii header present -- must be a CPU profile\n    $main::profile_type = 'cpu';\n    $result = ReadCPUProfile($prog, $fname, *PROFILE);\n  }\n\n  close(PROFILE);\n\n  # if we got symbols along with the profile, return those as well\n  if (defined($symbols)) {\n    $result->{symbols} = $symbols;\n  }\n\n  return $result;\n}\n\n# Subtract one from caller pc so we map back to call instr.\n# However, don't do this if we're reading a symbolized profile\n# file, in which case the subtract-one was done when the file\n# was written.\n#\n# We apply the same logic to all readers, though ReadCPUProfile uses an\n# independent implementation.\nsub FixCallerAddresses {\n  my $stack = shift;\n  if ($main::use_symbolized_profile) {\n    return $stack;\n  } else {\n    $stack =~ /(\\s)/;\n    my $delimiter = $1;\n    my @addrs = split(' ', $stack);\n    my @fixedaddrs;\n    $#fixedaddrs = $#addrs;\n    if ($#addrs >= 0) {\n      $fixedaddrs[0] = $addrs[0];\n    }\n    for (my $i = 1; $i <= $#addrs; $i++) {\n      $fixedaddrs[$i] = AddressSub($addrs[$i], \"0x1\");\n    }\n    return join $delimiter, @fixedaddrs;\n  }\n}\n\n# CPU profile reader\nsub ReadCPUProfile {\n  my $prog = shift;\n  my $fname = shift;       # just used for logging\n  local *PROFILE = shift;\n  my $version;\n  my $period;\n  my $i;\n  my $profile = {};\n  my $pcs = {};\n\n  # Parse string into array of slots.\n  my $slots = CpuProfileStream->new(*PROFILE, $fname);\n\n  # Read header.  The current header version is a 5-element structure\n  # containing:\n  #   0: header count (always 0)\n  #   1: header \"words\" (after this one: 3)\n  #   2: format version (0)\n  #   3: sampling period (usec)\n  #   4: unused padding (always 0)\n  if ($slots->get(0) != 0 ) {\n    error(\"$fname: not a profile file, or old format profile file\\n\");\n  }\n  $i = 2 + $slots->get(1);\n  $version = $slots->get(2);\n  $period = $slots->get(3);\n  # Do some sanity checking on these header values.\n  if ($version > (2**32) || $period > (2**32) || $i > (2**32) || $i < 5) {\n    error(\"$fname: not a profile file, or corrupted profile file\\n\");\n  }\n\n  # Parse profile\n  while ($slots->get($i) != -1) {\n    my $n = $slots->get($i++);\n    my $d = $slots->get($i++);\n    if ($d > (2**16)) {  # TODO(csilvers): what's a reasonable max-stack-depth?\n      my $addr = sprintf(\"0%o\", $i * ($address_length == 8 ? 4 : 8));\n      print STDERR \"At index $i (address $addr):\\n\";\n      error(\"$fname: stack trace depth >= 2**32\\n\");\n    }\n    if ($slots->get($i) == 0) {\n      # End of profile data marker\n      $i += $d;\n      last;\n    }\n\n    # Make key out of the stack entries\n    my @k = ();\n    for (my $j = 0; $j < $d; $j++) {\n      my $pc = $slots->get($i+$j);\n      # Subtract one from caller pc so we map back to call instr.\n      # However, don't do this if we're reading a symbolized profile\n      # file, in which case the subtract-one was done when the file\n      # was written.\n      if ($j > 0 && !$main::use_symbolized_profile) {\n        $pc--;\n      }\n      $pc = sprintf(\"%0*x\", $address_length, $pc);\n      $pcs->{$pc} = 1;\n      push @k, $pc;\n    }\n\n    AddEntry($profile, (join \"\\n\", @k), $n);\n    $i += $d;\n  }\n\n  # Parse map\n  my $map = '';\n  seek(PROFILE, $i * 4, 0);\n  read(PROFILE, $map, (stat PROFILE)[7]);\n\n  my $r = {};\n  $r->{version} = $version;\n  $r->{period} = $period;\n  $r->{profile} = $profile;\n  $r->{libs} = ParseLibraries($prog, $map, $pcs);\n  $r->{pcs} = $pcs;\n\n  return $r;\n}\n\nsub ReadHeapProfile {\n  my $prog = shift;\n  local *PROFILE = shift;\n  my $header = shift;\n\n  my $index = 1;\n  if ($main::opt_inuse_space) {\n    $index = 1;\n  } elsif ($main::opt_inuse_objects) {\n    $index = 0;\n  } elsif ($main::opt_alloc_space) {\n    $index = 3;\n  } elsif ($main::opt_alloc_objects) {\n    $index = 2;\n  }\n\n  # Find the type of this profile.  The header line looks like:\n  #    heap profile:   1246:  8800744 [  1246:  8800744] @ <heap-url>/266053\n  # There are two pairs <count: size>, the first inuse objects/space, and the\n  # second allocated objects/space.  This is followed optionally by a profile\n  # type, and if that is present, optionally by a sampling frequency.\n  # For remote heap profiles (v1):\n  # The interpretation of the sampling frequency is that the profiler, for\n  # each sample, calculates a uniformly distributed random integer less than\n  # the given value, and records the next sample after that many bytes have\n  # been allocated.  Therefore, the expected sample interval is half of the\n  # given frequency.  By default, if not specified, the expected sample\n  # interval is 128KB.  Only remote-heap-page profiles are adjusted for\n  # sample size.\n  # For remote heap profiles (v2):\n  # The sampling frequency is the rate of a Poisson process. This means that\n  # the probability of sampling an allocation of size X with sampling rate Y\n  # is 1 - exp(-X/Y)\n  # For version 2, a typical header line might look like this:\n  # heap profile:   1922: 127792360 [  1922: 127792360] @ <heap-url>_v2/524288\n  # the trailing number (524288) is the sampling rate. (Version 1 showed\n  # double the 'rate' here)\n  my $sampling_algorithm = 0;\n  my $sample_adjustment = 0;\n  chomp($header);\n  my $type = \"unknown\";\n  if ($header =~ m\"^heap profile:\\s*(\\d+):\\s+(\\d+)\\s+\\[\\s*(\\d+):\\s+(\\d+)\\](\\s*@\\s*([^/]*)(/(\\d+))?)?\") {\n    if (defined($6) && ($6 ne '')) {\n      $type = $6;\n      my $sample_period = $8;\n      # $type is \"heapprofile\" for profiles generated by the\n      # heap-profiler, and either \"heap\" or \"heap_v2\" for profiles\n      # generated by sampling directly within tcmalloc.  It can also\n      # be \"growth\" for heap-growth profiles.  The first is typically\n      # found for profiles generated locally, and the others for\n      # remote profiles.\n      if (($type eq \"heapprofile\") || ($type !~ /heap/) ) {\n        # No need to adjust for the sampling rate with heap-profiler-derived data\n        $sampling_algorithm = 0;\n      } elsif ($type =~ /_v2/) {\n        $sampling_algorithm = 2;     # version 2 sampling\n        if (defined($sample_period) && ($sample_period ne '')) {\n          $sample_adjustment = int($sample_period);\n        }\n      } else {\n        $sampling_algorithm = 1;     # version 1 sampling\n        if (defined($sample_period) && ($sample_period ne '')) {\n          $sample_adjustment = int($sample_period)/2;\n        }\n      }\n    } else {\n      # We detect whether or not this is a remote-heap profile by checking\n      # that the total-allocated stats ($n2,$s2) are exactly the\n      # same as the in-use stats ($n1,$s1).  It is remotely conceivable\n      # that a non-remote-heap profile may pass this check, but it is hard\n      # to imagine how that could happen.\n      # In this case it's so old it's guaranteed to be remote-heap version 1.\n      my ($n1, $s1, $n2, $s2) = ($1, $2, $3, $4);\n      if (($n1 == $n2) && ($s1 == $s2)) {\n        # This is likely to be a remote-heap based sample profile\n        $sampling_algorithm = 1;\n      }\n    }\n  }\n\n  if ($sampling_algorithm > 0) {\n    # For remote-heap generated profiles, adjust the counts and sizes to\n    # account for the sample rate (we sample once every 128KB by default).\n    if ($sample_adjustment == 0) {\n      # Turn on profile adjustment.\n      $sample_adjustment = 128*1024;\n      print STDERR \"Adjusting heap profiles for 1-in-128KB sampling rate\\n\";\n    } else {\n      printf STDERR (\"Adjusting heap profiles for 1-in-%d sampling rate\\n\",\n                     $sample_adjustment);\n    }\n    if ($sampling_algorithm > 1) {\n      # We don't bother printing anything for the original version (version 1)\n      printf STDERR \"Heap version $sampling_algorithm\\n\";\n    }\n  }\n\n  my $profile = {};\n  my $pcs = {};\n  my $map = \"\";\n\n  while (<PROFILE>) {\n    s/\\r//g;         # turn windows-looking lines into unix-looking lines\n    if (/^MAPPED_LIBRARIES:/) {\n      # Read the /proc/self/maps data\n      while (<PROFILE>) {\n        s/\\r//g;         # turn windows-looking lines into unix-looking lines\n        $map .= $_;\n      }\n      last;\n    }\n\n    if (/^--- Memory map:/) {\n      # Read /proc/self/maps data as formatted by DumpAddressMap()\n      my $buildvar = \"\";\n      while (<PROFILE>) {\n        s/\\r//g;         # turn windows-looking lines into unix-looking lines\n        # Parse \"build=<dir>\" specification if supplied\n        if (m/^\\s*build=(.*)\\n/) {\n          $buildvar = $1;\n        }\n\n        # Expand \"$build\" variable if available\n        $_ =~ s/\\$build\\b/$buildvar/g;\n\n        $map .= $_;\n      }\n      last;\n    }\n\n    # Read entry of the form:\n    #  <count1>: <bytes1> [<count2>: <bytes2>] @ a1 a2 a3 ... an\n    s/^\\s*//;\n    s/\\s*$//;\n    if (m/^\\s*(\\d+):\\s+(\\d+)\\s+\\[\\s*(\\d+):\\s+(\\d+)\\]\\s+@\\s+(.*)$/) {\n      my $stack = $5;\n      my ($n1, $s1, $n2, $s2) = ($1, $2, $3, $4);\n\n      if ($sample_adjustment) {\n        if ($sampling_algorithm == 2) {\n          # Remote-heap version 2\n          # The sampling frequency is the rate of a Poisson process.\n          # This means that the probability of sampling an allocation of\n          # size X with sampling rate Y is 1 - exp(-X/Y)\n          if ($n1 != 0) {\n            my $ratio = (($s1*1.0)/$n1)/($sample_adjustment);\n            my $scale_factor = 1/(1 - exp(-$ratio));\n            $n1 *= $scale_factor;\n            $s1 *= $scale_factor;\n          }\n          if ($n2 != 0) {\n            my $ratio = (($s2*1.0)/$n2)/($sample_adjustment);\n            my $scale_factor = 1/(1 - exp(-$ratio));\n            $n2 *= $scale_factor;\n            $s2 *= $scale_factor;\n          }\n        } else {\n          # Remote-heap version 1\n          my $ratio;\n          $ratio = (($s1*1.0)/$n1)/($sample_adjustment);\n          if ($ratio < 1) {\n            $n1 /= $ratio;\n            $s1 /= $ratio;\n          }\n          $ratio = (($s2*1.0)/$n2)/($sample_adjustment);\n          if ($ratio < 1) {\n            $n2 /= $ratio;\n            $s2 /= $ratio;\n          }\n        }\n      }\n\n      my @counts = ($n1, $s1, $n2, $s2);\n      AddEntries($profile, $pcs, FixCallerAddresses($stack), $counts[$index]);\n    }\n  }\n\n  my $r = {};\n  $r->{version} = \"heap\";\n  $r->{period} = 1;\n  $r->{profile} = $profile;\n  $r->{libs} = ParseLibraries($prog, $map, $pcs);\n  $r->{pcs} = $pcs;\n  return $r;\n}\n\nsub ReadSynchProfile {\n  my $prog = shift;\n  local *PROFILE = shift;\n  my $header = shift;\n\n  my $map = '';\n  my $profile = {};\n  my $pcs = {};\n  my $sampling_period = 1;\n  my $cyclespernanosec = 2.8;   # Default assumption for old binaries\n  my $seen_clockrate = 0;\n  my $line;\n\n  my $index = 0;\n  if ($main::opt_total_delay) {\n    $index = 0;\n  } elsif ($main::opt_contentions) {\n    $index = 1;\n  } elsif ($main::opt_mean_delay) {\n    $index = 2;\n  }\n\n  while ( $line = <PROFILE> ) {\n    $line =~ s/\\r//g;      # turn windows-looking lines into unix-looking lines\n    if ( $line =~ /^\\s*(\\d+)\\s+(\\d+) \\@\\s*(.*?)\\s*$/ ) {\n      my ($cycles, $count, $stack) = ($1, $2, $3);\n\n      # Convert cycles to nanoseconds\n      $cycles /= $cyclespernanosec;\n\n      # Adjust for sampling done by application\n      $cycles *= $sampling_period;\n      $count *= $sampling_period;\n\n      my @values = ($cycles, $count, $cycles / $count);\n      AddEntries($profile, $pcs, FixCallerAddresses($stack), $values[$index]);\n\n    } elsif ( $line =~ /^(slow release).*thread \\d+  \\@\\s*(.*?)\\s*$/ ||\n              $line =~ /^\\s*(\\d+) \\@\\s*(.*?)\\s*$/ ) {\n      my ($cycles, $stack) = ($1, $2);\n      if ($cycles !~ /^\\d+$/) {\n        next;\n      }\n\n      # Convert cycles to nanoseconds\n      $cycles /= $cyclespernanosec;\n\n      # Adjust for sampling done by application\n      $cycles *= $sampling_period;\n\n      AddEntries($profile, $pcs, FixCallerAddresses($stack), $cycles);\n\n    } elsif ( $line =~ m/^([a-z][^=]*)=(.*)$/ ) {\n      my ($variable, $value) = ($1,$2);\n      for ($variable, $value) {\n        s/^\\s+//;\n        s/\\s+$//;\n      }\n      if ($variable eq \"cycles/second\") {\n        $cyclespernanosec = $value / 1e9;\n        $seen_clockrate = 1;\n      } elsif ($variable eq \"sampling period\") {\n        $sampling_period = $value;\n      } elsif ($variable eq \"ms since reset\") {\n        # Currently nothing is done with this value in pprof\n        # So we just silently ignore it for now\n      } elsif ($variable eq \"discarded samples\") {\n        # Currently nothing is done with this value in pprof\n        # So we just silently ignore it for now\n      } else {\n        printf STDERR (\"Ignoring unnknown variable in /contention output: \" .\n                       \"'%s' = '%s'\\n\",$variable,$value);\n      }\n    } else {\n      # Memory map entry\n      $map .= $line;\n    }\n  }\n\n  if (!$seen_clockrate) {\n    printf STDERR (\"No cycles/second entry in profile; Guessing %.1f GHz\\n\",\n                   $cyclespernanosec);\n  }\n\n  my $r = {};\n  $r->{version} = 0;\n  $r->{period} = $sampling_period;\n  $r->{profile} = $profile;\n  $r->{libs} = ParseLibraries($prog, $map, $pcs);\n  $r->{pcs} = $pcs;\n  return $r;\n}\n\n# Given a hex value in the form \"0x1abcd\" or \"1abcd\", return either\n# \"0001abcd\" or \"000000000001abcd\", depending on the current (global)\n# address length.\nsub HexExtend {\n  my $addr = shift;\n\n  $addr =~ s/^(0x)?0*//;\n  my $zeros_needed = $address_length - length($addr);\n  if ($zeros_needed < 0) {\n    printf STDERR \"Warning: address $addr is longer than address length $address_length\\n\";\n    return $addr;\n  }\n  return (\"0\" x $zeros_needed) . $addr;\n}\n\n##### Symbol extraction #####\n\n# Aggressively search the lib_prefix values for the given library\n# If all else fails, just return the name of the library unmodified.\n# If the lib_prefix is \"/my/path,/other/path\" and $file is \"/lib/dir/mylib.so\"\n# it will search the following locations in this order, until it finds a file:\n#   /my/path/lib/dir/mylib.so\n#   /other/path/lib/dir/mylib.so\n#   /my/path/dir/mylib.so\n#   /other/path/dir/mylib.so\n#   /my/path/mylib.so\n#   /other/path/mylib.so\n#   /lib/dir/mylib.so              (returned as last resort)\nsub FindLibrary {\n  my $file = shift;\n  my $suffix = $file;\n\n  # Search for the library as described above\n  do {\n    foreach my $prefix (@prefix_list) {\n      my $fullpath = $prefix . $suffix;\n      if (-e $fullpath) {\n        return $fullpath;\n      }\n    }\n  } while ($suffix =~ s|^/[^/]+/|/|);\n  return $file;\n}\n\n# Return path to library with debugging symbols.\n# For libc libraries, the copy in /usr/lib/debug contains debugging symbols\nsub DebuggingLibrary {\n  my $file = shift;\n  if ($file =~ m|^/| && -f \"/usr/lib/debug$file\") {\n    return \"/usr/lib/debug$file\";\n  }\n  return undef;\n}\n\n# Parse text section header of a library using objdump\nsub ParseTextSectionHeaderFromObjdump {\n  my $lib = shift;\n\n  my $size = undef;\n  my $vma;\n  my $file_offset;\n  # Get objdump output from the library file to figure out how to\n  # map between mapped addresses and addresses in the library.\n  my $cmd = ShellEscape($obj_tool_map{\"objdump\"}, \"-h\", $lib);\n  open(OBJDUMP, \"$cmd |\") || error(\"$cmd: $!\\n\");\n  while (<OBJDUMP>) {\n    s/\\r//g;         # turn windows-looking lines into unix-looking lines\n    # Idx Name          Size      VMA       LMA       File off  Algn\n    #  10 .text         00104b2c  420156f0  420156f0  000156f0  2**4\n    # For 64-bit objects, VMA and LMA will be 16 hex digits, size and file\n    # offset may still be 8.  But AddressSub below will still handle that.\n    my @x = split;\n    if (($#x >= 6) && ($x[1] eq '.text')) {\n      $size = $x[2];\n      $vma = $x[3];\n      $file_offset = $x[5];\n      last;\n    }\n  }\n  close(OBJDUMP);\n\n  if (!defined($size)) {\n    return undef;\n  }\n\n  my $r = {};\n  $r->{size} = $size;\n  $r->{vma} = $vma;\n  $r->{file_offset} = $file_offset;\n\n  return $r;\n}\n\n# Parse text section header of a library using otool (on OS X)\nsub ParseTextSectionHeaderFromOtool {\n  my $lib = shift;\n\n  my $size = undef;\n  my $vma = undef;\n  my $file_offset = undef;\n  # Get otool output from the library file to figure out how to\n  # map between mapped addresses and addresses in the library.\n  my $command = ShellEscape($obj_tool_map{\"otool\"}, \"-l\", $lib);\n  open(OTOOL, \"$command |\") || error(\"$command: $!\\n\");\n  my $cmd = \"\";\n  my $sectname = \"\";\n  my $segname = \"\";\n  foreach my $line (<OTOOL>) {\n    $line =~ s/\\r//g;      # turn windows-looking lines into unix-looking lines\n    # Load command <#>\n    #       cmd LC_SEGMENT\n    # [...]\n    # Section\n    #   sectname __text\n    #    segname __TEXT\n    #       addr 0x000009f8\n    #       size 0x00018b9e\n    #     offset 2552\n    #      align 2^2 (4)\n    # We will need to strip off the leading 0x from the hex addresses,\n    # and convert the offset into hex.\n    if ($line =~ /Load command/) {\n      $cmd = \"\";\n      $sectname = \"\";\n      $segname = \"\";\n    } elsif ($line =~ /Section/) {\n      $sectname = \"\";\n      $segname = \"\";\n    } elsif ($line =~ /cmd (\\w+)/) {\n      $cmd = $1;\n    } elsif ($line =~ /sectname (\\w+)/) {\n      $sectname = $1;\n    } elsif ($line =~ /segname (\\w+)/) {\n      $segname = $1;\n    } elsif (!(($cmd eq \"LC_SEGMENT\" || $cmd eq \"LC_SEGMENT_64\") &&\n               $sectname eq \"__text\" &&\n               $segname eq \"__TEXT\")) {\n      next;\n    } elsif ($line =~ /\\baddr 0x([0-9a-fA-F]+)/) {\n      $vma = $1;\n    } elsif ($line =~ /\\bsize 0x([0-9a-fA-F]+)/) {\n      $size = $1;\n    } elsif ($line =~ /\\boffset ([0-9]+)/) {\n      $file_offset = sprintf(\"%016x\", $1);\n    }\n    if (defined($vma) && defined($size) && defined($file_offset)) {\n      last;\n    }\n  }\n  close(OTOOL);\n\n  if (!defined($vma) || !defined($size) || !defined($file_offset)) {\n     return undef;\n  }\n\n  my $r = {};\n  $r->{size} = $size;\n  $r->{vma} = $vma;\n  $r->{file_offset} = $file_offset;\n\n  return $r;\n}\n\nsub ParseTextSectionHeader {\n  # obj_tool_map(\"otool\") is only defined if we're in a Mach-O environment\n  if (defined($obj_tool_map{\"otool\"})) {\n    my $r = ParseTextSectionHeaderFromOtool(@_);\n    if (defined($r)){\n      return $r;\n    }\n  }\n  # If otool doesn't work, or we don't have it, fall back to objdump\n  return ParseTextSectionHeaderFromObjdump(@_);\n}\n\n# Split /proc/pid/maps dump into a list of libraries\nsub ParseLibraries {\n  return if $main::use_symbol_page;  # We don't need libraries info.\n  my $prog = shift;\n  my $map = shift;\n  my $pcs = shift;\n\n  my $result = [];\n  my $h = \"[a-f0-9]+\";\n  my $zero_offset = HexExtend(\"0\");\n\n  my $buildvar = \"\";\n  foreach my $l (split(\"\\n\", $map)) {\n    if ($l =~ m/^\\s*build=(.*)$/) {\n      $buildvar = $1;\n    }\n\n    my $start;\n    my $finish;\n    my $offset;\n    my $lib;\n    if ($l =~ /^($h)-($h)\\s+..x.\\s+($h)\\s+\\S+:\\S+\\s+\\d+\\s+(\\S+\\.(so|dll|dylib|bundle)((\\.\\d+)+\\w*(\\.\\d+){0,3})?)$/i) {\n      # Full line from /proc/self/maps.  Example:\n      #   40000000-40015000 r-xp 00000000 03:01 12845071   /lib/ld-2.3.2.so\n      $start = HexExtend($1);\n      $finish = HexExtend($2);\n      $offset = HexExtend($3);\n      $lib = $4;\n      $lib =~ s|\\\\|/|g;     # turn windows-style paths into unix-style paths\n    } elsif ($l =~ /^\\s*($h)-($h):\\s*(\\S+\\.so(\\.\\d+)*)/) {\n      # Cooked line from DumpAddressMap.  Example:\n      #   40000000-40015000: /lib/ld-2.3.2.so\n      $start = HexExtend($1);\n      $finish = HexExtend($2);\n      $offset = $zero_offset;\n      $lib = $3;\n    } else {\n      next;\n    }\n\n    # Expand \"$build\" variable if available\n    $lib =~ s/\\$build\\b/$buildvar/g;\n\n    $lib = FindLibrary($lib);\n\n    # Check for pre-relocated libraries, which use pre-relocated symbol tables\n    # and thus require adjusting the offset that we'll use to translate\n    # VM addresses into symbol table addresses.\n    # Only do this if we're not going to fetch the symbol table from a\n    # debugging copy of the library.\n    if (!DebuggingLibrary($lib)) {\n      my $text = ParseTextSectionHeader($lib);\n      if (defined($text)) {\n         my $vma_offset = AddressSub($text->{vma}, $text->{file_offset});\n         $offset = AddressAdd($offset, $vma_offset);\n      }\n    }\n\n    push(@{$result}, [$lib, $start, $finish, $offset]);\n  }\n\n  # Append special entry for additional library (not relocated)\n  if ($main::opt_lib ne \"\") {\n    my $text = ParseTextSectionHeader($main::opt_lib);\n    if (defined($text)) {\n       my $start = $text->{vma};\n       my $finish = AddressAdd($start, $text->{size});\n\n       push(@{$result}, [$main::opt_lib, $start, $finish, $start]);\n    }\n  }\n\n  # Append special entry for the main program.  This covers\n  # 0..max_pc_value_seen, so that we assume pc values not found in one\n  # of the library ranges will be treated as coming from the main\n  # program binary.\n  my $min_pc = HexExtend(\"0\");\n  my $max_pc = $min_pc;          # find the maximal PC value in any sample\n  foreach my $pc (keys(%{$pcs})) {\n    if (HexExtend($pc) gt $max_pc) { $max_pc = HexExtend($pc); }\n  }\n  push(@{$result}, [$prog, $min_pc, $max_pc, $zero_offset]);\n\n  return $result;\n}\n\n# Add two hex addresses of length $address_length.\n# Run pprof --test for unit test if this is changed.\nsub AddressAdd {\n  my $addr1 = shift;\n  my $addr2 = shift;\n  my $sum;\n\n  if ($address_length == 8) {\n    # Perl doesn't cope with wraparound arithmetic, so do it explicitly:\n    $sum = (hex($addr1)+hex($addr2)) % (0x10000000 * 16);\n    return sprintf(\"%08x\", $sum);\n\n  } else {\n    # Do the addition in 7-nibble chunks to trivialize carry handling.\n\n    if ($main::opt_debug and $main::opt_test) {\n      print STDERR \"AddressAdd $addr1 + $addr2 = \";\n    }\n\n    my $a1 = substr($addr1,-7);\n    $addr1 = substr($addr1,0,-7);\n    my $a2 = substr($addr2,-7);\n    $addr2 = substr($addr2,0,-7);\n    $sum = hex($a1) + hex($a2);\n    my $c = 0;\n    if ($sum > 0xfffffff) {\n      $c = 1;\n      $sum -= 0x10000000;\n    }\n    my $r = sprintf(\"%07x\", $sum);\n\n    $a1 = substr($addr1,-7);\n    $addr1 = substr($addr1,0,-7);\n    $a2 = substr($addr2,-7);\n    $addr2 = substr($addr2,0,-7);\n    $sum = hex($a1) + hex($a2) + $c;\n    $c = 0;\n    if ($sum > 0xfffffff) {\n      $c = 1;\n      $sum -= 0x10000000;\n    }\n    $r = sprintf(\"%07x\", $sum) . $r;\n\n    $sum = hex($addr1) + hex($addr2) + $c;\n    if ($sum > 0xff) { $sum -= 0x100; }\n    $r = sprintf(\"%02x\", $sum) . $r;\n\n    if ($main::opt_debug and $main::opt_test) { print STDERR \"$r\\n\"; }\n\n    return $r;\n  }\n}\n\n\n# Subtract two hex addresses of length $address_length.\n# Run pprof --test for unit test if this is changed.\nsub AddressSub {\n  my $addr1 = shift;\n  my $addr2 = shift;\n  my $diff;\n\n  if ($address_length == 8) {\n    # Perl doesn't cope with wraparound arithmetic, so do it explicitly:\n    $diff = (hex($addr1)-hex($addr2)) % (0x10000000 * 16);\n    return sprintf(\"%08x\", $diff);\n\n  } else {\n    # Do the addition in 7-nibble chunks to trivialize borrow handling.\n    # if ($main::opt_debug) { print STDERR \"AddressSub $addr1 - $addr2 = \"; }\n\n    my $a1 = hex(substr($addr1,-7));\n    $addr1 = substr($addr1,0,-7);\n    my $a2 = hex(substr($addr2,-7));\n    $addr2 = substr($addr2,0,-7);\n    my $b = 0;\n    if ($a2 > $a1) {\n      $b = 1;\n      $a1 += 0x10000000;\n    }\n    $diff = $a1 - $a2;\n    my $r = sprintf(\"%07x\", $diff);\n\n    $a1 = hex(substr($addr1,-7));\n    $addr1 = substr($addr1,0,-7);\n    $a2 = hex(substr($addr2,-7)) + $b;\n    $addr2 = substr($addr2,0,-7);\n    $b = 0;\n    if ($a2 > $a1) {\n      $b = 1;\n      $a1 += 0x10000000;\n    }\n    $diff = $a1 - $a2;\n    $r = sprintf(\"%07x\", $diff) . $r;\n\n    $a1 = hex($addr1);\n    $a2 = hex($addr2) + $b;\n    if ($a2 > $a1) { $a1 += 0x100; }\n    $diff = $a1 - $a2;\n    $r = sprintf(\"%02x\", $diff) . $r;\n\n    # if ($main::opt_debug) { print STDERR \"$r\\n\"; }\n\n    return $r;\n  }\n}\n\n# Increment a hex addresses of length $address_length.\n# Run pprof --test for unit test if this is changed.\nsub AddressInc {\n  my $addr = shift;\n  my $sum;\n\n  if ($address_length == 8) {\n    # Perl doesn't cope with wraparound arithmetic, so do it explicitly:\n    $sum = (hex($addr)+1) % (0x10000000 * 16);\n    return sprintf(\"%08x\", $sum);\n\n  } else {\n    # Do the addition in 7-nibble chunks to trivialize carry handling.\n    # We are always doing this to step through the addresses in a function,\n    # and will almost never overflow the first chunk, so we check for this\n    # case and exit early.\n\n    # if ($main::opt_debug) { print STDERR \"AddressInc $addr1 = \"; }\n\n    my $a1 = substr($addr,-7);\n    $addr = substr($addr,0,-7);\n    $sum = hex($a1) + 1;\n    my $r = sprintf(\"%07x\", $sum);\n    if ($sum <= 0xfffffff) {\n      $r = $addr . $r;\n      # if ($main::opt_debug) { print STDERR \"$r\\n\"; }\n      return HexExtend($r);\n    } else {\n      $r = \"0000000\";\n    }\n\n    $a1 = substr($addr,-7);\n    $addr = substr($addr,0,-7);\n    $sum = hex($a1) + 1;\n    $r = sprintf(\"%07x\", $sum) . $r;\n    if ($sum <= 0xfffffff) {\n      $r = $addr . $r;\n      # if ($main::opt_debug) { print STDERR \"$r\\n\"; }\n      return HexExtend($r);\n    } else {\n      $r = \"00000000000000\";\n    }\n\n    $sum = hex($addr) + 1;\n    if ($sum > 0xff) { $sum -= 0x100; }\n    $r = sprintf(\"%02x\", $sum) . $r;\n\n    # if ($main::opt_debug) { print STDERR \"$r\\n\"; }\n    return $r;\n  }\n}\n\n# Extract symbols for all PC values found in profile\nsub ExtractSymbols {\n  my $libs = shift;\n  my $pcset = shift;\n\n  my $symbols = {};\n\n  # Map each PC value to the containing library.  To make this faster,\n  # we sort libraries by their starting pc value (highest first), and\n  # advance through the libraries as we advance the pc.  Sometimes the\n  # addresses of libraries may overlap with the addresses of the main\n  # binary, so to make sure the libraries 'win', we iterate over the\n  # libraries in reverse order (which assumes the binary doesn't start\n  # in the middle of a library, which seems a fair assumption).\n  my @pcs = (sort { $a cmp $b } keys(%{$pcset}));  # pcset is 0-extended strings\n  foreach my $lib (sort {$b->[1] cmp $a->[1]} @{$libs}) {\n    my $libname = $lib->[0];\n    my $start = $lib->[1];\n    my $finish = $lib->[2];\n    my $offset = $lib->[3];\n\n    # Get list of pcs that belong in this library.\n    my $contained = [];\n    my ($start_pc_index, $finish_pc_index);\n    # Find smallest finish_pc_index such that $finish < $pc[$finish_pc_index].\n    for ($finish_pc_index = $#pcs + 1; $finish_pc_index > 0;\n         $finish_pc_index--) {\n      last if $pcs[$finish_pc_index - 1] le $finish;\n    }\n    # Find smallest start_pc_index such that $start <= $pc[$start_pc_index].\n    for ($start_pc_index = $finish_pc_index; $start_pc_index > 0;\n         $start_pc_index--) {\n      last if $pcs[$start_pc_index - 1] lt $start;\n    }\n    # This keeps PC values higher than $pc[$finish_pc_index] in @pcs,\n    # in case there are overlaps in libraries and the main binary.\n    @{$contained} = splice(@pcs, $start_pc_index,\n                           $finish_pc_index - $start_pc_index);\n    # Map to symbols\n    MapToSymbols($libname, AddressSub($start, $offset), $contained, $symbols);\n  }\n\n  return $symbols;\n}\n\n# Map list of PC values to symbols for a given image\nsub MapToSymbols {\n  my $image = shift;\n  my $offset = shift;\n  my $pclist = shift;\n  my $symbols = shift;\n\n  my $debug = 0;\n\n  # Ignore empty binaries\n  if ($#{$pclist} < 0) { return; }\n\n  # Figure out the addr2line command to use\n  my $addr2line = $obj_tool_map{\"addr2line\"};\n  my $cmd = ShellEscape($addr2line, \"-f\", \"-C\", \"-e\", $image);\n  if (exists $obj_tool_map{\"addr2line_pdb\"}) {\n    $addr2line = $obj_tool_map{\"addr2line_pdb\"};\n    $cmd = ShellEscape($addr2line, \"--demangle\", \"-f\", \"-C\", \"-e\", $image);\n  }\n\n  # If \"addr2line\" isn't installed on the system at all, just use\n  # nm to get what info we can (function names, but not line numbers).\n  if (system(ShellEscape($addr2line, \"--help\") . \" >$dev_null 2>&1\") != 0) {\n    MapSymbolsWithNM($image, $offset, $pclist, $symbols);\n    return;\n  }\n\n  # \"addr2line -i\" can produce a variable number of lines per input\n  # address, with no separator that allows us to tell when data for\n  # the next address starts.  So we find the address for a special\n  # symbol (_fini) and interleave this address between all real\n  # addresses passed to addr2line.  The name of this special symbol\n  # can then be used as a separator.\n  $sep_address = undef;  # May be filled in by MapSymbolsWithNM()\n  my $nm_symbols = {};\n  MapSymbolsWithNM($image, $offset, $pclist, $nm_symbols);\n  if (defined($sep_address)) {\n    # Only add \" -i\" to addr2line if the binary supports it.\n    # addr2line --help returns 0, but not if it sees an unknown flag first.\n    if (system(\"$cmd -i --help >$dev_null 2>&1\") == 0) {\n      $cmd .= \" -i\";\n    } else {\n      $sep_address = undef;   # no need for sep_address if we don't support -i\n    }\n  }\n\n  # Make file with all PC values with intervening 'sep_address' so\n  # that we can reliably detect the end of inlined function list\n  open(ADDRESSES, \">$main::tmpfile_sym\") || error(\"$main::tmpfile_sym: $!\\n\");\n  if ($debug) { print(\"---- $image ---\\n\"); }\n  for (my $i = 0; $i <= $#{$pclist}; $i++) {\n    # addr2line always reads hex addresses, and does not need '0x' prefix.\n    if ($debug) { printf STDERR (\"%s\\n\", $pclist->[$i]); }\n    printf ADDRESSES (\"%s\\n\", AddressSub($pclist->[$i], $offset));\n    if (defined($sep_address)) {\n      printf ADDRESSES (\"%s\\n\", $sep_address);\n    }\n  }\n  close(ADDRESSES);\n  if ($debug) {\n    print(\"----\\n\");\n    system(\"cat\", $main::tmpfile_sym);\n    print(\"----\\n\");\n    system(\"$cmd < \" . ShellEscape($main::tmpfile_sym));\n    print(\"----\\n\");\n  }\n\n  open(SYMBOLS, \"$cmd <\" . ShellEscape($main::tmpfile_sym) . \" |\")\n      || error(\"$cmd: $!\\n\");\n  my $count = 0;   # Index in pclist\n  while (<SYMBOLS>) {\n    # Read fullfunction and filelineinfo from next pair of lines\n    s/\\r?\\n$//g;\n    my $fullfunction = $_;\n    $_ = <SYMBOLS>;\n    s/\\r?\\n$//g;\n    my $filelinenum = $_;\n\n    if (defined($sep_address) && $fullfunction eq $sep_symbol) {\n      # Terminating marker for data for this address\n      $count++;\n      next;\n    }\n\n    $filelinenum =~ s|\\\\|/|g; # turn windows-style paths into unix-style paths\n\n    my $pcstr = $pclist->[$count];\n    my $function = ShortFunctionName($fullfunction);\n    my $nms = $nm_symbols->{$pcstr};\n    if (defined($nms)) {\n      if ($fullfunction eq '??') {\n        # nm found a symbol for us.\n        $function = $nms->[0];\n        $fullfunction = $nms->[2];\n      } else {\n\t# MapSymbolsWithNM tags each routine with its starting address,\n\t# useful in case the image has multiple occurrences of this\n\t# routine.  (It uses a syntax that resembles template paramters,\n\t# that are automatically stripped out by ShortFunctionName().)\n\t# addr2line does not provide the same information.  So we check\n\t# if nm disambiguated our symbol, and if so take the annotated\n\t# (nm) version of the routine-name.  TODO(csilvers): this won't\n\t# catch overloaded, inlined symbols, which nm doesn't see.\n\t# Better would be to do a check similar to nm's, in this fn.\n\tif ($nms->[2] =~ m/^\\Q$function\\E/) {  # sanity check it's the right fn\n\t  $function = $nms->[0];\n\t  $fullfunction = $nms->[2];\n\t}\n      }\n    }\n    \n    # Prepend to accumulated symbols for pcstr\n    # (so that caller comes before callee)\n    my $sym = $symbols->{$pcstr};\n    if (!defined($sym)) {\n      $sym = [];\n      $symbols->{$pcstr} = $sym;\n    }\n    unshift(@{$sym}, $function, $filelinenum, $fullfunction);\n    if ($debug) { printf STDERR (\"%s => [%s]\\n\", $pcstr, join(\" \", @{$sym})); }\n    if (!defined($sep_address)) {\n      # Inlining is off, so this entry ends immediately\n      $count++;\n    }\n  }\n  close(SYMBOLS);\n}\n\n# Use nm to map the list of referenced PCs to symbols.  Return true iff we\n# are able to read procedure information via nm.\nsub MapSymbolsWithNM {\n  my $image = shift;\n  my $offset = shift;\n  my $pclist = shift;\n  my $symbols = shift;\n\n  # Get nm output sorted by increasing address\n  my $symbol_table = GetProcedureBoundaries($image, \".\");\n  if (!%{$symbol_table}) {\n    return 0;\n  }\n  # Start addresses are already the right length (8 or 16 hex digits).\n  my @names = sort { $symbol_table->{$a}->[0] cmp $symbol_table->{$b}->[0] }\n    keys(%{$symbol_table});\n\n  if ($#names < 0) {\n    # No symbols: just use addresses\n    foreach my $pc (@{$pclist}) {\n      my $pcstr = \"0x\" . $pc;\n      $symbols->{$pc} = [$pcstr, \"?\", $pcstr];\n    }\n    return 0;\n  }\n\n  # Sort addresses so we can do a join against nm output\n  my $index = 0;\n  my $fullname = $names[0];\n  my $name = ShortFunctionName($fullname);\n  foreach my $pc (sort { $a cmp $b } @{$pclist}) {\n    # Adjust for mapped offset\n    my $mpc = AddressSub($pc, $offset);\n    while (($index < $#names) && ($mpc ge $symbol_table->{$fullname}->[1])){\n      $index++;\n      $fullname = $names[$index];\n      $name = ShortFunctionName($fullname);\n    }\n    if ($mpc lt $symbol_table->{$fullname}->[1]) {\n      $symbols->{$pc} = [$name, \"?\", $fullname];\n    } else {\n      my $pcstr = \"0x\" . $pc;\n      $symbols->{$pc} = [$pcstr, \"?\", $pcstr];\n    }\n  }\n  return 1;\n}\n\nsub ShortFunctionName {\n  my $function = shift;\n  while ($function =~ s/\\([^()]*\\)(\\s*const)?//g) { }   # Argument types\n  while ($function =~ s/<[^<>]*>//g)  { }    # Remove template arguments\n  $function =~ s/^.*\\s+(\\w+::)/$1/;          # Remove leading type\n  return $function;\n}\n\n# Trim overly long symbols found in disassembler output\nsub CleanDisassembly {\n  my $d = shift;\n  while ($d =~ s/\\([^()%]*\\)(\\s*const)?//g) { } # Argument types, not (%rax)\n  while ($d =~ s/(\\w+)<[^<>]*>/$1/g)  { }       # Remove template arguments\n  return $d;\n}\n\n# Clean file name for display\nsub CleanFileName {\n  my ($f) = @_;\n  $f =~ s|^/proc/self/cwd/||;\n  $f =~ s|^\\./||;\n  return $f;\n}\n\n# Make address relative to section and clean up for display\nsub UnparseAddress {\n  my ($offset, $address) = @_;\n  $address = AddressSub($address, $offset);\n  $address =~ s/^0x//;\n  $address =~ s/^0*//;\n  return $address;\n}\n\n##### Miscellaneous #####\n\n# Find the right versions of the above object tools to use.  The\n# argument is the program file being analyzed, and should be an ELF\n# 32-bit or ELF 64-bit executable file.  The location of the tools\n# is determined by considering the following options in this order:\n#   1) --tools option, if set\n#   2) PPROF_TOOLS environment variable, if set\n#   3) the environment\nsub ConfigureObjTools {\n  my $prog_file = shift;\n\n  # Check for the existence of $prog_file because /usr/bin/file does not\n  # predictably return error status in prod.\n  (-e $prog_file)  || error(\"$prog_file does not exist.\\n\");\n\n  my $file_type = undef;\n  if (-e \"/usr/bin/file\") {\n    # Follow symlinks (at least for systems where \"file\" supports that).\n    my $escaped_prog_file = ShellEscape($prog_file);\n    $file_type = `/usr/bin/file -L $escaped_prog_file 2>$dev_null ||\n                  /usr/bin/file $escaped_prog_file`;\n  } elsif ($^O == \"MSWin32\") {\n    $file_type = \"MS Windows\";\n  } else {\n    print STDERR \"WARNING: Can't determine the file type of $prog_file\";\n  }\n\n  if ($file_type =~ /64-bit/) {\n    # Change $address_length to 16 if the program file is ELF 64-bit.\n    # We can't detect this from many (most?) heap or lock contention\n    # profiles, since the actual addresses referenced are generally in low\n    # memory even for 64-bit programs.\n    $address_length = 16;\n  }\n\n  if ($file_type =~ /MS Windows/) {\n    # For windows, we provide a version of nm and addr2line as part of\n    # the opensource release, which is capable of parsing\n    # Windows-style PDB executables.  It should live in the path, or\n    # in the same directory as pprof.\n    $obj_tool_map{\"nm_pdb\"} = \"nm-pdb\";\n    $obj_tool_map{\"addr2line_pdb\"} = \"addr2line-pdb\";\n  }\n\n  if ($file_type =~ /Mach-O/) {\n    # OS X uses otool to examine Mach-O files, rather than objdump.\n    $obj_tool_map{\"otool\"} = \"otool\";\n    $obj_tool_map{\"addr2line\"} = \"false\";  # no addr2line\n    $obj_tool_map{\"objdump\"} = \"false\";  # no objdump\n  }\n\n  # Go fill in %obj_tool_map with the pathnames to use:\n  foreach my $tool (keys %obj_tool_map) {\n    $obj_tool_map{$tool} = ConfigureTool($obj_tool_map{$tool});\n  }\n}\n\n# Returns the path of a caller-specified object tool.  If --tools or\n# PPROF_TOOLS are specified, then returns the full path to the tool\n# with that prefix.  Otherwise, returns the path unmodified (which\n# means we will look for it on PATH).\nsub ConfigureTool {\n  my $tool = shift;\n  my $path;\n\n  # --tools (or $PPROF_TOOLS) is a comma separated list, where each\n  # item is either a) a pathname prefix, or b) a map of the form\n  # <tool>:<path>.  First we look for an entry of type (b) for our\n  # tool.  If one is found, we use it.  Otherwise, we consider all the\n  # pathname prefixes in turn, until one yields an existing file.  If\n  # none does, we use a default path.\n  my $tools = $main::opt_tools || $ENV{\"PPROF_TOOLS\"} || \"\";\n  if ($tools =~ m/(,|^)\\Q$tool\\E:([^,]*)/) {\n    $path = $2;\n    # TODO(csilvers): sanity-check that $path exists?  Hard if it's relative.\n  } elsif ($tools ne '') {\n    foreach my $prefix (split(',', $tools)) {\n      next if ($prefix =~ /:/);    # ignore \"tool:fullpath\" entries in the list\n      if (-x $prefix . $tool) {\n        $path = $prefix . $tool;\n        last;\n      }\n    }\n    if (!$path) {\n      error(\"No '$tool' found with prefix specified by \" .\n            \"--tools (or \\$PPROF_TOOLS) '$tools'\\n\");\n    }\n  } else {\n    # ... otherwise use the version that exists in the same directory as\n    # pprof.  If there's nothing there, use $PATH.\n    $0 =~ m,[^/]*$,;     # this is everything after the last slash\n    my $dirname = $`;    # this is everything up to and including the last slash\n    if (-x \"$dirname$tool\") {\n      $path = \"$dirname$tool\";\n    } else { \n      $path = $tool;\n    }\n  }\n  if ($main::opt_debug) { print STDERR \"Using '$path' for '$tool'.\\n\"; }\n  return $path;\n}\n\nsub ShellEscape {\n  my @escaped_words = ();\n  foreach my $word (@_) {\n    my $escaped_word = $word;\n    if ($word =~ m![^a-zA-Z0-9/.,_=-]!) {  # check for anything not in whitelist\n      $escaped_word =~ s/'/'\\\\''/;\n      $escaped_word = \"'$escaped_word'\";\n    }\n    push(@escaped_words, $escaped_word);\n  }\n  return join(\" \", @escaped_words);\n}\n\nsub cleanup {\n  unlink($main::tmpfile_sym);\n  unlink(keys %main::tempnames);\n\n  # We leave any collected profiles in $HOME/pprof in case the user wants\n  # to look at them later.  We print a message informing them of this.\n  if ((scalar(@main::profile_files) > 0) &&\n      defined($main::collected_profile)) {\n    if (scalar(@main::profile_files) == 1) {\n      print STDERR \"Dynamically gathered profile is in $main::collected_profile\\n\";\n    }\n    print STDERR \"If you want to investigate this profile further, you can do:\\n\";\n    print STDERR \"\\n\";\n    print STDERR \"  pprof \\\\\\n\";\n    print STDERR \"    $main::prog \\\\\\n\";\n    print STDERR \"    $main::collected_profile\\n\";\n    print STDERR \"\\n\";\n  }\n}\n\nsub sighandler {\n  cleanup();\n  exit(1);\n}\n\nsub error {\n  my $msg = shift;\n  print STDERR $msg;\n  cleanup();\n  exit(1);\n}\n\n\n# Run $nm_command and get all the resulting procedure boundaries whose\n# names match \"$regexp\" and returns them in a hashtable mapping from\n# procedure name to a two-element vector of [start address, end address]\nsub GetProcedureBoundariesViaNm {\n  my $escaped_nm_command = shift;    # shell-escaped\n  my $regexp = shift;\n\n  my $symbol_table = {};\n  open(NM, \"$escaped_nm_command |\") || error(\"$escaped_nm_command: $!\\n\");\n  my $last_start = \"0\";\n  my $routine = \"\";\n  while (<NM>) {\n    s/\\r//g;         # turn windows-looking lines into unix-looking lines\n    if (m/^\\s*([0-9a-f]+) (.) (..*)/) {\n      my $start_val = $1;\n      my $type = $2;\n      my $this_routine = $3;\n\n      # It's possible for two symbols to share the same address, if\n      # one is a zero-length variable (like __start_google_malloc) or\n      # one symbol is a weak alias to another (like __libc_malloc).\n      # In such cases, we want to ignore all values except for the\n      # actual symbol, which in nm-speak has type \"T\".  The logic\n      # below does this, though it's a bit tricky: what happens when\n      # we have a series of lines with the same address, is the first\n      # one gets queued up to be processed.  However, it won't\n      # *actually* be processed until later, when we read a line with\n      # a different address.  That means that as long as we're reading\n      # lines with the same address, we have a chance to replace that\n      # item in the queue, which we do whenever we see a 'T' entry --\n      # that is, a line with type 'T'.  If we never see a 'T' entry,\n      # we'll just go ahead and process the first entry (which never\n      # got touched in the queue), and ignore the others.\n      if ($start_val eq $last_start && $type =~ /t/i) {\n        # We are the 'T' symbol at this address, replace previous symbol.\n        $routine = $this_routine;\n        next;\n      } elsif ($start_val eq $last_start) {\n        # We're not the 'T' symbol at this address, so ignore us.\n        next;\n      }\n\n      if ($this_routine eq $sep_symbol) {\n        $sep_address = HexExtend($start_val);\n      }\n\n      # Tag this routine with the starting address in case the image\n      # has multiple occurrences of this routine.  We use a syntax\n      # that resembles template paramters that are automatically\n      # stripped out by ShortFunctionName()\n      $this_routine .= \"<$start_val>\";\n\n      if (defined($routine) && $routine =~ m/$regexp/) {\n        $symbol_table->{$routine} = [HexExtend($last_start),\n                                     HexExtend($start_val)];\n      }\n      $last_start = $start_val;\n      $routine = $this_routine;\n    } elsif (m/^Loaded image name: (.+)/) {\n      # The win32 nm workalike emits information about the binary it is using.\n      if ($main::opt_debug) { print STDERR \"Using Image $1\\n\"; }\n    } elsif (m/^PDB file name: (.+)/) {\n      # The win32 nm workalike emits information about the pdb it is using.\n      if ($main::opt_debug) { print STDERR \"Using PDB $1\\n\"; }\n    }\n  }\n  close(NM);\n  # Handle the last line in the nm output.  Unfortunately, we don't know\n  # how big this last symbol is, because we don't know how big the file\n  # is.  For now, we just give it a size of 0.\n  # TODO(csilvers): do better here.\n  if (defined($routine) && $routine =~ m/$regexp/) {\n    $symbol_table->{$routine} = [HexExtend($last_start),\n                                 HexExtend($last_start)];\n  }\n  return $symbol_table;\n}\n\n# Gets the procedure boundaries for all routines in \"$image\" whose names\n# match \"$regexp\" and returns them in a hashtable mapping from procedure\n# name to a two-element vector of [start address, end address].\n# Will return an empty map if nm is not installed or not working properly.\nsub GetProcedureBoundaries {\n  my $image = shift;\n  my $regexp = shift;\n\n  # If $image doesn't start with /, then put ./ in front of it.  This works\n  # around an obnoxious bug in our probing of nm -f behavior.\n  # \"nm -f $image\" is supposed to fail on GNU nm, but if:\n  #\n  # a. $image starts with [BbSsPp] (for example, bin/foo/bar), AND\n  # b. you have a.out in your current directory (a not uncommon occurence)\n  #\n  # then \"nm -f $image\" succeeds because -f only looks at the first letter of\n  # the argument, which looks valid because it's [BbSsPp], and then since\n  # there's no image provided, it looks for a.out and finds it.\n  #\n  # This regex makes sure that $image starts with . or /, forcing the -f\n  # parsing to fail since . and / are not valid formats.\n  $image =~ s#^[^/]#./$&#;\n\n  # For libc libraries, the copy in /usr/lib/debug contains debugging symbols\n  my $debugging = DebuggingLibrary($image);\n  if ($debugging) {\n    $image = $debugging;\n  }\n\n  my $nm = $obj_tool_map{\"nm\"};\n  my $cppfilt = $obj_tool_map{\"c++filt\"};\n\n  # nm can fail for two reasons: 1) $image isn't a debug library; 2) nm\n  # binary doesn't support --demangle.  In addition, for OS X we need\n  # to use the -f flag to get 'flat' nm output (otherwise we don't sort\n  # properly and get incorrect results).  Unfortunately, GNU nm uses -f\n  # in an incompatible way.  So first we test whether our nm supports\n  # --demangle and -f.\n  my $demangle_flag = \"\";\n  my $cppfilt_flag = \"\";\n  my $to_devnull = \">$dev_null 2>&1\";\n  if (system(ShellEscape($nm, \"--demangle\", \"image\") . $to_devnull) == 0) {\n    # In this mode, we do \"nm --demangle <foo>\"\n    $demangle_flag = \"--demangle\";\n    $cppfilt_flag = \"\";\n  } elsif (system(ShellEscape($cppfilt, $image) . $to_devnull) == 0) {\n    # In this mode, we do \"nm <foo> | c++filt\"\n    $cppfilt_flag = \" | \" . ShellEscape($cppfilt);\n  };\n  my $flatten_flag = \"\";\n  if (system(ShellEscape($nm, \"-f\", $image) . $to_devnull) == 0) {\n    $flatten_flag = \"-f\";\n  }\n\n  # Finally, in the case $imagie isn't a debug library, we try again with\n  # -D to at least get *exported* symbols.  If we can't use --demangle,\n  # we use c++filt instead, if it exists on this system.\n  my @nm_commands = (ShellEscape($nm, \"-n\", $flatten_flag, $demangle_flag,\n                                 $image) . \" 2>$dev_null $cppfilt_flag\",\n                     ShellEscape($nm, \"-D\", \"-n\", $flatten_flag, $demangle_flag,\n                                 $image) . \" 2>$dev_null $cppfilt_flag\",\n                     # 6nm is for Go binaries\n                     ShellEscape(\"6nm\", \"$image\") . \" 2>$dev_null | sort\",\n                     );\n\n  # If the executable is an MS Windows PDB-format executable, we'll\n  # have set up obj_tool_map(\"nm_pdb\").  In this case, we actually\n  # want to use both unix nm and windows-specific nm_pdb, since\n  # PDB-format executables can apparently include dwarf .o files.\n  if (exists $obj_tool_map{\"nm_pdb\"}) {\n    push(@nm_commands,\n         ShellEscape($obj_tool_map{\"nm_pdb\"}, \"--demangle\", $image)\n         . \" 2>$dev_null\");\n  }\n\n  foreach my $nm_command (@nm_commands) {\n    my $symbol_table = GetProcedureBoundariesViaNm($nm_command, $regexp);\n    return $symbol_table if (%{$symbol_table});\n  }\n  my $symbol_table = {};\n  return $symbol_table;\n}\n\n\n# The test vectors for AddressAdd/Sub/Inc are 8-16-nibble hex strings.\n# To make them more readable, we add underscores at interesting places.\n# This routine removes the underscores, producing the canonical representation\n# used by pprof to represent addresses, particularly in the tested routines.\nsub CanonicalHex {\n  my $arg = shift;\n  return join '', (split '_',$arg);\n}\n\n\n# Unit test for AddressAdd:\nsub AddressAddUnitTest {\n  my $test_data_8 = shift;\n  my $test_data_16 = shift;\n  my $error_count = 0;\n  my $fail_count = 0;\n  my $pass_count = 0;\n  # print STDERR \"AddressAddUnitTest: \", 1+$#{$test_data_8}, \" tests\\n\";\n\n  # First a few 8-nibble addresses.  Note that this implementation uses\n  # plain old arithmetic, so a quick sanity check along with verifying what\n  # happens to overflow (we want it to wrap):\n  $address_length = 8;\n  foreach my $row (@{$test_data_8}) {\n    if ($main::opt_debug and $main::opt_test) { print STDERR \"@{$row}\\n\"; }\n    my $sum = AddressAdd ($row->[0], $row->[1]);\n    if ($sum ne $row->[2]) {\n      printf STDERR \"ERROR: %s != %s + %s = %s\\n\", $sum,\n             $row->[0], $row->[1], $row->[2];\n      ++$fail_count;\n    } else {\n      ++$pass_count;\n    }\n  }\n  printf STDERR \"AddressAdd 32-bit tests: %d passes, %d failures\\n\",\n         $pass_count, $fail_count;\n  $error_count = $fail_count;\n  $fail_count = 0;\n  $pass_count = 0;\n\n  # Now 16-nibble addresses.\n  $address_length = 16;\n  foreach my $row (@{$test_data_16}) {\n    if ($main::opt_debug and $main::opt_test) { print STDERR \"@{$row}\\n\"; }\n    my $sum = AddressAdd (CanonicalHex($row->[0]), CanonicalHex($row->[1]));\n    my $expected = join '', (split '_',$row->[2]);\n    if ($sum ne CanonicalHex($row->[2])) {\n      printf STDERR \"ERROR: %s != %s + %s = %s\\n\", $sum,\n             $row->[0], $row->[1], $row->[2];\n      ++$fail_count;\n    } else {\n      ++$pass_count;\n    }\n  }\n  printf STDERR \"AddressAdd 64-bit tests: %d passes, %d failures\\n\",\n         $pass_count, $fail_count;\n  $error_count += $fail_count;\n\n  return $error_count;\n}\n\n\n# Unit test for AddressSub:\nsub AddressSubUnitTest {\n  my $test_data_8 = shift;\n  my $test_data_16 = shift;\n  my $error_count = 0;\n  my $fail_count = 0;\n  my $pass_count = 0;\n  # print STDERR \"AddressSubUnitTest: \", 1+$#{$test_data_8}, \" tests\\n\";\n\n  # First a few 8-nibble addresses.  Note that this implementation uses\n  # plain old arithmetic, so a quick sanity check along with verifying what\n  # happens to overflow (we want it to wrap):\n  $address_length = 8;\n  foreach my $row (@{$test_data_8}) {\n    if ($main::opt_debug and $main::opt_test) { print STDERR \"@{$row}\\n\"; }\n    my $sum = AddressSub ($row->[0], $row->[1]);\n    if ($sum ne $row->[3]) {\n      printf STDERR \"ERROR: %s != %s - %s = %s\\n\", $sum,\n             $row->[0], $row->[1], $row->[3];\n      ++$fail_count;\n    } else {\n      ++$pass_count;\n    }\n  }\n  printf STDERR \"AddressSub 32-bit tests: %d passes, %d failures\\n\",\n         $pass_count, $fail_count;\n  $error_count = $fail_count;\n  $fail_count = 0;\n  $pass_count = 0;\n\n  # Now 16-nibble addresses.\n  $address_length = 16;\n  foreach my $row (@{$test_data_16}) {\n    if ($main::opt_debug and $main::opt_test) { print STDERR \"@{$row}\\n\"; }\n    my $sum = AddressSub (CanonicalHex($row->[0]), CanonicalHex($row->[1]));\n    if ($sum ne CanonicalHex($row->[3])) {\n      printf STDERR \"ERROR: %s != %s - %s = %s\\n\", $sum,\n             $row->[0], $row->[1], $row->[3];\n      ++$fail_count;\n    } else {\n      ++$pass_count;\n    }\n  }\n  printf STDERR \"AddressSub 64-bit tests: %d passes, %d failures\\n\",\n         $pass_count, $fail_count;\n  $error_count += $fail_count;\n\n  return $error_count;\n}\n\n\n# Unit test for AddressInc:\nsub AddressIncUnitTest {\n  my $test_data_8 = shift;\n  my $test_data_16 = shift;\n  my $error_count = 0;\n  my $fail_count = 0;\n  my $pass_count = 0;\n  # print STDERR \"AddressIncUnitTest: \", 1+$#{$test_data_8}, \" tests\\n\";\n\n  # First a few 8-nibble addresses.  Note that this implementation uses\n  # plain old arithmetic, so a quick sanity check along with verifying what\n  # happens to overflow (we want it to wrap):\n  $address_length = 8;\n  foreach my $row (@{$test_data_8}) {\n    if ($main::opt_debug and $main::opt_test) { print STDERR \"@{$row}\\n\"; }\n    my $sum = AddressInc ($row->[0]);\n    if ($sum ne $row->[4]) {\n      printf STDERR \"ERROR: %s != %s + 1 = %s\\n\", $sum,\n             $row->[0], $row->[4];\n      ++$fail_count;\n    } else {\n      ++$pass_count;\n    }\n  }\n  printf STDERR \"AddressInc 32-bit tests: %d passes, %d failures\\n\",\n         $pass_count, $fail_count;\n  $error_count = $fail_count;\n  $fail_count = 0;\n  $pass_count = 0;\n\n  # Now 16-nibble addresses.\n  $address_length = 16;\n  foreach my $row (@{$test_data_16}) {\n    if ($main::opt_debug and $main::opt_test) { print STDERR \"@{$row}\\n\"; }\n    my $sum = AddressInc (CanonicalHex($row->[0]));\n    if ($sum ne CanonicalHex($row->[4])) {\n      printf STDERR \"ERROR: %s != %s + 1 = %s\\n\", $sum,\n             $row->[0], $row->[4];\n      ++$fail_count;\n    } else {\n      ++$pass_count;\n    }\n  }\n  printf STDERR \"AddressInc 64-bit tests: %d passes, %d failures\\n\",\n         $pass_count, $fail_count;\n  $error_count += $fail_count;\n\n  return $error_count;\n}\n\n\n# Driver for unit tests.\n# Currently just the address add/subtract/increment routines for 64-bit.\nsub RunUnitTests {\n  my $error_count = 0;\n\n  # This is a list of tuples [a, b, a+b, a-b, a+1]\n  my $unit_test_data_8 = [\n    [qw(aaaaaaaa 50505050 fafafafa 5a5a5a5a aaaaaaab)],\n    [qw(50505050 aaaaaaaa fafafafa a5a5a5a6 50505051)],\n    [qw(ffffffff aaaaaaaa aaaaaaa9 55555555 00000000)],\n    [qw(00000001 ffffffff 00000000 00000002 00000002)],\n    [qw(00000001 fffffff0 fffffff1 00000011 00000002)],\n  ];\n  my $unit_test_data_16 = [\n    # The implementation handles data in 7-nibble chunks, so those are the\n    # interesting boundaries.\n    [qw(aaaaaaaa 50505050\n        00_000000f_afafafa 00_0000005_a5a5a5a 00_000000a_aaaaaab)],\n    [qw(50505050 aaaaaaaa\n        00_000000f_afafafa ff_ffffffa_5a5a5a6 00_0000005_0505051)],\n    [qw(ffffffff aaaaaaaa\n        00_000001a_aaaaaa9 00_0000005_5555555 00_0000010_0000000)],\n    [qw(00000001 ffffffff\n        00_0000010_0000000 ff_ffffff0_0000002 00_0000000_0000002)],\n    [qw(00000001 fffffff0\n        00_000000f_ffffff1 ff_ffffff0_0000011 00_0000000_0000002)],\n\n    [qw(00_a00000a_aaaaaaa 50505050\n        00_a00000f_afafafa 00_a000005_a5a5a5a 00_a00000a_aaaaaab)],\n    [qw(0f_fff0005_0505050 aaaaaaaa\n        0f_fff000f_afafafa 0f_ffefffa_5a5a5a6 0f_fff0005_0505051)],\n    [qw(00_000000f_fffffff 01_800000a_aaaaaaa\n        01_800001a_aaaaaa9 fe_8000005_5555555 00_0000010_0000000)],\n    [qw(00_0000000_0000001 ff_fffffff_fffffff\n        00_0000000_0000000 00_0000000_0000002 00_0000000_0000002)],\n    [qw(00_0000000_0000001 ff_fffffff_ffffff0\n        ff_fffffff_ffffff1 00_0000000_0000011 00_0000000_0000002)],\n  ];\n\n  $error_count += AddressAddUnitTest($unit_test_data_8, $unit_test_data_16);\n  $error_count += AddressSubUnitTest($unit_test_data_8, $unit_test_data_16);\n  $error_count += AddressIncUnitTest($unit_test_data_8, $unit_test_data_16);\n  if ($error_count > 0) {\n    print STDERR $error_count, \" errors: FAILED\\n\";\n  } else {\n    print STDERR \"PASS\\n\";\n  }\n  exit ($error_count);\n}\n"
  },
  {
    "path": "deps/jemalloc/config.guess",
    "content": "#! /bin/sh\n# Attempt to guess a canonical system name.\n#   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,\n#   2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,\n#   2011, 2012 Free Software Foundation, Inc.\n\ntimestamp='2012-02-10'\n\n# This file is free software; you can redistribute it and/or modify it\n# under the terms of the GNU General Public License as published by\n# the Free Software Foundation; either version 2 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful, but\n# WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n# General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program; if not, see <http://www.gnu.org/licenses/>.\n#\n# As a special exception to the GNU General Public License, if you\n# distribute this file as part of a program that contains a\n# configuration script generated by Autoconf, you may include it under\n# the same distribution terms that you use for the rest of that program.\n\n\n# Originally written by Per Bothner.  Please send patches (context\n# diff format) to <config-patches@gnu.org> and include a ChangeLog\n# entry.\n#\n# This script attempts to guess a canonical system name similar to\n# config.sub.  If it succeeds, it prints the system name on stdout, and\n# exits with 0.  Otherwise, it exits with 1.\n#\n# You can get the latest version of this script from:\n# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD\n\nme=`echo \"$0\" | sed -e 's,.*/,,'`\n\nusage=\"\\\nUsage: $0 [OPTION]\n\nOutput the configuration name of the system \\`$me' is run on.\n\nOperation modes:\n  -h, --help         print this help, then exit\n  -t, --time-stamp   print date of last modification, then exit\n  -v, --version      print version number, then exit\n\nReport bugs and patches to <config-patches@gnu.org>.\"\n\nversion=\"\\\nGNU config.guess ($timestamp)\n\nOriginally written by Per Bothner.\nCopyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,\n2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012\nFree Software Foundation, Inc.\n\nThis is free software; see the source for copying conditions.  There is NO\nwarranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\"\n\nhelp=\"\nTry \\`$me --help' for more information.\"\n\n# Parse command line\nwhile test $# -gt 0 ; do\n  case $1 in\n    --time-stamp | --time* | -t )\n       echo \"$timestamp\" ; exit ;;\n    --version | -v )\n       echo \"$version\" ; exit ;;\n    --help | --h* | -h )\n       echo \"$usage\"; exit ;;\n    -- )     # Stop option processing\n       shift; break ;;\n    - )\t# Use stdin as input.\n       break ;;\n    -* )\n       echo \"$me: invalid option $1$help\" >&2\n       exit 1 ;;\n    * )\n       break ;;\n  esac\ndone\n\nif test $# != 0; then\n  echo \"$me: too many arguments$help\" >&2\n  exit 1\nfi\n\ntrap 'exit 1' 1 2 15\n\n# CC_FOR_BUILD -- compiler used by this script. Note that the use of a\n# compiler to aid in system detection is discouraged as it requires\n# temporary files to be created and, as you can see below, it is a\n# headache to deal with in a portable fashion.\n\n# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still\n# use `HOST_CC' if defined, but it is deprecated.\n\n# Portable tmp directory creation inspired by the Autoconf team.\n\nset_cc_for_build='\ntrap \"exitcode=\\$?; (rm -f \\$tmpfiles 2>/dev/null; rmdir \\$tmp 2>/dev/null) && exit \\$exitcode\" 0 ;\ntrap \"rm -f \\$tmpfiles 2>/dev/null; rmdir \\$tmp 2>/dev/null; exit 1\" 1 2 13 15 ;\n: ${TMPDIR=/tmp} ;\n { tmp=`(umask 077 && mktemp -d \"$TMPDIR/cgXXXXXX\") 2>/dev/null` && test -n \"$tmp\" && test -d \"$tmp\" ; } ||\n { test -n \"$RANDOM\" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } ||\n { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo \"Warning: creating insecure temp directory\" >&2 ; } ||\n { echo \"$me: cannot create a temporary directory in $TMPDIR\" >&2 ; exit 1 ; } ;\ndummy=$tmp/dummy ;\ntmpfiles=\"$dummy.c $dummy.o $dummy.rel $dummy\" ;\ncase $CC_FOR_BUILD,$HOST_CC,$CC in\n ,,)    echo \"int x;\" > $dummy.c ;\n\tfor c in cc gcc c89 c99 ; do\n\t  if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then\n\t     CC_FOR_BUILD=\"$c\"; break ;\n\t  fi ;\n\tdone ;\n\tif test x\"$CC_FOR_BUILD\" = x ; then\n\t  CC_FOR_BUILD=no_compiler_found ;\n\tfi\n\t;;\n ,,*)   CC_FOR_BUILD=$CC ;;\n ,*,*)  CC_FOR_BUILD=$HOST_CC ;;\nesac ; set_cc_for_build= ;'\n\n# This is needed to find uname on a Pyramid OSx when run in the BSD universe.\n# (ghazi@noc.rutgers.edu 1994-08-24)\nif (test -f /.attbin/uname) >/dev/null 2>&1 ; then\n\tPATH=$PATH:/.attbin ; export PATH\nfi\n\nUNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown\nUNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown\nUNAME_SYSTEM=`(uname -s) 2>/dev/null`  || UNAME_SYSTEM=unknown\nUNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown\n\n# Note: order is significant - the case branches are not exclusive.\n\ncase \"${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}\" in\n    *:NetBSD:*:*)\n\t# NetBSD (nbsd) targets should (where applicable) match one or\n\t# more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*,\n\t# *-*-netbsdecoff* and *-*-netbsd*.  For targets that recently\n\t# switched to ELF, *-*-netbsd* would select the old\n\t# object file format.  This provides both forward\n\t# compatibility and a consistent mechanism for selecting the\n\t# object file format.\n\t#\n\t# Note: NetBSD doesn't particularly care about the vendor\n\t# portion of the name.  We always set it to \"unknown\".\n\tsysctl=\"sysctl -n hw.machine_arch\"\n\tUNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \\\n\t    /usr/sbin/$sysctl 2>/dev/null || echo unknown)`\n\tcase \"${UNAME_MACHINE_ARCH}\" in\n\t    armeb) machine=armeb-unknown ;;\n\t    arm*) machine=arm-unknown ;;\n\t    sh3el) machine=shl-unknown ;;\n\t    sh3eb) machine=sh-unknown ;;\n\t    sh5el) machine=sh5le-unknown ;;\n\t    *) machine=${UNAME_MACHINE_ARCH}-unknown ;;\n\tesac\n\t# The Operating System including object format, if it has switched\n\t# to ELF recently, or will in the future.\n\tcase \"${UNAME_MACHINE_ARCH}\" in\n\t    arm*|i386|m68k|ns32k|sh3*|sparc|vax)\n\t\teval $set_cc_for_build\n\t\tif echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \\\n\t\t\t| grep -q __ELF__\n\t\tthen\n\t\t    # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).\n\t\t    # Return netbsd for either.  FIX?\n\t\t    os=netbsd\n\t\telse\n\t\t    os=netbsdelf\n\t\tfi\n\t\t;;\n\t    *)\n\t\tos=netbsd\n\t\t;;\n\tesac\n\t# The OS release\n\t# Debian GNU/NetBSD machines have a different userland, and\n\t# thus, need a distinct triplet. However, they do not need\n\t# kernel version information, so it can be replaced with a\n\t# suitable tag, in the style of linux-gnu.\n\tcase \"${UNAME_VERSION}\" in\n\t    Debian*)\n\t\trelease='-gnu'\n\t\t;;\n\t    *)\n\t\trelease=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\\./'`\n\t\t;;\n\tesac\n\t# Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:\n\t# contains redundant information, the shorter form:\n\t# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.\n\techo \"${machine}-${os}${release}\"\n\texit ;;\n    *:OpenBSD:*:*)\n\tUNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'`\n\techo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE}\n\texit ;;\n    *:ekkoBSD:*:*)\n\techo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE}\n\texit ;;\n    *:SolidBSD:*:*)\n\techo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE}\n\texit ;;\n    macppc:MirBSD:*:*)\n\techo powerpc-unknown-mirbsd${UNAME_RELEASE}\n\texit ;;\n    *:MirBSD:*:*)\n\techo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE}\n\texit ;;\n    alpha:OSF1:*:*)\n\tcase $UNAME_RELEASE in\n\t*4.0)\n\t\tUNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`\n\t\t;;\n\t*5.*)\n\t\tUNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`\n\t\t;;\n\tesac\n\t# According to Compaq, /usr/sbin/psrinfo has been available on\n\t# OSF/1 and Tru64 systems produced since 1995.  I hope that\n\t# covers most systems running today.  This code pipes the CPU\n\t# types through head -n 1, so we only detect the type of CPU 0.\n\tALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^  The alpha \\(.*\\) processor.*$/\\1/p' | head -n 1`\n\tcase \"$ALPHA_CPU_TYPE\" in\n\t    \"EV4 (21064)\")\n\t\tUNAME_MACHINE=\"alpha\" ;;\n\t    \"EV4.5 (21064)\")\n\t\tUNAME_MACHINE=\"alpha\" ;;\n\t    \"LCA4 (21066/21068)\")\n\t\tUNAME_MACHINE=\"alpha\" ;;\n\t    \"EV5 (21164)\")\n\t\tUNAME_MACHINE=\"alphaev5\" ;;\n\t    \"EV5.6 (21164A)\")\n\t\tUNAME_MACHINE=\"alphaev56\" ;;\n\t    \"EV5.6 (21164PC)\")\n\t\tUNAME_MACHINE=\"alphapca56\" ;;\n\t    \"EV5.7 (21164PC)\")\n\t\tUNAME_MACHINE=\"alphapca57\" ;;\n\t    \"EV6 (21264)\")\n\t\tUNAME_MACHINE=\"alphaev6\" ;;\n\t    \"EV6.7 (21264A)\")\n\t\tUNAME_MACHINE=\"alphaev67\" ;;\n\t    \"EV6.8CB (21264C)\")\n\t\tUNAME_MACHINE=\"alphaev68\" ;;\n\t    \"EV6.8AL (21264B)\")\n\t\tUNAME_MACHINE=\"alphaev68\" ;;\n\t    \"EV6.8CX (21264D)\")\n\t\tUNAME_MACHINE=\"alphaev68\" ;;\n\t    \"EV6.9A (21264/EV69A)\")\n\t\tUNAME_MACHINE=\"alphaev69\" ;;\n\t    \"EV7 (21364)\")\n\t\tUNAME_MACHINE=\"alphaev7\" ;;\n\t    \"EV7.9 (21364A)\")\n\t\tUNAME_MACHINE=\"alphaev79\" ;;\n\tesac\n\t# A Pn.n version is a patched version.\n\t# A Vn.n version is a released version.\n\t# A Tn.n version is a released field test version.\n\t# A Xn.n version is an unreleased experimental baselevel.\n\t# 1.2 uses \"1.2\" for uname -r.\n\techo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`\n\t# Reset EXIT trap before exiting to avoid spurious non-zero exit code.\n\texitcode=$?\n\ttrap '' 0\n\texit $exitcode ;;\n    Alpha\\ *:Windows_NT*:*)\n\t# How do we know it's Interix rather than the generic POSIX subsystem?\n\t# Should we change UNAME_MACHINE based on the output of uname instead\n\t# of the specific Alpha model?\n\techo alpha-pc-interix\n\texit ;;\n    21064:Windows_NT:50:3)\n\techo alpha-dec-winnt3.5\n\texit ;;\n    Amiga*:UNIX_System_V:4.0:*)\n\techo m68k-unknown-sysv4\n\texit ;;\n    *:[Aa]miga[Oo][Ss]:*:*)\n\techo ${UNAME_MACHINE}-unknown-amigaos\n\texit ;;\n    *:[Mm]orph[Oo][Ss]:*:*)\n\techo ${UNAME_MACHINE}-unknown-morphos\n\texit ;;\n    *:OS/390:*:*)\n\techo i370-ibm-openedition\n\texit ;;\n    *:z/VM:*:*)\n\techo s390-ibm-zvmoe\n\texit ;;\n    *:OS400:*:*)\n\techo powerpc-ibm-os400\n\texit ;;\n    arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)\n\techo arm-acorn-riscix${UNAME_RELEASE}\n\texit ;;\n    arm:riscos:*:*|arm:RISCOS:*:*)\n\techo arm-unknown-riscos\n\texit ;;\n    SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)\n\techo hppa1.1-hitachi-hiuxmpp\n\texit ;;\n    Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)\n\t# akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.\n\tif test \"`(/bin/universe) 2>/dev/null`\" = att ; then\n\t\techo pyramid-pyramid-sysv3\n\telse\n\t\techo pyramid-pyramid-bsd\n\tfi\n\texit ;;\n    NILE*:*:*:dcosx)\n\techo pyramid-pyramid-svr4\n\texit ;;\n    DRS?6000:unix:4.0:6*)\n\techo sparc-icl-nx6\n\texit ;;\n    DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*)\n\tcase `/usr/bin/uname -p` in\n\t    sparc) echo sparc-icl-nx7; exit ;;\n\tesac ;;\n    s390x:SunOS:*:*)\n\techo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`\n\texit ;;\n    sun4H:SunOS:5.*:*)\n\techo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`\n\texit ;;\n    sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)\n\techo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`\n\texit ;;\n    i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*)\n\techo i386-pc-auroraux${UNAME_RELEASE}\n\texit ;;\n    i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*)\n\teval $set_cc_for_build\n\tSUN_ARCH=\"i386\"\n\t# If there is a compiler, see if it is configured for 64-bit objects.\n\t# Note that the Sun cc does not turn __LP64__ into 1 like gcc does.\n\t# This test works for both compilers.\n\tif [ \"$CC_FOR_BUILD\" != 'no_compiler_found' ]; then\n\t    if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \\\n\t\t(CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \\\n\t\tgrep IS_64BIT_ARCH >/dev/null\n\t    then\n\t\tSUN_ARCH=\"x86_64\"\n\t    fi\n\tfi\n\techo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`\n\texit ;;\n    sun4*:SunOS:6*:*)\n\t# According to config.sub, this is the proper way to canonicalize\n\t# SunOS6.  Hard to guess exactly what SunOS6 will be like, but\n\t# it's likely to be more like Solaris than SunOS4.\n\techo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`\n\texit ;;\n    sun4*:SunOS:*:*)\n\tcase \"`/usr/bin/arch -k`\" in\n\t    Series*|S4*)\n\t\tUNAME_RELEASE=`uname -v`\n\t\t;;\n\tesac\n\t# Japanese Language versions have a version number like `4.1.3-JL'.\n\techo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'`\n\texit ;;\n    sun3*:SunOS:*:*)\n\techo m68k-sun-sunos${UNAME_RELEASE}\n\texit ;;\n    sun*:*:4.2BSD:*)\n\tUNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`\n\ttest \"x${UNAME_RELEASE}\" = \"x\" && UNAME_RELEASE=3\n\tcase \"`/bin/arch`\" in\n\t    sun3)\n\t\techo m68k-sun-sunos${UNAME_RELEASE}\n\t\t;;\n\t    sun4)\n\t\techo sparc-sun-sunos${UNAME_RELEASE}\n\t\t;;\n\tesac\n\texit ;;\n    aushp:SunOS:*:*)\n\techo sparc-auspex-sunos${UNAME_RELEASE}\n\texit ;;\n    # The situation for MiNT is a little confusing.  The machine name\n    # can be virtually everything (everything which is not\n    # \"atarist\" or \"atariste\" at least should have a processor\n    # > m68000).  The system name ranges from \"MiNT\" over \"FreeMiNT\"\n    # to the lowercase version \"mint\" (or \"freemint\").  Finally\n    # the system name \"TOS\" denotes a system which is actually not\n    # MiNT.  But MiNT is downward compatible to TOS, so this should\n    # be no problem.\n    atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)\n\techo m68k-atari-mint${UNAME_RELEASE}\n\texit ;;\n    atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)\n\techo m68k-atari-mint${UNAME_RELEASE}\n\texit ;;\n    *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)\n\techo m68k-atari-mint${UNAME_RELEASE}\n\texit ;;\n    milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)\n\techo m68k-milan-mint${UNAME_RELEASE}\n\texit ;;\n    hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)\n\techo m68k-hades-mint${UNAME_RELEASE}\n\texit ;;\n    *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)\n\techo m68k-unknown-mint${UNAME_RELEASE}\n\texit ;;\n    m68k:machten:*:*)\n\techo m68k-apple-machten${UNAME_RELEASE}\n\texit ;;\n    powerpc:machten:*:*)\n\techo powerpc-apple-machten${UNAME_RELEASE}\n\texit ;;\n    RISC*:Mach:*:*)\n\techo mips-dec-mach_bsd4.3\n\texit ;;\n    RISC*:ULTRIX:*:*)\n\techo mips-dec-ultrix${UNAME_RELEASE}\n\texit ;;\n    VAX*:ULTRIX*:*:*)\n\techo vax-dec-ultrix${UNAME_RELEASE}\n\texit ;;\n    2020:CLIX:*:* | 2430:CLIX:*:*)\n\techo clipper-intergraph-clix${UNAME_RELEASE}\n\texit ;;\n    mips:*:*:UMIPS | mips:*:*:RISCos)\n\teval $set_cc_for_build\n\tsed 's/^\t//' << EOF >$dummy.c\n#ifdef __cplusplus\n#include <stdio.h>  /* for printf() prototype */\n\tint main (int argc, char *argv[]) {\n#else\n\tint main (argc, argv) int argc; char *argv[]; {\n#endif\n\t#if defined (host_mips) && defined (MIPSEB)\n\t#if defined (SYSTYPE_SYSV)\n\t  printf (\"mips-mips-riscos%ssysv\\n\", argv[1]); exit (0);\n\t#endif\n\t#if defined (SYSTYPE_SVR4)\n\t  printf (\"mips-mips-riscos%ssvr4\\n\", argv[1]); exit (0);\n\t#endif\n\t#if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)\n\t  printf (\"mips-mips-riscos%sbsd\\n\", argv[1]); exit (0);\n\t#endif\n\t#endif\n\t  exit (-1);\n\t}\nEOF\n\t$CC_FOR_BUILD -o $dummy $dummy.c &&\n\t  dummyarg=`echo \"${UNAME_RELEASE}\" | sed -n 's/\\([0-9]*\\).*/\\1/p'` &&\n\t  SYSTEM_NAME=`$dummy $dummyarg` &&\n\t    { echo \"$SYSTEM_NAME\"; exit; }\n\techo mips-mips-riscos${UNAME_RELEASE}\n\texit ;;\n    Motorola:PowerMAX_OS:*:*)\n\techo powerpc-motorola-powermax\n\texit ;;\n    Motorola:*:4.3:PL8-*)\n\techo powerpc-harris-powermax\n\texit ;;\n    Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*)\n\techo powerpc-harris-powermax\n\texit ;;\n    Night_Hawk:Power_UNIX:*:*)\n\techo powerpc-harris-powerunix\n\texit ;;\n    m88k:CX/UX:7*:*)\n\techo m88k-harris-cxux7\n\texit ;;\n    m88k:*:4*:R4*)\n\techo m88k-motorola-sysv4\n\texit ;;\n    m88k:*:3*:R3*)\n\techo m88k-motorola-sysv3\n\texit ;;\n    AViiON:dgux:*:*)\n\t# DG/UX returns AViiON for all architectures\n\tUNAME_PROCESSOR=`/usr/bin/uname -p`\n\tif [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ]\n\tthen\n\t    if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \\\n\t       [ ${TARGET_BINARY_INTERFACE}x = x ]\n\t    then\n\t\techo m88k-dg-dgux${UNAME_RELEASE}\n\t    else\n\t\techo m88k-dg-dguxbcs${UNAME_RELEASE}\n\t    fi\n\telse\n\t    echo i586-dg-dgux${UNAME_RELEASE}\n\tfi\n\texit ;;\n    M88*:DolphinOS:*:*)\t# DolphinOS (SVR3)\n\techo m88k-dolphin-sysv3\n\texit ;;\n    M88*:*:R3*:*)\n\t# Delta 88k system running SVR3\n\techo m88k-motorola-sysv3\n\texit ;;\n    XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)\n\techo m88k-tektronix-sysv3\n\texit ;;\n    Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)\n\techo m68k-tektronix-bsd\n\texit ;;\n    *:IRIX*:*:*)\n\techo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'`\n\texit ;;\n    ????????:AIX?:[12].1:2)   # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.\n\techo romp-ibm-aix     # uname -m gives an 8 hex-code CPU id\n\texit ;;               # Note that: echo \"'`uname -s`'\" gives 'AIX '\n    i*86:AIX:*:*)\n\techo i386-ibm-aix\n\texit ;;\n    ia64:AIX:*:*)\n\tif [ -x /usr/bin/oslevel ] ; then\n\t\tIBM_REV=`/usr/bin/oslevel`\n\telse\n\t\tIBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}\n\tfi\n\techo ${UNAME_MACHINE}-ibm-aix${IBM_REV}\n\texit ;;\n    *:AIX:2:3)\n\tif grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then\n\t\teval $set_cc_for_build\n\t\tsed 's/^\t\t//' << EOF >$dummy.c\n\t\t#include <sys/systemcfg.h>\n\n\t\tmain()\n\t\t\t{\n\t\t\tif (!__power_pc())\n\t\t\t\texit(1);\n\t\t\tputs(\"powerpc-ibm-aix3.2.5\");\n\t\t\texit(0);\n\t\t\t}\nEOF\n\t\tif $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy`\n\t\tthen\n\t\t\techo \"$SYSTEM_NAME\"\n\t\telse\n\t\t\techo rs6000-ibm-aix3.2.5\n\t\tfi\n\telif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then\n\t\techo rs6000-ibm-aix3.2.4\n\telse\n\t\techo rs6000-ibm-aix3.2\n\tfi\n\texit ;;\n    *:AIX:*:[4567])\n\tIBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`\n\tif /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then\n\t\tIBM_ARCH=rs6000\n\telse\n\t\tIBM_ARCH=powerpc\n\tfi\n\tif [ -x /usr/bin/oslevel ] ; then\n\t\tIBM_REV=`/usr/bin/oslevel`\n\telse\n\t\tIBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}\n\tfi\n\techo ${IBM_ARCH}-ibm-aix${IBM_REV}\n\texit ;;\n    *:AIX:*:*)\n\techo rs6000-ibm-aix\n\texit ;;\n    ibmrt:4.4BSD:*|romp-ibm:BSD:*)\n\techo romp-ibm-bsd4.4\n\texit ;;\n    ibmrt:*BSD:*|romp-ibm:BSD:*)            # covers RT/PC BSD and\n\techo romp-ibm-bsd${UNAME_RELEASE}   # 4.3 with uname added to\n\texit ;;                             # report: romp-ibm BSD 4.3\n    *:BOSX:*:*)\n\techo rs6000-bull-bosx\n\texit ;;\n    DPX/2?00:B.O.S.:*:*)\n\techo m68k-bull-sysv3\n\texit ;;\n    9000/[34]??:4.3bsd:1.*:*)\n\techo m68k-hp-bsd\n\texit ;;\n    hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)\n\techo m68k-hp-bsd4.4\n\texit ;;\n    9000/[34678]??:HP-UX:*:*)\n\tHPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`\n\tcase \"${UNAME_MACHINE}\" in\n\t    9000/31? )            HP_ARCH=m68000 ;;\n\t    9000/[34]?? )         HP_ARCH=m68k ;;\n\t    9000/[678][0-9][0-9])\n\t\tif [ -x /usr/bin/getconf ]; then\n\t\t    sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`\n\t\t    sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`\n\t\t    case \"${sc_cpu_version}\" in\n\t\t      523) HP_ARCH=\"hppa1.0\" ;; # CPU_PA_RISC1_0\n\t\t      528) HP_ARCH=\"hppa1.1\" ;; # CPU_PA_RISC1_1\n\t\t      532)                      # CPU_PA_RISC2_0\n\t\t\tcase \"${sc_kernel_bits}\" in\n\t\t\t  32) HP_ARCH=\"hppa2.0n\" ;;\n\t\t\t  64) HP_ARCH=\"hppa2.0w\" ;;\n\t\t\t  '') HP_ARCH=\"hppa2.0\" ;;   # HP-UX 10.20\n\t\t\tesac ;;\n\t\t    esac\n\t\tfi\n\t\tif [ \"${HP_ARCH}\" = \"\" ]; then\n\t\t    eval $set_cc_for_build\n\t\t    sed 's/^\t\t//' << EOF >$dummy.c\n\n\t\t#define _HPUX_SOURCE\n\t\t#include <stdlib.h>\n\t\t#include <unistd.h>\n\n\t\tint main ()\n\t\t{\n\t\t#if defined(_SC_KERNEL_BITS)\n\t\t    long bits = sysconf(_SC_KERNEL_BITS);\n\t\t#endif\n\t\t    long cpu  = sysconf (_SC_CPU_VERSION);\n\n\t\t    switch (cpu)\n\t\t\t{\n\t\t\tcase CPU_PA_RISC1_0: puts (\"hppa1.0\"); break;\n\t\t\tcase CPU_PA_RISC1_1: puts (\"hppa1.1\"); break;\n\t\t\tcase CPU_PA_RISC2_0:\n\t\t#if defined(_SC_KERNEL_BITS)\n\t\t\t    switch (bits)\n\t\t\t\t{\n\t\t\t\tcase 64: puts (\"hppa2.0w\"); break;\n\t\t\t\tcase 32: puts (\"hppa2.0n\"); break;\n\t\t\t\tdefault: puts (\"hppa2.0\"); break;\n\t\t\t\t} break;\n\t\t#else  /* !defined(_SC_KERNEL_BITS) */\n\t\t\t    puts (\"hppa2.0\"); break;\n\t\t#endif\n\t\t\tdefault: puts (\"hppa1.0\"); break;\n\t\t\t}\n\t\t    exit (0);\n\t\t}\nEOF\n\t\t    (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy`\n\t\t    test -z \"$HP_ARCH\" && HP_ARCH=hppa\n\t\tfi ;;\n\tesac\n\tif [ ${HP_ARCH} = \"hppa2.0w\" ]\n\tthen\n\t    eval $set_cc_for_build\n\n\t    # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating\n\t    # 32-bit code.  hppa64-hp-hpux* has the same kernel and a compiler\n\t    # generating 64-bit code.  GNU and HP use different nomenclature:\n\t    #\n\t    # $ CC_FOR_BUILD=cc ./config.guess\n\t    # => hppa2.0w-hp-hpux11.23\n\t    # $ CC_FOR_BUILD=\"cc +DA2.0w\" ./config.guess\n\t    # => hppa64-hp-hpux11.23\n\n\t    if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) |\n\t\tgrep -q __LP64__\n\t    then\n\t\tHP_ARCH=\"hppa2.0w\"\n\t    else\n\t\tHP_ARCH=\"hppa64\"\n\t    fi\n\tfi\n\techo ${HP_ARCH}-hp-hpux${HPUX_REV}\n\texit ;;\n    ia64:HP-UX:*:*)\n\tHPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`\n\techo ia64-hp-hpux${HPUX_REV}\n\texit ;;\n    3050*:HI-UX:*:*)\n\teval $set_cc_for_build\n\tsed 's/^\t//' << EOF >$dummy.c\n\t#include <unistd.h>\n\tint\n\tmain ()\n\t{\n\t  long cpu = sysconf (_SC_CPU_VERSION);\n\t  /* The order matters, because CPU_IS_HP_MC68K erroneously returns\n\t     true for CPU_PA_RISC1_0.  CPU_IS_PA_RISC returns correct\n\t     results, however.  */\n\t  if (CPU_IS_PA_RISC (cpu))\n\t    {\n\t      switch (cpu)\n\t\t{\n\t\t  case CPU_PA_RISC1_0: puts (\"hppa1.0-hitachi-hiuxwe2\"); break;\n\t\t  case CPU_PA_RISC1_1: puts (\"hppa1.1-hitachi-hiuxwe2\"); break;\n\t\t  case CPU_PA_RISC2_0: puts (\"hppa2.0-hitachi-hiuxwe2\"); break;\n\t\t  default: puts (\"hppa-hitachi-hiuxwe2\"); break;\n\t\t}\n\t    }\n\t  else if (CPU_IS_HP_MC68K (cpu))\n\t    puts (\"m68k-hitachi-hiuxwe2\");\n\t  else puts (\"unknown-hitachi-hiuxwe2\");\n\t  exit (0);\n\t}\nEOF\n\t$CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` &&\n\t\t{ echo \"$SYSTEM_NAME\"; exit; }\n\techo unknown-hitachi-hiuxwe2\n\texit ;;\n    9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* )\n\techo hppa1.1-hp-bsd\n\texit ;;\n    9000/8??:4.3bsd:*:*)\n\techo hppa1.0-hp-bsd\n\texit ;;\n    *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)\n\techo hppa1.0-hp-mpeix\n\texit ;;\n    hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )\n\techo hppa1.1-hp-osf\n\texit ;;\n    hp8??:OSF1:*:*)\n\techo hppa1.0-hp-osf\n\texit ;;\n    i*86:OSF1:*:*)\n\tif [ -x /usr/sbin/sysversion ] ; then\n\t    echo ${UNAME_MACHINE}-unknown-osf1mk\n\telse\n\t    echo ${UNAME_MACHINE}-unknown-osf1\n\tfi\n\texit ;;\n    parisc*:Lites*:*:*)\n\techo hppa1.1-hp-lites\n\texit ;;\n    C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)\n\techo c1-convex-bsd\n\texit ;;\n    C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)\n\tif getsysinfo -f scalar_acc\n\tthen echo c32-convex-bsd\n\telse echo c2-convex-bsd\n\tfi\n\texit ;;\n    C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)\n\techo c34-convex-bsd\n\texit ;;\n    C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)\n\techo c38-convex-bsd\n\texit ;;\n    C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)\n\techo c4-convex-bsd\n\texit ;;\n    CRAY*Y-MP:*:*:*)\n\techo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\\.[^.]*$/.X/'\n\texit ;;\n    CRAY*[A-Z]90:*:*:*)\n\techo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \\\n\t| sed -e 's/CRAY.*\\([A-Z]90\\)/\\1/' \\\n\t      -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \\\n\t      -e 's/\\.[^.]*$/.X/'\n\texit ;;\n    CRAY*TS:*:*:*)\n\techo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\\.[^.]*$/.X/'\n\texit ;;\n    CRAY*T3E:*:*:*)\n\techo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\\.[^.]*$/.X/'\n\texit ;;\n    CRAY*SV1:*:*:*)\n\techo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\\.[^.]*$/.X/'\n\texit ;;\n    *:UNICOS/mp:*:*)\n\techo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\\.[^.]*$/.X/'\n\texit ;;\n    F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)\n\tFUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`\n\tFUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\\///'`\n\tFUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`\n\techo \"${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}\"\n\texit ;;\n    5000:UNIX_System_V:4.*:*)\n\tFUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\\///'`\n\tFUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'`\n\techo \"sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}\"\n\texit ;;\n    i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\\ Embedded/OS:*:*)\n\techo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}\n\texit ;;\n    sparc*:BSD/OS:*:*)\n\techo sparc-unknown-bsdi${UNAME_RELEASE}\n\texit ;;\n    *:BSD/OS:*:*)\n\techo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}\n\texit ;;\n    *:FreeBSD:*:*)\n\tUNAME_PROCESSOR=`/usr/bin/uname -p`\n\tcase ${UNAME_PROCESSOR} in\n\t    amd64)\n\t\techo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;\n\t    *)\n\t\techo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;\n\tesac\n\texit ;;\n    i*:CYGWIN*:*)\n\techo ${UNAME_MACHINE}-pc-cygwin\n\texit ;;\n    *:MINGW*:*)\n\techo ${UNAME_MACHINE}-pc-mingw32\n\texit ;;\n    i*:MSYS*:*)\n\techo ${UNAME_MACHINE}-pc-msys\n\texit ;;\n    i*:windows32*:*)\n\t# uname -m includes \"-pc\" on this system.\n\techo ${UNAME_MACHINE}-mingw32\n\texit ;;\n    i*:PW*:*)\n\techo ${UNAME_MACHINE}-pc-pw32\n\texit ;;\n    *:Interix*:*)\n\tcase ${UNAME_MACHINE} in\n\t    x86)\n\t\techo i586-pc-interix${UNAME_RELEASE}\n\t\texit ;;\n\t    authenticamd | genuineintel | EM64T)\n\t\techo x86_64-unknown-interix${UNAME_RELEASE}\n\t\texit ;;\n\t    IA64)\n\t\techo ia64-unknown-interix${UNAME_RELEASE}\n\t\texit ;;\n\tesac ;;\n    [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*)\n\techo i${UNAME_MACHINE}-pc-mks\n\texit ;;\n    8664:Windows_NT:*)\n\techo x86_64-pc-mks\n\texit ;;\n    i*:Windows_NT*:* | Pentium*:Windows_NT*:*)\n\t# How do we know it's Interix rather than the generic POSIX subsystem?\n\t# It also conflicts with pre-2.0 versions of AT&T UWIN. Should we\n\t# UNAME_MACHINE based on the output of uname instead of i386?\n\techo i586-pc-interix\n\texit ;;\n    i*:UWIN*:*)\n\techo ${UNAME_MACHINE}-pc-uwin\n\texit ;;\n    amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*)\n\techo x86_64-unknown-cygwin\n\texit ;;\n    p*:CYGWIN*:*)\n\techo powerpcle-unknown-cygwin\n\texit ;;\n    prep*:SunOS:5.*:*)\n\techo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`\n\texit ;;\n    *:GNU:*:*)\n\t# the GNU system\n\techo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`\n\texit ;;\n    *:GNU/*:*:*)\n\t# other systems with GNU libc and userland\n\techo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu\n\texit ;;\n    i*86:Minix:*:*)\n\techo ${UNAME_MACHINE}-pc-minix\n\texit ;;\n    aarch64:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-gnu\n\texit ;;\n    aarch64_be:Linux:*:*)\n\tUNAME_MACHINE=aarch64_be\n\techo ${UNAME_MACHINE}-unknown-linux-gnu\n\texit ;;\n    alpha:Linux:*:*)\n\tcase `sed -n '/^cpu model/s/^.*: \\(.*\\)/\\1/p' < /proc/cpuinfo` in\n\t  EV5)   UNAME_MACHINE=alphaev5 ;;\n\t  EV56)  UNAME_MACHINE=alphaev56 ;;\n\t  PCA56) UNAME_MACHINE=alphapca56 ;;\n\t  PCA57) UNAME_MACHINE=alphapca56 ;;\n\t  EV6)   UNAME_MACHINE=alphaev6 ;;\n\t  EV67)  UNAME_MACHINE=alphaev67 ;;\n\t  EV68*) UNAME_MACHINE=alphaev68 ;;\n\tesac\n\tobjdump --private-headers /bin/sh | grep -q ld.so.1\n\tif test \"$?\" = 0 ; then LIBC=\"libc1\" ; else LIBC=\"\" ; fi\n\techo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC}\n\texit ;;\n    arm*:Linux:*:*)\n\teval $set_cc_for_build\n\tif echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \\\n\t    | grep -q __ARM_EABI__\n\tthen\n\t    echo ${UNAME_MACHINE}-unknown-linux-gnu\n\telse\n\t    if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \\\n\t\t| grep -q __ARM_PCS_VFP\n\t    then\n\t\techo ${UNAME_MACHINE}-unknown-linux-gnueabi\n\t    else\n\t\techo ${UNAME_MACHINE}-unknown-linux-gnueabihf\n\t    fi\n\tfi\n\texit ;;\n    avr32*:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-gnu\n\texit ;;\n    cris:Linux:*:*)\n\techo ${UNAME_MACHINE}-axis-linux-gnu\n\texit ;;\n    crisv32:Linux:*:*)\n\techo ${UNAME_MACHINE}-axis-linux-gnu\n\texit ;;\n    frv:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-gnu\n\texit ;;\n    hexagon:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-gnu\n\texit ;;\n    i*86:Linux:*:*)\n\tLIBC=gnu\n\teval $set_cc_for_build\n\tsed 's/^\t//' << EOF >$dummy.c\n\t#ifdef __dietlibc__\n\tLIBC=dietlibc\n\t#endif\nEOF\n\teval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC'`\n\techo \"${UNAME_MACHINE}-pc-linux-${LIBC}\"\n\texit ;;\n    ia64:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-gnu\n\texit ;;\n    m32r*:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-gnu\n\texit ;;\n    m68*:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-gnu\n\texit ;;\n    mips:Linux:*:* | mips64:Linux:*:*)\n\teval $set_cc_for_build\n\tsed 's/^\t//' << EOF >$dummy.c\n\t#undef CPU\n\t#undef ${UNAME_MACHINE}\n\t#undef ${UNAME_MACHINE}el\n\t#if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)\n\tCPU=${UNAME_MACHINE}el\n\t#else\n\t#if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)\n\tCPU=${UNAME_MACHINE}\n\t#else\n\tCPU=\n\t#endif\n\t#endif\nEOF\n\teval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'`\n\ttest x\"${CPU}\" != x && { echo \"${CPU}-unknown-linux-gnu\"; exit; }\n\t;;\n    or32:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-gnu\n\texit ;;\n    padre:Linux:*:*)\n\techo sparc-unknown-linux-gnu\n\texit ;;\n    parisc64:Linux:*:* | hppa64:Linux:*:*)\n\techo hppa64-unknown-linux-gnu\n\texit ;;\n    parisc:Linux:*:* | hppa:Linux:*:*)\n\t# Look for CPU level\n\tcase `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in\n\t  PA7*) echo hppa1.1-unknown-linux-gnu ;;\n\t  PA8*) echo hppa2.0-unknown-linux-gnu ;;\n\t  *)    echo hppa-unknown-linux-gnu ;;\n\tesac\n\texit ;;\n    ppc64:Linux:*:*)\n\techo powerpc64-unknown-linux-gnu\n\texit ;;\n    ppc:Linux:*:*)\n\techo powerpc-unknown-linux-gnu\n\texit ;;\n    s390:Linux:*:* | s390x:Linux:*:*)\n\techo ${UNAME_MACHINE}-ibm-linux\n\texit ;;\n    sh64*:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-gnu\n\texit ;;\n    sh*:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-gnu\n\texit ;;\n    sparc:Linux:*:* | sparc64:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-gnu\n\texit ;;\n    tile*:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-gnu\n\texit ;;\n    vax:Linux:*:*)\n\techo ${UNAME_MACHINE}-dec-linux-gnu\n\texit ;;\n    x86_64:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-gnu\n\texit ;;\n    xtensa*:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-gnu\n\texit ;;\n    i*86:DYNIX/ptx:4*:*)\n\t# ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.\n\t# earlier versions are messed up and put the nodename in both\n\t# sysname and nodename.\n\techo i386-sequent-sysv4\n\texit ;;\n    i*86:UNIX_SV:4.2MP:2.*)\n\t# Unixware is an offshoot of SVR4, but it has its own version\n\t# number series starting with 2...\n\t# I am not positive that other SVR4 systems won't match this,\n\t# I just have to hope.  -- rms.\n\t# Use sysv4.2uw... so that sysv4* matches it.\n\techo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}\n\texit ;;\n    i*86:OS/2:*:*)\n\t# If we were able to find `uname', then EMX Unix compatibility\n\t# is probably installed.\n\techo ${UNAME_MACHINE}-pc-os2-emx\n\texit ;;\n    i*86:XTS-300:*:STOP)\n\techo ${UNAME_MACHINE}-unknown-stop\n\texit ;;\n    i*86:atheos:*:*)\n\techo ${UNAME_MACHINE}-unknown-atheos\n\texit ;;\n    i*86:syllable:*:*)\n\techo ${UNAME_MACHINE}-pc-syllable\n\texit ;;\n    i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*)\n\techo i386-unknown-lynxos${UNAME_RELEASE}\n\texit ;;\n    i*86:*DOS:*:*)\n\techo ${UNAME_MACHINE}-pc-msdosdjgpp\n\texit ;;\n    i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*)\n\tUNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\\/MP$//'`\n\tif grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then\n\t\techo ${UNAME_MACHINE}-univel-sysv${UNAME_REL}\n\telse\n\t\techo ${UNAME_MACHINE}-pc-sysv${UNAME_REL}\n\tfi\n\texit ;;\n    i*86:*:5:[678]*)\n\t# UnixWare 7.x, OpenUNIX and OpenServer 6.\n\tcase `/bin/uname -X | grep \"^Machine\"` in\n\t    *486*)\t     UNAME_MACHINE=i486 ;;\n\t    *Pentium)\t     UNAME_MACHINE=i586 ;;\n\t    *Pent*|*Celeron) UNAME_MACHINE=i686 ;;\n\tesac\n\techo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}\n\texit ;;\n    i*86:*:3.2:*)\n\tif test -f /usr/options/cb.name; then\n\t\tUNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`\n\t\techo ${UNAME_MACHINE}-pc-isc$UNAME_REL\n\telif /bin/uname -X 2>/dev/null >/dev/null ; then\n\t\tUNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')`\n\t\t(/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486\n\t\t(/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \\\n\t\t\t&& UNAME_MACHINE=i586\n\t\t(/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \\\n\t\t\t&& UNAME_MACHINE=i686\n\t\t(/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \\\n\t\t\t&& UNAME_MACHINE=i686\n\t\techo ${UNAME_MACHINE}-pc-sco$UNAME_REL\n\telse\n\t\techo ${UNAME_MACHINE}-pc-sysv32\n\tfi\n\texit ;;\n    pc:*:*:*)\n\t# Left here for compatibility:\n\t# uname -m prints for DJGPP always 'pc', but it prints nothing about\n\t# the processor, so we play safe by assuming i586.\n\t# Note: whatever this is, it MUST be the same as what config.sub\n\t# prints for the \"djgpp\" host, or else GDB configury will decide that\n\t# this is a cross-build.\n\techo i586-pc-msdosdjgpp\n\texit ;;\n    Intel:Mach:3*:*)\n\techo i386-pc-mach3\n\texit ;;\n    paragon:*:*:*)\n\techo i860-intel-osf1\n\texit ;;\n    i860:*:4.*:*) # i860-SVR4\n\tif grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then\n\t  echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4\n\telse # Add other i860-SVR4 vendors below as they are discovered.\n\t  echo i860-unknown-sysv${UNAME_RELEASE}  # Unknown i860-SVR4\n\tfi\n\texit ;;\n    mini*:CTIX:SYS*5:*)\n\t# \"miniframe\"\n\techo m68010-convergent-sysv\n\texit ;;\n    mc68k:UNIX:SYSTEM5:3.51m)\n\techo m68k-convergent-sysv\n\texit ;;\n    M680?0:D-NIX:5.3:*)\n\techo m68k-diab-dnix\n\texit ;;\n    M68*:*:R3V[5678]*:*)\n\ttest -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;;\n    3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0)\n\tOS_REL=''\n\ttest -r /etc/.relid \\\n\t&& OS_REL=.`sed -n 's/[^ ]* [^ ]* \\([0-9][0-9]\\).*/\\1/p' < /etc/.relid`\n\t/bin/uname -p 2>/dev/null | grep 86 >/dev/null \\\n\t  && { echo i486-ncr-sysv4.3${OS_REL}; exit; }\n\t/bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \\\n\t  && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;;\n    3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)\n\t/bin/uname -p 2>/dev/null | grep 86 >/dev/null \\\n\t  && { echo i486-ncr-sysv4; exit; } ;;\n    NCR*:*:4.2:* | MPRAS*:*:4.2:*)\n\tOS_REL='.3'\n\ttest -r /etc/.relid \\\n\t    && OS_REL=.`sed -n 's/[^ ]* [^ ]* \\([0-9][0-9]\\).*/\\1/p' < /etc/.relid`\n\t/bin/uname -p 2>/dev/null | grep 86 >/dev/null \\\n\t    && { echo i486-ncr-sysv4.3${OS_REL}; exit; }\n\t/bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \\\n\t    && { echo i586-ncr-sysv4.3${OS_REL}; exit; }\n\t/bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \\\n\t    && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;;\n    m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)\n\techo m68k-unknown-lynxos${UNAME_RELEASE}\n\texit ;;\n    mc68030:UNIX_System_V:4.*:*)\n\techo m68k-atari-sysv4\n\texit ;;\n    TSUNAMI:LynxOS:2.*:*)\n\techo sparc-unknown-lynxos${UNAME_RELEASE}\n\texit ;;\n    rs6000:LynxOS:2.*:*)\n\techo rs6000-unknown-lynxos${UNAME_RELEASE}\n\texit ;;\n    PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*)\n\techo powerpc-unknown-lynxos${UNAME_RELEASE}\n\texit ;;\n    SM[BE]S:UNIX_SV:*:*)\n\techo mips-dde-sysv${UNAME_RELEASE}\n\texit ;;\n    RM*:ReliantUNIX-*:*:*)\n\techo mips-sni-sysv4\n\texit ;;\n    RM*:SINIX-*:*:*)\n\techo mips-sni-sysv4\n\texit ;;\n    *:SINIX-*:*:*)\n\tif uname -p 2>/dev/null >/dev/null ; then\n\t\tUNAME_MACHINE=`(uname -p) 2>/dev/null`\n\t\techo ${UNAME_MACHINE}-sni-sysv4\n\telse\n\t\techo ns32k-sni-sysv\n\tfi\n\texit ;;\n    PENTIUM:*:4.0*:*)\t# Unisys `ClearPath HMP IX 4000' SVR4/MP effort\n\t\t\t# says <Richard.M.Bartel@ccMail.Census.GOV>\n\techo i586-unisys-sysv4\n\texit ;;\n    *:UNIX_System_V:4*:FTX*)\n\t# From Gerald Hewes <hewes@openmarket.com>.\n\t# How about differentiating between stratus architectures? -djm\n\techo hppa1.1-stratus-sysv4\n\texit ;;\n    *:*:*:FTX*)\n\t# From seanf@swdc.stratus.com.\n\techo i860-stratus-sysv4\n\texit ;;\n    i*86:VOS:*:*)\n\t# From Paul.Green@stratus.com.\n\techo ${UNAME_MACHINE}-stratus-vos\n\texit ;;\n    *:VOS:*:*)\n\t# From Paul.Green@stratus.com.\n\techo hppa1.1-stratus-vos\n\texit ;;\n    mc68*:A/UX:*:*)\n\techo m68k-apple-aux${UNAME_RELEASE}\n\texit ;;\n    news*:NEWS-OS:6*:*)\n\techo mips-sony-newsos6\n\texit ;;\n    R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)\n\tif [ -d /usr/nec ]; then\n\t\techo mips-nec-sysv${UNAME_RELEASE}\n\telse\n\t\techo mips-unknown-sysv${UNAME_RELEASE}\n\tfi\n\texit ;;\n    BeBox:BeOS:*:*)\t# BeOS running on hardware made by Be, PPC only.\n\techo powerpc-be-beos\n\texit ;;\n    BeMac:BeOS:*:*)\t# BeOS running on Mac or Mac clone, PPC only.\n\techo powerpc-apple-beos\n\texit ;;\n    BePC:BeOS:*:*)\t# BeOS running on Intel PC compatible.\n\techo i586-pc-beos\n\texit ;;\n    BePC:Haiku:*:*)\t# Haiku running on Intel PC compatible.\n\techo i586-pc-haiku\n\texit ;;\n    SX-4:SUPER-UX:*:*)\n\techo sx4-nec-superux${UNAME_RELEASE}\n\texit ;;\n    SX-5:SUPER-UX:*:*)\n\techo sx5-nec-superux${UNAME_RELEASE}\n\texit ;;\n    SX-6:SUPER-UX:*:*)\n\techo sx6-nec-superux${UNAME_RELEASE}\n\texit ;;\n    SX-7:SUPER-UX:*:*)\n\techo sx7-nec-superux${UNAME_RELEASE}\n\texit ;;\n    SX-8:SUPER-UX:*:*)\n\techo sx8-nec-superux${UNAME_RELEASE}\n\texit ;;\n    SX-8R:SUPER-UX:*:*)\n\techo sx8r-nec-superux${UNAME_RELEASE}\n\texit ;;\n    Power*:Rhapsody:*:*)\n\techo powerpc-apple-rhapsody${UNAME_RELEASE}\n\texit ;;\n    *:Rhapsody:*:*)\n\techo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE}\n\texit ;;\n    *:Darwin:*:*)\n\tUNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown\n\tcase $UNAME_PROCESSOR in\n\t    i386)\n\t\teval $set_cc_for_build\n\t\tif [ \"$CC_FOR_BUILD\" != 'no_compiler_found' ]; then\n\t\t  if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \\\n\t\t      (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \\\n\t\t      grep IS_64BIT_ARCH >/dev/null\n\t\t  then\n\t\t      UNAME_PROCESSOR=\"x86_64\"\n\t\t  fi\n\t\tfi ;;\n\t    unknown) UNAME_PROCESSOR=powerpc ;;\n\tesac\n\techo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}\n\texit ;;\n    *:procnto*:*:* | *:QNX:[0123456789]*:*)\n\tUNAME_PROCESSOR=`uname -p`\n\tif test \"$UNAME_PROCESSOR\" = \"x86\"; then\n\t\tUNAME_PROCESSOR=i386\n\t\tUNAME_MACHINE=pc\n\tfi\n\techo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE}\n\texit ;;\n    *:QNX:*:4*)\n\techo i386-pc-qnx\n\texit ;;\n    NEO-?:NONSTOP_KERNEL:*:*)\n\techo neo-tandem-nsk${UNAME_RELEASE}\n\texit ;;\n    NSE-?:NONSTOP_KERNEL:*:*)\n\techo nse-tandem-nsk${UNAME_RELEASE}\n\texit ;;\n    NSR-?:NONSTOP_KERNEL:*:*)\n\techo nsr-tandem-nsk${UNAME_RELEASE}\n\texit ;;\n    *:NonStop-UX:*:*)\n\techo mips-compaq-nonstopux\n\texit ;;\n    BS2000:POSIX*:*:*)\n\techo bs2000-siemens-sysv\n\texit ;;\n    DS/*:UNIX_System_V:*:*)\n\techo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE}\n\texit ;;\n    *:Plan9:*:*)\n\t# \"uname -m\" is not consistent, so use $cputype instead. 386\n\t# is converted to i386 for consistency with other x86\n\t# operating systems.\n\tif test \"$cputype\" = \"386\"; then\n\t    UNAME_MACHINE=i386\n\telse\n\t    UNAME_MACHINE=\"$cputype\"\n\tfi\n\techo ${UNAME_MACHINE}-unknown-plan9\n\texit ;;\n    *:TOPS-10:*:*)\n\techo pdp10-unknown-tops10\n\texit ;;\n    *:TENEX:*:*)\n\techo pdp10-unknown-tenex\n\texit ;;\n    KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)\n\techo pdp10-dec-tops20\n\texit ;;\n    XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)\n\techo pdp10-xkl-tops20\n\texit ;;\n    *:TOPS-20:*:*)\n\techo pdp10-unknown-tops20\n\texit ;;\n    *:ITS:*:*)\n\techo pdp10-unknown-its\n\texit ;;\n    SEI:*:*:SEIUX)\n\techo mips-sei-seiux${UNAME_RELEASE}\n\texit ;;\n    *:DragonFly:*:*)\n\techo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`\n\texit ;;\n    *:*VMS:*:*)\n\tUNAME_MACHINE=`(uname -p) 2>/dev/null`\n\tcase \"${UNAME_MACHINE}\" in\n\t    A*) echo alpha-dec-vms ; exit ;;\n\t    I*) echo ia64-dec-vms ; exit ;;\n\t    V*) echo vax-dec-vms ; exit ;;\n\tesac ;;\n    *:XENIX:*:SysV)\n\techo i386-pc-xenix\n\texit ;;\n    i*86:skyos:*:*)\n\techo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//'\n\texit ;;\n    i*86:rdos:*:*)\n\techo ${UNAME_MACHINE}-pc-rdos\n\texit ;;\n    i*86:AROS:*:*)\n\techo ${UNAME_MACHINE}-pc-aros\n\texit ;;\n    x86_64:VMkernel:*:*)\n\techo ${UNAME_MACHINE}-unknown-esx\n\texit ;;\nesac\n\n#echo '(No uname command or uname output not recognized.)' 1>&2\n#echo \"${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}\" 1>&2\n\neval $set_cc_for_build\ncat >$dummy.c <<EOF\n#ifdef _SEQUENT_\n# include <sys/types.h>\n# include <sys/utsname.h>\n#endif\nmain ()\n{\n#if defined (sony)\n#if defined (MIPSEB)\n  /* BFD wants \"bsd\" instead of \"newsos\".  Perhaps BFD should be changed,\n     I don't know....  */\n  printf (\"mips-sony-bsd\\n\"); exit (0);\n#else\n#include <sys/param.h>\n  printf (\"m68k-sony-newsos%s\\n\",\n#ifdef NEWSOS4\n\t\"4\"\n#else\n\t\"\"\n#endif\n\t); exit (0);\n#endif\n#endif\n\n#if defined (__arm) && defined (__acorn) && defined (__unix)\n  printf (\"arm-acorn-riscix\\n\"); exit (0);\n#endif\n\n#if defined (hp300) && !defined (hpux)\n  printf (\"m68k-hp-bsd\\n\"); exit (0);\n#endif\n\n#if defined (NeXT)\n#if !defined (__ARCHITECTURE__)\n#define __ARCHITECTURE__ \"m68k\"\n#endif\n  int version;\n  version=`(hostinfo | sed -n 's/.*NeXT Mach \\([0-9]*\\).*/\\1/p') 2>/dev/null`;\n  if (version < 4)\n    printf (\"%s-next-nextstep%d\\n\", __ARCHITECTURE__, version);\n  else\n    printf (\"%s-next-openstep%d\\n\", __ARCHITECTURE__, version);\n  exit (0);\n#endif\n\n#if defined (MULTIMAX) || defined (n16)\n#if defined (UMAXV)\n  printf (\"ns32k-encore-sysv\\n\"); exit (0);\n#else\n#if defined (CMU)\n  printf (\"ns32k-encore-mach\\n\"); exit (0);\n#else\n  printf (\"ns32k-encore-bsd\\n\"); exit (0);\n#endif\n#endif\n#endif\n\n#if defined (__386BSD__)\n  printf (\"i386-pc-bsd\\n\"); exit (0);\n#endif\n\n#if defined (sequent)\n#if defined (i386)\n  printf (\"i386-sequent-dynix\\n\"); exit (0);\n#endif\n#if defined (ns32000)\n  printf (\"ns32k-sequent-dynix\\n\"); exit (0);\n#endif\n#endif\n\n#if defined (_SEQUENT_)\n    struct utsname un;\n\n    uname(&un);\n\n    if (strncmp(un.version, \"V2\", 2) == 0) {\n\tprintf (\"i386-sequent-ptx2\\n\"); exit (0);\n    }\n    if (strncmp(un.version, \"V1\", 2) == 0) { /* XXX is V1 correct? */\n\tprintf (\"i386-sequent-ptx1\\n\"); exit (0);\n    }\n    printf (\"i386-sequent-ptx\\n\"); exit (0);\n\n#endif\n\n#if defined (vax)\n# if !defined (ultrix)\n#  include <sys/param.h>\n#  if defined (BSD)\n#   if BSD == 43\n      printf (\"vax-dec-bsd4.3\\n\"); exit (0);\n#   else\n#    if BSD == 199006\n      printf (\"vax-dec-bsd4.3reno\\n\"); exit (0);\n#    else\n      printf (\"vax-dec-bsd\\n\"); exit (0);\n#    endif\n#   endif\n#  else\n    printf (\"vax-dec-bsd\\n\"); exit (0);\n#  endif\n# else\n    printf (\"vax-dec-ultrix\\n\"); exit (0);\n# endif\n#endif\n\n#if defined (alliant) && defined (i860)\n  printf (\"i860-alliant-bsd\\n\"); exit (0);\n#endif\n\n  exit (1);\n}\nEOF\n\n$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` &&\n\t{ echo \"$SYSTEM_NAME\"; exit; }\n\n# Apollos put the system type in the environment.\n\ntest -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; }\n\n# Convex versions that predate uname can use getsysinfo(1)\n\nif [ -x /usr/convex/getsysinfo ]\nthen\n    case `getsysinfo -f cpu_type` in\n    c1*)\n\techo c1-convex-bsd\n\texit ;;\n    c2*)\n\tif getsysinfo -f scalar_acc\n\tthen echo c32-convex-bsd\n\telse echo c2-convex-bsd\n\tfi\n\texit ;;\n    c34*)\n\techo c34-convex-bsd\n\texit ;;\n    c38*)\n\techo c38-convex-bsd\n\texit ;;\n    c4*)\n\techo c4-convex-bsd\n\texit ;;\n    esac\nfi\n\ncat >&2 <<EOF\n$0: unable to guess system type\n\nThis script, last modified $timestamp, has failed to recognize\nthe operating system you are using. It is advised that you\ndownload the most up to date version of the config scripts from\n\n  http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD\nand\n  http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD\n\nIf the version you run ($0) is already up to date, please\nsend the following data and any information you think might be\npertinent to <config-patches@gnu.org> in order to provide the needed\ninformation to handle your system.\n\nconfig.guess timestamp = $timestamp\n\nuname -m = `(uname -m) 2>/dev/null || echo unknown`\nuname -r = `(uname -r) 2>/dev/null || echo unknown`\nuname -s = `(uname -s) 2>/dev/null || echo unknown`\nuname -v = `(uname -v) 2>/dev/null || echo unknown`\n\n/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null`\n/bin/uname -X     = `(/bin/uname -X) 2>/dev/null`\n\nhostinfo               = `(hostinfo) 2>/dev/null`\n/bin/universe          = `(/bin/universe) 2>/dev/null`\n/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null`\n/bin/arch              = `(/bin/arch) 2>/dev/null`\n/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null`\n/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`\n\nUNAME_MACHINE = ${UNAME_MACHINE}\nUNAME_RELEASE = ${UNAME_RELEASE}\nUNAME_SYSTEM  = ${UNAME_SYSTEM}\nUNAME_VERSION = ${UNAME_VERSION}\nEOF\n\nexit 1\n\n# Local variables:\n# eval: (add-hook 'write-file-hooks 'time-stamp)\n# time-stamp-start: \"timestamp='\"\n# time-stamp-format: \"%:y-%02m-%02d\"\n# time-stamp-end: \"'\"\n# End:\n"
  },
  {
    "path": "deps/jemalloc/config.stamp.in",
    "content": ""
  },
  {
    "path": "deps/jemalloc/config.sub",
    "content": "#! /bin/sh\n# Configuration validation subroutine script.\n#   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,\n#   2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,\n#   2011, 2012 Free Software Foundation, Inc.\n\ntimestamp='2012-02-10'\n\n# This file is (in principle) common to ALL GNU software.\n# The presence of a machine in this file suggests that SOME GNU software\n# can handle that machine.  It does not imply ALL GNU software can.\n#\n# This file is free software; you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation; either version 2 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program; if not, see <http://www.gnu.org/licenses/>.\n#\n# As a special exception to the GNU General Public License, if you\n# distribute this file as part of a program that contains a\n# configuration script generated by Autoconf, you may include it under\n# the same distribution terms that you use for the rest of that program.\n\n\n# Please send patches to <config-patches@gnu.org>.  Submit a context\n# diff and a properly formatted GNU ChangeLog entry.\n#\n# Configuration subroutine to validate and canonicalize a configuration type.\n# Supply the specified configuration type as an argument.\n# If it is invalid, we print an error message on stderr and exit with code 1.\n# Otherwise, we print the canonical config type on stdout and succeed.\n\n# You can get the latest version of this script from:\n# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD\n\n# This file is supposed to be the same for all GNU packages\n# and recognize all the CPU types, system types and aliases\n# that are meaningful with *any* GNU software.\n# Each package is responsible for reporting which valid configurations\n# it does not support.  The user should be able to distinguish\n# a failure to support a valid configuration from a meaningless\n# configuration.\n\n# The goal of this file is to map all the various variations of a given\n# machine specification into a single specification in the form:\n#\tCPU_TYPE-MANUFACTURER-OPERATING_SYSTEM\n# or in some cases, the newer four-part form:\n#\tCPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM\n# It is wrong to echo any other type of specification.\n\nme=`echo \"$0\" | sed -e 's,.*/,,'`\n\nusage=\"\\\nUsage: $0 [OPTION] CPU-MFR-OPSYS\n       $0 [OPTION] ALIAS\n\nCanonicalize a configuration name.\n\nOperation modes:\n  -h, --help         print this help, then exit\n  -t, --time-stamp   print date of last modification, then exit\n  -v, --version      print version number, then exit\n\nReport bugs and patches to <config-patches@gnu.org>.\"\n\nversion=\"\\\nGNU config.sub ($timestamp)\n\nCopyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,\n2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012\nFree Software Foundation, Inc.\n\nThis is free software; see the source for copying conditions.  There is NO\nwarranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\"\n\nhelp=\"\nTry \\`$me --help' for more information.\"\n\n# Parse command line\nwhile test $# -gt 0 ; do\n  case $1 in\n    --time-stamp | --time* | -t )\n       echo \"$timestamp\" ; exit ;;\n    --version | -v )\n       echo \"$version\" ; exit ;;\n    --help | --h* | -h )\n       echo \"$usage\"; exit ;;\n    -- )     # Stop option processing\n       shift; break ;;\n    - )\t# Use stdin as input.\n       break ;;\n    -* )\n       echo \"$me: invalid option $1$help\"\n       exit 1 ;;\n\n    *local*)\n       # First pass through any local machine types.\n       echo $1\n       exit ;;\n\n    * )\n       break ;;\n  esac\ndone\n\ncase $# in\n 0) echo \"$me: missing argument$help\" >&2\n    exit 1;;\n 1) ;;\n *) echo \"$me: too many arguments$help\" >&2\n    exit 1;;\nesac\n\n# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any).\n# Here we must recognize all the valid KERNEL-OS combinations.\nmaybe_os=`echo $1 | sed 's/^\\(.*\\)-\\([^-]*-[^-]*\\)$/\\2/'`\ncase $maybe_os in\n  nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \\\n  linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \\\n  knetbsd*-gnu* | netbsd*-gnu* | \\\n  kopensolaris*-gnu* | \\\n  storm-chaos* | os2-emx* | rtmk-nova*)\n    os=-$maybe_os\n    basic_machine=`echo $1 | sed 's/^\\(.*\\)-\\([^-]*-[^-]*\\)$/\\1/'`\n    ;;\n  android-linux)\n    os=-linux-android\n    basic_machine=`echo $1 | sed 's/^\\(.*\\)-\\([^-]*-[^-]*\\)$/\\1/'`-unknown\n    ;;\n  *)\n    basic_machine=`echo $1 | sed 's/-[^-]*$//'`\n    if [ $basic_machine != $1 ]\n    then os=`echo $1 | sed 's/.*-/-/'`\n    else os=; fi\n    ;;\nesac\n\n### Let's recognize common machines as not being operating systems so\n### that things like config.sub decstation-3100 work.  We also\n### recognize some manufacturers as not being operating systems, so we\n### can provide default operating systems below.\ncase $os in\n\t-sun*os*)\n\t\t# Prevent following clause from handling this invalid input.\n\t\t;;\n\t-dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \\\n\t-att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \\\n\t-unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \\\n\t-convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\\\n\t-c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \\\n\t-harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \\\n\t-apple | -axis | -knuth | -cray | -microblaze)\n\t\tos=\n\t\tbasic_machine=$1\n\t\t;;\n\t-bluegene*)\n\t\tos=-cnk\n\t\t;;\n\t-sim | -cisco | -oki | -wec | -winbond)\n\t\tos=\n\t\tbasic_machine=$1\n\t\t;;\n\t-scout)\n\t\t;;\n\t-wrs)\n\t\tos=-vxworks\n\t\tbasic_machine=$1\n\t\t;;\n\t-chorusos*)\n\t\tos=-chorusos\n\t\tbasic_machine=$1\n\t\t;;\n\t-chorusrdb)\n\t\tos=-chorusrdb\n\t\tbasic_machine=$1\n\t\t;;\n\t-hiux*)\n\t\tos=-hiuxwe2\n\t\t;;\n\t-sco6)\n\t\tos=-sco5v6\n\t\tbasic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`\n\t\t;;\n\t-sco5)\n\t\tos=-sco3.2v5\n\t\tbasic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`\n\t\t;;\n\t-sco4)\n\t\tos=-sco3.2v4\n\t\tbasic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`\n\t\t;;\n\t-sco3.2.[4-9]*)\n\t\tos=`echo $os | sed -e 's/sco3.2./sco3.2v/'`\n\t\tbasic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`\n\t\t;;\n\t-sco3.2v[4-9]*)\n\t\t# Don't forget version if it is 3.2v4 or newer.\n\t\tbasic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`\n\t\t;;\n\t-sco5v6*)\n\t\t# Don't forget version if it is 3.2v4 or newer.\n\t\tbasic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`\n\t\t;;\n\t-sco*)\n\t\tos=-sco3.2v2\n\t\tbasic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`\n\t\t;;\n\t-udk*)\n\t\tbasic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`\n\t\t;;\n\t-isc)\n\t\tos=-isc2.2\n\t\tbasic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`\n\t\t;;\n\t-clix*)\n\t\tbasic_machine=clipper-intergraph\n\t\t;;\n\t-isc*)\n\t\tbasic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`\n\t\t;;\n\t-lynx*)\n\t\tos=-lynxos\n\t\t;;\n\t-ptx*)\n\t\tbasic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'`\n\t\t;;\n\t-windowsnt*)\n\t\tos=`echo $os | sed -e 's/windowsnt/winnt/'`\n\t\t;;\n\t-psos*)\n\t\tos=-psos\n\t\t;;\n\t-mint | -mint[0-9]*)\n\t\tbasic_machine=m68k-atari\n\t\tos=-mint\n\t\t;;\nesac\n\n# Decode aliases for certain CPU-COMPANY combinations.\ncase $basic_machine in\n\t# Recognize the basic CPU types without company name.\n\t# Some are omitted here because they have special meanings below.\n\t1750a | 580 \\\n\t| a29k \\\n\t| aarch64 | aarch64_be \\\n\t| alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \\\n\t| alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \\\n\t| am33_2.0 \\\n\t| arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr | avr32 \\\n        | be32 | be64 \\\n\t| bfin \\\n\t| c4x | clipper \\\n\t| d10v | d30v | dlx | dsp16xx \\\n\t| epiphany \\\n\t| fido | fr30 | frv \\\n\t| h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \\\n\t| hexagon \\\n\t| i370 | i860 | i960 | ia64 \\\n\t| ip2k | iq2000 \\\n\t| le32 | le64 \\\n\t| lm32 \\\n\t| m32c | m32r | m32rle | m68000 | m68k | m88k \\\n\t| maxq | mb | microblaze | mcore | mep | metag \\\n\t| mips | mipsbe | mipseb | mipsel | mipsle \\\n\t| mips16 \\\n\t| mips64 | mips64el \\\n\t| mips64octeon | mips64octeonel \\\n\t| mips64orion | mips64orionel \\\n\t| mips64r5900 | mips64r5900el \\\n\t| mips64vr | mips64vrel \\\n\t| mips64vr4100 | mips64vr4100el \\\n\t| mips64vr4300 | mips64vr4300el \\\n\t| mips64vr5000 | mips64vr5000el \\\n\t| mips64vr5900 | mips64vr5900el \\\n\t| mipsisa32 | mipsisa32el \\\n\t| mipsisa32r2 | mipsisa32r2el \\\n\t| mipsisa64 | mipsisa64el \\\n\t| mipsisa64r2 | mipsisa64r2el \\\n\t| mipsisa64sb1 | mipsisa64sb1el \\\n\t| mipsisa64sr71k | mipsisa64sr71kel \\\n\t| mipstx39 | mipstx39el \\\n\t| mn10200 | mn10300 \\\n\t| moxie \\\n\t| mt \\\n\t| msp430 \\\n\t| nds32 | nds32le | nds32be \\\n\t| nios | nios2 \\\n\t| ns16k | ns32k \\\n\t| open8 \\\n\t| or32 \\\n\t| pdp10 | pdp11 | pj | pjl \\\n\t| powerpc | powerpc64 | powerpc64le | powerpcle \\\n\t| pyramid \\\n\t| rl78 | rx \\\n\t| score \\\n\t| sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \\\n\t| sh64 | sh64le \\\n\t| sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \\\n\t| sparcv8 | sparcv9 | sparcv9b | sparcv9v \\\n\t| spu \\\n\t| tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \\\n\t| ubicom32 \\\n\t| v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \\\n\t| we32k \\\n\t| x86 | xc16x | xstormy16 | xtensa \\\n\t| z8k | z80)\n\t\tbasic_machine=$basic_machine-unknown\n\t\t;;\n\tc54x)\n\t\tbasic_machine=tic54x-unknown\n\t\t;;\n\tc55x)\n\t\tbasic_machine=tic55x-unknown\n\t\t;;\n\tc6x)\n\t\tbasic_machine=tic6x-unknown\n\t\t;;\n\tm6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | picochip)\n\t\tbasic_machine=$basic_machine-unknown\n\t\tos=-none\n\t\t;;\n\tm88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k)\n\t\t;;\n\tms1)\n\t\tbasic_machine=mt-unknown\n\t\t;;\n\n\tstrongarm | thumb | xscale)\n\t\tbasic_machine=arm-unknown\n\t\t;;\n\txgate)\n\t\tbasic_machine=$basic_machine-unknown\n\t\tos=-none\n\t\t;;\n\txscaleeb)\n\t\tbasic_machine=armeb-unknown\n\t\t;;\n\n\txscaleel)\n\t\tbasic_machine=armel-unknown\n\t\t;;\n\n\t# We use `pc' rather than `unknown'\n\t# because (1) that's what they normally are, and\n\t# (2) the word \"unknown\" tends to confuse beginning users.\n\ti*86 | x86_64)\n\t  basic_machine=$basic_machine-pc\n\t  ;;\n\t# Object if more than one company name word.\n\t*-*-*)\n\t\techo Invalid configuration \\`$1\\': machine \\`$basic_machine\\' not recognized 1>&2\n\t\texit 1\n\t\t;;\n\t# Recognize the basic CPU types with company name.\n\t580-* \\\n\t| a29k-* \\\n\t| aarch64-* | aarch64_be-* \\\n\t| alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \\\n\t| alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \\\n\t| alphapca5[67]-* | alpha64pca5[67]-* | arc-* \\\n\t| arm-*  | armbe-* | armle-* | armeb-* | armv*-* \\\n\t| avr-* | avr32-* \\\n\t| be32-* | be64-* \\\n\t| bfin-* | bs2000-* \\\n\t| c[123]* | c30-* | [cjt]90-* | c4x-* \\\n\t| clipper-* | craynv-* | cydra-* \\\n\t| d10v-* | d30v-* | dlx-* \\\n\t| elxsi-* \\\n\t| f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \\\n\t| h8300-* | h8500-* \\\n\t| hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \\\n\t| hexagon-* \\\n\t| i*86-* | i860-* | i960-* | ia64-* \\\n\t| ip2k-* | iq2000-* \\\n\t| le32-* | le64-* \\\n\t| lm32-* \\\n\t| m32c-* | m32r-* | m32rle-* \\\n\t| m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \\\n\t| m88110-* | m88k-* | maxq-* | mcore-* | metag-* | microblaze-* \\\n\t| mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \\\n\t| mips16-* \\\n\t| mips64-* | mips64el-* \\\n\t| mips64octeon-* | mips64octeonel-* \\\n\t| mips64orion-* | mips64orionel-* \\\n\t| mips64r5900-* | mips64r5900el-* \\\n\t| mips64vr-* | mips64vrel-* \\\n\t| mips64vr4100-* | mips64vr4100el-* \\\n\t| mips64vr4300-* | mips64vr4300el-* \\\n\t| mips64vr5000-* | mips64vr5000el-* \\\n\t| mips64vr5900-* | mips64vr5900el-* \\\n\t| mipsisa32-* | mipsisa32el-* \\\n\t| mipsisa32r2-* | mipsisa32r2el-* \\\n\t| mipsisa64-* | mipsisa64el-* \\\n\t| mipsisa64r2-* | mipsisa64r2el-* \\\n\t| mipsisa64sb1-* | mipsisa64sb1el-* \\\n\t| mipsisa64sr71k-* | mipsisa64sr71kel-* \\\n\t| mipstx39-* | mipstx39el-* \\\n\t| mmix-* \\\n\t| mt-* \\\n\t| msp430-* \\\n\t| nds32-* | nds32le-* | nds32be-* \\\n\t| nios-* | nios2-* \\\n\t| none-* | np1-* | ns16k-* | ns32k-* \\\n\t| open8-* \\\n\t| orion-* \\\n\t| pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \\\n\t| powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \\\n\t| pyramid-* \\\n\t| rl78-* | romp-* | rs6000-* | rx-* \\\n\t| sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \\\n\t| shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \\\n\t| sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \\\n\t| sparclite-* \\\n\t| sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx?-* \\\n\t| tahoe-* \\\n\t| tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \\\n\t| tile*-* \\\n\t| tron-* \\\n\t| ubicom32-* \\\n\t| v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \\\n\t| vax-* \\\n\t| we32k-* \\\n\t| x86-* | x86_64-* | xc16x-* | xps100-* \\\n\t| xstormy16-* | xtensa*-* \\\n\t| ymp-* \\\n\t| z8k-* | z80-*)\n\t\t;;\n\t# Recognize the basic CPU types without company name, with glob match.\n\txtensa*)\n\t\tbasic_machine=$basic_machine-unknown\n\t\t;;\n\t# Recognize the various machine names and aliases which stand\n\t# for a CPU type and a company and sometimes even an OS.\n\t386bsd)\n\t\tbasic_machine=i386-unknown\n\t\tos=-bsd\n\t\t;;\n\t3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)\n\t\tbasic_machine=m68000-att\n\t\t;;\n\t3b*)\n\t\tbasic_machine=we32k-att\n\t\t;;\n\ta29khif)\n\t\tbasic_machine=a29k-amd\n\t\tos=-udi\n\t\t;;\n\tabacus)\n\t\tbasic_machine=abacus-unknown\n\t\t;;\n\tadobe68k)\n\t\tbasic_machine=m68010-adobe\n\t\tos=-scout\n\t\t;;\n\talliant | fx80)\n\t\tbasic_machine=fx80-alliant\n\t\t;;\n\taltos | altos3068)\n\t\tbasic_machine=m68k-altos\n\t\t;;\n\tam29k)\n\t\tbasic_machine=a29k-none\n\t\tos=-bsd\n\t\t;;\n\tamd64)\n\t\tbasic_machine=x86_64-pc\n\t\t;;\n\tamd64-*)\n\t\tbasic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'`\n\t\t;;\n\tamdahl)\n\t\tbasic_machine=580-amdahl\n\t\tos=-sysv\n\t\t;;\n\tamiga | amiga-*)\n\t\tbasic_machine=m68k-unknown\n\t\t;;\n\tamigaos | amigados)\n\t\tbasic_machine=m68k-unknown\n\t\tos=-amigaos\n\t\t;;\n\tamigaunix | amix)\n\t\tbasic_machine=m68k-unknown\n\t\tos=-sysv4\n\t\t;;\n\tapollo68)\n\t\tbasic_machine=m68k-apollo\n\t\tos=-sysv\n\t\t;;\n\tapollo68bsd)\n\t\tbasic_machine=m68k-apollo\n\t\tos=-bsd\n\t\t;;\n\taros)\n\t\tbasic_machine=i386-pc\n\t\tos=-aros\n\t\t;;\n\taux)\n\t\tbasic_machine=m68k-apple\n\t\tos=-aux\n\t\t;;\n\tbalance)\n\t\tbasic_machine=ns32k-sequent\n\t\tos=-dynix\n\t\t;;\n\tblackfin)\n\t\tbasic_machine=bfin-unknown\n\t\tos=-linux\n\t\t;;\n\tblackfin-*)\n\t\tbasic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'`\n\t\tos=-linux\n\t\t;;\n\tbluegene*)\n\t\tbasic_machine=powerpc-ibm\n\t\tos=-cnk\n\t\t;;\n\tc54x-*)\n\t\tbasic_machine=tic54x-`echo $basic_machine | sed 's/^[^-]*-//'`\n\t\t;;\n\tc55x-*)\n\t\tbasic_machine=tic55x-`echo $basic_machine | sed 's/^[^-]*-//'`\n\t\t;;\n\tc6x-*)\n\t\tbasic_machine=tic6x-`echo $basic_machine | sed 's/^[^-]*-//'`\n\t\t;;\n\tc90)\n\t\tbasic_machine=c90-cray\n\t\tos=-unicos\n\t\t;;\n\tcegcc)\n\t\tbasic_machine=arm-unknown\n\t\tos=-cegcc\n\t\t;;\n\tconvex-c1)\n\t\tbasic_machine=c1-convex\n\t\tos=-bsd\n\t\t;;\n\tconvex-c2)\n\t\tbasic_machine=c2-convex\n\t\tos=-bsd\n\t\t;;\n\tconvex-c32)\n\t\tbasic_machine=c32-convex\n\t\tos=-bsd\n\t\t;;\n\tconvex-c34)\n\t\tbasic_machine=c34-convex\n\t\tos=-bsd\n\t\t;;\n\tconvex-c38)\n\t\tbasic_machine=c38-convex\n\t\tos=-bsd\n\t\t;;\n\tcray | j90)\n\t\tbasic_machine=j90-cray\n\t\tos=-unicos\n\t\t;;\n\tcraynv)\n\t\tbasic_machine=craynv-cray\n\t\tos=-unicosmp\n\t\t;;\n\tcr16 | cr16-*)\n\t\tbasic_machine=cr16-unknown\n\t\tos=-elf\n\t\t;;\n\tcrds | unos)\n\t\tbasic_machine=m68k-crds\n\t\t;;\n\tcrisv32 | crisv32-* | etraxfs*)\n\t\tbasic_machine=crisv32-axis\n\t\t;;\n\tcris | cris-* | etrax*)\n\t\tbasic_machine=cris-axis\n\t\t;;\n\tcrx)\n\t\tbasic_machine=crx-unknown\n\t\tos=-elf\n\t\t;;\n\tda30 | da30-*)\n\t\tbasic_machine=m68k-da30\n\t\t;;\n\tdecstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn)\n\t\tbasic_machine=mips-dec\n\t\t;;\n\tdecsystem10* | dec10*)\n\t\tbasic_machine=pdp10-dec\n\t\tos=-tops10\n\t\t;;\n\tdecsystem20* | dec20*)\n\t\tbasic_machine=pdp10-dec\n\t\tos=-tops20\n\t\t;;\n\tdelta | 3300 | motorola-3300 | motorola-delta \\\n\t      | 3300-motorola | delta-motorola)\n\t\tbasic_machine=m68k-motorola\n\t\t;;\n\tdelta88)\n\t\tbasic_machine=m88k-motorola\n\t\tos=-sysv3\n\t\t;;\n\tdicos)\n\t\tbasic_machine=i686-pc\n\t\tos=-dicos\n\t\t;;\n\tdjgpp)\n\t\tbasic_machine=i586-pc\n\t\tos=-msdosdjgpp\n\t\t;;\n\tdpx20 | dpx20-*)\n\t\tbasic_machine=rs6000-bull\n\t\tos=-bosx\n\t\t;;\n\tdpx2* | dpx2*-bull)\n\t\tbasic_machine=m68k-bull\n\t\tos=-sysv3\n\t\t;;\n\tebmon29k)\n\t\tbasic_machine=a29k-amd\n\t\tos=-ebmon\n\t\t;;\n\telxsi)\n\t\tbasic_machine=elxsi-elxsi\n\t\tos=-bsd\n\t\t;;\n\tencore | umax | mmax)\n\t\tbasic_machine=ns32k-encore\n\t\t;;\n\tes1800 | OSE68k | ose68k | ose | OSE)\n\t\tbasic_machine=m68k-ericsson\n\t\tos=-ose\n\t\t;;\n\tfx2800)\n\t\tbasic_machine=i860-alliant\n\t\t;;\n\tgenix)\n\t\tbasic_machine=ns32k-ns\n\t\t;;\n\tgmicro)\n\t\tbasic_machine=tron-gmicro\n\t\tos=-sysv\n\t\t;;\n\tgo32)\n\t\tbasic_machine=i386-pc\n\t\tos=-go32\n\t\t;;\n\th3050r* | hiux*)\n\t\tbasic_machine=hppa1.1-hitachi\n\t\tos=-hiuxwe2\n\t\t;;\n\th8300hms)\n\t\tbasic_machine=h8300-hitachi\n\t\tos=-hms\n\t\t;;\n\th8300xray)\n\t\tbasic_machine=h8300-hitachi\n\t\tos=-xray\n\t\t;;\n\th8500hms)\n\t\tbasic_machine=h8500-hitachi\n\t\tos=-hms\n\t\t;;\n\tharris)\n\t\tbasic_machine=m88k-harris\n\t\tos=-sysv3\n\t\t;;\n\thp300-*)\n\t\tbasic_machine=m68k-hp\n\t\t;;\n\thp300bsd)\n\t\tbasic_machine=m68k-hp\n\t\tos=-bsd\n\t\t;;\n\thp300hpux)\n\t\tbasic_machine=m68k-hp\n\t\tos=-hpux\n\t\t;;\n\thp3k9[0-9][0-9] | hp9[0-9][0-9])\n\t\tbasic_machine=hppa1.0-hp\n\t\t;;\n\thp9k2[0-9][0-9] | hp9k31[0-9])\n\t\tbasic_machine=m68000-hp\n\t\t;;\n\thp9k3[2-9][0-9])\n\t\tbasic_machine=m68k-hp\n\t\t;;\n\thp9k6[0-9][0-9] | hp6[0-9][0-9])\n\t\tbasic_machine=hppa1.0-hp\n\t\t;;\n\thp9k7[0-79][0-9] | hp7[0-79][0-9])\n\t\tbasic_machine=hppa1.1-hp\n\t\t;;\n\thp9k78[0-9] | hp78[0-9])\n\t\t# FIXME: really hppa2.0-hp\n\t\tbasic_machine=hppa1.1-hp\n\t\t;;\n\thp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)\n\t\t# FIXME: really hppa2.0-hp\n\t\tbasic_machine=hppa1.1-hp\n\t\t;;\n\thp9k8[0-9][13679] | hp8[0-9][13679])\n\t\tbasic_machine=hppa1.1-hp\n\t\t;;\n\thp9k8[0-9][0-9] | hp8[0-9][0-9])\n\t\tbasic_machine=hppa1.0-hp\n\t\t;;\n\thppa-next)\n\t\tos=-nextstep3\n\t\t;;\n\thppaosf)\n\t\tbasic_machine=hppa1.1-hp\n\t\tos=-osf\n\t\t;;\n\thppro)\n\t\tbasic_machine=hppa1.1-hp\n\t\tos=-proelf\n\t\t;;\n\ti370-ibm* | ibm*)\n\t\tbasic_machine=i370-ibm\n\t\t;;\n\ti*86v32)\n\t\tbasic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`\n\t\tos=-sysv32\n\t\t;;\n\ti*86v4*)\n\t\tbasic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`\n\t\tos=-sysv4\n\t\t;;\n\ti*86v)\n\t\tbasic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`\n\t\tos=-sysv\n\t\t;;\n\ti*86sol2)\n\t\tbasic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`\n\t\tos=-solaris2\n\t\t;;\n\ti386mach)\n\t\tbasic_machine=i386-mach\n\t\tos=-mach\n\t\t;;\n\ti386-vsta | vsta)\n\t\tbasic_machine=i386-unknown\n\t\tos=-vsta\n\t\t;;\n\tiris | iris4d)\n\t\tbasic_machine=mips-sgi\n\t\tcase $os in\n\t\t    -irix*)\n\t\t\t;;\n\t\t    *)\n\t\t\tos=-irix4\n\t\t\t;;\n\t\tesac\n\t\t;;\n\tisi68 | isi)\n\t\tbasic_machine=m68k-isi\n\t\tos=-sysv\n\t\t;;\n\tm68knommu)\n\t\tbasic_machine=m68k-unknown\n\t\tos=-linux\n\t\t;;\n\tm68knommu-*)\n\t\tbasic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'`\n\t\tos=-linux\n\t\t;;\n\tm88k-omron*)\n\t\tbasic_machine=m88k-omron\n\t\t;;\n\tmagnum | m3230)\n\t\tbasic_machine=mips-mips\n\t\tos=-sysv\n\t\t;;\n\tmerlin)\n\t\tbasic_machine=ns32k-utek\n\t\tos=-sysv\n\t\t;;\n\tmicroblaze)\n\t\tbasic_machine=microblaze-xilinx\n\t\t;;\n\tmingw32)\n\t\tbasic_machine=i386-pc\n\t\tos=-mingw32\n\t\t;;\n\tmingw32ce)\n\t\tbasic_machine=arm-unknown\n\t\tos=-mingw32ce\n\t\t;;\n\tminiframe)\n\t\tbasic_machine=m68000-convergent\n\t\t;;\n\t*mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*)\n\t\tbasic_machine=m68k-atari\n\t\tos=-mint\n\t\t;;\n\tmips3*-*)\n\t\tbasic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`\n\t\t;;\n\tmips3*)\n\t\tbasic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown\n\t\t;;\n\tmonitor)\n\t\tbasic_machine=m68k-rom68k\n\t\tos=-coff\n\t\t;;\n\tmorphos)\n\t\tbasic_machine=powerpc-unknown\n\t\tos=-morphos\n\t\t;;\n\tmsdos)\n\t\tbasic_machine=i386-pc\n\t\tos=-msdos\n\t\t;;\n\tms1-*)\n\t\tbasic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'`\n\t\t;;\n\tmsys)\n\t\tbasic_machine=i386-pc\n\t\tos=-msys\n\t\t;;\n\tmvs)\n\t\tbasic_machine=i370-ibm\n\t\tos=-mvs\n\t\t;;\n\tnacl)\n\t\tbasic_machine=le32-unknown\n\t\tos=-nacl\n\t\t;;\n\tncr3000)\n\t\tbasic_machine=i486-ncr\n\t\tos=-sysv4\n\t\t;;\n\tnetbsd386)\n\t\tbasic_machine=i386-unknown\n\t\tos=-netbsd\n\t\t;;\n\tnetwinder)\n\t\tbasic_machine=armv4l-rebel\n\t\tos=-linux\n\t\t;;\n\tnews | news700 | news800 | news900)\n\t\tbasic_machine=m68k-sony\n\t\tos=-newsos\n\t\t;;\n\tnews1000)\n\t\tbasic_machine=m68030-sony\n\t\tos=-newsos\n\t\t;;\n\tnews-3600 | risc-news)\n\t\tbasic_machine=mips-sony\n\t\tos=-newsos\n\t\t;;\n\tnecv70)\n\t\tbasic_machine=v70-nec\n\t\tos=-sysv\n\t\t;;\n\tnext | m*-next )\n\t\tbasic_machine=m68k-next\n\t\tcase $os in\n\t\t    -nextstep* )\n\t\t\t;;\n\t\t    -ns2*)\n\t\t      os=-nextstep2\n\t\t\t;;\n\t\t    *)\n\t\t      os=-nextstep3\n\t\t\t;;\n\t\tesac\n\t\t;;\n\tnh3000)\n\t\tbasic_machine=m68k-harris\n\t\tos=-cxux\n\t\t;;\n\tnh[45]000)\n\t\tbasic_machine=m88k-harris\n\t\tos=-cxux\n\t\t;;\n\tnindy960)\n\t\tbasic_machine=i960-intel\n\t\tos=-nindy\n\t\t;;\n\tmon960)\n\t\tbasic_machine=i960-intel\n\t\tos=-mon960\n\t\t;;\n\tnonstopux)\n\t\tbasic_machine=mips-compaq\n\t\tos=-nonstopux\n\t\t;;\n\tnp1)\n\t\tbasic_machine=np1-gould\n\t\t;;\n\tneo-tandem)\n\t\tbasic_machine=neo-tandem\n\t\t;;\n\tnse-tandem)\n\t\tbasic_machine=nse-tandem\n\t\t;;\n\tnsr-tandem)\n\t\tbasic_machine=nsr-tandem\n\t\t;;\n\top50n-* | op60c-*)\n\t\tbasic_machine=hppa1.1-oki\n\t\tos=-proelf\n\t\t;;\n\topenrisc | openrisc-*)\n\t\tbasic_machine=or32-unknown\n\t\t;;\n\tos400)\n\t\tbasic_machine=powerpc-ibm\n\t\tos=-os400\n\t\t;;\n\tOSE68000 | ose68000)\n\t\tbasic_machine=m68000-ericsson\n\t\tos=-ose\n\t\t;;\n\tos68k)\n\t\tbasic_machine=m68k-none\n\t\tos=-os68k\n\t\t;;\n\tpa-hitachi)\n\t\tbasic_machine=hppa1.1-hitachi\n\t\tos=-hiuxwe2\n\t\t;;\n\tparagon)\n\t\tbasic_machine=i860-intel\n\t\tos=-osf\n\t\t;;\n\tparisc)\n\t\tbasic_machine=hppa-unknown\n\t\tos=-linux\n\t\t;;\n\tparisc-*)\n\t\tbasic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'`\n\t\tos=-linux\n\t\t;;\n\tpbd)\n\t\tbasic_machine=sparc-tti\n\t\t;;\n\tpbb)\n\t\tbasic_machine=m68k-tti\n\t\t;;\n\tpc532 | pc532-*)\n\t\tbasic_machine=ns32k-pc532\n\t\t;;\n\tpc98)\n\t\tbasic_machine=i386-pc\n\t\t;;\n\tpc98-*)\n\t\tbasic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'`\n\t\t;;\n\tpentium | p5 | k5 | k6 | nexgen | viac3)\n\t\tbasic_machine=i586-pc\n\t\t;;\n\tpentiumpro | p6 | 6x86 | athlon | athlon_*)\n\t\tbasic_machine=i686-pc\n\t\t;;\n\tpentiumii | pentium2 | pentiumiii | pentium3)\n\t\tbasic_machine=i686-pc\n\t\t;;\n\tpentium4)\n\t\tbasic_machine=i786-pc\n\t\t;;\n\tpentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)\n\t\tbasic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'`\n\t\t;;\n\tpentiumpro-* | p6-* | 6x86-* | athlon-*)\n\t\tbasic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`\n\t\t;;\n\tpentiumii-* | pentium2-* | pentiumiii-* | pentium3-*)\n\t\tbasic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`\n\t\t;;\n\tpentium4-*)\n\t\tbasic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'`\n\t\t;;\n\tpn)\n\t\tbasic_machine=pn-gould\n\t\t;;\n\tpower)\tbasic_machine=power-ibm\n\t\t;;\n\tppc | ppcbe)\tbasic_machine=powerpc-unknown\n\t\t;;\n\tppc-* | ppcbe-*)\n\t\tbasic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`\n\t\t;;\n\tppcle | powerpclittle | ppc-le | powerpc-little)\n\t\tbasic_machine=powerpcle-unknown\n\t\t;;\n\tppcle-* | powerpclittle-*)\n\t\tbasic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'`\n\t\t;;\n\tppc64)\tbasic_machine=powerpc64-unknown\n\t\t;;\n\tppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'`\n\t\t;;\n\tppc64le | powerpc64little | ppc64-le | powerpc64-little)\n\t\tbasic_machine=powerpc64le-unknown\n\t\t;;\n\tppc64le-* | powerpc64little-*)\n\t\tbasic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'`\n\t\t;;\n\tps2)\n\t\tbasic_machine=i386-ibm\n\t\t;;\n\tpw32)\n\t\tbasic_machine=i586-unknown\n\t\tos=-pw32\n\t\t;;\n\trdos)\n\t\tbasic_machine=i386-pc\n\t\tos=-rdos\n\t\t;;\n\trom68k)\n\t\tbasic_machine=m68k-rom68k\n\t\tos=-coff\n\t\t;;\n\trm[46]00)\n\t\tbasic_machine=mips-siemens\n\t\t;;\n\trtpc | rtpc-*)\n\t\tbasic_machine=romp-ibm\n\t\t;;\n\ts390 | s390-*)\n\t\tbasic_machine=s390-ibm\n\t\t;;\n\ts390x | s390x-*)\n\t\tbasic_machine=s390x-ibm\n\t\t;;\n\tsa29200)\n\t\tbasic_machine=a29k-amd\n\t\tos=-udi\n\t\t;;\n\tsb1)\n\t\tbasic_machine=mipsisa64sb1-unknown\n\t\t;;\n\tsb1el)\n\t\tbasic_machine=mipsisa64sb1el-unknown\n\t\t;;\n\tsde)\n\t\tbasic_machine=mipsisa32-sde\n\t\tos=-elf\n\t\t;;\n\tsei)\n\t\tbasic_machine=mips-sei\n\t\tos=-seiux\n\t\t;;\n\tsequent)\n\t\tbasic_machine=i386-sequent\n\t\t;;\n\tsh)\n\t\tbasic_machine=sh-hitachi\n\t\tos=-hms\n\t\t;;\n\tsh5el)\n\t\tbasic_machine=sh5le-unknown\n\t\t;;\n\tsh64)\n\t\tbasic_machine=sh64-unknown\n\t\t;;\n\tsparclite-wrs | simso-wrs)\n\t\tbasic_machine=sparclite-wrs\n\t\tos=-vxworks\n\t\t;;\n\tsps7)\n\t\tbasic_machine=m68k-bull\n\t\tos=-sysv2\n\t\t;;\n\tspur)\n\t\tbasic_machine=spur-unknown\n\t\t;;\n\tst2000)\n\t\tbasic_machine=m68k-tandem\n\t\t;;\n\tstratus)\n\t\tbasic_machine=i860-stratus\n\t\tos=-sysv4\n\t\t;;\n\tstrongarm-* | thumb-*)\n\t\tbasic_machine=arm-`echo $basic_machine | sed 's/^[^-]*-//'`\n\t\t;;\n\tsun2)\n\t\tbasic_machine=m68000-sun\n\t\t;;\n\tsun2os3)\n\t\tbasic_machine=m68000-sun\n\t\tos=-sunos3\n\t\t;;\n\tsun2os4)\n\t\tbasic_machine=m68000-sun\n\t\tos=-sunos4\n\t\t;;\n\tsun3os3)\n\t\tbasic_machine=m68k-sun\n\t\tos=-sunos3\n\t\t;;\n\tsun3os4)\n\t\tbasic_machine=m68k-sun\n\t\tos=-sunos4\n\t\t;;\n\tsun4os3)\n\t\tbasic_machine=sparc-sun\n\t\tos=-sunos3\n\t\t;;\n\tsun4os4)\n\t\tbasic_machine=sparc-sun\n\t\tos=-sunos4\n\t\t;;\n\tsun4sol2)\n\t\tbasic_machine=sparc-sun\n\t\tos=-solaris2\n\t\t;;\n\tsun3 | sun3-*)\n\t\tbasic_machine=m68k-sun\n\t\t;;\n\tsun4)\n\t\tbasic_machine=sparc-sun\n\t\t;;\n\tsun386 | sun386i | roadrunner)\n\t\tbasic_machine=i386-sun\n\t\t;;\n\tsv1)\n\t\tbasic_machine=sv1-cray\n\t\tos=-unicos\n\t\t;;\n\tsymmetry)\n\t\tbasic_machine=i386-sequent\n\t\tos=-dynix\n\t\t;;\n\tt3e)\n\t\tbasic_machine=alphaev5-cray\n\t\tos=-unicos\n\t\t;;\n\tt90)\n\t\tbasic_machine=t90-cray\n\t\tos=-unicos\n\t\t;;\n\ttile*)\n\t\tbasic_machine=$basic_machine-unknown\n\t\tos=-linux-gnu\n\t\t;;\n\ttx39)\n\t\tbasic_machine=mipstx39-unknown\n\t\t;;\n\ttx39el)\n\t\tbasic_machine=mipstx39el-unknown\n\t\t;;\n\ttoad1)\n\t\tbasic_machine=pdp10-xkl\n\t\tos=-tops20\n\t\t;;\n\ttower | tower-32)\n\t\tbasic_machine=m68k-ncr\n\t\t;;\n\ttpf)\n\t\tbasic_machine=s390x-ibm\n\t\tos=-tpf\n\t\t;;\n\tudi29k)\n\t\tbasic_machine=a29k-amd\n\t\tos=-udi\n\t\t;;\n\tultra3)\n\t\tbasic_machine=a29k-nyu\n\t\tos=-sym1\n\t\t;;\n\tv810 | necv810)\n\t\tbasic_machine=v810-nec\n\t\tos=-none\n\t\t;;\n\tvaxv)\n\t\tbasic_machine=vax-dec\n\t\tos=-sysv\n\t\t;;\n\tvms)\n\t\tbasic_machine=vax-dec\n\t\tos=-vms\n\t\t;;\n\tvpp*|vx|vx-*)\n\t\tbasic_machine=f301-fujitsu\n\t\t;;\n\tvxworks960)\n\t\tbasic_machine=i960-wrs\n\t\tos=-vxworks\n\t\t;;\n\tvxworks68)\n\t\tbasic_machine=m68k-wrs\n\t\tos=-vxworks\n\t\t;;\n\tvxworks29k)\n\t\tbasic_machine=a29k-wrs\n\t\tos=-vxworks\n\t\t;;\n\tw65*)\n\t\tbasic_machine=w65-wdc\n\t\tos=-none\n\t\t;;\n\tw89k-*)\n\t\tbasic_machine=hppa1.1-winbond\n\t\tos=-proelf\n\t\t;;\n\txbox)\n\t\tbasic_machine=i686-pc\n\t\tos=-mingw32\n\t\t;;\n\txps | xps100)\n\t\tbasic_machine=xps100-honeywell\n\t\t;;\n\txscale-* | xscalee[bl]-*)\n\t\tbasic_machine=`echo $basic_machine | sed 's/^xscale/arm/'`\n\t\t;;\n\tymp)\n\t\tbasic_machine=ymp-cray\n\t\tos=-unicos\n\t\t;;\n\tz8k-*-coff)\n\t\tbasic_machine=z8k-unknown\n\t\tos=-sim\n\t\t;;\n\tz80-*-coff)\n\t\tbasic_machine=z80-unknown\n\t\tos=-sim\n\t\t;;\n\tnone)\n\t\tbasic_machine=none-none\n\t\tos=-none\n\t\t;;\n\n# Here we handle the default manufacturer of certain CPU types.  It is in\n# some cases the only manufacturer, in others, it is the most popular.\n\tw89k)\n\t\tbasic_machine=hppa1.1-winbond\n\t\t;;\n\top50n)\n\t\tbasic_machine=hppa1.1-oki\n\t\t;;\n\top60c)\n\t\tbasic_machine=hppa1.1-oki\n\t\t;;\n\tromp)\n\t\tbasic_machine=romp-ibm\n\t\t;;\n\tmmix)\n\t\tbasic_machine=mmix-knuth\n\t\t;;\n\trs6000)\n\t\tbasic_machine=rs6000-ibm\n\t\t;;\n\tvax)\n\t\tbasic_machine=vax-dec\n\t\t;;\n\tpdp10)\n\t\t# there are many clones, so DEC is not a safe bet\n\t\tbasic_machine=pdp10-unknown\n\t\t;;\n\tpdp11)\n\t\tbasic_machine=pdp11-dec\n\t\t;;\n\twe32k)\n\t\tbasic_machine=we32k-att\n\t\t;;\n\tsh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele)\n\t\tbasic_machine=sh-unknown\n\t\t;;\n\tsparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v)\n\t\tbasic_machine=sparc-sun\n\t\t;;\n\tcydra)\n\t\tbasic_machine=cydra-cydrome\n\t\t;;\n\torion)\n\t\tbasic_machine=orion-highlevel\n\t\t;;\n\torion105)\n\t\tbasic_machine=clipper-highlevel\n\t\t;;\n\tmac | mpw | mac-mpw)\n\t\tbasic_machine=m68k-apple\n\t\t;;\n\tpmac | pmac-mpw)\n\t\tbasic_machine=powerpc-apple\n\t\t;;\n\t*-unknown)\n\t\t# Make sure to match an already-canonicalized machine name.\n\t\t;;\n\t*)\n\t\techo Invalid configuration \\`$1\\': machine \\`$basic_machine\\' not recognized 1>&2\n\t\texit 1\n\t\t;;\nesac\n\n# Here we canonicalize certain aliases for manufacturers.\ncase $basic_machine in\n\t*-digital*)\n\t\tbasic_machine=`echo $basic_machine | sed 's/digital.*/dec/'`\n\t\t;;\n\t*-commodore*)\n\t\tbasic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'`\n\t\t;;\n\t*)\n\t\t;;\nesac\n\n# Decode manufacturer-specific aliases for certain operating systems.\n\nif [ x\"$os\" != x\"\" ]\nthen\ncase $os in\n\t# First match some system type aliases\n\t# that might get confused with valid system types.\n\t# -solaris* is a basic system type, with this one exception.\n\t-auroraux)\n\t\tos=-auroraux\n\t\t;;\n\t-solaris1 | -solaris1.*)\n\t\tos=`echo $os | sed -e 's|solaris1|sunos4|'`\n\t\t;;\n\t-solaris)\n\t\tos=-solaris2\n\t\t;;\n\t-svr4*)\n\t\tos=-sysv4\n\t\t;;\n\t-unixware*)\n\t\tos=-sysv4.2uw\n\t\t;;\n\t-gnu/linux*)\n\t\tos=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`\n\t\t;;\n\t# First accept the basic system types.\n\t# The portable systems comes first.\n\t# Each alternative MUST END IN A *, to match a version number.\n\t# -sysv* is not here because it comes later, after sysvr4.\n\t-gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \\\n\t      | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\\\n\t      | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \\\n\t      | -sym* | -kopensolaris* \\\n\t      | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \\\n\t      | -aos* | -aros* \\\n\t      | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \\\n\t      | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \\\n\t      | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \\\n\t      | -openbsd* | -solidbsd* \\\n\t      | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \\\n\t      | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \\\n\t      | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \\\n\t      | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \\\n\t      | -chorusos* | -chorusrdb* | -cegcc* \\\n\t      | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \\\n\t      | -mingw32* | -linux-gnu* | -linux-android* \\\n\t      | -linux-newlib* | -linux-uclibc* \\\n\t      | -uxpv* | -beos* | -mpeix* | -udk* \\\n\t      | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \\\n\t      | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \\\n\t      | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \\\n\t      | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \\\n\t      | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \\\n\t      | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \\\n\t      | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es*)\n\t# Remember, each alternative MUST END IN *, to match a version number.\n\t\t;;\n\t-qnx*)\n\t\tcase $basic_machine in\n\t\t    x86-* | i*86-*)\n\t\t\t;;\n\t\t    *)\n\t\t\tos=-nto$os\n\t\t\t;;\n\t\tesac\n\t\t;;\n\t-nto-qnx*)\n\t\t;;\n\t-nto*)\n\t\tos=`echo $os | sed -e 's|nto|nto-qnx|'`\n\t\t;;\n\t-sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \\\n\t      | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \\\n\t      | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*)\n\t\t;;\n\t-mac*)\n\t\tos=`echo $os | sed -e 's|mac|macos|'`\n\t\t;;\n\t-linux-dietlibc)\n\t\tos=-linux-dietlibc\n\t\t;;\n\t-linux*)\n\t\tos=`echo $os | sed -e 's|linux|linux-gnu|'`\n\t\t;;\n\t-sunos5*)\n\t\tos=`echo $os | sed -e 's|sunos5|solaris2|'`\n\t\t;;\n\t-sunos6*)\n\t\tos=`echo $os | sed -e 's|sunos6|solaris3|'`\n\t\t;;\n\t-opened*)\n\t\tos=-openedition\n\t\t;;\n\t-os400*)\n\t\tos=-os400\n\t\t;;\n\t-wince*)\n\t\tos=-wince\n\t\t;;\n\t-osfrose*)\n\t\tos=-osfrose\n\t\t;;\n\t-osf*)\n\t\tos=-osf\n\t\t;;\n\t-utek*)\n\t\tos=-bsd\n\t\t;;\n\t-dynix*)\n\t\tos=-bsd\n\t\t;;\n\t-acis*)\n\t\tos=-aos\n\t\t;;\n\t-atheos*)\n\t\tos=-atheos\n\t\t;;\n\t-syllable*)\n\t\tos=-syllable\n\t\t;;\n\t-386bsd)\n\t\tos=-bsd\n\t\t;;\n\t-ctix* | -uts*)\n\t\tos=-sysv\n\t\t;;\n\t-nova*)\n\t\tos=-rtmk-nova\n\t\t;;\n\t-ns2 )\n\t\tos=-nextstep2\n\t\t;;\n\t-nsk*)\n\t\tos=-nsk\n\t\t;;\n\t# Preserve the version number of sinix5.\n\t-sinix5.*)\n\t\tos=`echo $os | sed -e 's|sinix|sysv|'`\n\t\t;;\n\t-sinix*)\n\t\tos=-sysv4\n\t\t;;\n\t-tpf*)\n\t\tos=-tpf\n\t\t;;\n\t-triton*)\n\t\tos=-sysv3\n\t\t;;\n\t-oss*)\n\t\tos=-sysv3\n\t\t;;\n\t-svr4)\n\t\tos=-sysv4\n\t\t;;\n\t-svr3)\n\t\tos=-sysv3\n\t\t;;\n\t-sysvr4)\n\t\tos=-sysv4\n\t\t;;\n\t# This must come after -sysvr4.\n\t-sysv*)\n\t\t;;\n\t-ose*)\n\t\tos=-ose\n\t\t;;\n\t-es1800*)\n\t\tos=-ose\n\t\t;;\n\t-xenix)\n\t\tos=-xenix\n\t\t;;\n\t-*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)\n\t\tos=-mint\n\t\t;;\n\t-aros*)\n\t\tos=-aros\n\t\t;;\n\t-kaos*)\n\t\tos=-kaos\n\t\t;;\n\t-zvmoe)\n\t\tos=-zvmoe\n\t\t;;\n\t-dicos*)\n\t\tos=-dicos\n\t\t;;\n\t-nacl*)\n\t\t;;\n\t-none)\n\t\t;;\n\t*)\n\t\t# Get rid of the `-' at the beginning of $os.\n\t\tos=`echo $os | sed 's/[^-]*-//'`\n\t\techo Invalid configuration \\`$1\\': system \\`$os\\' not recognized 1>&2\n\t\texit 1\n\t\t;;\nesac\nelse\n\n# Here we handle the default operating systems that come with various machines.\n# The value should be what the vendor currently ships out the door with their\n# machine or put another way, the most popular os provided with the machine.\n\n# Note that if you're going to try to match \"-MANUFACTURER\" here (say,\n# \"-sun\"), then you have to tell the case statement up towards the top\n# that MANUFACTURER isn't an operating system.  Otherwise, code above\n# will signal an error saying that MANUFACTURER isn't an operating\n# system, and we'll never get to this point.\n\ncase $basic_machine in\n\tscore-*)\n\t\tos=-elf\n\t\t;;\n\tspu-*)\n\t\tos=-elf\n\t\t;;\n\t*-acorn)\n\t\tos=-riscix1.2\n\t\t;;\n\tarm*-rebel)\n\t\tos=-linux\n\t\t;;\n\tarm*-semi)\n\t\tos=-aout\n\t\t;;\n\tc4x-* | tic4x-*)\n\t\tos=-coff\n\t\t;;\n\ttic54x-*)\n\t\tos=-coff\n\t\t;;\n\ttic55x-*)\n\t\tos=-coff\n\t\t;;\n\ttic6x-*)\n\t\tos=-coff\n\t\t;;\n\t# This must come before the *-dec entry.\n\tpdp10-*)\n\t\tos=-tops20\n\t\t;;\n\tpdp11-*)\n\t\tos=-none\n\t\t;;\n\t*-dec | vax-*)\n\t\tos=-ultrix4.2\n\t\t;;\n\tm68*-apollo)\n\t\tos=-domain\n\t\t;;\n\ti386-sun)\n\t\tos=-sunos4.0.2\n\t\t;;\n\tm68000-sun)\n\t\tos=-sunos3\n\t\t;;\n\tm68*-cisco)\n\t\tos=-aout\n\t\t;;\n\tmep-*)\n\t\tos=-elf\n\t\t;;\n\tmips*-cisco)\n\t\tos=-elf\n\t\t;;\n\tmips*-*)\n\t\tos=-elf\n\t\t;;\n\tor32-*)\n\t\tos=-coff\n\t\t;;\n\t*-tti)\t# must be before sparc entry or we get the wrong os.\n\t\tos=-sysv3\n\t\t;;\n\tsparc-* | *-sun)\n\t\tos=-sunos4.1.1\n\t\t;;\n\t*-be)\n\t\tos=-beos\n\t\t;;\n\t*-haiku)\n\t\tos=-haiku\n\t\t;;\n\t*-ibm)\n\t\tos=-aix\n\t\t;;\n\t*-knuth)\n\t\tos=-mmixware\n\t\t;;\n\t*-wec)\n\t\tos=-proelf\n\t\t;;\n\t*-winbond)\n\t\tos=-proelf\n\t\t;;\n\t*-oki)\n\t\tos=-proelf\n\t\t;;\n\t*-hp)\n\t\tos=-hpux\n\t\t;;\n\t*-hitachi)\n\t\tos=-hiux\n\t\t;;\n\ti860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)\n\t\tos=-sysv\n\t\t;;\n\t*-cbm)\n\t\tos=-amigaos\n\t\t;;\n\t*-dg)\n\t\tos=-dgux\n\t\t;;\n\t*-dolphin)\n\t\tos=-sysv3\n\t\t;;\n\tm68k-ccur)\n\t\tos=-rtu\n\t\t;;\n\tm88k-omron*)\n\t\tos=-luna\n\t\t;;\n\t*-next )\n\t\tos=-nextstep\n\t\t;;\n\t*-sequent)\n\t\tos=-ptx\n\t\t;;\n\t*-crds)\n\t\tos=-unos\n\t\t;;\n\t*-ns)\n\t\tos=-genix\n\t\t;;\n\ti370-*)\n\t\tos=-mvs\n\t\t;;\n\t*-next)\n\t\tos=-nextstep3\n\t\t;;\n\t*-gould)\n\t\tos=-sysv\n\t\t;;\n\t*-highlevel)\n\t\tos=-bsd\n\t\t;;\n\t*-encore)\n\t\tos=-bsd\n\t\t;;\n\t*-sgi)\n\t\tos=-irix\n\t\t;;\n\t*-siemens)\n\t\tos=-sysv4\n\t\t;;\n\t*-masscomp)\n\t\tos=-rtu\n\t\t;;\n\tf30[01]-fujitsu | f700-fujitsu)\n\t\tos=-uxpv\n\t\t;;\n\t*-rom68k)\n\t\tos=-coff\n\t\t;;\n\t*-*bug)\n\t\tos=-coff\n\t\t;;\n\t*-apple)\n\t\tos=-macos\n\t\t;;\n\t*-atari*)\n\t\tos=-mint\n\t\t;;\n\t*)\n\t\tos=-none\n\t\t;;\nesac\nfi\n\n# Here we handle the case where we know the os, and the CPU type, but not the\n# manufacturer.  We pick the logical manufacturer.\nvendor=unknown\ncase $basic_machine in\n\t*-unknown)\n\t\tcase $os in\n\t\t\t-riscix*)\n\t\t\t\tvendor=acorn\n\t\t\t\t;;\n\t\t\t-sunos*)\n\t\t\t\tvendor=sun\n\t\t\t\t;;\n\t\t\t-cnk*|-aix*)\n\t\t\t\tvendor=ibm\n\t\t\t\t;;\n\t\t\t-beos*)\n\t\t\t\tvendor=be\n\t\t\t\t;;\n\t\t\t-hpux*)\n\t\t\t\tvendor=hp\n\t\t\t\t;;\n\t\t\t-mpeix*)\n\t\t\t\tvendor=hp\n\t\t\t\t;;\n\t\t\t-hiux*)\n\t\t\t\tvendor=hitachi\n\t\t\t\t;;\n\t\t\t-unos*)\n\t\t\t\tvendor=crds\n\t\t\t\t;;\n\t\t\t-dgux*)\n\t\t\t\tvendor=dg\n\t\t\t\t;;\n\t\t\t-luna*)\n\t\t\t\tvendor=omron\n\t\t\t\t;;\n\t\t\t-genix*)\n\t\t\t\tvendor=ns\n\t\t\t\t;;\n\t\t\t-mvs* | -opened*)\n\t\t\t\tvendor=ibm\n\t\t\t\t;;\n\t\t\t-os400*)\n\t\t\t\tvendor=ibm\n\t\t\t\t;;\n\t\t\t-ptx*)\n\t\t\t\tvendor=sequent\n\t\t\t\t;;\n\t\t\t-tpf*)\n\t\t\t\tvendor=ibm\n\t\t\t\t;;\n\t\t\t-vxsim* | -vxworks* | -windiss*)\n\t\t\t\tvendor=wrs\n\t\t\t\t;;\n\t\t\t-aux*)\n\t\t\t\tvendor=apple\n\t\t\t\t;;\n\t\t\t-hms*)\n\t\t\t\tvendor=hitachi\n\t\t\t\t;;\n\t\t\t-mpw* | -macos*)\n\t\t\t\tvendor=apple\n\t\t\t\t;;\n\t\t\t-*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)\n\t\t\t\tvendor=atari\n\t\t\t\t;;\n\t\t\t-vos*)\n\t\t\t\tvendor=stratus\n\t\t\t\t;;\n\t\tesac\n\t\tbasic_machine=`echo $basic_machine | sed \"s/unknown/$vendor/\"`\n\t\t;;\nesac\n\necho $basic_machine$os\nexit\n\n# Local variables:\n# eval: (add-hook 'write-file-hooks 'time-stamp)\n# time-stamp-start: \"timestamp='\"\n# time-stamp-format: \"%:y-%02m-%02d\"\n# time-stamp-end: \"'\"\n# End:\n"
  },
  {
    "path": "deps/jemalloc/configure",
    "content": "#! /bin/sh\n# Guess values for system-dependent variables and create Makefiles.\n# Generated by GNU Autoconf 2.68.\n#\n#\n# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,\n# 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software\n# Foundation, Inc.\n#\n#\n# This configure script is free software; the Free Software Foundation\n# gives unlimited permission to copy, distribute and modify it.\n## -------------------- ##\n## M4sh Initialization. ##\n## -------------------- ##\n\n# Be more Bourne compatible\nDUALCASE=1; export DUALCASE # for MKS sh\nif test -n \"${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :\n  emulate sh\n  NULLCMD=:\n  # Pre-4.2 versions of Zsh do word splitting on ${1+\"$@\"}, which\n  # is contrary to our usage.  Disable this feature.\n  alias -g '${1+\"$@\"}'='\"$@\"'\n  setopt NO_GLOB_SUBST\nelse\n  case `(set -o) 2>/dev/null` in #(\n  *posix*) :\n    set -o posix ;; #(\n  *) :\n     ;;\nesac\nfi\n\n\nas_nl='\n'\nexport as_nl\n# Printing a long string crashes Solaris 7 /usr/bin/printf.\nas_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'\nas_echo=$as_echo$as_echo$as_echo$as_echo$as_echo\nas_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo\n# Prefer a ksh shell builtin over an external printf program on Solaris,\n# but without wasting forks for bash or zsh.\nif test -z \"$BASH_VERSION$ZSH_VERSION\" \\\n    && (test \"X`print -r -- $as_echo`\" = \"X$as_echo\") 2>/dev/null; then\n  as_echo='print -r --'\n  as_echo_n='print -rn --'\nelif (test \"X`printf %s $as_echo`\" = \"X$as_echo\") 2>/dev/null; then\n  as_echo='printf %s\\n'\n  as_echo_n='printf %s'\nelse\n  if test \"X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`\" = \"X-n $as_echo\"; then\n    as_echo_body='eval /usr/ucb/echo -n \"$1$as_nl\"'\n    as_echo_n='/usr/ucb/echo -n'\n  else\n    as_echo_body='eval expr \"X$1\" : \"X\\\\(.*\\\\)\"'\n    as_echo_n_body='eval\n      arg=$1;\n      case $arg in #(\n      *\"$as_nl\"*)\n\texpr \"X$arg\" : \"X\\\\(.*\\\\)$as_nl\";\n\targ=`expr \"X$arg\" : \".*$as_nl\\\\(.*\\\\)\"`;;\n      esac;\n      expr \"X$arg\" : \"X\\\\(.*\\\\)\" | tr -d \"$as_nl\"\n    '\n    export as_echo_n_body\n    as_echo_n='sh -c $as_echo_n_body as_echo'\n  fi\n  export as_echo_body\n  as_echo='sh -c $as_echo_body as_echo'\nfi\n\n# The user is always right.\nif test \"${PATH_SEPARATOR+set}\" != set; then\n  PATH_SEPARATOR=:\n  (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {\n    (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||\n      PATH_SEPARATOR=';'\n  }\nfi\n\n\n# IFS\n# We need space, tab and new line, in precisely that order.  Quoting is\n# there to prevent editors from complaining about space-tab.\n# (If _AS_PATH_WALK were called with IFS unset, it would disable word\n# splitting by setting IFS to empty value.)\nIFS=\" \"\"\t$as_nl\"\n\n# Find who we are.  Look in the path if we contain no directory separator.\nas_myself=\ncase $0 in #((\n  *[\\\\/]* ) as_myself=$0 ;;\n  *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    test -r \"$as_dir/$0\" && as_myself=$as_dir/$0 && break\n  done\nIFS=$as_save_IFS\n\n     ;;\nesac\n# We did not find ourselves, most probably we were run as `sh COMMAND'\n# in which case we are not to be found in the path.\nif test \"x$as_myself\" = x; then\n  as_myself=$0\nfi\nif test ! -f \"$as_myself\"; then\n  $as_echo \"$as_myself: error: cannot find myself; rerun with an absolute file name\" >&2\n  exit 1\nfi\n\n# Unset variables that we do not need and which cause bugs (e.g. in\n# pre-3.0 UWIN ksh).  But do not cause bugs in bash 2.01; the \"|| exit 1\"\n# suppresses any \"Segmentation fault\" message there.  '((' could\n# trigger a bug in pdksh 5.2.14.\nfor as_var in BASH_ENV ENV MAIL MAILPATH\ndo eval test x\\${$as_var+set} = xset \\\n  && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :\ndone\nPS1='$ '\nPS2='> '\nPS4='+ '\n\n# NLS nuisances.\nLC_ALL=C\nexport LC_ALL\nLANGUAGE=C\nexport LANGUAGE\n\n# CDPATH.\n(unset CDPATH) >/dev/null 2>&1 && unset CDPATH\n\nif test \"x$CONFIG_SHELL\" = x; then\n  as_bourne_compatible=\"if test -n \\\"\\${ZSH_VERSION+set}\\\" && (emulate sh) >/dev/null 2>&1; then :\n  emulate sh\n  NULLCMD=:\n  # Pre-4.2 versions of Zsh do word splitting on \\${1+\\\"\\$@\\\"}, which\n  # is contrary to our usage.  Disable this feature.\n  alias -g '\\${1+\\\"\\$@\\\"}'='\\\"\\$@\\\"'\n  setopt NO_GLOB_SUBST\nelse\n  case \\`(set -o) 2>/dev/null\\` in #(\n  *posix*) :\n    set -o posix ;; #(\n  *) :\n     ;;\nesac\nfi\n\"\n  as_required=\"as_fn_return () { (exit \\$1); }\nas_fn_success () { as_fn_return 0; }\nas_fn_failure () { as_fn_return 1; }\nas_fn_ret_success () { return 0; }\nas_fn_ret_failure () { return 1; }\n\nexitcode=0\nas_fn_success || { exitcode=1; echo as_fn_success failed.; }\nas_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; }\nas_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; }\nas_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; }\nif ( set x; as_fn_ret_success y && test x = \\\"\\$1\\\" ); then :\n\nelse\n  exitcode=1; echo positional parameters were not saved.\nfi\ntest x\\$exitcode = x0 || exit 1\"\n  as_suggested=\"  as_lineno_1=\";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested\" as_lineno_1a=\\$LINENO\n  as_lineno_2=\";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested\" as_lineno_2a=\\$LINENO\n  eval 'test \\\"x\\$as_lineno_1'\\$as_run'\\\" != \\\"x\\$as_lineno_2'\\$as_run'\\\" &&\n  test \\\"x\\`expr \\$as_lineno_1'\\$as_run' + 1\\`\\\" = \\\"x\\$as_lineno_2'\\$as_run'\\\"' || exit 1\ntest \\$(( 1 + 1 )) = 2 || exit 1\"\n  if (eval \"$as_required\") 2>/dev/null; then :\n  as_have_required=yes\nelse\n  as_have_required=no\nfi\n  if test x$as_have_required = xyes && (eval \"$as_suggested\") 2>/dev/null; then :\n\nelse\n  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nas_found=false\nfor as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n  as_found=:\n  case $as_dir in #(\n\t /*)\n\t   for as_base in sh bash ksh sh5; do\n\t     # Try only shells that exist, to save several forks.\n\t     as_shell=$as_dir/$as_base\n\t     if { test -f \"$as_shell\" || test -f \"$as_shell.exe\"; } &&\n\t\t    { $as_echo \"$as_bourne_compatible\"\"$as_required\" | as_run=a \"$as_shell\"; } 2>/dev/null; then :\n  CONFIG_SHELL=$as_shell as_have_required=yes\n\t\t   if { $as_echo \"$as_bourne_compatible\"\"$as_suggested\" | as_run=a \"$as_shell\"; } 2>/dev/null; then :\n  break 2\nfi\nfi\n\t   done;;\n       esac\n  as_found=false\ndone\n$as_found || { if { test -f \"$SHELL\" || test -f \"$SHELL.exe\"; } &&\n\t      { $as_echo \"$as_bourne_compatible\"\"$as_required\" | as_run=a \"$SHELL\"; } 2>/dev/null; then :\n  CONFIG_SHELL=$SHELL as_have_required=yes\nfi; }\nIFS=$as_save_IFS\n\n\n      if test \"x$CONFIG_SHELL\" != x; then :\n  # We cannot yet assume a decent shell, so we have to provide a\n\t# neutralization value for shells without unset; and this also\n\t# works around shells that cannot unset nonexistent variables.\n\t# Preserve -v and -x to the replacement shell.\n\tBASH_ENV=/dev/null\n\tENV=/dev/null\n\t(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV\n\texport CONFIG_SHELL\n\tcase $- in # ((((\n\t  *v*x* | *x*v* ) as_opts=-vx ;;\n\t  *v* ) as_opts=-v ;;\n\t  *x* ) as_opts=-x ;;\n\t  * ) as_opts= ;;\n\tesac\n\texec \"$CONFIG_SHELL\" $as_opts \"$as_myself\" ${1+\"$@\"}\nfi\n\n    if test x$as_have_required = xno; then :\n  $as_echo \"$0: This script requires a shell more modern than all\"\n  $as_echo \"$0: the shells that I found on your system.\"\n  if test x${ZSH_VERSION+set} = xset ; then\n    $as_echo \"$0: In particular, zsh $ZSH_VERSION has bugs and should\"\n    $as_echo \"$0: be upgraded to zsh 4.3.4 or later.\"\n  else\n    $as_echo \"$0: Please tell bug-autoconf@gnu.org about your system,\n$0: including any error possibly output before this\n$0: message. Then install a modern shell, or manually run\n$0: the script under such a shell if you do have one.\"\n  fi\n  exit 1\nfi\nfi\nfi\nSHELL=${CONFIG_SHELL-/bin/sh}\nexport SHELL\n# Unset more variables known to interfere with behavior of common tools.\nCLICOLOR_FORCE= GREP_OPTIONS=\nunset CLICOLOR_FORCE GREP_OPTIONS\n\n## --------------------- ##\n## M4sh Shell Functions. ##\n## --------------------- ##\n# as_fn_unset VAR\n# ---------------\n# Portably unset VAR.\nas_fn_unset ()\n{\n  { eval $1=; unset $1;}\n}\nas_unset=as_fn_unset\n\n# as_fn_set_status STATUS\n# -----------------------\n# Set $? to STATUS, without forking.\nas_fn_set_status ()\n{\n  return $1\n} # as_fn_set_status\n\n# as_fn_exit STATUS\n# -----------------\n# Exit the shell with STATUS, even in a \"trap 0\" or \"set -e\" context.\nas_fn_exit ()\n{\n  set +e\n  as_fn_set_status $1\n  exit $1\n} # as_fn_exit\n\n# as_fn_mkdir_p\n# -------------\n# Create \"$as_dir\" as a directory, including parents if necessary.\nas_fn_mkdir_p ()\n{\n\n  case $as_dir in #(\n  -*) as_dir=./$as_dir;;\n  esac\n  test -d \"$as_dir\" || eval $as_mkdir_p || {\n    as_dirs=\n    while :; do\n      case $as_dir in #(\n      *\\'*) as_qdir=`$as_echo \"$as_dir\" | sed \"s/'/'\\\\\\\\\\\\\\\\''/g\"`;; #'(\n      *) as_qdir=$as_dir;;\n      esac\n      as_dirs=\"'$as_qdir' $as_dirs\"\n      as_dir=`$as_dirname -- \"$as_dir\" ||\n$as_expr X\"$as_dir\" : 'X\\(.*[^/]\\)//*[^/][^/]*/*$' \\| \\\n\t X\"$as_dir\" : 'X\\(//\\)[^/]' \\| \\\n\t X\"$as_dir\" : 'X\\(//\\)$' \\| \\\n\t X\"$as_dir\" : 'X\\(/\\)' \\| . 2>/dev/null ||\n$as_echo X\"$as_dir\" |\n    sed '/^X\\(.*[^/]\\)\\/\\/*[^/][^/]*\\/*$/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  /^X\\(\\/\\/\\)[^/].*/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  /^X\\(\\/\\/\\)$/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  /^X\\(\\/\\).*/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  s/.*/./; q'`\n      test -d \"$as_dir\" && break\n    done\n    test -z \"$as_dirs\" || eval \"mkdir $as_dirs\"\n  } || test -d \"$as_dir\" || as_fn_error $? \"cannot create directory $as_dir\"\n\n\n} # as_fn_mkdir_p\n# as_fn_append VAR VALUE\n# ----------------------\n# Append the text in VALUE to the end of the definition contained in VAR. Take\n# advantage of any shell optimizations that allow amortized linear growth over\n# repeated appends, instead of the typical quadratic growth present in naive\n# implementations.\nif (eval \"as_var=1; as_var+=2; test x\\$as_var = x12\") 2>/dev/null; then :\n  eval 'as_fn_append ()\n  {\n    eval $1+=\\$2\n  }'\nelse\n  as_fn_append ()\n  {\n    eval $1=\\$$1\\$2\n  }\nfi # as_fn_append\n\n# as_fn_arith ARG...\n# ------------------\n# Perform arithmetic evaluation on the ARGs, and store the result in the\n# global $as_val. Take advantage of shells that can avoid forks. The arguments\n# must be portable across $(()) and expr.\nif (eval \"test \\$(( 1 + 1 )) = 2\") 2>/dev/null; then :\n  eval 'as_fn_arith ()\n  {\n    as_val=$(( $* ))\n  }'\nelse\n  as_fn_arith ()\n  {\n    as_val=`expr \"$@\" || test $? -eq 1`\n  }\nfi # as_fn_arith\n\n\n# as_fn_error STATUS ERROR [LINENO LOG_FD]\n# ----------------------------------------\n# Output \"`basename $0`: error: ERROR\" to stderr. If LINENO and LOG_FD are\n# provided, also output the error to LOG_FD, referencing LINENO. Then exit the\n# script with STATUS, using 1 if that was 0.\nas_fn_error ()\n{\n  as_status=$1; test $as_status -eq 0 && as_status=1\n  if test \"$4\"; then\n    as_lineno=${as_lineno-\"$3\"} as_lineno_stack=as_lineno_stack=$as_lineno_stack\n    $as_echo \"$as_me:${as_lineno-$LINENO}: error: $2\" >&$4\n  fi\n  $as_echo \"$as_me: error: $2\" >&2\n  as_fn_exit $as_status\n} # as_fn_error\n\nif expr a : '\\(a\\)' >/dev/null 2>&1 &&\n   test \"X`expr 00001 : '.*\\(...\\)'`\" = X001; then\n  as_expr=expr\nelse\n  as_expr=false\nfi\n\nif (basename -- /) >/dev/null 2>&1 && test \"X`basename -- / 2>&1`\" = \"X/\"; then\n  as_basename=basename\nelse\n  as_basename=false\nfi\n\nif (as_dir=`dirname -- /` && test \"X$as_dir\" = X/) >/dev/null 2>&1; then\n  as_dirname=dirname\nelse\n  as_dirname=false\nfi\n\nas_me=`$as_basename -- \"$0\" ||\n$as_expr X/\"$0\" : '.*/\\([^/][^/]*\\)/*$' \\| \\\n\t X\"$0\" : 'X\\(//\\)$' \\| \\\n\t X\"$0\" : 'X\\(/\\)' \\| . 2>/dev/null ||\n$as_echo X/\"$0\" |\n    sed '/^.*\\/\\([^/][^/]*\\)\\/*$/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  /^X\\/\\(\\/\\/\\)$/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  /^X\\/\\(\\/\\).*/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  s/.*/./; q'`\n\n# Avoid depending upon Character Ranges.\nas_cr_letters='abcdefghijklmnopqrstuvwxyz'\nas_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'\nas_cr_Letters=$as_cr_letters$as_cr_LETTERS\nas_cr_digits='0123456789'\nas_cr_alnum=$as_cr_Letters$as_cr_digits\n\n\n  as_lineno_1=$LINENO as_lineno_1a=$LINENO\n  as_lineno_2=$LINENO as_lineno_2a=$LINENO\n  eval 'test \"x$as_lineno_1'$as_run'\" != \"x$as_lineno_2'$as_run'\" &&\n  test \"x`expr $as_lineno_1'$as_run' + 1`\" = \"x$as_lineno_2'$as_run'\"' || {\n  # Blame Lee E. McMahon (1931-1989) for sed's syntax.  :-)\n  sed -n '\n    p\n    /[$]LINENO/=\n  ' <$as_myself |\n    sed '\n      s/[$]LINENO.*/&-/\n      t lineno\n      b\n      :lineno\n      N\n      :loop\n      s/[$]LINENO\\([^'$as_cr_alnum'_].*\\n\\)\\(.*\\)/\\2\\1\\2/\n      t loop\n      s/-\\n.*//\n    ' >$as_me.lineno &&\n  chmod +x \"$as_me.lineno\" ||\n    { $as_echo \"$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell\" >&2; as_fn_exit 1; }\n\n  # Don't try to exec as it changes $[0], causing all sort of problems\n  # (the dirname of $[0] is not the place where we might find the\n  # original and so on.  Autoconf is especially sensitive to this).\n  . \"./$as_me.lineno\"\n  # Exit status is that of the last command.\n  exit\n}\n\nECHO_C= ECHO_N= ECHO_T=\ncase `echo -n x` in #(((((\n-n*)\n  case `echo 'xy\\c'` in\n  *c*) ECHO_T='\t';;\t# ECHO_T is single tab character.\n  xy)  ECHO_C='\\c';;\n  *)   echo `echo ksh88 bug on AIX 6.1` > /dev/null\n       ECHO_T='\t';;\n  esac;;\n*)\n  ECHO_N='-n';;\nesac\n\nrm -f conf$$ conf$$.exe conf$$.file\nif test -d conf$$.dir; then\n  rm -f conf$$.dir/conf$$.file\nelse\n  rm -f conf$$.dir\n  mkdir conf$$.dir 2>/dev/null\nfi\nif (echo >conf$$.file) 2>/dev/null; then\n  if ln -s conf$$.file conf$$ 2>/dev/null; then\n    as_ln_s='ln -s'\n    # ... but there are two gotchas:\n    # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.\n    # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.\n    # In both cases, we have to default to `cp -p'.\n    ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||\n      as_ln_s='cp -p'\n  elif ln conf$$.file conf$$ 2>/dev/null; then\n    as_ln_s=ln\n  else\n    as_ln_s='cp -p'\n  fi\nelse\n  as_ln_s='cp -p'\nfi\nrm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file\nrmdir conf$$.dir 2>/dev/null\n\nif mkdir -p . 2>/dev/null; then\n  as_mkdir_p='mkdir -p \"$as_dir\"'\nelse\n  test -d ./-p && rmdir ./-p\n  as_mkdir_p=false\nfi\n\nif test -x / >/dev/null 2>&1; then\n  as_test_x='test -x'\nelse\n  if ls -dL / >/dev/null 2>&1; then\n    as_ls_L_option=L\n  else\n    as_ls_L_option=\n  fi\n  as_test_x='\n    eval sh -c '\\''\n      if test -d \"$1\"; then\n\ttest -d \"$1/.\";\n      else\n\tcase $1 in #(\n\t-*)set \"./$1\";;\n\tesac;\n\tcase `ls -ld'$as_ls_L_option' \"$1\" 2>/dev/null` in #((\n\t???[sx]*):;;*)false;;esac;fi\n    '\\'' sh\n  '\nfi\nas_executable_p=$as_test_x\n\n# Sed expression to map a string onto a valid CPP name.\nas_tr_cpp=\"eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'\"\n\n# Sed expression to map a string onto a valid variable name.\nas_tr_sh=\"eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'\"\n\n\ntest -n \"$DJDIR\" || exec 7<&0 </dev/null\nexec 6>&1\n\n# Name of the host.\n# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status,\n# so uname gets run too.\nac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`\n\n#\n# Initializations.\n#\nac_default_prefix=/usr/local\nac_clean_files=\nac_config_libobj_dir=.\nLIBOBJS=\ncross_compiling=no\nsubdirs=\nMFLAGS=\nMAKEFLAGS=\n\n# Identity of this package.\nPACKAGE_NAME=\nPACKAGE_TARNAME=\nPACKAGE_VERSION=\nPACKAGE_STRING=\nPACKAGE_BUGREPORT=\nPACKAGE_URL=\n\nac_unique_file=\"Makefile.in\"\n# Factoring default headers for most tests.\nac_includes_default=\"\\\n#include <stdio.h>\n#ifdef HAVE_SYS_TYPES_H\n# include <sys/types.h>\n#endif\n#ifdef HAVE_SYS_STAT_H\n# include <sys/stat.h>\n#endif\n#ifdef STDC_HEADERS\n# include <stdlib.h>\n# include <stddef.h>\n#else\n# ifdef HAVE_STDLIB_H\n#  include <stdlib.h>\n# endif\n#endif\n#ifdef HAVE_STRING_H\n# if !defined STDC_HEADERS && defined HAVE_MEMORY_H\n#  include <memory.h>\n# endif\n# include <string.h>\n#endif\n#ifdef HAVE_STRINGS_H\n# include <strings.h>\n#endif\n#ifdef HAVE_INTTYPES_H\n# include <inttypes.h>\n#endif\n#ifdef HAVE_STDINT_H\n# include <stdint.h>\n#endif\n#ifdef HAVE_UNISTD_H\n# include <unistd.h>\n#endif\"\n\nac_subst_vars='LTLIBOBJS\nLIBOBJS\ncfgoutputs_out\ncfgoutputs_in\ncfghdrs_out\ncfghdrs_in\nenable_tls\nenable_lazy_lock\njemalloc_version_gid\njemalloc_version_nrev\njemalloc_version_bugfix\njemalloc_version_minor\njemalloc_version_major\njemalloc_version\nenable_xmalloc\nenable_valgrind\nenable_utrace\nenable_fill\nenable_dss\nenable_munmap\nenable_mremap\nenable_tcache\nenable_prof\nenable_stats\nenable_debug\ninstall_suffix\nenable_experimental\nAUTOCONF\nLD\nAR\nRANLIB\nINSTALL_DATA\nINSTALL_SCRIPT\nINSTALL_PROGRAM\nenable_autogen\nRPATH_EXTRA\nCC_MM\nMKLIB\nLDTARGET\nCTARGET\nPIC_CFLAGS\nSOREV\nEXTRA_LDFLAGS\nDSO_LDFLAGS\nlibprefix\nexe\na\no\nimportlib\nso\nLD_PRELOAD_VAR\nRPATH\nabi\nhost_os\nhost_vendor\nhost_cpu\nhost\nbuild_os\nbuild_vendor\nbuild_cpu\nbuild\nEGREP\nGREP\nCPP\nOBJEXT\nEXEEXT\nac_ct_CC\nCPPFLAGS\nLDFLAGS\nCFLAGS\nCC\nXSLROOT\nXSLTPROC\nMANDIR\nDATADIR\nLIBDIR\nINCLUDEDIR\nBINDIR\nPREFIX\nabs_objroot\nobjroot\nabs_srcroot\nsrcroot\nrev\ntarget_alias\nhost_alias\nbuild_alias\nLIBS\nECHO_T\nECHO_N\nECHO_C\nDEFS\nmandir\nlocaledir\nlibdir\npsdir\npdfdir\ndvidir\nhtmldir\ninfodir\ndocdir\noldincludedir\nincludedir\nlocalstatedir\nsharedstatedir\nsysconfdir\ndatadir\ndatarootdir\nlibexecdir\nsbindir\nbindir\nprogram_transform_name\nprefix\nexec_prefix\nPACKAGE_URL\nPACKAGE_BUGREPORT\nPACKAGE_STRING\nPACKAGE_VERSION\nPACKAGE_TARNAME\nPACKAGE_NAME\nPATH_SEPARATOR\nSHELL'\nac_subst_files=''\nac_user_opts='\nenable_option_checking\nwith_xslroot\nwith_rpath\nenable_autogen\nenable_experimental\nwith_mangling\nwith_jemalloc_prefix\nwith_private_namespace\nwith_install_suffix\nenable_cc_silence\nenable_debug\nenable_stats\nenable_prof\nenable_prof_libunwind\nwith_static_libunwind\nenable_prof_libgcc\nenable_prof_gcc\nenable_tcache\nenable_mremap\nenable_munmap\nenable_dss\nenable_fill\nenable_utrace\nenable_valgrind\nenable_xmalloc\nenable_lazy_lock\nenable_tls\n'\n      ac_precious_vars='build_alias\nhost_alias\ntarget_alias\nCC\nCFLAGS\nLDFLAGS\nLIBS\nCPPFLAGS\nCPP'\n\n\n# Initialize some variables set by options.\nac_init_help=\nac_init_version=false\nac_unrecognized_opts=\nac_unrecognized_sep=\n# The variables have the same names as the options, with\n# dashes changed to underlines.\ncache_file=/dev/null\nexec_prefix=NONE\nno_create=\nno_recursion=\nprefix=NONE\nprogram_prefix=NONE\nprogram_suffix=NONE\nprogram_transform_name=s,x,x,\nsilent=\nsite=\nsrcdir=\nverbose=\nx_includes=NONE\nx_libraries=NONE\n\n# Installation directory options.\n# These are left unexpanded so users can \"make install exec_prefix=/foo\"\n# and all the variables that are supposed to be based on exec_prefix\n# by default will actually change.\n# Use braces instead of parens because sh, perl, etc. also accept them.\n# (The list follows the same order as the GNU Coding Standards.)\nbindir='${exec_prefix}/bin'\nsbindir='${exec_prefix}/sbin'\nlibexecdir='${exec_prefix}/libexec'\ndatarootdir='${prefix}/share'\ndatadir='${datarootdir}'\nsysconfdir='${prefix}/etc'\nsharedstatedir='${prefix}/com'\nlocalstatedir='${prefix}/var'\nincludedir='${prefix}/include'\noldincludedir='/usr/include'\ndocdir='${datarootdir}/doc/${PACKAGE}'\ninfodir='${datarootdir}/info'\nhtmldir='${docdir}'\ndvidir='${docdir}'\npdfdir='${docdir}'\npsdir='${docdir}'\nlibdir='${exec_prefix}/lib'\nlocaledir='${datarootdir}/locale'\nmandir='${datarootdir}/man'\n\nac_prev=\nac_dashdash=\nfor ac_option\ndo\n  # If the previous option needs an argument, assign it.\n  if test -n \"$ac_prev\"; then\n    eval $ac_prev=\\$ac_option\n    ac_prev=\n    continue\n  fi\n\n  case $ac_option in\n  *=?*) ac_optarg=`expr \"X$ac_option\" : '[^=]*=\\(.*\\)'` ;;\n  *=)   ac_optarg= ;;\n  *)    ac_optarg=yes ;;\n  esac\n\n  # Accept the important Cygnus configure options, so we can diagnose typos.\n\n  case $ac_dashdash$ac_option in\n  --)\n    ac_dashdash=yes ;;\n\n  -bindir | --bindir | --bindi | --bind | --bin | --bi)\n    ac_prev=bindir ;;\n  -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)\n    bindir=$ac_optarg ;;\n\n  -build | --build | --buil | --bui | --bu)\n    ac_prev=build_alias ;;\n  -build=* | --build=* | --buil=* | --bui=* | --bu=*)\n    build_alias=$ac_optarg ;;\n\n  -cache-file | --cache-file | --cache-fil | --cache-fi \\\n  | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)\n    ac_prev=cache_file ;;\n  -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \\\n  | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)\n    cache_file=$ac_optarg ;;\n\n  --config-cache | -C)\n    cache_file=config.cache ;;\n\n  -datadir | --datadir | --datadi | --datad)\n    ac_prev=datadir ;;\n  -datadir=* | --datadir=* | --datadi=* | --datad=*)\n    datadir=$ac_optarg ;;\n\n  -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \\\n  | --dataroo | --dataro | --datar)\n    ac_prev=datarootdir ;;\n  -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \\\n  | --dataroot=* | --dataroo=* | --dataro=* | --datar=*)\n    datarootdir=$ac_optarg ;;\n\n  -disable-* | --disable-*)\n    ac_useropt=`expr \"x$ac_option\" : 'x-*disable-\\(.*\\)'`\n    # Reject names that are not valid shell variable names.\n    expr \"x$ac_useropt\" : \".*[^-+._$as_cr_alnum]\" >/dev/null &&\n      as_fn_error $? \"invalid feature name: $ac_useropt\"\n    ac_useropt_orig=$ac_useropt\n    ac_useropt=`$as_echo \"$ac_useropt\" | sed 's/[-+.]/_/g'`\n    case $ac_user_opts in\n      *\"\n\"enable_$ac_useropt\"\n\"*) ;;\n      *) ac_unrecognized_opts=\"$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig\"\n\t ac_unrecognized_sep=', ';;\n    esac\n    eval enable_$ac_useropt=no ;;\n\n  -docdir | --docdir | --docdi | --doc | --do)\n    ac_prev=docdir ;;\n  -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*)\n    docdir=$ac_optarg ;;\n\n  -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv)\n    ac_prev=dvidir ;;\n  -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*)\n    dvidir=$ac_optarg ;;\n\n  -enable-* | --enable-*)\n    ac_useropt=`expr \"x$ac_option\" : 'x-*enable-\\([^=]*\\)'`\n    # Reject names that are not valid shell variable names.\n    expr \"x$ac_useropt\" : \".*[^-+._$as_cr_alnum]\" >/dev/null &&\n      as_fn_error $? \"invalid feature name: $ac_useropt\"\n    ac_useropt_orig=$ac_useropt\n    ac_useropt=`$as_echo \"$ac_useropt\" | sed 's/[-+.]/_/g'`\n    case $ac_user_opts in\n      *\"\n\"enable_$ac_useropt\"\n\"*) ;;\n      *) ac_unrecognized_opts=\"$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig\"\n\t ac_unrecognized_sep=', ';;\n    esac\n    eval enable_$ac_useropt=\\$ac_optarg ;;\n\n  -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \\\n  | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \\\n  | --exec | --exe | --ex)\n    ac_prev=exec_prefix ;;\n  -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \\\n  | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \\\n  | --exec=* | --exe=* | --ex=*)\n    exec_prefix=$ac_optarg ;;\n\n  -gas | --gas | --ga | --g)\n    # Obsolete; use --with-gas.\n    with_gas=yes ;;\n\n  -help | --help | --hel | --he | -h)\n    ac_init_help=long ;;\n  -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)\n    ac_init_help=recursive ;;\n  -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)\n    ac_init_help=short ;;\n\n  -host | --host | --hos | --ho)\n    ac_prev=host_alias ;;\n  -host=* | --host=* | --hos=* | --ho=*)\n    host_alias=$ac_optarg ;;\n\n  -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht)\n    ac_prev=htmldir ;;\n  -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \\\n  | --ht=*)\n    htmldir=$ac_optarg ;;\n\n  -includedir | --includedir | --includedi | --included | --include \\\n  | --includ | --inclu | --incl | --inc)\n    ac_prev=includedir ;;\n  -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \\\n  | --includ=* | --inclu=* | --incl=* | --inc=*)\n    includedir=$ac_optarg ;;\n\n  -infodir | --infodir | --infodi | --infod | --info | --inf)\n    ac_prev=infodir ;;\n  -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)\n    infodir=$ac_optarg ;;\n\n  -libdir | --libdir | --libdi | --libd)\n    ac_prev=libdir ;;\n  -libdir=* | --libdir=* | --libdi=* | --libd=*)\n    libdir=$ac_optarg ;;\n\n  -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \\\n  | --libexe | --libex | --libe)\n    ac_prev=libexecdir ;;\n  -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \\\n  | --libexe=* | --libex=* | --libe=*)\n    libexecdir=$ac_optarg ;;\n\n  -localedir | --localedir | --localedi | --localed | --locale)\n    ac_prev=localedir ;;\n  -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*)\n    localedir=$ac_optarg ;;\n\n  -localstatedir | --localstatedir | --localstatedi | --localstated \\\n  | --localstate | --localstat | --localsta | --localst | --locals)\n    ac_prev=localstatedir ;;\n  -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \\\n  | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*)\n    localstatedir=$ac_optarg ;;\n\n  -mandir | --mandir | --mandi | --mand | --man | --ma | --m)\n    ac_prev=mandir ;;\n  -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)\n    mandir=$ac_optarg ;;\n\n  -nfp | --nfp | --nf)\n    # Obsolete; use --without-fp.\n    with_fp=no ;;\n\n  -no-create | --no-create | --no-creat | --no-crea | --no-cre \\\n  | --no-cr | --no-c | -n)\n    no_create=yes ;;\n\n  -no-recursion | --no-recursion | --no-recursio | --no-recursi \\\n  | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)\n    no_recursion=yes ;;\n\n  -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \\\n  | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \\\n  | --oldin | --oldi | --old | --ol | --o)\n    ac_prev=oldincludedir ;;\n  -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \\\n  | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \\\n  | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)\n    oldincludedir=$ac_optarg ;;\n\n  -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)\n    ac_prev=prefix ;;\n  -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)\n    prefix=$ac_optarg ;;\n\n  -program-prefix | --program-prefix | --program-prefi | --program-pref \\\n  | --program-pre | --program-pr | --program-p)\n    ac_prev=program_prefix ;;\n  -program-prefix=* | --program-prefix=* | --program-prefi=* \\\n  | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)\n    program_prefix=$ac_optarg ;;\n\n  -program-suffix | --program-suffix | --program-suffi | --program-suff \\\n  | --program-suf | --program-su | --program-s)\n    ac_prev=program_suffix ;;\n  -program-suffix=* | --program-suffix=* | --program-suffi=* \\\n  | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)\n    program_suffix=$ac_optarg ;;\n\n  -program-transform-name | --program-transform-name \\\n  | --program-transform-nam | --program-transform-na \\\n  | --program-transform-n | --program-transform- \\\n  | --program-transform | --program-transfor \\\n  | --program-transfo | --program-transf \\\n  | --program-trans | --program-tran \\\n  | --progr-tra | --program-tr | --program-t)\n    ac_prev=program_transform_name ;;\n  -program-transform-name=* | --program-transform-name=* \\\n  | --program-transform-nam=* | --program-transform-na=* \\\n  | --program-transform-n=* | --program-transform-=* \\\n  | --program-transform=* | --program-transfor=* \\\n  | --program-transfo=* | --program-transf=* \\\n  | --program-trans=* | --program-tran=* \\\n  | --progr-tra=* | --program-tr=* | --program-t=*)\n    program_transform_name=$ac_optarg ;;\n\n  -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd)\n    ac_prev=pdfdir ;;\n  -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*)\n    pdfdir=$ac_optarg ;;\n\n  -psdir | --psdir | --psdi | --psd | --ps)\n    ac_prev=psdir ;;\n  -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*)\n    psdir=$ac_optarg ;;\n\n  -q | -quiet | --quiet | --quie | --qui | --qu | --q \\\n  | -silent | --silent | --silen | --sile | --sil)\n    silent=yes ;;\n\n  -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)\n    ac_prev=sbindir ;;\n  -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \\\n  | --sbi=* | --sb=*)\n    sbindir=$ac_optarg ;;\n\n  -sharedstatedir | --sharedstatedir | --sharedstatedi \\\n  | --sharedstated | --sharedstate | --sharedstat | --sharedsta \\\n  | --sharedst | --shareds | --shared | --share | --shar \\\n  | --sha | --sh)\n    ac_prev=sharedstatedir ;;\n  -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \\\n  | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \\\n  | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \\\n  | --sha=* | --sh=*)\n    sharedstatedir=$ac_optarg ;;\n\n  -site | --site | --sit)\n    ac_prev=site ;;\n  -site=* | --site=* | --sit=*)\n    site=$ac_optarg ;;\n\n  -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)\n    ac_prev=srcdir ;;\n  -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)\n    srcdir=$ac_optarg ;;\n\n  -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \\\n  | --syscon | --sysco | --sysc | --sys | --sy)\n    ac_prev=sysconfdir ;;\n  -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \\\n  | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)\n    sysconfdir=$ac_optarg ;;\n\n  -target | --target | --targe | --targ | --tar | --ta | --t)\n    ac_prev=target_alias ;;\n  -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)\n    target_alias=$ac_optarg ;;\n\n  -v | -verbose | --verbose | --verbos | --verbo | --verb)\n    verbose=yes ;;\n\n  -version | --version | --versio | --versi | --vers | -V)\n    ac_init_version=: ;;\n\n  -with-* | --with-*)\n    ac_useropt=`expr \"x$ac_option\" : 'x-*with-\\([^=]*\\)'`\n    # Reject names that are not valid shell variable names.\n    expr \"x$ac_useropt\" : \".*[^-+._$as_cr_alnum]\" >/dev/null &&\n      as_fn_error $? \"invalid package name: $ac_useropt\"\n    ac_useropt_orig=$ac_useropt\n    ac_useropt=`$as_echo \"$ac_useropt\" | sed 's/[-+.]/_/g'`\n    case $ac_user_opts in\n      *\"\n\"with_$ac_useropt\"\n\"*) ;;\n      *) ac_unrecognized_opts=\"$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig\"\n\t ac_unrecognized_sep=', ';;\n    esac\n    eval with_$ac_useropt=\\$ac_optarg ;;\n\n  -without-* | --without-*)\n    ac_useropt=`expr \"x$ac_option\" : 'x-*without-\\(.*\\)'`\n    # Reject names that are not valid shell variable names.\n    expr \"x$ac_useropt\" : \".*[^-+._$as_cr_alnum]\" >/dev/null &&\n      as_fn_error $? \"invalid package name: $ac_useropt\"\n    ac_useropt_orig=$ac_useropt\n    ac_useropt=`$as_echo \"$ac_useropt\" | sed 's/[-+.]/_/g'`\n    case $ac_user_opts in\n      *\"\n\"with_$ac_useropt\"\n\"*) ;;\n      *) ac_unrecognized_opts=\"$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig\"\n\t ac_unrecognized_sep=', ';;\n    esac\n    eval with_$ac_useropt=no ;;\n\n  --x)\n    # Obsolete; use --with-x.\n    with_x=yes ;;\n\n  -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \\\n  | --x-incl | --x-inc | --x-in | --x-i)\n    ac_prev=x_includes ;;\n  -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \\\n  | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)\n    x_includes=$ac_optarg ;;\n\n  -x-libraries | --x-libraries | --x-librarie | --x-librari \\\n  | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)\n    ac_prev=x_libraries ;;\n  -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \\\n  | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)\n    x_libraries=$ac_optarg ;;\n\n  -*) as_fn_error $? \"unrecognized option: \\`$ac_option'\nTry \\`$0 --help' for more information\"\n    ;;\n\n  *=*)\n    ac_envvar=`expr \"x$ac_option\" : 'x\\([^=]*\\)='`\n    # Reject names that are not valid shell variable names.\n    case $ac_envvar in #(\n      '' | [0-9]* | *[!_$as_cr_alnum]* )\n      as_fn_error $? \"invalid variable name: \\`$ac_envvar'\" ;;\n    esac\n    eval $ac_envvar=\\$ac_optarg\n    export $ac_envvar ;;\n\n  *)\n    # FIXME: should be removed in autoconf 3.0.\n    $as_echo \"$as_me: WARNING: you should use --build, --host, --target\" >&2\n    expr \"x$ac_option\" : \".*[^-._$as_cr_alnum]\" >/dev/null &&\n      $as_echo \"$as_me: WARNING: invalid host type: $ac_option\" >&2\n    : \"${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}\"\n    ;;\n\n  esac\ndone\n\nif test -n \"$ac_prev\"; then\n  ac_option=--`echo $ac_prev | sed 's/_/-/g'`\n  as_fn_error $? \"missing argument to $ac_option\"\nfi\n\nif test -n \"$ac_unrecognized_opts\"; then\n  case $enable_option_checking in\n    no) ;;\n    fatal) as_fn_error $? \"unrecognized options: $ac_unrecognized_opts\" ;;\n    *)     $as_echo \"$as_me: WARNING: unrecognized options: $ac_unrecognized_opts\" >&2 ;;\n  esac\nfi\n\n# Check all directory arguments for consistency.\nfor ac_var in\texec_prefix prefix bindir sbindir libexecdir datarootdir \\\n\t\tdatadir sysconfdir sharedstatedir localstatedir includedir \\\n\t\toldincludedir docdir infodir htmldir dvidir pdfdir psdir \\\n\t\tlibdir localedir mandir\ndo\n  eval ac_val=\\$$ac_var\n  # Remove trailing slashes.\n  case $ac_val in\n    */ )\n      ac_val=`expr \"X$ac_val\" : 'X\\(.*[^/]\\)' \\| \"X$ac_val\" : 'X\\(.*\\)'`\n      eval $ac_var=\\$ac_val;;\n  esac\n  # Be sure to have absolute directory names.\n  case $ac_val in\n    [\\\\/$]* | ?:[\\\\/]* )  continue;;\n    NONE | '' ) case $ac_var in *prefix ) continue;; esac;;\n  esac\n  as_fn_error $? \"expected an absolute directory name for --$ac_var: $ac_val\"\ndone\n\n# There might be people who depend on the old broken behavior: `$host'\n# used to hold the argument of --host etc.\n# FIXME: To remove some day.\nbuild=$build_alias\nhost=$host_alias\ntarget=$target_alias\n\n# FIXME: To remove some day.\nif test \"x$host_alias\" != x; then\n  if test \"x$build_alias\" = x; then\n    cross_compiling=maybe\n    $as_echo \"$as_me: WARNING: if you wanted to set the --build type, don't use --host.\n    If a cross compiler is detected then cross compile mode will be used\" >&2\n  elif test \"x$build_alias\" != \"x$host_alias\"; then\n    cross_compiling=yes\n  fi\nfi\n\nac_tool_prefix=\ntest -n \"$host_alias\" && ac_tool_prefix=$host_alias-\n\ntest \"$silent\" = yes && exec 6>/dev/null\n\n\nac_pwd=`pwd` && test -n \"$ac_pwd\" &&\nac_ls_di=`ls -di .` &&\nac_pwd_ls_di=`cd \"$ac_pwd\" && ls -di .` ||\n  as_fn_error $? \"working directory cannot be determined\"\ntest \"X$ac_ls_di\" = \"X$ac_pwd_ls_di\" ||\n  as_fn_error $? \"pwd does not report name of working directory\"\n\n\n# Find the source files, if location was not specified.\nif test -z \"$srcdir\"; then\n  ac_srcdir_defaulted=yes\n  # Try the directory containing this script, then the parent directory.\n  ac_confdir=`$as_dirname -- \"$as_myself\" ||\n$as_expr X\"$as_myself\" : 'X\\(.*[^/]\\)//*[^/][^/]*/*$' \\| \\\n\t X\"$as_myself\" : 'X\\(//\\)[^/]' \\| \\\n\t X\"$as_myself\" : 'X\\(//\\)$' \\| \\\n\t X\"$as_myself\" : 'X\\(/\\)' \\| . 2>/dev/null ||\n$as_echo X\"$as_myself\" |\n    sed '/^X\\(.*[^/]\\)\\/\\/*[^/][^/]*\\/*$/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  /^X\\(\\/\\/\\)[^/].*/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  /^X\\(\\/\\/\\)$/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  /^X\\(\\/\\).*/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  s/.*/./; q'`\n  srcdir=$ac_confdir\n  if test ! -r \"$srcdir/$ac_unique_file\"; then\n    srcdir=..\n  fi\nelse\n  ac_srcdir_defaulted=no\nfi\nif test ! -r \"$srcdir/$ac_unique_file\"; then\n  test \"$ac_srcdir_defaulted\" = yes && srcdir=\"$ac_confdir or ..\"\n  as_fn_error $? \"cannot find sources ($ac_unique_file) in $srcdir\"\nfi\nac_msg=\"sources are in $srcdir, but \\`cd $srcdir' does not work\"\nac_abs_confdir=`(\n\tcd \"$srcdir\" && test -r \"./$ac_unique_file\" || as_fn_error $? \"$ac_msg\"\n\tpwd)`\n# When building in place, set srcdir=.\nif test \"$ac_abs_confdir\" = \"$ac_pwd\"; then\n  srcdir=.\nfi\n# Remove unnecessary trailing slashes from srcdir.\n# Double slashes in file names in object file debugging info\n# mess up M-x gdb in Emacs.\ncase $srcdir in\n*/) srcdir=`expr \"X$srcdir\" : 'X\\(.*[^/]\\)' \\| \"X$srcdir\" : 'X\\(.*\\)'`;;\nesac\nfor ac_var in $ac_precious_vars; do\n  eval ac_env_${ac_var}_set=\\${${ac_var}+set}\n  eval ac_env_${ac_var}_value=\\$${ac_var}\n  eval ac_cv_env_${ac_var}_set=\\${${ac_var}+set}\n  eval ac_cv_env_${ac_var}_value=\\$${ac_var}\ndone\n\n#\n# Report the --help message.\n#\nif test \"$ac_init_help\" = \"long\"; then\n  # Omit some internal or obsolete options to make the list less imposing.\n  # This message is too long to be a string in the A/UX 3.1 sh.\n  cat <<_ACEOF\n\\`configure' configures this package to adapt to many kinds of systems.\n\nUsage: $0 [OPTION]... [VAR=VALUE]...\n\nTo assign environment variables (e.g., CC, CFLAGS...), specify them as\nVAR=VALUE.  See below for descriptions of some of the useful variables.\n\nDefaults for the options are specified in brackets.\n\nConfiguration:\n  -h, --help              display this help and exit\n      --help=short        display options specific to this package\n      --help=recursive    display the short help of all the included packages\n  -V, --version           display version information and exit\n  -q, --quiet, --silent   do not print \\`checking ...' messages\n      --cache-file=FILE   cache test results in FILE [disabled]\n  -C, --config-cache      alias for \\`--cache-file=config.cache'\n  -n, --no-create         do not create output files\n      --srcdir=DIR        find the sources in DIR [configure dir or \\`..']\n\nInstallation directories:\n  --prefix=PREFIX         install architecture-independent files in PREFIX\n                          [$ac_default_prefix]\n  --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX\n                          [PREFIX]\n\nBy default, \\`make install' will install all the files in\n\\`$ac_default_prefix/bin', \\`$ac_default_prefix/lib' etc.  You can specify\nan installation prefix other than \\`$ac_default_prefix' using \\`--prefix',\nfor instance \\`--prefix=\\$HOME'.\n\nFor better control, use the options below.\n\nFine tuning of the installation directories:\n  --bindir=DIR            user executables [EPREFIX/bin]\n  --sbindir=DIR           system admin executables [EPREFIX/sbin]\n  --libexecdir=DIR        program executables [EPREFIX/libexec]\n  --sysconfdir=DIR        read-only single-machine data [PREFIX/etc]\n  --sharedstatedir=DIR    modifiable architecture-independent data [PREFIX/com]\n  --localstatedir=DIR     modifiable single-machine data [PREFIX/var]\n  --libdir=DIR            object code libraries [EPREFIX/lib]\n  --includedir=DIR        C header files [PREFIX/include]\n  --oldincludedir=DIR     C header files for non-gcc [/usr/include]\n  --datarootdir=DIR       read-only arch.-independent data root [PREFIX/share]\n  --datadir=DIR           read-only architecture-independent data [DATAROOTDIR]\n  --infodir=DIR           info documentation [DATAROOTDIR/info]\n  --localedir=DIR         locale-dependent data [DATAROOTDIR/locale]\n  --mandir=DIR            man documentation [DATAROOTDIR/man]\n  --docdir=DIR            documentation root [DATAROOTDIR/doc/PACKAGE]\n  --htmldir=DIR           html documentation [DOCDIR]\n  --dvidir=DIR            dvi documentation [DOCDIR]\n  --pdfdir=DIR            pdf documentation [DOCDIR]\n  --psdir=DIR             ps documentation [DOCDIR]\n_ACEOF\n\n  cat <<\\_ACEOF\n\nSystem types:\n  --build=BUILD     configure for building on BUILD [guessed]\n  --host=HOST       cross-compile to build programs to run on HOST [BUILD]\n_ACEOF\nfi\n\nif test -n \"$ac_init_help\"; then\n\n  cat <<\\_ACEOF\n\nOptional Features:\n  --disable-option-checking  ignore unrecognized --enable/--with options\n  --disable-FEATURE       do not include FEATURE (same as --enable-FEATURE=no)\n  --enable-FEATURE[=ARG]  include FEATURE [ARG=yes]\n  --enable-autogen        Automatically regenerate configure output\n  --disable-experimental  Disable support for the experimental API\n  --enable-cc-silence     Silence irrelevant compiler warnings\n  --enable-debug          Build debugging code\n  --disable-stats         Disable statistics calculation/reporting\n  --enable-prof           Enable allocation profiling\n  --enable-prof-libunwind Use libunwind for backtracing\n  --disable-prof-libgcc   Do not use libgcc for backtracing\n  --disable-prof-gcc      Do not use gcc intrinsics for backtracing\n  --disable-tcache        Disable per thread caches\n  --enable-mremap         Enable mremap(2) for huge realloc()\n  --disable-munmap        Disable VM deallocation via munmap(2)\n  --enable-dss            Enable allocation from DSS\n  --disable-fill          Disable support for junk/zero filling, quarantine,\n                          and redzones\n  --enable-utrace         Enable utrace(2)-based tracing\n  --disable-valgrind      Disable support for Valgrind\n  --enable-xmalloc        Support xmalloc option\n  --enable-lazy-lock      Enable lazy locking (only lock when multi-threaded)\n  --disable-tls           Disable thread-local storage (__thread keyword)\n\nOptional Packages:\n  --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]\n  --without-PACKAGE       do not use PACKAGE (same as --with-PACKAGE=no)\n  --with-xslroot=<path>   XSL stylesheet root path\n  --with-rpath=<rpath>    Colon-separated rpath (ELF systems only)\n  --with-mangling=<map>   Mangle symbols in <map>\n  --with-jemalloc-prefix=<prefix>\n                          Prefix to prepend to all public APIs\n  --with-private-namespace=<prefix>\n                          Prefix to prepend to all library-private APIs\n  --with-install-suffix=<suffix>\n                          Suffix to append to all installed files\n  --with-static-libunwind=<libunwind.a>\n                          Path to static libunwind library; use rather than\n                          dynamically linking\n\nSome influential environment variables:\n  CC          C compiler command\n  CFLAGS      C compiler flags\n  LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a\n              nonstandard directory <lib dir>\n  LIBS        libraries to pass to the linker, e.g. -l<library>\n  CPPFLAGS    (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if\n              you have headers in a nonstandard directory <include dir>\n  CPP         C preprocessor\n\nUse these variables to override the choices made by `configure' or to help\nit to find libraries and programs with nonstandard names/locations.\n\nReport bugs to the package provider.\n_ACEOF\nac_status=$?\nfi\n\nif test \"$ac_init_help\" = \"recursive\"; then\n  # If there are subdirs, report their specific --help.\n  for ac_dir in : $ac_subdirs_all; do test \"x$ac_dir\" = x: && continue\n    test -d \"$ac_dir\" ||\n      { cd \"$srcdir\" && ac_pwd=`pwd` && srcdir=. && test -d \"$ac_dir\"; } ||\n      continue\n    ac_builddir=.\n\ncase \"$ac_dir\" in\n.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;\n*)\n  ac_dir_suffix=/`$as_echo \"$ac_dir\" | sed 's|^\\.[\\\\/]||'`\n  # A \"..\" for each directory in $ac_dir_suffix.\n  ac_top_builddir_sub=`$as_echo \"$ac_dir_suffix\" | sed 's|/[^\\\\/]*|/..|g;s|/||'`\n  case $ac_top_builddir_sub in\n  \"\") ac_top_builddir_sub=. ac_top_build_prefix= ;;\n  *)  ac_top_build_prefix=$ac_top_builddir_sub/ ;;\n  esac ;;\nesac\nac_abs_top_builddir=$ac_pwd\nac_abs_builddir=$ac_pwd$ac_dir_suffix\n# for backward compatibility:\nac_top_builddir=$ac_top_build_prefix\n\ncase $srcdir in\n  .)  # We are building in place.\n    ac_srcdir=.\n    ac_top_srcdir=$ac_top_builddir_sub\n    ac_abs_top_srcdir=$ac_pwd ;;\n  [\\\\/]* | ?:[\\\\/]* )  # Absolute name.\n    ac_srcdir=$srcdir$ac_dir_suffix;\n    ac_top_srcdir=$srcdir\n    ac_abs_top_srcdir=$srcdir ;;\n  *) # Relative name.\n    ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix\n    ac_top_srcdir=$ac_top_build_prefix$srcdir\n    ac_abs_top_srcdir=$ac_pwd/$srcdir ;;\nesac\nac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix\n\n    cd \"$ac_dir\" || { ac_status=$?; continue; }\n    # Check for guested configure.\n    if test -f \"$ac_srcdir/configure.gnu\"; then\n      echo &&\n      $SHELL \"$ac_srcdir/configure.gnu\" --help=recursive\n    elif test -f \"$ac_srcdir/configure\"; then\n      echo &&\n      $SHELL \"$ac_srcdir/configure\" --help=recursive\n    else\n      $as_echo \"$as_me: WARNING: no configuration information is in $ac_dir\" >&2\n    fi || ac_status=$?\n    cd \"$ac_pwd\" || { ac_status=$?; break; }\n  done\nfi\n\ntest -n \"$ac_init_help\" && exit $ac_status\nif $ac_init_version; then\n  cat <<\\_ACEOF\nconfigure\ngenerated by GNU Autoconf 2.68\n\nCopyright (C) 2010 Free Software Foundation, Inc.\nThis configure script is free software; the Free Software Foundation\ngives unlimited permission to copy, distribute and modify it.\n_ACEOF\n  exit\nfi\n\n## ------------------------ ##\n## Autoconf initialization. ##\n## ------------------------ ##\n\n# ac_fn_c_try_compile LINENO\n# --------------------------\n# Try to compile conftest.$ac_ext, and return whether this succeeded.\nac_fn_c_try_compile ()\n{\n  as_lineno=${as_lineno-\"$1\"} as_lineno_stack=as_lineno_stack=$as_lineno_stack\n  rm -f conftest.$ac_objext\n  if { { ac_try=\"$ac_compile\"\ncase \"(($ac_try\" in\n  *\\\"* | *\\`* | *\\\\*) ac_try_echo=\\$ac_try;;\n  *) ac_try_echo=$ac_try;;\nesac\neval ac_try_echo=\"\\\"\\$as_me:${as_lineno-$LINENO}: $ac_try_echo\\\"\"\n$as_echo \"$ac_try_echo\"; } >&5\n  (eval \"$ac_compile\") 2>conftest.err\n  ac_status=$?\n  if test -s conftest.err; then\n    grep -v '^ *+' conftest.err >conftest.er1\n    cat conftest.er1 >&5\n    mv -f conftest.er1 conftest.err\n  fi\n  $as_echo \"$as_me:${as_lineno-$LINENO}: \\$? = $ac_status\" >&5\n  test $ac_status = 0; } && {\n\t test -z \"$ac_c_werror_flag\" ||\n\t test ! -s conftest.err\n       } && test -s conftest.$ac_objext; then :\n  ac_retval=0\nelse\n  $as_echo \"$as_me: failed program was:\" >&5\nsed 's/^/| /' conftest.$ac_ext >&5\n\n\tac_retval=1\nfi\n  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno\n  as_fn_set_status $ac_retval\n\n} # ac_fn_c_try_compile\n\n# ac_fn_c_try_cpp LINENO\n# ----------------------\n# Try to preprocess conftest.$ac_ext, and return whether this succeeded.\nac_fn_c_try_cpp ()\n{\n  as_lineno=${as_lineno-\"$1\"} as_lineno_stack=as_lineno_stack=$as_lineno_stack\n  if { { ac_try=\"$ac_cpp conftest.$ac_ext\"\ncase \"(($ac_try\" in\n  *\\\"* | *\\`* | *\\\\*) ac_try_echo=\\$ac_try;;\n  *) ac_try_echo=$ac_try;;\nesac\neval ac_try_echo=\"\\\"\\$as_me:${as_lineno-$LINENO}: $ac_try_echo\\\"\"\n$as_echo \"$ac_try_echo\"; } >&5\n  (eval \"$ac_cpp conftest.$ac_ext\") 2>conftest.err\n  ac_status=$?\n  if test -s conftest.err; then\n    grep -v '^ *+' conftest.err >conftest.er1\n    cat conftest.er1 >&5\n    mv -f conftest.er1 conftest.err\n  fi\n  $as_echo \"$as_me:${as_lineno-$LINENO}: \\$? = $ac_status\" >&5\n  test $ac_status = 0; } > conftest.i && {\n\t test -z \"$ac_c_preproc_warn_flag$ac_c_werror_flag\" ||\n\t test ! -s conftest.err\n       }; then :\n  ac_retval=0\nelse\n  $as_echo \"$as_me: failed program was:\" >&5\nsed 's/^/| /' conftest.$ac_ext >&5\n\n    ac_retval=1\nfi\n  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno\n  as_fn_set_status $ac_retval\n\n} # ac_fn_c_try_cpp\n\n# ac_fn_c_try_run LINENO\n# ----------------------\n# Try to link conftest.$ac_ext, and return whether this succeeded. Assumes\n# that executables *can* be run.\nac_fn_c_try_run ()\n{\n  as_lineno=${as_lineno-\"$1\"} as_lineno_stack=as_lineno_stack=$as_lineno_stack\n  if { { ac_try=\"$ac_link\"\ncase \"(($ac_try\" in\n  *\\\"* | *\\`* | *\\\\*) ac_try_echo=\\$ac_try;;\n  *) ac_try_echo=$ac_try;;\nesac\neval ac_try_echo=\"\\\"\\$as_me:${as_lineno-$LINENO}: $ac_try_echo\\\"\"\n$as_echo \"$ac_try_echo\"; } >&5\n  (eval \"$ac_link\") 2>&5\n  ac_status=$?\n  $as_echo \"$as_me:${as_lineno-$LINENO}: \\$? = $ac_status\" >&5\n  test $ac_status = 0; } && { ac_try='./conftest$ac_exeext'\n  { { case \"(($ac_try\" in\n  *\\\"* | *\\`* | *\\\\*) ac_try_echo=\\$ac_try;;\n  *) ac_try_echo=$ac_try;;\nesac\neval ac_try_echo=\"\\\"\\$as_me:${as_lineno-$LINENO}: $ac_try_echo\\\"\"\n$as_echo \"$ac_try_echo\"; } >&5\n  (eval \"$ac_try\") 2>&5\n  ac_status=$?\n  $as_echo \"$as_me:${as_lineno-$LINENO}: \\$? = $ac_status\" >&5\n  test $ac_status = 0; }; }; then :\n  ac_retval=0\nelse\n  $as_echo \"$as_me: program exited with status $ac_status\" >&5\n       $as_echo \"$as_me: failed program was:\" >&5\nsed 's/^/| /' conftest.$ac_ext >&5\n\n       ac_retval=$ac_status\nfi\n  rm -rf conftest.dSYM conftest_ipa8_conftest.oo\n  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno\n  as_fn_set_status $ac_retval\n\n} # ac_fn_c_try_run\n\n# ac_fn_c_compute_int LINENO EXPR VAR INCLUDES\n# --------------------------------------------\n# Tries to find the compile-time value of EXPR in a program that includes\n# INCLUDES, setting VAR accordingly. Returns whether the value could be\n# computed\nac_fn_c_compute_int ()\n{\n  as_lineno=${as_lineno-\"$1\"} as_lineno_stack=as_lineno_stack=$as_lineno_stack\n  if test \"$cross_compiling\" = yes; then\n    # Depending upon the size, compute the lo and hi bounds.\ncat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n$4\nint\nmain ()\n{\nstatic int test_array [1 - 2 * !(($2) >= 0)];\ntest_array [0] = 0\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  ac_lo=0 ac_mid=0\n  while :; do\n    cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n$4\nint\nmain ()\n{\nstatic int test_array [1 - 2 * !(($2) <= $ac_mid)];\ntest_array [0] = 0\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  ac_hi=$ac_mid; break\nelse\n  as_fn_arith $ac_mid + 1 && ac_lo=$as_val\n\t\t\tif test $ac_lo -le $ac_mid; then\n\t\t\t  ac_lo= ac_hi=\n\t\t\t  break\n\t\t\tfi\n\t\t\tas_fn_arith 2 '*' $ac_mid + 1 && ac_mid=$as_val\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\n  done\nelse\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n$4\nint\nmain ()\n{\nstatic int test_array [1 - 2 * !(($2) < 0)];\ntest_array [0] = 0\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  ac_hi=-1 ac_mid=-1\n  while :; do\n    cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n$4\nint\nmain ()\n{\nstatic int test_array [1 - 2 * !(($2) >= $ac_mid)];\ntest_array [0] = 0\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  ac_lo=$ac_mid; break\nelse\n  as_fn_arith '(' $ac_mid ')' - 1 && ac_hi=$as_val\n\t\t\tif test $ac_mid -le $ac_hi; then\n\t\t\t  ac_lo= ac_hi=\n\t\t\t  break\n\t\t\tfi\n\t\t\tas_fn_arith 2 '*' $ac_mid && ac_mid=$as_val\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\n  done\nelse\n  ac_lo= ac_hi=\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\n# Binary search between lo and hi bounds.\nwhile test \"x$ac_lo\" != \"x$ac_hi\"; do\n  as_fn_arith '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo && ac_mid=$as_val\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n$4\nint\nmain ()\n{\nstatic int test_array [1 - 2 * !(($2) <= $ac_mid)];\ntest_array [0] = 0\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  ac_hi=$ac_mid\nelse\n  as_fn_arith '(' $ac_mid ')' + 1 && ac_lo=$as_val\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\ndone\ncase $ac_lo in #((\n?*) eval \"$3=\\$ac_lo\"; ac_retval=0 ;;\n'') ac_retval=1 ;;\nesac\n  else\n    cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n$4\nstatic long int longval () { return $2; }\nstatic unsigned long int ulongval () { return $2; }\n#include <stdio.h>\n#include <stdlib.h>\nint\nmain ()\n{\n\n  FILE *f = fopen (\"conftest.val\", \"w\");\n  if (! f)\n    return 1;\n  if (($2) < 0)\n    {\n      long int i = longval ();\n      if (i != ($2))\n\treturn 1;\n      fprintf (f, \"%ld\", i);\n    }\n  else\n    {\n      unsigned long int i = ulongval ();\n      if (i != ($2))\n\treturn 1;\n      fprintf (f, \"%lu\", i);\n    }\n  /* Do not output a trailing newline, as this causes \\r\\n confusion\n     on some platforms.  */\n  return ferror (f) || fclose (f) != 0;\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_run \"$LINENO\"; then :\n  echo >>conftest.val; read $3 <conftest.val; ac_retval=0\nelse\n  ac_retval=1\nfi\nrm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \\\n  conftest.$ac_objext conftest.beam conftest.$ac_ext\nrm -f conftest.val\n\n  fi\n  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno\n  as_fn_set_status $ac_retval\n\n} # ac_fn_c_compute_int\n\n# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES\n# -------------------------------------------------------\n# Tests whether HEADER exists and can be compiled using the include files in\n# INCLUDES, setting the cache variable VAR accordingly.\nac_fn_c_check_header_compile ()\n{\n  as_lineno=${as_lineno-\"$1\"} as_lineno_stack=as_lineno_stack=$as_lineno_stack\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $2\" >&5\n$as_echo_n \"checking for $2... \" >&6; }\nif eval \\${$3+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n$4\n#include <$2>\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  eval \"$3=yes\"\nelse\n  eval \"$3=no\"\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\nfi\neval ac_res=\\$$3\n\t       { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_res\" >&5\n$as_echo \"$ac_res\" >&6; }\n  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno\n\n} # ac_fn_c_check_header_compile\n\n# ac_fn_c_try_link LINENO\n# -----------------------\n# Try to link conftest.$ac_ext, and return whether this succeeded.\nac_fn_c_try_link ()\n{\n  as_lineno=${as_lineno-\"$1\"} as_lineno_stack=as_lineno_stack=$as_lineno_stack\n  rm -f conftest.$ac_objext conftest$ac_exeext\n  if { { ac_try=\"$ac_link\"\ncase \"(($ac_try\" in\n  *\\\"* | *\\`* | *\\\\*) ac_try_echo=\\$ac_try;;\n  *) ac_try_echo=$ac_try;;\nesac\neval ac_try_echo=\"\\\"\\$as_me:${as_lineno-$LINENO}: $ac_try_echo\\\"\"\n$as_echo \"$ac_try_echo\"; } >&5\n  (eval \"$ac_link\") 2>conftest.err\n  ac_status=$?\n  if test -s conftest.err; then\n    grep -v '^ *+' conftest.err >conftest.er1\n    cat conftest.er1 >&5\n    mv -f conftest.er1 conftest.err\n  fi\n  $as_echo \"$as_me:${as_lineno-$LINENO}: \\$? = $ac_status\" >&5\n  test $ac_status = 0; } && {\n\t test -z \"$ac_c_werror_flag\" ||\n\t test ! -s conftest.err\n       } && test -s conftest$ac_exeext && {\n\t test \"$cross_compiling\" = yes ||\n\t $as_test_x conftest$ac_exeext\n       }; then :\n  ac_retval=0\nelse\n  $as_echo \"$as_me: failed program was:\" >&5\nsed 's/^/| /' conftest.$ac_ext >&5\n\n\tac_retval=1\nfi\n  # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information\n  # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would\n  # interfere with the next link command; also delete a directory that is\n  # left behind by Apple's compiler.  We do this before executing the actions.\n  rm -rf conftest.dSYM conftest_ipa8_conftest.oo\n  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno\n  as_fn_set_status $ac_retval\n\n} # ac_fn_c_try_link\n\n# ac_fn_c_check_func LINENO FUNC VAR\n# ----------------------------------\n# Tests whether FUNC exists, setting the cache variable VAR accordingly\nac_fn_c_check_func ()\n{\n  as_lineno=${as_lineno-\"$1\"} as_lineno_stack=as_lineno_stack=$as_lineno_stack\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $2\" >&5\n$as_echo_n \"checking for $2... \" >&6; }\nif eval \\${$3+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n/* Define $2 to an innocuous variant, in case <limits.h> declares $2.\n   For example, HP-UX 11i <limits.h> declares gettimeofday.  */\n#define $2 innocuous_$2\n\n/* System header to define __stub macros and hopefully few prototypes,\n    which can conflict with char $2 (); below.\n    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since\n    <limits.h> exists even on freestanding compilers.  */\n\n#ifdef __STDC__\n# include <limits.h>\n#else\n# include <assert.h>\n#endif\n\n#undef $2\n\n/* Override any GCC internal prototype to avoid an error.\n   Use char because int might match the return type of a GCC\n   builtin and then its argument prototype would still apply.  */\n#ifdef __cplusplus\nextern \"C\"\n#endif\nchar $2 ();\n/* The GNU C library defines this for functions which it implements\n    to always fail with ENOSYS.  Some functions are actually named\n    something starting with __ and the normal name is an alias.  */\n#if defined __stub_$2 || defined __stub___$2\nchoke me\n#endif\n\nint\nmain ()\n{\nreturn $2 ();\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_link \"$LINENO\"; then :\n  eval \"$3=yes\"\nelse\n  eval \"$3=no\"\nfi\nrm -f core conftest.err conftest.$ac_objext \\\n    conftest$ac_exeext conftest.$ac_ext\nfi\neval ac_res=\\$$3\n\t       { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_res\" >&5\n$as_echo \"$ac_res\" >&6; }\n  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno\n\n} # ac_fn_c_check_func\n\n# ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES\n# -------------------------------------------------------\n# Tests whether HEADER exists, giving a warning if it cannot be compiled using\n# the include files in INCLUDES and setting the cache variable VAR\n# accordingly.\nac_fn_c_check_header_mongrel ()\n{\n  as_lineno=${as_lineno-\"$1\"} as_lineno_stack=as_lineno_stack=$as_lineno_stack\n  if eval \\${$3+:} false; then :\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $2\" >&5\n$as_echo_n \"checking for $2... \" >&6; }\nif eval \\${$3+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nfi\neval ac_res=\\$$3\n\t       { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_res\" >&5\n$as_echo \"$ac_res\" >&6; }\nelse\n  # Is the header compilable?\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking $2 usability\" >&5\n$as_echo_n \"checking $2 usability... \" >&6; }\ncat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n$4\n#include <$2>\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  ac_header_compiler=yes\nelse\n  ac_header_compiler=no\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler\" >&5\n$as_echo \"$ac_header_compiler\" >&6; }\n\n# Is the header present?\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking $2 presence\" >&5\n$as_echo_n \"checking $2 presence... \" >&6; }\ncat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n#include <$2>\n_ACEOF\nif ac_fn_c_try_cpp \"$LINENO\"; then :\n  ac_header_preproc=yes\nelse\n  ac_header_preproc=no\nfi\nrm -f conftest.err conftest.i conftest.$ac_ext\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc\" >&5\n$as_echo \"$ac_header_preproc\" >&6; }\n\n# So?  What about this header?\ncase $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #((\n  yes:no: )\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!\" >&5\n$as_echo \"$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!\" >&2;}\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result\" >&5\n$as_echo \"$as_me: WARNING: $2: proceeding with the compiler's result\" >&2;}\n    ;;\n  no:yes:* )\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled\" >&5\n$as_echo \"$as_me: WARNING: $2: present but cannot be compiled\" >&2;}\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: $2:     check for missing prerequisite headers?\" >&5\n$as_echo \"$as_me: WARNING: $2:     check for missing prerequisite headers?\" >&2;}\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation\" >&5\n$as_echo \"$as_me: WARNING: $2: see the Autoconf documentation\" >&2;}\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: $2:     section \\\"Present But Cannot Be Compiled\\\"\" >&5\n$as_echo \"$as_me: WARNING: $2:     section \\\"Present But Cannot Be Compiled\\\"\" >&2;}\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result\" >&5\n$as_echo \"$as_me: WARNING: $2: proceeding with the compiler's result\" >&2;}\n    ;;\nesac\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $2\" >&5\n$as_echo_n \"checking for $2... \" >&6; }\nif eval \\${$3+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  eval \"$3=\\$ac_header_compiler\"\nfi\neval ac_res=\\$$3\n\t       { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_res\" >&5\n$as_echo \"$ac_res\" >&6; }\nfi\n  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno\n\n} # ac_fn_c_check_header_mongrel\n\n# ac_fn_c_check_type LINENO TYPE VAR INCLUDES\n# -------------------------------------------\n# Tests whether TYPE exists after having included INCLUDES, setting cache\n# variable VAR accordingly.\nac_fn_c_check_type ()\n{\n  as_lineno=${as_lineno-\"$1\"} as_lineno_stack=as_lineno_stack=$as_lineno_stack\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $2\" >&5\n$as_echo_n \"checking for $2... \" >&6; }\nif eval \\${$3+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  eval \"$3=no\"\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n$4\nint\nmain ()\n{\nif (sizeof ($2))\n\t return 0;\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n$4\nint\nmain ()\n{\nif (sizeof (($2)))\n\t    return 0;\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n\nelse\n  eval \"$3=yes\"\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\nfi\neval ac_res=\\$$3\n\t       { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_res\" >&5\n$as_echo \"$ac_res\" >&6; }\n  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno\n\n} # ac_fn_c_check_type\ncat >config.log <<_ACEOF\nThis file contains any messages produced by compilers while\nrunning configure, to aid debugging if configure makes a mistake.\n\nIt was created by $as_me, which was\ngenerated by GNU Autoconf 2.68.  Invocation command line was\n\n  $ $0 $@\n\n_ACEOF\nexec 5>>config.log\n{\ncat <<_ASUNAME\n## --------- ##\n## Platform. ##\n## --------- ##\n\nhostname = `(hostname || uname -n) 2>/dev/null | sed 1q`\nuname -m = `(uname -m) 2>/dev/null || echo unknown`\nuname -r = `(uname -r) 2>/dev/null || echo unknown`\nuname -s = `(uname -s) 2>/dev/null || echo unknown`\nuname -v = `(uname -v) 2>/dev/null || echo unknown`\n\n/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`\n/bin/uname -X     = `(/bin/uname -X) 2>/dev/null     || echo unknown`\n\n/bin/arch              = `(/bin/arch) 2>/dev/null              || echo unknown`\n/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null       || echo unknown`\n/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`\n/usr/bin/hostinfo      = `(/usr/bin/hostinfo) 2>/dev/null      || echo unknown`\n/bin/machine           = `(/bin/machine) 2>/dev/null           || echo unknown`\n/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null       || echo unknown`\n/bin/universe          = `(/bin/universe) 2>/dev/null          || echo unknown`\n\n_ASUNAME\n\nas_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    $as_echo \"PATH: $as_dir\"\n  done\nIFS=$as_save_IFS\n\n} >&5\n\ncat >&5 <<_ACEOF\n\n\n## ----------- ##\n## Core tests. ##\n## ----------- ##\n\n_ACEOF\n\n\n# Keep a trace of the command line.\n# Strip out --no-create and --no-recursion so they do not pile up.\n# Strip out --silent because we don't want to record it for future runs.\n# Also quote any args containing shell meta-characters.\n# Make two passes to allow for proper duplicate-argument suppression.\nac_configure_args=\nac_configure_args0=\nac_configure_args1=\nac_must_keep_next=false\nfor ac_pass in 1 2\ndo\n  for ac_arg\n  do\n    case $ac_arg in\n    -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;\n    -q | -quiet | --quiet | --quie | --qui | --qu | --q \\\n    | -silent | --silent | --silen | --sile | --sil)\n      continue ;;\n    *\\'*)\n      ac_arg=`$as_echo \"$ac_arg\" | sed \"s/'/'\\\\\\\\\\\\\\\\''/g\"` ;;\n    esac\n    case $ac_pass in\n    1) as_fn_append ac_configure_args0 \" '$ac_arg'\" ;;\n    2)\n      as_fn_append ac_configure_args1 \" '$ac_arg'\"\n      if test $ac_must_keep_next = true; then\n\tac_must_keep_next=false # Got value, back to normal.\n      else\n\tcase $ac_arg in\n\t  *=* | --config-cache | -C | -disable-* | --disable-* \\\n\t  | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \\\n\t  | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \\\n\t  | -with-* | --with-* | -without-* | --without-* | --x)\n\t    case \"$ac_configure_args0 \" in\n\t      \"$ac_configure_args1\"*\" '$ac_arg' \"* ) continue ;;\n\t    esac\n\t    ;;\n\t  -* ) ac_must_keep_next=true ;;\n\tesac\n      fi\n      as_fn_append ac_configure_args \" '$ac_arg'\"\n      ;;\n    esac\n  done\ndone\n{ ac_configure_args0=; unset ac_configure_args0;}\n{ ac_configure_args1=; unset ac_configure_args1;}\n\n# When interrupted or exit'd, cleanup temporary files, and complete\n# config.log.  We remove comments because anyway the quotes in there\n# would cause problems or look ugly.\n# WARNING: Use '\\'' to represent an apostrophe within the trap.\n# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug.\ntrap 'exit_status=$?\n  # Save into config.log some information that might help in debugging.\n  {\n    echo\n\n    $as_echo \"## ---------------- ##\n## Cache variables. ##\n## ---------------- ##\"\n    echo\n    # The following way of writing the cache mishandles newlines in values,\n(\n  for ac_var in `(set) 2>&1 | sed -n '\\''s/^\\([a-zA-Z_][a-zA-Z0-9_]*\\)=.*/\\1/p'\\''`; do\n    eval ac_val=\\$$ac_var\n    case $ac_val in #(\n    *${as_nl}*)\n      case $ac_var in #(\n      *_cv_*) { $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline\" >&5\n$as_echo \"$as_me: WARNING: cache variable $ac_var contains a newline\" >&2;} ;;\n      esac\n      case $ac_var in #(\n      _ | IFS | as_nl) ;; #(\n      BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(\n      *) { eval $ac_var=; unset $ac_var;} ;;\n      esac ;;\n    esac\n  done\n  (set) 2>&1 |\n    case $as_nl`(ac_space='\\'' '\\''; set) 2>&1` in #(\n    *${as_nl}ac_space=\\ *)\n      sed -n \\\n\t\"s/'\\''/'\\''\\\\\\\\'\\'''\\''/g;\n\t  s/^\\\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\\\)=\\\\(.*\\\\)/\\\\1='\\''\\\\2'\\''/p\"\n      ;; #(\n    *)\n      sed -n \"/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p\"\n      ;;\n    esac |\n    sort\n)\n    echo\n\n    $as_echo \"## ----------------- ##\n## Output variables. ##\n## ----------------- ##\"\n    echo\n    for ac_var in $ac_subst_vars\n    do\n      eval ac_val=\\$$ac_var\n      case $ac_val in\n      *\\'\\''*) ac_val=`$as_echo \"$ac_val\" | sed \"s/'\\''/'\\''\\\\\\\\\\\\\\\\'\\'''\\''/g\"`;;\n      esac\n      $as_echo \"$ac_var='\\''$ac_val'\\''\"\n    done | sort\n    echo\n\n    if test -n \"$ac_subst_files\"; then\n      $as_echo \"## ------------------- ##\n## File substitutions. ##\n## ------------------- ##\"\n      echo\n      for ac_var in $ac_subst_files\n      do\n\teval ac_val=\\$$ac_var\n\tcase $ac_val in\n\t*\\'\\''*) ac_val=`$as_echo \"$ac_val\" | sed \"s/'\\''/'\\''\\\\\\\\\\\\\\\\'\\'''\\''/g\"`;;\n\tesac\n\t$as_echo \"$ac_var='\\''$ac_val'\\''\"\n      done | sort\n      echo\n    fi\n\n    if test -s confdefs.h; then\n      $as_echo \"## ----------- ##\n## confdefs.h. ##\n## ----------- ##\"\n      echo\n      cat confdefs.h\n      echo\n    fi\n    test \"$ac_signal\" != 0 &&\n      $as_echo \"$as_me: caught signal $ac_signal\"\n    $as_echo \"$as_me: exit $exit_status\"\n  } >&5\n  rm -f core *.core core.conftest.* &&\n    rm -f -r conftest* confdefs* conf$$* $ac_clean_files &&\n    exit $exit_status\n' 0\nfor ac_signal in 1 2 13 15; do\n  trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal\ndone\nac_signal=0\n\n# confdefs.h avoids OS command line length limits that DEFS can exceed.\nrm -f -r conftest* confdefs.h\n\n$as_echo \"/* confdefs.h */\" > confdefs.h\n\n# Predefined preprocessor variables.\n\ncat >>confdefs.h <<_ACEOF\n#define PACKAGE_NAME \"$PACKAGE_NAME\"\n_ACEOF\n\ncat >>confdefs.h <<_ACEOF\n#define PACKAGE_TARNAME \"$PACKAGE_TARNAME\"\n_ACEOF\n\ncat >>confdefs.h <<_ACEOF\n#define PACKAGE_VERSION \"$PACKAGE_VERSION\"\n_ACEOF\n\ncat >>confdefs.h <<_ACEOF\n#define PACKAGE_STRING \"$PACKAGE_STRING\"\n_ACEOF\n\ncat >>confdefs.h <<_ACEOF\n#define PACKAGE_BUGREPORT \"$PACKAGE_BUGREPORT\"\n_ACEOF\n\ncat >>confdefs.h <<_ACEOF\n#define PACKAGE_URL \"$PACKAGE_URL\"\n_ACEOF\n\n\n# Let the site file select an alternate cache file if it wants to.\n# Prefer an explicitly selected file to automatically selected ones.\nac_site_file1=NONE\nac_site_file2=NONE\nif test -n \"$CONFIG_SITE\"; then\n  # We do not want a PATH search for config.site.\n  case $CONFIG_SITE in #((\n    -*)  ac_site_file1=./$CONFIG_SITE;;\n    */*) ac_site_file1=$CONFIG_SITE;;\n    *)   ac_site_file1=./$CONFIG_SITE;;\n  esac\nelif test \"x$prefix\" != xNONE; then\n  ac_site_file1=$prefix/share/config.site\n  ac_site_file2=$prefix/etc/config.site\nelse\n  ac_site_file1=$ac_default_prefix/share/config.site\n  ac_site_file2=$ac_default_prefix/etc/config.site\nfi\nfor ac_site_file in \"$ac_site_file1\" \"$ac_site_file2\"\ndo\n  test \"x$ac_site_file\" = xNONE && continue\n  if test /dev/null != \"$ac_site_file\" && test -r \"$ac_site_file\"; then\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file\" >&5\n$as_echo \"$as_me: loading site script $ac_site_file\" >&6;}\n    sed 's/^/| /' \"$ac_site_file\" >&5\n    . \"$ac_site_file\" \\\n      || { { $as_echo \"$as_me:${as_lineno-$LINENO}: error: in \\`$ac_pwd':\" >&5\n$as_echo \"$as_me: error: in \\`$ac_pwd':\" >&2;}\nas_fn_error $? \"failed to load site script $ac_site_file\nSee \\`config.log' for more details\" \"$LINENO\" 5; }\n  fi\ndone\n\nif test -r \"$cache_file\"; then\n  # Some versions of bash will fail to source /dev/null (special files\n  # actually), so we avoid doing that.  DJGPP emulates it as a regular file.\n  if test /dev/null != \"$cache_file\" && test -f \"$cache_file\"; then\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: loading cache $cache_file\" >&5\n$as_echo \"$as_me: loading cache $cache_file\" >&6;}\n    case $cache_file in\n      [\\\\/]* | ?:[\\\\/]* ) . \"$cache_file\";;\n      *)                      . \"./$cache_file\";;\n    esac\n  fi\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: creating cache $cache_file\" >&5\n$as_echo \"$as_me: creating cache $cache_file\" >&6;}\n  >$cache_file\nfi\n\n# Check that the precious variables saved in the cache have kept the same\n# value.\nac_cache_corrupted=false\nfor ac_var in $ac_precious_vars; do\n  eval ac_old_set=\\$ac_cv_env_${ac_var}_set\n  eval ac_new_set=\\$ac_env_${ac_var}_set\n  eval ac_old_val=\\$ac_cv_env_${ac_var}_value\n  eval ac_new_val=\\$ac_env_${ac_var}_value\n  case $ac_old_set,$ac_new_set in\n    set,)\n      { $as_echo \"$as_me:${as_lineno-$LINENO}: error: \\`$ac_var' was set to \\`$ac_old_val' in the previous run\" >&5\n$as_echo \"$as_me: error: \\`$ac_var' was set to \\`$ac_old_val' in the previous run\" >&2;}\n      ac_cache_corrupted=: ;;\n    ,set)\n      { $as_echo \"$as_me:${as_lineno-$LINENO}: error: \\`$ac_var' was not set in the previous run\" >&5\n$as_echo \"$as_me: error: \\`$ac_var' was not set in the previous run\" >&2;}\n      ac_cache_corrupted=: ;;\n    ,);;\n    *)\n      if test \"x$ac_old_val\" != \"x$ac_new_val\"; then\n\t# differences in whitespace do not lead to failure.\n\tac_old_val_w=`echo x $ac_old_val`\n\tac_new_val_w=`echo x $ac_new_val`\n\tif test \"$ac_old_val_w\" != \"$ac_new_val_w\"; then\n\t  { $as_echo \"$as_me:${as_lineno-$LINENO}: error: \\`$ac_var' has changed since the previous run:\" >&5\n$as_echo \"$as_me: error: \\`$ac_var' has changed since the previous run:\" >&2;}\n\t  ac_cache_corrupted=:\n\telse\n\t  { $as_echo \"$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \\`$ac_var' since the previous run:\" >&5\n$as_echo \"$as_me: warning: ignoring whitespace changes in \\`$ac_var' since the previous run:\" >&2;}\n\t  eval $ac_var=\\$ac_old_val\n\tfi\n\t{ $as_echo \"$as_me:${as_lineno-$LINENO}:   former value:  \\`$ac_old_val'\" >&5\n$as_echo \"$as_me:   former value:  \\`$ac_old_val'\" >&2;}\n\t{ $as_echo \"$as_me:${as_lineno-$LINENO}:   current value: \\`$ac_new_val'\" >&5\n$as_echo \"$as_me:   current value: \\`$ac_new_val'\" >&2;}\n      fi;;\n  esac\n  # Pass precious variables to config.status.\n  if test \"$ac_new_set\" = set; then\n    case $ac_new_val in\n    *\\'*) ac_arg=$ac_var=`$as_echo \"$ac_new_val\" | sed \"s/'/'\\\\\\\\\\\\\\\\''/g\"` ;;\n    *) ac_arg=$ac_var=$ac_new_val ;;\n    esac\n    case \" $ac_configure_args \" in\n      *\" '$ac_arg' \"*) ;; # Avoid dups.  Use of quotes ensures accuracy.\n      *) as_fn_append ac_configure_args \" '$ac_arg'\" ;;\n    esac\n  fi\ndone\nif $ac_cache_corrupted; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: error: in \\`$ac_pwd':\" >&5\n$as_echo \"$as_me: error: in \\`$ac_pwd':\" >&2;}\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build\" >&5\n$as_echo \"$as_me: error: changes in the environment can compromise the build\" >&2;}\n  as_fn_error $? \"run \\`make distclean' and/or \\`rm $cache_file' and start over\" \"$LINENO\" 5\nfi\n## -------------------- ##\n## Main body of script. ##\n## -------------------- ##\n\nac_ext=c\nac_cpp='$CPP $CPPFLAGS'\nac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'\nac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'\nac_compiler_gnu=$ac_cv_c_compiler_gnu\n\n\n\n\n\n\n\n\n\nrev=1\n\n\nsrcroot=$srcdir\nif test \"x${srcroot}\" = \"x.\" ; then\n  srcroot=\"\"\nelse\n  srcroot=\"${srcroot}/\"\nfi\n\nabs_srcroot=\"`cd \\\"${srcdir}\\\"; pwd`/\"\n\n\nobjroot=\"\"\n\nabs_objroot=\"`pwd`/\"\n\n\nif test \"x$prefix\" = \"xNONE\" ; then\n  prefix=\"/usr/local\"\nfi\nif test \"x$exec_prefix\" = \"xNONE\" ; then\n  exec_prefix=$prefix\nfi\nPREFIX=$prefix\n\nBINDIR=`eval echo $bindir`\nBINDIR=`eval echo $BINDIR`\n\nINCLUDEDIR=`eval echo $includedir`\nINCLUDEDIR=`eval echo $INCLUDEDIR`\n\nLIBDIR=`eval echo $libdir`\nLIBDIR=`eval echo $LIBDIR`\n\nDATADIR=`eval echo $datadir`\nDATADIR=`eval echo $DATADIR`\n\nMANDIR=`eval echo $mandir`\nMANDIR=`eval echo $MANDIR`\n\n\n# Extract the first word of \"xsltproc\", so it can be a program name with args.\nset dummy xsltproc; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_path_XSLTPROC+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  case $XSLTPROC in\n  [\\\\/]* | ?:[\\\\/]*)\n  ac_cv_path_XSLTPROC=\"$XSLTPROC\" # Let the user override the test with a path.\n  ;;\n  *)\n  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if { test -f \"$as_dir/$ac_word$ac_exec_ext\" && $as_test_x \"$as_dir/$ac_word$ac_exec_ext\"; }; then\n    ac_cv_path_XSLTPROC=\"$as_dir/$ac_word$ac_exec_ext\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\n  ;;\nesac\nfi\nXSLTPROC=$ac_cv_path_XSLTPROC\nif test -n \"$XSLTPROC\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $XSLTPROC\" >&5\n$as_echo \"$XSLTPROC\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n\nif test -d \"/usr/share/xml/docbook/stylesheet/docbook-xsl\" ; then\n  DEFAULT_XSLROOT=\"/usr/share/xml/docbook/stylesheet/docbook-xsl\"\nelif test -d \"/usr/share/sgml/docbook/xsl-stylesheets\" ; then\n  DEFAULT_XSLROOT=\"/usr/share/sgml/docbook/xsl-stylesheets\"\nelse\n    DEFAULT_XSLROOT=\"\"\nfi\n\n# Check whether --with-xslroot was given.\nif test \"${with_xslroot+set}\" = set; then :\n  withval=$with_xslroot;\nif test \"x$with_xslroot\" = \"xno\" ; then\n  XSLROOT=\"${DEFAULT_XSLROOT}\"\nelse\n  XSLROOT=\"${with_xslroot}\"\nfi\n\nelse\n  XSLROOT=\"${DEFAULT_XSLROOT}\"\n\nfi\n\n\n\nCFLAGS=$CFLAGS\nac_ext=c\nac_cpp='$CPP $CPPFLAGS'\nac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'\nac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'\nac_compiler_gnu=$ac_cv_c_compiler_gnu\nif test -n \"$ac_tool_prefix\"; then\n  # Extract the first word of \"${ac_tool_prefix}gcc\", so it can be a program name with args.\nset dummy ${ac_tool_prefix}gcc; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_prog_CC+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if test -n \"$CC\"; then\n  ac_cv_prog_CC=\"$CC\" # Let the user override the test.\nelse\nas_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if { test -f \"$as_dir/$ac_word$ac_exec_ext\" && $as_test_x \"$as_dir/$ac_word$ac_exec_ext\"; }; then\n    ac_cv_prog_CC=\"${ac_tool_prefix}gcc\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\nfi\nfi\nCC=$ac_cv_prog_CC\nif test -n \"$CC\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $CC\" >&5\n$as_echo \"$CC\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n\nfi\nif test -z \"$ac_cv_prog_CC\"; then\n  ac_ct_CC=$CC\n  # Extract the first word of \"gcc\", so it can be a program name with args.\nset dummy gcc; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_prog_ac_ct_CC+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if test -n \"$ac_ct_CC\"; then\n  ac_cv_prog_ac_ct_CC=\"$ac_ct_CC\" # Let the user override the test.\nelse\nas_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if { test -f \"$as_dir/$ac_word$ac_exec_ext\" && $as_test_x \"$as_dir/$ac_word$ac_exec_ext\"; }; then\n    ac_cv_prog_ac_ct_CC=\"gcc\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\nfi\nfi\nac_ct_CC=$ac_cv_prog_ac_ct_CC\nif test -n \"$ac_ct_CC\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC\" >&5\n$as_echo \"$ac_ct_CC\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n  if test \"x$ac_ct_CC\" = x; then\n    CC=\"\"\n  else\n    case $cross_compiling:$ac_tool_warned in\nyes:)\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet\" >&5\n$as_echo \"$as_me: WARNING: using cross tools not prefixed with host triplet\" >&2;}\nac_tool_warned=yes ;;\nesac\n    CC=$ac_ct_CC\n  fi\nelse\n  CC=\"$ac_cv_prog_CC\"\nfi\n\nif test -z \"$CC\"; then\n          if test -n \"$ac_tool_prefix\"; then\n    # Extract the first word of \"${ac_tool_prefix}cc\", so it can be a program name with args.\nset dummy ${ac_tool_prefix}cc; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_prog_CC+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if test -n \"$CC\"; then\n  ac_cv_prog_CC=\"$CC\" # Let the user override the test.\nelse\nas_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if { test -f \"$as_dir/$ac_word$ac_exec_ext\" && $as_test_x \"$as_dir/$ac_word$ac_exec_ext\"; }; then\n    ac_cv_prog_CC=\"${ac_tool_prefix}cc\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\nfi\nfi\nCC=$ac_cv_prog_CC\nif test -n \"$CC\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $CC\" >&5\n$as_echo \"$CC\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n\n  fi\nfi\nif test -z \"$CC\"; then\n  # Extract the first word of \"cc\", so it can be a program name with args.\nset dummy cc; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_prog_CC+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if test -n \"$CC\"; then\n  ac_cv_prog_CC=\"$CC\" # Let the user override the test.\nelse\n  ac_prog_rejected=no\nas_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if { test -f \"$as_dir/$ac_word$ac_exec_ext\" && $as_test_x \"$as_dir/$ac_word$ac_exec_ext\"; }; then\n    if test \"$as_dir/$ac_word$ac_exec_ext\" = \"/usr/ucb/cc\"; then\n       ac_prog_rejected=yes\n       continue\n     fi\n    ac_cv_prog_CC=\"cc\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\nif test $ac_prog_rejected = yes; then\n  # We found a bogon in the path, so make sure we never use it.\n  set dummy $ac_cv_prog_CC\n  shift\n  if test $# != 0; then\n    # We chose a different compiler from the bogus one.\n    # However, it has the same basename, so the bogon will be chosen\n    # first if we set CC to just the basename; use the full file name.\n    shift\n    ac_cv_prog_CC=\"$as_dir/$ac_word${1+' '}$@\"\n  fi\nfi\nfi\nfi\nCC=$ac_cv_prog_CC\nif test -n \"$CC\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $CC\" >&5\n$as_echo \"$CC\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n\nfi\nif test -z \"$CC\"; then\n  if test -n \"$ac_tool_prefix\"; then\n  for ac_prog in cl.exe\n  do\n    # Extract the first word of \"$ac_tool_prefix$ac_prog\", so it can be a program name with args.\nset dummy $ac_tool_prefix$ac_prog; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_prog_CC+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if test -n \"$CC\"; then\n  ac_cv_prog_CC=\"$CC\" # Let the user override the test.\nelse\nas_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if { test -f \"$as_dir/$ac_word$ac_exec_ext\" && $as_test_x \"$as_dir/$ac_word$ac_exec_ext\"; }; then\n    ac_cv_prog_CC=\"$ac_tool_prefix$ac_prog\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\nfi\nfi\nCC=$ac_cv_prog_CC\nif test -n \"$CC\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $CC\" >&5\n$as_echo \"$CC\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n\n    test -n \"$CC\" && break\n  done\nfi\nif test -z \"$CC\"; then\n  ac_ct_CC=$CC\n  for ac_prog in cl.exe\ndo\n  # Extract the first word of \"$ac_prog\", so it can be a program name with args.\nset dummy $ac_prog; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_prog_ac_ct_CC+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if test -n \"$ac_ct_CC\"; then\n  ac_cv_prog_ac_ct_CC=\"$ac_ct_CC\" # Let the user override the test.\nelse\nas_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if { test -f \"$as_dir/$ac_word$ac_exec_ext\" && $as_test_x \"$as_dir/$ac_word$ac_exec_ext\"; }; then\n    ac_cv_prog_ac_ct_CC=\"$ac_prog\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\nfi\nfi\nac_ct_CC=$ac_cv_prog_ac_ct_CC\nif test -n \"$ac_ct_CC\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC\" >&5\n$as_echo \"$ac_ct_CC\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n\n  test -n \"$ac_ct_CC\" && break\ndone\n\n  if test \"x$ac_ct_CC\" = x; then\n    CC=\"\"\n  else\n    case $cross_compiling:$ac_tool_warned in\nyes:)\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet\" >&5\n$as_echo \"$as_me: WARNING: using cross tools not prefixed with host triplet\" >&2;}\nac_tool_warned=yes ;;\nesac\n    CC=$ac_ct_CC\n  fi\nfi\n\nfi\n\n\ntest -z \"$CC\" && { { $as_echo \"$as_me:${as_lineno-$LINENO}: error: in \\`$ac_pwd':\" >&5\n$as_echo \"$as_me: error: in \\`$ac_pwd':\" >&2;}\nas_fn_error $? \"no acceptable C compiler found in \\$PATH\nSee \\`config.log' for more details\" \"$LINENO\" 5; }\n\n# Provide some information about the compiler.\n$as_echo \"$as_me:${as_lineno-$LINENO}: checking for C compiler version\" >&5\nset X $ac_compile\nac_compiler=$2\nfor ac_option in --version -v -V -qversion; do\n  { { ac_try=\"$ac_compiler $ac_option >&5\"\ncase \"(($ac_try\" in\n  *\\\"* | *\\`* | *\\\\*) ac_try_echo=\\$ac_try;;\n  *) ac_try_echo=$ac_try;;\nesac\neval ac_try_echo=\"\\\"\\$as_me:${as_lineno-$LINENO}: $ac_try_echo\\\"\"\n$as_echo \"$ac_try_echo\"; } >&5\n  (eval \"$ac_compiler $ac_option >&5\") 2>conftest.err\n  ac_status=$?\n  if test -s conftest.err; then\n    sed '10a\\\n... rest of stderr output deleted ...\n         10q' conftest.err >conftest.er1\n    cat conftest.er1 >&5\n  fi\n  rm -f conftest.er1 conftest.err\n  $as_echo \"$as_me:${as_lineno-$LINENO}: \\$? = $ac_status\" >&5\n  test $ac_status = 0; }\ndone\n\ncat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\nint\nmain ()\n{\n\n  ;\n  return 0;\n}\n_ACEOF\nac_clean_files_save=$ac_clean_files\nac_clean_files=\"$ac_clean_files a.out a.out.dSYM a.exe b.out\"\n# Try to create an executable without -o first, disregard a.out.\n# It will help us diagnose broken compilers, and finding out an intuition\n# of exeext.\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether the C compiler works\" >&5\n$as_echo_n \"checking whether the C compiler works... \" >&6; }\nac_link_default=`$as_echo \"$ac_link\" | sed 's/ -o *conftest[^ ]*//'`\n\n# The possible output files:\nac_files=\"a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*\"\n\nac_rmfiles=\nfor ac_file in $ac_files\ndo\n  case $ac_file in\n    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;\n    * ) ac_rmfiles=\"$ac_rmfiles $ac_file\";;\n  esac\ndone\nrm -f $ac_rmfiles\n\nif { { ac_try=\"$ac_link_default\"\ncase \"(($ac_try\" in\n  *\\\"* | *\\`* | *\\\\*) ac_try_echo=\\$ac_try;;\n  *) ac_try_echo=$ac_try;;\nesac\neval ac_try_echo=\"\\\"\\$as_me:${as_lineno-$LINENO}: $ac_try_echo\\\"\"\n$as_echo \"$ac_try_echo\"; } >&5\n  (eval \"$ac_link_default\") 2>&5\n  ac_status=$?\n  $as_echo \"$as_me:${as_lineno-$LINENO}: \\$? = $ac_status\" >&5\n  test $ac_status = 0; }; then :\n  # Autoconf-2.13 could set the ac_cv_exeext variable to `no'.\n# So ignore a value of `no', otherwise this would lead to `EXEEXT = no'\n# in a Makefile.  We should not override ac_cv_exeext if it was cached,\n# so that the user can short-circuit this test for compilers unknown to\n# Autoconf.\nfor ac_file in $ac_files ''\ndo\n  test -f \"$ac_file\" || continue\n  case $ac_file in\n    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj )\n\t;;\n    [ab].out )\n\t# We found the default executable, but exeext='' is most\n\t# certainly right.\n\tbreak;;\n    *.* )\n\tif test \"${ac_cv_exeext+set}\" = set && test \"$ac_cv_exeext\" != no;\n\tthen :; else\n\t   ac_cv_exeext=`expr \"$ac_file\" : '[^.]*\\(\\..*\\)'`\n\tfi\n\t# We set ac_cv_exeext here because the later test for it is not\n\t# safe: cross compilers may not add the suffix if given an `-o'\n\t# argument, so we may need to know it at that point already.\n\t# Even if this section looks crufty: it has the advantage of\n\t# actually working.\n\tbreak;;\n    * )\n\tbreak;;\n  esac\ndone\ntest \"$ac_cv_exeext\" = no && ac_cv_exeext=\n\nelse\n  ac_file=''\nfi\nif test -z \"$ac_file\"; then :\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\n$as_echo \"$as_me: failed program was:\" >&5\nsed 's/^/| /' conftest.$ac_ext >&5\n\n{ { $as_echo \"$as_me:${as_lineno-$LINENO}: error: in \\`$ac_pwd':\" >&5\n$as_echo \"$as_me: error: in \\`$ac_pwd':\" >&2;}\nas_fn_error 77 \"C compiler cannot create executables\nSee \\`config.log' for more details\" \"$LINENO\" 5; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: yes\" >&5\n$as_echo \"yes\" >&6; }\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name\" >&5\n$as_echo_n \"checking for C compiler default output file name... \" >&6; }\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_file\" >&5\n$as_echo \"$ac_file\" >&6; }\nac_exeext=$ac_cv_exeext\n\nrm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out\nac_clean_files=$ac_clean_files_save\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for suffix of executables\" >&5\n$as_echo_n \"checking for suffix of executables... \" >&6; }\nif { { ac_try=\"$ac_link\"\ncase \"(($ac_try\" in\n  *\\\"* | *\\`* | *\\\\*) ac_try_echo=\\$ac_try;;\n  *) ac_try_echo=$ac_try;;\nesac\neval ac_try_echo=\"\\\"\\$as_me:${as_lineno-$LINENO}: $ac_try_echo\\\"\"\n$as_echo \"$ac_try_echo\"; } >&5\n  (eval \"$ac_link\") 2>&5\n  ac_status=$?\n  $as_echo \"$as_me:${as_lineno-$LINENO}: \\$? = $ac_status\" >&5\n  test $ac_status = 0; }; then :\n  # If both `conftest.exe' and `conftest' are `present' (well, observable)\n# catch `conftest.exe'.  For instance with Cygwin, `ls conftest' will\n# work properly (i.e., refer to `conftest.exe'), while it won't with\n# `rm'.\nfor ac_file in conftest.exe conftest conftest.*; do\n  test -f \"$ac_file\" || continue\n  case $ac_file in\n    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;\n    *.* ) ac_cv_exeext=`expr \"$ac_file\" : '[^.]*\\(\\..*\\)'`\n\t  break;;\n    * ) break;;\n  esac\ndone\nelse\n  { { $as_echo \"$as_me:${as_lineno-$LINENO}: error: in \\`$ac_pwd':\" >&5\n$as_echo \"$as_me: error: in \\`$ac_pwd':\" >&2;}\nas_fn_error $? \"cannot compute suffix of executables: cannot compile and link\nSee \\`config.log' for more details\" \"$LINENO\" 5; }\nfi\nrm -f conftest conftest$ac_cv_exeext\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext\" >&5\n$as_echo \"$ac_cv_exeext\" >&6; }\n\nrm -f conftest.$ac_ext\nEXEEXT=$ac_cv_exeext\nac_exeext=$EXEEXT\ncat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n#include <stdio.h>\nint\nmain ()\n{\nFILE *f = fopen (\"conftest.out\", \"w\");\n return ferror (f) || fclose (f) != 0;\n\n  ;\n  return 0;\n}\n_ACEOF\nac_clean_files=\"$ac_clean_files conftest.out\"\n# Check that the compiler produces executables we can run.  If not, either\n# the compiler is broken, or we cross compile.\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling\" >&5\n$as_echo_n \"checking whether we are cross compiling... \" >&6; }\nif test \"$cross_compiling\" != yes; then\n  { { ac_try=\"$ac_link\"\ncase \"(($ac_try\" in\n  *\\\"* | *\\`* | *\\\\*) ac_try_echo=\\$ac_try;;\n  *) ac_try_echo=$ac_try;;\nesac\neval ac_try_echo=\"\\\"\\$as_me:${as_lineno-$LINENO}: $ac_try_echo\\\"\"\n$as_echo \"$ac_try_echo\"; } >&5\n  (eval \"$ac_link\") 2>&5\n  ac_status=$?\n  $as_echo \"$as_me:${as_lineno-$LINENO}: \\$? = $ac_status\" >&5\n  test $ac_status = 0; }\n  if { ac_try='./conftest$ac_cv_exeext'\n  { { case \"(($ac_try\" in\n  *\\\"* | *\\`* | *\\\\*) ac_try_echo=\\$ac_try;;\n  *) ac_try_echo=$ac_try;;\nesac\neval ac_try_echo=\"\\\"\\$as_me:${as_lineno-$LINENO}: $ac_try_echo\\\"\"\n$as_echo \"$ac_try_echo\"; } >&5\n  (eval \"$ac_try\") 2>&5\n  ac_status=$?\n  $as_echo \"$as_me:${as_lineno-$LINENO}: \\$? = $ac_status\" >&5\n  test $ac_status = 0; }; }; then\n    cross_compiling=no\n  else\n    if test \"$cross_compiling\" = maybe; then\n\tcross_compiling=yes\n    else\n\t{ { $as_echo \"$as_me:${as_lineno-$LINENO}: error: in \\`$ac_pwd':\" >&5\n$as_echo \"$as_me: error: in \\`$ac_pwd':\" >&2;}\nas_fn_error $? \"cannot run C compiled programs.\nIf you meant to cross compile, use \\`--host'.\nSee \\`config.log' for more details\" \"$LINENO\" 5; }\n    fi\n  fi\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $cross_compiling\" >&5\n$as_echo \"$cross_compiling\" >&6; }\n\nrm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out\nac_clean_files=$ac_clean_files_save\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for suffix of object files\" >&5\n$as_echo_n \"checking for suffix of object files... \" >&6; }\nif ${ac_cv_objext+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\nint\nmain ()\n{\n\n  ;\n  return 0;\n}\n_ACEOF\nrm -f conftest.o conftest.obj\nif { { ac_try=\"$ac_compile\"\ncase \"(($ac_try\" in\n  *\\\"* | *\\`* | *\\\\*) ac_try_echo=\\$ac_try;;\n  *) ac_try_echo=$ac_try;;\nesac\neval ac_try_echo=\"\\\"\\$as_me:${as_lineno-$LINENO}: $ac_try_echo\\\"\"\n$as_echo \"$ac_try_echo\"; } >&5\n  (eval \"$ac_compile\") 2>&5\n  ac_status=$?\n  $as_echo \"$as_me:${as_lineno-$LINENO}: \\$? = $ac_status\" >&5\n  test $ac_status = 0; }; then :\n  for ac_file in conftest.o conftest.obj conftest.*; do\n  test -f \"$ac_file\" || continue;\n  case $ac_file in\n    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;;\n    *) ac_cv_objext=`expr \"$ac_file\" : '.*\\.\\(.*\\)'`\n       break;;\n  esac\ndone\nelse\n  $as_echo \"$as_me: failed program was:\" >&5\nsed 's/^/| /' conftest.$ac_ext >&5\n\n{ { $as_echo \"$as_me:${as_lineno-$LINENO}: error: in \\`$ac_pwd':\" >&5\n$as_echo \"$as_me: error: in \\`$ac_pwd':\" >&2;}\nas_fn_error $? \"cannot compute suffix of object files: cannot compile\nSee \\`config.log' for more details\" \"$LINENO\" 5; }\nfi\nrm -f conftest.$ac_cv_objext conftest.$ac_ext\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext\" >&5\n$as_echo \"$ac_cv_objext\" >&6; }\nOBJEXT=$ac_cv_objext\nac_objext=$OBJEXT\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler\" >&5\n$as_echo_n \"checking whether we are using the GNU C compiler... \" >&6; }\nif ${ac_cv_c_compiler_gnu+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\nint\nmain ()\n{\n#ifndef __GNUC__\n       choke me\n#endif\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  ac_compiler_gnu=yes\nelse\n  ac_compiler_gnu=no\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\nac_cv_c_compiler_gnu=$ac_compiler_gnu\n\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu\" >&5\n$as_echo \"$ac_cv_c_compiler_gnu\" >&6; }\nif test $ac_compiler_gnu = yes; then\n  GCC=yes\nelse\n  GCC=\nfi\nac_test_CFLAGS=${CFLAGS+set}\nac_save_CFLAGS=$CFLAGS\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g\" >&5\n$as_echo_n \"checking whether $CC accepts -g... \" >&6; }\nif ${ac_cv_prog_cc_g+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  ac_save_c_werror_flag=$ac_c_werror_flag\n   ac_c_werror_flag=yes\n   ac_cv_prog_cc_g=no\n   CFLAGS=\"-g\"\n   cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\nint\nmain ()\n{\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  ac_cv_prog_cc_g=yes\nelse\n  CFLAGS=\"\"\n      cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\nint\nmain ()\n{\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n\nelse\n  ac_c_werror_flag=$ac_save_c_werror_flag\n\t CFLAGS=\"-g\"\n\t cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\nint\nmain ()\n{\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  ac_cv_prog_cc_g=yes\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\n   ac_c_werror_flag=$ac_save_c_werror_flag\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g\" >&5\n$as_echo \"$ac_cv_prog_cc_g\" >&6; }\nif test \"$ac_test_CFLAGS\" = set; then\n  CFLAGS=$ac_save_CFLAGS\nelif test $ac_cv_prog_cc_g = yes; then\n  if test \"$GCC\" = yes; then\n    CFLAGS=\"-g -O2\"\n  else\n    CFLAGS=\"-g\"\n  fi\nelse\n  if test \"$GCC\" = yes; then\n    CFLAGS=\"-O2\"\n  else\n    CFLAGS=\n  fi\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89\" >&5\n$as_echo_n \"checking for $CC option to accept ISO C89... \" >&6; }\nif ${ac_cv_prog_cc_c89+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  ac_cv_prog_cc_c89=no\nac_save_CC=$CC\ncat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n#include <stdarg.h>\n#include <stdio.h>\n#include <sys/types.h>\n#include <sys/stat.h>\n/* Most of the following tests are stolen from RCS 5.7's src/conf.sh.  */\nstruct buf { int x; };\nFILE * (*rcsopen) (struct buf *, struct stat *, int);\nstatic char *e (p, i)\n     char **p;\n     int i;\n{\n  return p[i];\n}\nstatic char *f (char * (*g) (char **, int), char **p, ...)\n{\n  char *s;\n  va_list v;\n  va_start (v,p);\n  s = g (p, va_arg (v,int));\n  va_end (v);\n  return s;\n}\n\n/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default.  It has\n   function prototypes and stuff, but not '\\xHH' hex character constants.\n   These don't provoke an error unfortunately, instead are silently treated\n   as 'x'.  The following induces an error, until -std is added to get\n   proper ANSI mode.  Curiously '\\x00'!='x' always comes out true, for an\n   array size at least.  It's necessary to write '\\x00'==0 to get something\n   that's true only with -std.  */\nint osf4_cc_array ['\\x00' == 0 ? 1 : -1];\n\n/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters\n   inside strings and character constants.  */\n#define FOO(x) 'x'\nint xlc6_cc_array[FOO(a) == 'x' ? 1 : -1];\n\nint test (int i, double x);\nstruct s1 {int (*f) (int a);};\nstruct s2 {int (*f) (double a);};\nint pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);\nint argc;\nchar **argv;\nint\nmain ()\n{\nreturn f (e, argv, 0) != argv[0]  ||  f (e, argv, 1) != argv[1];\n  ;\n  return 0;\n}\n_ACEOF\nfor ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \\\n\t-Ae \"-Aa -D_HPUX_SOURCE\" \"-Xc -D__EXTENSIONS__\"\ndo\n  CC=\"$ac_save_CC $ac_arg\"\n  if ac_fn_c_try_compile \"$LINENO\"; then :\n  ac_cv_prog_cc_c89=$ac_arg\nfi\nrm -f core conftest.err conftest.$ac_objext\n  test \"x$ac_cv_prog_cc_c89\" != \"xno\" && break\ndone\nrm -f conftest.$ac_ext\nCC=$ac_save_CC\n\nfi\n# AC_CACHE_VAL\ncase \"x$ac_cv_prog_cc_c89\" in\n  x)\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: result: none needed\" >&5\n$as_echo \"none needed\" >&6; } ;;\n  xno)\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: result: unsupported\" >&5\n$as_echo \"unsupported\" >&6; } ;;\n  *)\n    CC=\"$CC $ac_cv_prog_cc_c89\"\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89\" >&5\n$as_echo \"$ac_cv_prog_cc_c89\" >&6; } ;;\nesac\nif test \"x$ac_cv_prog_cc_c89\" != xno; then :\n\nfi\n\nac_ext=c\nac_cpp='$CPP $CPPFLAGS'\nac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'\nac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'\nac_compiler_gnu=$ac_cv_c_compiler_gnu\n\nif test \"x$GCC\" != \"xyes\" ; then\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether compiler is MSVC\" >&5\n$as_echo_n \"checking whether compiler is MSVC... \" >&6; }\nif ${je_cv_msvc+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\nint\nmain ()\n{\n\n#ifndef _MSC_VER\n  int fail-1;\n#endif\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  je_cv_msvc=yes\nelse\n  je_cv_msvc=no\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $je_cv_msvc\" >&5\n$as_echo \"$je_cv_msvc\" >&6; }\nfi\n\nif test \"x$CFLAGS\" = \"x\" ; then\n  no_CFLAGS=\"yes\"\n  if test \"x$GCC\" = \"xyes\" ; then\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether compiler supports -std=gnu99\" >&5\n$as_echo_n \"checking whether compiler supports -std=gnu99... \" >&6; }\nTCFLAGS=\"${CFLAGS}\"\nif test \"x${CFLAGS}\" = \"x\" ; then\n  CFLAGS=\"-std=gnu99\"\nelse\n  CFLAGS=\"${CFLAGS} -std=gnu99\"\nfi\ncat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\n\nint\nmain ()\n{\n\n    return 0;\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: yes\" >&5\n$as_echo \"yes\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\n              CFLAGS=\"${TCFLAGS}\"\n\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\n\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether compiler supports -Wall\" >&5\n$as_echo_n \"checking whether compiler supports -Wall... \" >&6; }\nTCFLAGS=\"${CFLAGS}\"\nif test \"x${CFLAGS}\" = \"x\" ; then\n  CFLAGS=\"-Wall\"\nelse\n  CFLAGS=\"${CFLAGS} -Wall\"\nfi\ncat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\n\nint\nmain ()\n{\n\n    return 0;\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: yes\" >&5\n$as_echo \"yes\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\n              CFLAGS=\"${TCFLAGS}\"\n\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\n\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether compiler supports -pipe\" >&5\n$as_echo_n \"checking whether compiler supports -pipe... \" >&6; }\nTCFLAGS=\"${CFLAGS}\"\nif test \"x${CFLAGS}\" = \"x\" ; then\n  CFLAGS=\"-pipe\"\nelse\n  CFLAGS=\"${CFLAGS} -pipe\"\nfi\ncat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\n\nint\nmain ()\n{\n\n    return 0;\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: yes\" >&5\n$as_echo \"yes\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\n              CFLAGS=\"${TCFLAGS}\"\n\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\n\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether compiler supports -g3\" >&5\n$as_echo_n \"checking whether compiler supports -g3... \" >&6; }\nTCFLAGS=\"${CFLAGS}\"\nif test \"x${CFLAGS}\" = \"x\" ; then\n  CFLAGS=\"-g3\"\nelse\n  CFLAGS=\"${CFLAGS} -g3\"\nfi\ncat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\n\nint\nmain ()\n{\n\n    return 0;\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: yes\" >&5\n$as_echo \"yes\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\n              CFLAGS=\"${TCFLAGS}\"\n\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\n\n  elif test \"x$je_cv_msvc\" = \"xyes\" ; then\n    CC=\"$CC -nologo\"\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether compiler supports -Zi\" >&5\n$as_echo_n \"checking whether compiler supports -Zi... \" >&6; }\nTCFLAGS=\"${CFLAGS}\"\nif test \"x${CFLAGS}\" = \"x\" ; then\n  CFLAGS=\"-Zi\"\nelse\n  CFLAGS=\"${CFLAGS} -Zi\"\nfi\ncat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\n\nint\nmain ()\n{\n\n    return 0;\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: yes\" >&5\n$as_echo \"yes\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\n              CFLAGS=\"${TCFLAGS}\"\n\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\n\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether compiler supports -MT\" >&5\n$as_echo_n \"checking whether compiler supports -MT... \" >&6; }\nTCFLAGS=\"${CFLAGS}\"\nif test \"x${CFLAGS}\" = \"x\" ; then\n  CFLAGS=\"-MT\"\nelse\n  CFLAGS=\"${CFLAGS} -MT\"\nfi\ncat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\n\nint\nmain ()\n{\n\n    return 0;\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: yes\" >&5\n$as_echo \"yes\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\n              CFLAGS=\"${TCFLAGS}\"\n\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\n\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether compiler supports -W3\" >&5\n$as_echo_n \"checking whether compiler supports -W3... \" >&6; }\nTCFLAGS=\"${CFLAGS}\"\nif test \"x${CFLAGS}\" = \"x\" ; then\n  CFLAGS=\"-W3\"\nelse\n  CFLAGS=\"${CFLAGS} -W3\"\nfi\ncat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\n\nint\nmain ()\n{\n\n    return 0;\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: yes\" >&5\n$as_echo \"yes\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\n              CFLAGS=\"${TCFLAGS}\"\n\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\n\n    CPPFLAGS=\"$CPPFLAGS -I${srcroot}/include/msvc_compat\"\n  fi\nfi\nif test \"x$EXTRA_CFLAGS\" != \"x\" ; then\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether compiler supports $EXTRA_CFLAGS\" >&5\n$as_echo_n \"checking whether compiler supports $EXTRA_CFLAGS... \" >&6; }\nTCFLAGS=\"${CFLAGS}\"\nif test \"x${CFLAGS}\" = \"x\" ; then\n  CFLAGS=\"$EXTRA_CFLAGS\"\nelse\n  CFLAGS=\"${CFLAGS} $EXTRA_CFLAGS\"\nfi\ncat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\n\nint\nmain ()\n{\n\n    return 0;\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: yes\" >&5\n$as_echo \"yes\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\n              CFLAGS=\"${TCFLAGS}\"\n\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\n\nfi\nac_ext=c\nac_cpp='$CPP $CPPFLAGS'\nac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'\nac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'\nac_compiler_gnu=$ac_cv_c_compiler_gnu\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor\" >&5\n$as_echo_n \"checking how to run the C preprocessor... \" >&6; }\n# On Suns, sometimes $CPP names a directory.\nif test -n \"$CPP\" && test -d \"$CPP\"; then\n  CPP=\nfi\nif test -z \"$CPP\"; then\n  if ${ac_cv_prog_CPP+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n      # Double quotes because CPP needs to be expanded\n    for CPP in \"$CC -E\" \"$CC -E -traditional-cpp\" \"/lib/cpp\"\n    do\n      ac_preproc_ok=false\nfor ac_c_preproc_warn_flag in '' yes\ndo\n  # Use a header file that comes with gcc, so configuring glibc\n  # with a fresh cross-compiler works.\n  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since\n  # <limits.h> exists even on freestanding compilers.\n  # On the NeXT, cc -E runs the code through the compiler's parser,\n  # not just through cpp. \"Syntax error\" is here to catch this case.\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n#ifdef __STDC__\n# include <limits.h>\n#else\n# include <assert.h>\n#endif\n\t\t     Syntax error\n_ACEOF\nif ac_fn_c_try_cpp \"$LINENO\"; then :\n\nelse\n  # Broken: fails on valid input.\ncontinue\nfi\nrm -f conftest.err conftest.i conftest.$ac_ext\n\n  # OK, works on sane cases.  Now check whether nonexistent headers\n  # can be detected and how.\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n#include <ac_nonexistent.h>\n_ACEOF\nif ac_fn_c_try_cpp \"$LINENO\"; then :\n  # Broken: success on invalid input.\ncontinue\nelse\n  # Passes both tests.\nac_preproc_ok=:\nbreak\nfi\nrm -f conftest.err conftest.i conftest.$ac_ext\n\ndone\n# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.\nrm -f conftest.i conftest.err conftest.$ac_ext\nif $ac_preproc_ok; then :\n  break\nfi\n\n    done\n    ac_cv_prog_CPP=$CPP\n\nfi\n  CPP=$ac_cv_prog_CPP\nelse\n  ac_cv_prog_CPP=$CPP\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $CPP\" >&5\n$as_echo \"$CPP\" >&6; }\nac_preproc_ok=false\nfor ac_c_preproc_warn_flag in '' yes\ndo\n  # Use a header file that comes with gcc, so configuring glibc\n  # with a fresh cross-compiler works.\n  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since\n  # <limits.h> exists even on freestanding compilers.\n  # On the NeXT, cc -E runs the code through the compiler's parser,\n  # not just through cpp. \"Syntax error\" is here to catch this case.\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n#ifdef __STDC__\n# include <limits.h>\n#else\n# include <assert.h>\n#endif\n\t\t     Syntax error\n_ACEOF\nif ac_fn_c_try_cpp \"$LINENO\"; then :\n\nelse\n  # Broken: fails on valid input.\ncontinue\nfi\nrm -f conftest.err conftest.i conftest.$ac_ext\n\n  # OK, works on sane cases.  Now check whether nonexistent headers\n  # can be detected and how.\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n#include <ac_nonexistent.h>\n_ACEOF\nif ac_fn_c_try_cpp \"$LINENO\"; then :\n  # Broken: success on invalid input.\ncontinue\nelse\n  # Passes both tests.\nac_preproc_ok=:\nbreak\nfi\nrm -f conftest.err conftest.i conftest.$ac_ext\n\ndone\n# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.\nrm -f conftest.i conftest.err conftest.$ac_ext\nif $ac_preproc_ok; then :\n\nelse\n  { { $as_echo \"$as_me:${as_lineno-$LINENO}: error: in \\`$ac_pwd':\" >&5\n$as_echo \"$as_me: error: in \\`$ac_pwd':\" >&2;}\nas_fn_error $? \"C preprocessor \\\"$CPP\\\" fails sanity check\nSee \\`config.log' for more details\" \"$LINENO\" 5; }\nfi\n\nac_ext=c\nac_cpp='$CPP $CPPFLAGS'\nac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'\nac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'\nac_compiler_gnu=$ac_cv_c_compiler_gnu\n\n\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e\" >&5\n$as_echo_n \"checking for grep that handles long lines and -e... \" >&6; }\nif ${ac_cv_path_GREP+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if test -z \"$GREP\"; then\n  ac_path_GREP_found=false\n  # Loop through the user's path and test for each of PROGNAME-LIST\n  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_prog in grep ggrep; do\n    for ac_exec_ext in '' $ac_executable_extensions; do\n      ac_path_GREP=\"$as_dir/$ac_prog$ac_exec_ext\"\n      { test -f \"$ac_path_GREP\" && $as_test_x \"$ac_path_GREP\"; } || continue\n# Check for GNU ac_path_GREP and select it if it is found.\n  # Check for GNU $ac_path_GREP\ncase `\"$ac_path_GREP\" --version 2>&1` in\n*GNU*)\n  ac_cv_path_GREP=\"$ac_path_GREP\" ac_path_GREP_found=:;;\n*)\n  ac_count=0\n  $as_echo_n 0123456789 >\"conftest.in\"\n  while :\n  do\n    cat \"conftest.in\" \"conftest.in\" >\"conftest.tmp\"\n    mv \"conftest.tmp\" \"conftest.in\"\n    cp \"conftest.in\" \"conftest.nl\"\n    $as_echo 'GREP' >> \"conftest.nl\"\n    \"$ac_path_GREP\" -e 'GREP$' -e '-(cannot match)-' < \"conftest.nl\" >\"conftest.out\" 2>/dev/null || break\n    diff \"conftest.out\" \"conftest.nl\" >/dev/null 2>&1 || break\n    as_fn_arith $ac_count + 1 && ac_count=$as_val\n    if test $ac_count -gt ${ac_path_GREP_max-0}; then\n      # Best one so far, save it but keep looking for a better one\n      ac_cv_path_GREP=\"$ac_path_GREP\"\n      ac_path_GREP_max=$ac_count\n    fi\n    # 10*(2^10) chars as input seems more than enough\n    test $ac_count -gt 10 && break\n  done\n  rm -f conftest.in conftest.tmp conftest.nl conftest.out;;\nesac\n\n      $ac_path_GREP_found && break 3\n    done\n  done\n  done\nIFS=$as_save_IFS\n  if test -z \"$ac_cv_path_GREP\"; then\n    as_fn_error $? \"no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin\" \"$LINENO\" 5\n  fi\nelse\n  ac_cv_path_GREP=$GREP\nfi\n\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP\" >&5\n$as_echo \"$ac_cv_path_GREP\" >&6; }\n GREP=\"$ac_cv_path_GREP\"\n\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for egrep\" >&5\n$as_echo_n \"checking for egrep... \" >&6; }\nif ${ac_cv_path_EGREP+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if echo a | $GREP -E '(a|b)' >/dev/null 2>&1\n   then ac_cv_path_EGREP=\"$GREP -E\"\n   else\n     if test -z \"$EGREP\"; then\n  ac_path_EGREP_found=false\n  # Loop through the user's path and test for each of PROGNAME-LIST\n  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_prog in egrep; do\n    for ac_exec_ext in '' $ac_executable_extensions; do\n      ac_path_EGREP=\"$as_dir/$ac_prog$ac_exec_ext\"\n      { test -f \"$ac_path_EGREP\" && $as_test_x \"$ac_path_EGREP\"; } || continue\n# Check for GNU ac_path_EGREP and select it if it is found.\n  # Check for GNU $ac_path_EGREP\ncase `\"$ac_path_EGREP\" --version 2>&1` in\n*GNU*)\n  ac_cv_path_EGREP=\"$ac_path_EGREP\" ac_path_EGREP_found=:;;\n*)\n  ac_count=0\n  $as_echo_n 0123456789 >\"conftest.in\"\n  while :\n  do\n    cat \"conftest.in\" \"conftest.in\" >\"conftest.tmp\"\n    mv \"conftest.tmp\" \"conftest.in\"\n    cp \"conftest.in\" \"conftest.nl\"\n    $as_echo 'EGREP' >> \"conftest.nl\"\n    \"$ac_path_EGREP\" 'EGREP$' < \"conftest.nl\" >\"conftest.out\" 2>/dev/null || break\n    diff \"conftest.out\" \"conftest.nl\" >/dev/null 2>&1 || break\n    as_fn_arith $ac_count + 1 && ac_count=$as_val\n    if test $ac_count -gt ${ac_path_EGREP_max-0}; then\n      # Best one so far, save it but keep looking for a better one\n      ac_cv_path_EGREP=\"$ac_path_EGREP\"\n      ac_path_EGREP_max=$ac_count\n    fi\n    # 10*(2^10) chars as input seems more than enough\n    test $ac_count -gt 10 && break\n  done\n  rm -f conftest.in conftest.tmp conftest.nl conftest.out;;\nesac\n\n      $ac_path_EGREP_found && break 3\n    done\n  done\n  done\nIFS=$as_save_IFS\n  if test -z \"$ac_cv_path_EGREP\"; then\n    as_fn_error $? \"no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin\" \"$LINENO\" 5\n  fi\nelse\n  ac_cv_path_EGREP=$EGREP\nfi\n\n   fi\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP\" >&5\n$as_echo \"$ac_cv_path_EGREP\" >&6; }\n EGREP=\"$ac_cv_path_EGREP\"\n\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for ANSI C header files\" >&5\n$as_echo_n \"checking for ANSI C header files... \" >&6; }\nif ${ac_cv_header_stdc+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n#include <stdlib.h>\n#include <stdarg.h>\n#include <string.h>\n#include <float.h>\n\nint\nmain ()\n{\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  ac_cv_header_stdc=yes\nelse\n  ac_cv_header_stdc=no\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\n\nif test $ac_cv_header_stdc = yes; then\n  # SunOS 4.x string.h does not declare mem*, contrary to ANSI.\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n#include <string.h>\n\n_ACEOF\nif (eval \"$ac_cpp conftest.$ac_ext\") 2>&5 |\n  $EGREP \"memchr\" >/dev/null 2>&1; then :\n\nelse\n  ac_cv_header_stdc=no\nfi\nrm -f conftest*\n\nfi\n\nif test $ac_cv_header_stdc = yes; then\n  # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n#include <stdlib.h>\n\n_ACEOF\nif (eval \"$ac_cpp conftest.$ac_ext\") 2>&5 |\n  $EGREP \"free\" >/dev/null 2>&1; then :\n\nelse\n  ac_cv_header_stdc=no\nfi\nrm -f conftest*\n\nfi\n\nif test $ac_cv_header_stdc = yes; then\n  # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.\n  if test \"$cross_compiling\" = yes; then :\n  :\nelse\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n#include <ctype.h>\n#include <stdlib.h>\n#if ((' ' & 0x0FF) == 0x020)\n# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')\n# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))\n#else\n# define ISLOWER(c) \\\n\t\t   (('a' <= (c) && (c) <= 'i') \\\n\t\t     || ('j' <= (c) && (c) <= 'r') \\\n\t\t     || ('s' <= (c) && (c) <= 'z'))\n# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))\n#endif\n\n#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))\nint\nmain ()\n{\n  int i;\n  for (i = 0; i < 256; i++)\n    if (XOR (islower (i), ISLOWER (i))\n\t|| toupper (i) != TOUPPER (i))\n      return 2;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_run \"$LINENO\"; then :\n\nelse\n  ac_cv_header_stdc=no\nfi\nrm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \\\n  conftest.$ac_objext conftest.beam conftest.$ac_ext\nfi\n\nfi\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc\" >&5\n$as_echo \"$ac_cv_header_stdc\" >&6; }\nif test $ac_cv_header_stdc = yes; then\n\n$as_echo \"#define STDC_HEADERS 1\" >>confdefs.h\n\nfi\n\n# On IRIX 5.3, sys/types and inttypes.h are conflicting.\nfor ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \\\n\t\t  inttypes.h stdint.h unistd.h\ndo :\n  as_ac_Header=`$as_echo \"ac_cv_header_$ac_header\" | $as_tr_sh`\nac_fn_c_check_header_compile \"$LINENO\" \"$ac_header\" \"$as_ac_Header\" \"$ac_includes_default\n\"\nif eval test \\\"x\\$\"$as_ac_Header\"\\\" = x\"yes\"; then :\n  cat >>confdefs.h <<_ACEOF\n#define `$as_echo \"HAVE_$ac_header\" | $as_tr_cpp` 1\n_ACEOF\n\nfi\n\ndone\n\n\n# The cast to long int works around a bug in the HP C Compiler\n# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects\n# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.\n# This bug is HP SR number 8606223364.\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking size of void *\" >&5\n$as_echo_n \"checking size of void *... \" >&6; }\nif ${ac_cv_sizeof_void_p+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if ac_fn_c_compute_int \"$LINENO\" \"(long int) (sizeof (void *))\" \"ac_cv_sizeof_void_p\"        \"$ac_includes_default\"; then :\n\nelse\n  if test \"$ac_cv_type_void_p\" = yes; then\n     { { $as_echo \"$as_me:${as_lineno-$LINENO}: error: in \\`$ac_pwd':\" >&5\n$as_echo \"$as_me: error: in \\`$ac_pwd':\" >&2;}\nas_fn_error 77 \"cannot compute sizeof (void *)\nSee \\`config.log' for more details\" \"$LINENO\" 5; }\n   else\n     ac_cv_sizeof_void_p=0\n   fi\nfi\n\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_void_p\" >&5\n$as_echo \"$ac_cv_sizeof_void_p\" >&6; }\n\n\n\ncat >>confdefs.h <<_ACEOF\n#define SIZEOF_VOID_P $ac_cv_sizeof_void_p\n_ACEOF\n\n\nif test \"x${ac_cv_sizeof_void_p}\" = \"x8\" ; then\n  LG_SIZEOF_PTR=3\nelif test \"x${ac_cv_sizeof_void_p}\" = \"x4\" ; then\n  LG_SIZEOF_PTR=2\nelse\n  as_fn_error $? \"Unsupported pointer size: ${ac_cv_sizeof_void_p}\" \"$LINENO\" 5\nfi\ncat >>confdefs.h <<_ACEOF\n#define LG_SIZEOF_PTR $LG_SIZEOF_PTR\n_ACEOF\n\n\n# The cast to long int works around a bug in the HP C Compiler\n# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects\n# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.\n# This bug is HP SR number 8606223364.\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking size of int\" >&5\n$as_echo_n \"checking size of int... \" >&6; }\nif ${ac_cv_sizeof_int+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if ac_fn_c_compute_int \"$LINENO\" \"(long int) (sizeof (int))\" \"ac_cv_sizeof_int\"        \"$ac_includes_default\"; then :\n\nelse\n  if test \"$ac_cv_type_int\" = yes; then\n     { { $as_echo \"$as_me:${as_lineno-$LINENO}: error: in \\`$ac_pwd':\" >&5\n$as_echo \"$as_me: error: in \\`$ac_pwd':\" >&2;}\nas_fn_error 77 \"cannot compute sizeof (int)\nSee \\`config.log' for more details\" \"$LINENO\" 5; }\n   else\n     ac_cv_sizeof_int=0\n   fi\nfi\n\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_int\" >&5\n$as_echo \"$ac_cv_sizeof_int\" >&6; }\n\n\n\ncat >>confdefs.h <<_ACEOF\n#define SIZEOF_INT $ac_cv_sizeof_int\n_ACEOF\n\n\nif test \"x${ac_cv_sizeof_int}\" = \"x8\" ; then\n  LG_SIZEOF_INT=3\nelif test \"x${ac_cv_sizeof_int}\" = \"x4\" ; then\n  LG_SIZEOF_INT=2\nelse\n  as_fn_error $? \"Unsupported int size: ${ac_cv_sizeof_int}\" \"$LINENO\" 5\nfi\ncat >>confdefs.h <<_ACEOF\n#define LG_SIZEOF_INT $LG_SIZEOF_INT\n_ACEOF\n\n\n# The cast to long int works around a bug in the HP C Compiler\n# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects\n# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.\n# This bug is HP SR number 8606223364.\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking size of long\" >&5\n$as_echo_n \"checking size of long... \" >&6; }\nif ${ac_cv_sizeof_long+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if ac_fn_c_compute_int \"$LINENO\" \"(long int) (sizeof (long))\" \"ac_cv_sizeof_long\"        \"$ac_includes_default\"; then :\n\nelse\n  if test \"$ac_cv_type_long\" = yes; then\n     { { $as_echo \"$as_me:${as_lineno-$LINENO}: error: in \\`$ac_pwd':\" >&5\n$as_echo \"$as_me: error: in \\`$ac_pwd':\" >&2;}\nas_fn_error 77 \"cannot compute sizeof (long)\nSee \\`config.log' for more details\" \"$LINENO\" 5; }\n   else\n     ac_cv_sizeof_long=0\n   fi\nfi\n\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_long\" >&5\n$as_echo \"$ac_cv_sizeof_long\" >&6; }\n\n\n\ncat >>confdefs.h <<_ACEOF\n#define SIZEOF_LONG $ac_cv_sizeof_long\n_ACEOF\n\n\nif test \"x${ac_cv_sizeof_long}\" = \"x8\" ; then\n  LG_SIZEOF_LONG=3\nelif test \"x${ac_cv_sizeof_long}\" = \"x4\" ; then\n  LG_SIZEOF_LONG=2\nelse\n  as_fn_error $? \"Unsupported long size: ${ac_cv_sizeof_long}\" \"$LINENO\" 5\nfi\ncat >>confdefs.h <<_ACEOF\n#define LG_SIZEOF_LONG $LG_SIZEOF_LONG\n_ACEOF\n\n\n# The cast to long int works around a bug in the HP C Compiler\n# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects\n# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.\n# This bug is HP SR number 8606223364.\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking size of intmax_t\" >&5\n$as_echo_n \"checking size of intmax_t... \" >&6; }\nif ${ac_cv_sizeof_intmax_t+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if ac_fn_c_compute_int \"$LINENO\" \"(long int) (sizeof (intmax_t))\" \"ac_cv_sizeof_intmax_t\"        \"$ac_includes_default\"; then :\n\nelse\n  if test \"$ac_cv_type_intmax_t\" = yes; then\n     { { $as_echo \"$as_me:${as_lineno-$LINENO}: error: in \\`$ac_pwd':\" >&5\n$as_echo \"$as_me: error: in \\`$ac_pwd':\" >&2;}\nas_fn_error 77 \"cannot compute sizeof (intmax_t)\nSee \\`config.log' for more details\" \"$LINENO\" 5; }\n   else\n     ac_cv_sizeof_intmax_t=0\n   fi\nfi\n\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_intmax_t\" >&5\n$as_echo \"$ac_cv_sizeof_intmax_t\" >&6; }\n\n\n\ncat >>confdefs.h <<_ACEOF\n#define SIZEOF_INTMAX_T $ac_cv_sizeof_intmax_t\n_ACEOF\n\n\nif test \"x${ac_cv_sizeof_intmax_t}\" = \"x16\" ; then\n  LG_SIZEOF_INTMAX_T=4\nelif test \"x${ac_cv_sizeof_intmax_t}\" = \"x8\" ; then\n  LG_SIZEOF_INTMAX_T=3\nelif test \"x${ac_cv_sizeof_intmax_t}\" = \"x4\" ; then\n  LG_SIZEOF_INTMAX_T=2\nelse\n  as_fn_error $? \"Unsupported intmax_t size: ${ac_cv_sizeof_intmax_t}\" \"$LINENO\" 5\nfi\ncat >>confdefs.h <<_ACEOF\n#define LG_SIZEOF_INTMAX_T $LG_SIZEOF_INTMAX_T\n_ACEOF\n\n\nac_aux_dir=\nfor ac_dir in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"; do\n  if test -f \"$ac_dir/install-sh\"; then\n    ac_aux_dir=$ac_dir\n    ac_install_sh=\"$ac_aux_dir/install-sh -c\"\n    break\n  elif test -f \"$ac_dir/install.sh\"; then\n    ac_aux_dir=$ac_dir\n    ac_install_sh=\"$ac_aux_dir/install.sh -c\"\n    break\n  elif test -f \"$ac_dir/shtool\"; then\n    ac_aux_dir=$ac_dir\n    ac_install_sh=\"$ac_aux_dir/shtool install -c\"\n    break\n  fi\ndone\nif test -z \"$ac_aux_dir\"; then\n  as_fn_error $? \"cannot find install-sh, install.sh, or shtool in \\\"$srcdir\\\" \\\"$srcdir/..\\\" \\\"$srcdir/../..\\\"\" \"$LINENO\" 5\nfi\n\n# These three variables are undocumented and unsupported,\n# and are intended to be withdrawn in a future Autoconf release.\n# They can cause serious problems if a builder's source tree is in a directory\n# whose full name contains unusual characters.\nac_config_guess=\"$SHELL $ac_aux_dir/config.guess\"  # Please don't use this var.\nac_config_sub=\"$SHELL $ac_aux_dir/config.sub\"  # Please don't use this var.\nac_configure=\"$SHELL $ac_aux_dir/configure\"  # Please don't use this var.\n\n\n# Make sure we can run config.sub.\n$SHELL \"$ac_aux_dir/config.sub\" sun4 >/dev/null 2>&1 ||\n  as_fn_error $? \"cannot run $SHELL $ac_aux_dir/config.sub\" \"$LINENO\" 5\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking build system type\" >&5\n$as_echo_n \"checking build system type... \" >&6; }\nif ${ac_cv_build+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  ac_build_alias=$build_alias\ntest \"x$ac_build_alias\" = x &&\n  ac_build_alias=`$SHELL \"$ac_aux_dir/config.guess\"`\ntest \"x$ac_build_alias\" = x &&\n  as_fn_error $? \"cannot guess build type; you must specify one\" \"$LINENO\" 5\nac_cv_build=`$SHELL \"$ac_aux_dir/config.sub\" $ac_build_alias` ||\n  as_fn_error $? \"$SHELL $ac_aux_dir/config.sub $ac_build_alias failed\" \"$LINENO\" 5\n\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_cv_build\" >&5\n$as_echo \"$ac_cv_build\" >&6; }\ncase $ac_cv_build in\n*-*-*) ;;\n*) as_fn_error $? \"invalid value of canonical build\" \"$LINENO\" 5;;\nesac\nbuild=$ac_cv_build\nac_save_IFS=$IFS; IFS='-'\nset x $ac_cv_build\nshift\nbuild_cpu=$1\nbuild_vendor=$2\nshift; shift\n# Remember, the first character of IFS is used to create $*,\n# except with old shells:\nbuild_os=$*\nIFS=$ac_save_IFS\ncase $build_os in *\\ *) build_os=`echo \"$build_os\" | sed 's/ /-/g'`;; esac\n\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking host system type\" >&5\n$as_echo_n \"checking host system type... \" >&6; }\nif ${ac_cv_host+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if test \"x$host_alias\" = x; then\n  ac_cv_host=$ac_cv_build\nelse\n  ac_cv_host=`$SHELL \"$ac_aux_dir/config.sub\" $host_alias` ||\n    as_fn_error $? \"$SHELL $ac_aux_dir/config.sub $host_alias failed\" \"$LINENO\" 5\nfi\n\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_cv_host\" >&5\n$as_echo \"$ac_cv_host\" >&6; }\ncase $ac_cv_host in\n*-*-*) ;;\n*) as_fn_error $? \"invalid value of canonical host\" \"$LINENO\" 5;;\nesac\nhost=$ac_cv_host\nac_save_IFS=$IFS; IFS='-'\nset x $ac_cv_host\nshift\nhost_cpu=$1\nhost_vendor=$2\nshift; shift\n# Remember, the first character of IFS is used to create $*,\n# except with old shells:\nhost_os=$*\nIFS=$ac_save_IFS\ncase $host_os in *\\ *) host_os=`echo \"$host_os\" | sed 's/ /-/g'`;; esac\n\n\nCPU_SPINWAIT=\"\"\ncase \"${host_cpu}\" in\n  i[345]86)\n\t;;\n  i686)\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether __asm__ is compilable\" >&5\n$as_echo_n \"checking whether __asm__ is compilable... \" >&6; }\nif ${je_cv_asm+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\nint\nmain ()\n{\n__asm__ volatile(\"pause\"); return 0;\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_link \"$LINENO\"; then :\n  je_cv_asm=yes\nelse\n  je_cv_asm=no\nfi\nrm -f core conftest.err conftest.$ac_objext \\\n    conftest$ac_exeext conftest.$ac_ext\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $je_cv_asm\" >&5\n$as_echo \"$je_cv_asm\" >&6; }\n\n\tif test \"x${je_cv_asm}\" = \"xyes\" ; then\n\t    CPU_SPINWAIT='__asm__ volatile(\"pause\")'\n\tfi\n\t;;\n  x86_64)\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether __asm__ syntax is compilable\" >&5\n$as_echo_n \"checking whether __asm__ syntax is compilable... \" >&6; }\nif ${je_cv_asm+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\nint\nmain ()\n{\n__asm__ volatile(\"pause\"); return 0;\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_link \"$LINENO\"; then :\n  je_cv_asm=yes\nelse\n  je_cv_asm=no\nfi\nrm -f core conftest.err conftest.$ac_objext \\\n    conftest$ac_exeext conftest.$ac_ext\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $je_cv_asm\" >&5\n$as_echo \"$je_cv_asm\" >&6; }\n\n\tif test \"x${je_cv_asm}\" = \"xyes\" ; then\n\t    CPU_SPINWAIT='__asm__ volatile(\"pause\")'\n\tfi\n\t;;\n  *)\n\t;;\nesac\ncat >>confdefs.h <<_ACEOF\n#define CPU_SPINWAIT $CPU_SPINWAIT\n_ACEOF\n\n\nLD_PRELOAD_VAR=\"LD_PRELOAD\"\nso=\"so\"\nimportlib=\"${so}\"\no=\"$ac_objext\"\na=\"a\"\nexe=\"$ac_exeext\"\nlibprefix=\"lib\"\nDSO_LDFLAGS='-shared -Wl,-soname,$(@F)'\nRPATH='-Wl,-rpath,$(1)'\nSOREV=\"${so}.${rev}\"\nPIC_CFLAGS='-fPIC -DPIC'\nCTARGET='-o $@'\nLDTARGET='-o $@'\nEXTRA_LDFLAGS=\nMKLIB='ar crus $@'\nCC_MM=1\n\ndefault_munmap=\"1\"\nJEMALLOC_USABLE_SIZE_CONST=\"const\"\ncase \"${host}\" in\n  *-*-darwin*)\n\tCFLAGS=\"$CFLAGS\"\n\tabi=\"macho\"\n\t$as_echo \"#define JEMALLOC_PURGE_MADVISE_FREE  \" >>confdefs.h\n\n\tRPATH=\"\"\n\tLD_PRELOAD_VAR=\"DYLD_INSERT_LIBRARIES\"\n\tso=\"dylib\"\n\timportlib=\"${so}\"\n\tforce_tls=\"0\"\n\tDSO_LDFLAGS='-shared -Wl,-dylib_install_name,$(@F)'\n\tSOREV=\"${rev}.${so}\"\n\t;;\n  *-*-freebsd*)\n\tCFLAGS=\"$CFLAGS\"\n\tabi=\"elf\"\n\t$as_echo \"#define JEMALLOC_PURGE_MADVISE_FREE  \" >>confdefs.h\n\n\tforce_lazy_lock=\"1\"\n\t;;\n  *-*-linux*)\n\tCFLAGS=\"$CFLAGS\"\n\tCPPFLAGS=\"$CPPFLAGS -D_GNU_SOURCE\"\n\tabi=\"elf\"\n\t$as_echo \"#define JEMALLOC_PURGE_MADVISE_DONTNEED  \" >>confdefs.h\n\n\t$as_echo \"#define JEMALLOC_THREADED_INIT  \" >>confdefs.h\n\n\tJEMALLOC_USABLE_SIZE_CONST=\"\"\n\tdefault_munmap=\"0\"\n\t;;\n  *-*-netbsd*)\n\t{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking ABI\" >&5\n$as_echo_n \"checking ABI... \" >&6; }\n        cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n#ifdef __ELF__\n/* ELF */\n#else\n#error aout\n#endif\n\nint\nmain ()\n{\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  CFLAGS=\"$CFLAGS\"; abi=\"elf\"\nelse\n  abi=\"aout\"\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\n\t{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $abi\" >&5\n$as_echo \"$abi\" >&6; }\n\t$as_echo \"#define JEMALLOC_PURGE_MADVISE_FREE  \" >>confdefs.h\n\n\t;;\n  *-*-solaris2*)\n\tCFLAGS=\"$CFLAGS\"\n\tabi=\"elf\"\n\tRPATH='-Wl,-R,$(1)'\n\t\tCPPFLAGS=\"$CPPFLAGS -D_POSIX_PTHREAD_SEMANTICS\"\n\tLIBS=\"$LIBS -lposix4 -lsocket -lnsl\"\n\t;;\n  *-ibm-aix*)\n\tif \"$LG_SIZEOF_PTR\" = \"8\"; then\n\t  \t  LD_PRELOAD_VAR=\"LDR_PRELOAD64\"\n\telse\n\t  \t  LD_PRELOAD_VAR=\"LDR_PRELOAD\"\n\tfi\n\tabi=\"xcoff\"\n\t;;\n  *-*-mingw*)\n\tabi=\"pecoff\"\n\tforce_tls=\"0\"\n\tRPATH=\"\"\n\tso=\"dll\"\n\tif test \"x$je_cv_msvc\" = \"xyes\" ; then\n\t  importlib=\"lib\"\n\t  DSO_LDFLAGS=\"-LD\"\n\t  EXTRA_LDFLAGS=\"-link -DEBUG\"\n\t  CTARGET='-Fo$@'\n\t  LDTARGET='-Fe$@'\n\t  MKLIB='lib -nologo -out:$@'\n\t  CC_MM=\n        else\n\t  importlib=\"${so}\"\n\t  DSO_LDFLAGS=\"-shared\"\n\tfi\n\ta=\"lib\"\n\tlibprefix=\"\"\n\tSOREV=\"${so}\"\n\tPIC_CFLAGS=\"\"\n\t;;\n  *)\n\t{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: Unsupported operating system: ${host}\" >&5\n$as_echo \"Unsupported operating system: ${host}\" >&6; }\n\tabi=\"elf\"\n\t;;\nesac\ncat >>confdefs.h <<_ACEOF\n#define JEMALLOC_USABLE_SIZE_CONST $JEMALLOC_USABLE_SIZE_CONST\n_ACEOF\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nif test \"x$abi\" != \"xpecoff\"; then\n    LIBS=\"$LIBS -lm\"\nfi\n\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether __attribute__ syntax is compilable\" >&5\n$as_echo_n \"checking whether __attribute__ syntax is compilable... \" >&6; }\nif ${je_cv_attribute+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\nstatic __attribute__((unused)) void foo(void){}\nint\nmain ()\n{\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_link \"$LINENO\"; then :\n  je_cv_attribute=yes\nelse\n  je_cv_attribute=no\nfi\nrm -f core conftest.err conftest.$ac_objext \\\n    conftest$ac_exeext conftest.$ac_ext\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $je_cv_attribute\" >&5\n$as_echo \"$je_cv_attribute\" >&6; }\n\nif test \"x${je_cv_attribute}\" = \"xyes\" ; then\n  $as_echo \"#define JEMALLOC_HAVE_ATTR  \" >>confdefs.h\n\n  if test \"x${GCC}\" = \"xyes\" -a \"x${abi}\" = \"xelf\"; then\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether compiler supports -fvisibility=hidden\" >&5\n$as_echo_n \"checking whether compiler supports -fvisibility=hidden... \" >&6; }\nTCFLAGS=\"${CFLAGS}\"\nif test \"x${CFLAGS}\" = \"x\" ; then\n  CFLAGS=\"-fvisibility=hidden\"\nelse\n  CFLAGS=\"${CFLAGS} -fvisibility=hidden\"\nfi\ncat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\n\nint\nmain ()\n{\n\n    return 0;\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: yes\" >&5\n$as_echo \"yes\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\n              CFLAGS=\"${TCFLAGS}\"\n\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\n\n  fi\nfi\nSAVED_CFLAGS=\"${CFLAGS}\"\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether compiler supports -Werror\" >&5\n$as_echo_n \"checking whether compiler supports -Werror... \" >&6; }\nTCFLAGS=\"${CFLAGS}\"\nif test \"x${CFLAGS}\" = \"x\" ; then\n  CFLAGS=\"-Werror\"\nelse\n  CFLAGS=\"${CFLAGS} -Werror\"\nfi\ncat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\n\nint\nmain ()\n{\n\n    return 0;\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: yes\" >&5\n$as_echo \"yes\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\n              CFLAGS=\"${TCFLAGS}\"\n\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\n\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether tls_model attribute is compilable\" >&5\n$as_echo_n \"checking whether tls_model attribute is compilable... \" >&6; }\nif ${je_cv_tls_model+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\nint\nmain ()\n{\nstatic __thread int\n               __attribute__((tls_model(\"initial-exec\"))) foo;\n               foo = 0;\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_link \"$LINENO\"; then :\n  je_cv_tls_model=yes\nelse\n  je_cv_tls_model=no\nfi\nrm -f core conftest.err conftest.$ac_objext \\\n    conftest$ac_exeext conftest.$ac_ext\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $je_cv_tls_model\" >&5\n$as_echo \"$je_cv_tls_model\" >&6; }\n\nCFLAGS=\"${SAVED_CFLAGS}\"\nif test \"x${je_cv_tls_model}\" = \"xyes\" ; then\n  $as_echo \"#define JEMALLOC_TLS_MODEL __attribute__((tls_model(\\\"initial-exec\\\")))\" >>confdefs.h\n\nelse\n  $as_echo \"#define JEMALLOC_TLS_MODEL  \" >>confdefs.h\n\nfi\n\n\n# Check whether --with-rpath was given.\nif test \"${with_rpath+set}\" = set; then :\n  withval=$with_rpath; if test \"x$with_rpath\" = \"xno\" ; then\n  RPATH_EXTRA=\nelse\n  RPATH_EXTRA=\"`echo $with_rpath | tr \\\":\\\" \\\" \\\"`\"\nfi\nelse\n  RPATH_EXTRA=\n\nfi\n\n\n\n# Check whether --enable-autogen was given.\nif test \"${enable_autogen+set}\" = set; then :\n  enableval=$enable_autogen; if test \"x$enable_autogen\" = \"xno\" ; then\n  enable_autogen=\"0\"\nelse\n  enable_autogen=\"1\"\nfi\n\nelse\n  enable_autogen=\"0\"\n\nfi\n\n\n\n# Find a good install program.  We prefer a C program (faster),\n# so one script is as good as another.  But avoid the broken or\n# incompatible versions:\n# SysV /etc/install, /usr/sbin/install\n# SunOS /usr/etc/install\n# IRIX /sbin/install\n# AIX /bin/install\n# AmigaOS /C/install, which installs bootblocks on floppy discs\n# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag\n# AFS /usr/afsws/bin/install, which mishandles nonexistent args\n# SVR4 /usr/ucb/install, which tries to use the nonexistent group \"staff\"\n# OS/2's system install, which has a completely different semantic\n# ./install, which can be erroneously created by make from ./install.sh.\n# Reject install programs that cannot install multiple files.\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install\" >&5\n$as_echo_n \"checking for a BSD-compatible install... \" >&6; }\nif test -z \"$INSTALL\"; then\nif ${ac_cv_path_install+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    # Account for people who put trailing slashes in PATH elements.\ncase $as_dir/ in #((\n  ./ | .// | /[cC]/* | \\\n  /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \\\n  ?:[\\\\/]os2[\\\\/]install[\\\\/]* | ?:[\\\\/]OS2[\\\\/]INSTALL[\\\\/]* | \\\n  /usr/ucb/* ) ;;\n  *)\n    # OSF1 and SCO ODT 3.0 have their own names for install.\n    # Don't use installbsd from OSF since it installs stuff as root\n    # by default.\n    for ac_prog in ginstall scoinst install; do\n      for ac_exec_ext in '' $ac_executable_extensions; do\n\tif { test -f \"$as_dir/$ac_prog$ac_exec_ext\" && $as_test_x \"$as_dir/$ac_prog$ac_exec_ext\"; }; then\n\t  if test $ac_prog = install &&\n\t    grep dspmsg \"$as_dir/$ac_prog$ac_exec_ext\" >/dev/null 2>&1; then\n\t    # AIX install.  It has an incompatible calling convention.\n\t    :\n\t  elif test $ac_prog = install &&\n\t    grep pwplus \"$as_dir/$ac_prog$ac_exec_ext\" >/dev/null 2>&1; then\n\t    # program-specific install script used by HP pwplus--don't use.\n\t    :\n\t  else\n\t    rm -rf conftest.one conftest.two conftest.dir\n\t    echo one > conftest.one\n\t    echo two > conftest.two\n\t    mkdir conftest.dir\n\t    if \"$as_dir/$ac_prog$ac_exec_ext\" -c conftest.one conftest.two \"`pwd`/conftest.dir\" &&\n\t      test -s conftest.one && test -s conftest.two &&\n\t      test -s conftest.dir/conftest.one &&\n\t      test -s conftest.dir/conftest.two\n\t    then\n\t      ac_cv_path_install=\"$as_dir/$ac_prog$ac_exec_ext -c\"\n\t      break 3\n\t    fi\n\t  fi\n\tfi\n      done\n    done\n    ;;\nesac\n\n  done\nIFS=$as_save_IFS\n\nrm -rf conftest.one conftest.two conftest.dir\n\nfi\n  if test \"${ac_cv_path_install+set}\" = set; then\n    INSTALL=$ac_cv_path_install\n  else\n    # As a last resort, use the slow shell script.  Don't cache a\n    # value for INSTALL within a source directory, because that will\n    # break other packages using the cache if that directory is\n    # removed, or if the value is a relative name.\n    INSTALL=$ac_install_sh\n  fi\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $INSTALL\" >&5\n$as_echo \"$INSTALL\" >&6; }\n\n# Use test -z because SunOS4 sh mishandles braces in ${var-val}.\n# It thinks the first close brace ends the variable substitution.\ntest -z \"$INSTALL_PROGRAM\" && INSTALL_PROGRAM='${INSTALL}'\n\ntest -z \"$INSTALL_SCRIPT\" && INSTALL_SCRIPT='${INSTALL}'\n\ntest -z \"$INSTALL_DATA\" && INSTALL_DATA='${INSTALL} -m 644'\n\nif test -n \"$ac_tool_prefix\"; then\n  # Extract the first word of \"${ac_tool_prefix}ranlib\", so it can be a program name with args.\nset dummy ${ac_tool_prefix}ranlib; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_prog_RANLIB+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if test -n \"$RANLIB\"; then\n  ac_cv_prog_RANLIB=\"$RANLIB\" # Let the user override the test.\nelse\nas_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if { test -f \"$as_dir/$ac_word$ac_exec_ext\" && $as_test_x \"$as_dir/$ac_word$ac_exec_ext\"; }; then\n    ac_cv_prog_RANLIB=\"${ac_tool_prefix}ranlib\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\nfi\nfi\nRANLIB=$ac_cv_prog_RANLIB\nif test -n \"$RANLIB\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $RANLIB\" >&5\n$as_echo \"$RANLIB\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n\nfi\nif test -z \"$ac_cv_prog_RANLIB\"; then\n  ac_ct_RANLIB=$RANLIB\n  # Extract the first word of \"ranlib\", so it can be a program name with args.\nset dummy ranlib; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_prog_ac_ct_RANLIB+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if test -n \"$ac_ct_RANLIB\"; then\n  ac_cv_prog_ac_ct_RANLIB=\"$ac_ct_RANLIB\" # Let the user override the test.\nelse\nas_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if { test -f \"$as_dir/$ac_word$ac_exec_ext\" && $as_test_x \"$as_dir/$ac_word$ac_exec_ext\"; }; then\n    ac_cv_prog_ac_ct_RANLIB=\"ranlib\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\nfi\nfi\nac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB\nif test -n \"$ac_ct_RANLIB\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB\" >&5\n$as_echo \"$ac_ct_RANLIB\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n  if test \"x$ac_ct_RANLIB\" = x; then\n    RANLIB=\":\"\n  else\n    case $cross_compiling:$ac_tool_warned in\nyes:)\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet\" >&5\n$as_echo \"$as_me: WARNING: using cross tools not prefixed with host triplet\" >&2;}\nac_tool_warned=yes ;;\nesac\n    RANLIB=$ac_ct_RANLIB\n  fi\nelse\n  RANLIB=\"$ac_cv_prog_RANLIB\"\nfi\n\n# Extract the first word of \"ar\", so it can be a program name with args.\nset dummy ar; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_path_AR+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  case $AR in\n  [\\\\/]* | ?:[\\\\/]*)\n  ac_cv_path_AR=\"$AR\" # Let the user override the test with a path.\n  ;;\n  *)\n  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if { test -f \"$as_dir/$ac_word$ac_exec_ext\" && $as_test_x \"$as_dir/$ac_word$ac_exec_ext\"; }; then\n    ac_cv_path_AR=\"$as_dir/$ac_word$ac_exec_ext\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\n  ;;\nesac\nfi\nAR=$ac_cv_path_AR\nif test -n \"$AR\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $AR\" >&5\n$as_echo \"$AR\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n\n# Extract the first word of \"ld\", so it can be a program name with args.\nset dummy ld; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_path_LD+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  case $LD in\n  [\\\\/]* | ?:[\\\\/]*)\n  ac_cv_path_LD=\"$LD\" # Let the user override the test with a path.\n  ;;\n  *)\n  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if { test -f \"$as_dir/$ac_word$ac_exec_ext\" && $as_test_x \"$as_dir/$ac_word$ac_exec_ext\"; }; then\n    ac_cv_path_LD=\"$as_dir/$ac_word$ac_exec_ext\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\n  ;;\nesac\nfi\nLD=$ac_cv_path_LD\nif test -n \"$LD\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $LD\" >&5\n$as_echo \"$LD\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n\n# Extract the first word of \"autoconf\", so it can be a program name with args.\nset dummy autoconf; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_path_AUTOCONF+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  case $AUTOCONF in\n  [\\\\/]* | ?:[\\\\/]*)\n  ac_cv_path_AUTOCONF=\"$AUTOCONF\" # Let the user override the test with a path.\n  ;;\n  *)\n  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if { test -f \"$as_dir/$ac_word$ac_exec_ext\" && $as_test_x \"$as_dir/$ac_word$ac_exec_ext\"; }; then\n    ac_cv_path_AUTOCONF=\"$as_dir/$ac_word$ac_exec_ext\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\n  ;;\nesac\nfi\nAUTOCONF=$ac_cv_path_AUTOCONF\nif test -n \"$AUTOCONF\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $AUTOCONF\" >&5\n$as_echo \"$AUTOCONF\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n\n\npublic_syms=\"malloc_conf malloc_message malloc calloc posix_memalign aligned_alloc realloc free malloc_usable_size malloc_stats_print mallctl mallctlnametomib mallctlbymib\"\n\nac_fn_c_check_func \"$LINENO\" \"memalign\" \"ac_cv_func_memalign\"\nif test \"x$ac_cv_func_memalign\" = xyes; then :\n  $as_echo \"#define JEMALLOC_OVERRIDE_MEMALIGN  \" >>confdefs.h\n\n\t       public_syms=\"${public_syms} memalign\"\nfi\n\nac_fn_c_check_func \"$LINENO\" \"valloc\" \"ac_cv_func_valloc\"\nif test \"x$ac_cv_func_valloc\" = xyes; then :\n  $as_echo \"#define JEMALLOC_OVERRIDE_VALLOC  \" >>confdefs.h\n\n\t       public_syms=\"${public_syms} valloc\"\nfi\n\n\n# Check whether --enable-experimental was given.\nif test \"${enable_experimental+set}\" = set; then :\n  enableval=$enable_experimental; if test \"x$enable_experimental\" = \"xno\" ; then\n  enable_experimental=\"0\"\nelse\n  enable_experimental=\"1\"\nfi\n\nelse\n  enable_experimental=\"1\"\n\nfi\n\nif test \"x$enable_experimental\" = \"x1\" ; then\n  $as_echo \"#define JEMALLOC_EXPERIMENTAL  \" >>confdefs.h\n\n  public_syms=\"${public_syms} allocm dallocm nallocm rallocm sallocm\"\nfi\n\n\n\n# Check whether --with-mangling was given.\nif test \"${with_mangling+set}\" = set; then :\n  withval=$with_mangling; mangling_map=\"$with_mangling\"\nelse\n  mangling_map=\"\"\nfi\n\nfor nm in `echo ${mangling_map} |tr ',' ' '` ; do\n  k=\"`echo ${nm} |tr ':' ' ' |awk '{print $1}'`\"\n  n=\"je_${k}\"\n  m=`echo ${nm} |tr ':' ' ' |awk '{print $2}'`\n  cat >>confdefs.h <<_ACEOF\n#define ${n} ${m}\n_ACEOF\n\n    public_syms=`for sym in ${public_syms}; do echo \"${sym}\"; done |grep -v \"^${k}\\$\" |tr '\\n' ' '`\ndone\n\n\n# Check whether --with-jemalloc_prefix was given.\nif test \"${with_jemalloc_prefix+set}\" = set; then :\n  withval=$with_jemalloc_prefix; JEMALLOC_PREFIX=\"$with_jemalloc_prefix\"\nelse\n  if test \"x$abi\" != \"xmacho\" -a \"x$abi\" != \"xpecoff\"; then\n  JEMALLOC_PREFIX=\"\"\nelse\n  JEMALLOC_PREFIX=\"je_\"\nfi\n\nfi\n\nif test \"x$JEMALLOC_PREFIX\" != \"x\" ; then\n  JEMALLOC_CPREFIX=`echo ${JEMALLOC_PREFIX} | tr \"a-z\" \"A-Z\"`\n  cat >>confdefs.h <<_ACEOF\n#define JEMALLOC_PREFIX \"$JEMALLOC_PREFIX\"\n_ACEOF\n\n  cat >>confdefs.h <<_ACEOF\n#define JEMALLOC_CPREFIX \"$JEMALLOC_CPREFIX\"\n_ACEOF\n\nfi\nfor stem in ${public_syms}; do\n  n=\"je_${stem}\"\n  m=\"${JEMALLOC_PREFIX}${stem}\"\n  cat >>confdefs.h <<_ACEOF\n#define ${n} ${m}\n_ACEOF\n\ndone\n\n\n# Check whether --with-private_namespace was given.\nif test \"${with_private_namespace+set}\" = set; then :\n  withval=$with_private_namespace; JEMALLOC_PRIVATE_NAMESPACE=\"$with_private_namespace\"\nelse\n  JEMALLOC_PRIVATE_NAMESPACE=\"\"\n\nfi\n\ncat >>confdefs.h <<_ACEOF\n#define JEMALLOC_PRIVATE_NAMESPACE \"$JEMALLOC_PRIVATE_NAMESPACE\"\n_ACEOF\n\nif test \"x$JEMALLOC_PRIVATE_NAMESPACE\" != \"x\" ; then\n  cat >>confdefs.h <<_ACEOF\n#define JEMALLOC_N(string_that_no_one_should_want_to_use_as_a_jemalloc_private_namespace_prefix) ${JEMALLOC_PRIVATE_NAMESPACE}##string_that_no_one_should_want_to_use_as_a_jemalloc_private_namespace_prefix\n_ACEOF\n\nelse\n  cat >>confdefs.h <<_ACEOF\n#define JEMALLOC_N(string_that_no_one_should_want_to_use_as_a_jemalloc_private_namespace_prefix) string_that_no_one_should_want_to_use_as_a_jemalloc_private_namespace_prefix\n_ACEOF\n\nfi\n\n\n# Check whether --with-install_suffix was given.\nif test \"${with_install_suffix+set}\" = set; then :\n  withval=$with_install_suffix; INSTALL_SUFFIX=\"$with_install_suffix\"\nelse\n  INSTALL_SUFFIX=\n\nfi\n\ninstall_suffix=\"$INSTALL_SUFFIX\"\n\n\ncfgoutputs_in=\"${srcroot}Makefile.in\"\ncfgoutputs_in=\"${cfgoutputs_in} ${srcroot}doc/html.xsl.in\"\ncfgoutputs_in=\"${cfgoutputs_in} ${srcroot}doc/manpages.xsl.in\"\ncfgoutputs_in=\"${cfgoutputs_in} ${srcroot}doc/jemalloc.xml.in\"\ncfgoutputs_in=\"${cfgoutputs_in} ${srcroot}include/jemalloc/jemalloc.h.in\"\ncfgoutputs_in=\"${cfgoutputs_in} ${srcroot}include/jemalloc/internal/jemalloc_internal.h.in\"\ncfgoutputs_in=\"${cfgoutputs_in} ${srcroot}test/jemalloc_test.h.in\"\n\ncfgoutputs_out=\"Makefile\"\ncfgoutputs_out=\"${cfgoutputs_out} doc/html.xsl\"\ncfgoutputs_out=\"${cfgoutputs_out} doc/manpages.xsl\"\ncfgoutputs_out=\"${cfgoutputs_out} doc/jemalloc${install_suffix}.xml\"\ncfgoutputs_out=\"${cfgoutputs_out} include/jemalloc/jemalloc${install_suffix}.h\"\ncfgoutputs_out=\"${cfgoutputs_out} include/jemalloc/internal/jemalloc_internal.h\"\ncfgoutputs_out=\"${cfgoutputs_out} test/jemalloc_test.h\"\n\ncfgoutputs_tup=\"Makefile\"\ncfgoutputs_tup=\"${cfgoutputs_tup} doc/html.xsl:doc/html.xsl.in\"\ncfgoutputs_tup=\"${cfgoutputs_tup} doc/manpages.xsl:doc/manpages.xsl.in\"\ncfgoutputs_tup=\"${cfgoutputs_tup} doc/jemalloc${install_suffix}.xml:doc/jemalloc.xml.in\"\ncfgoutputs_tup=\"${cfgoutputs_tup} include/jemalloc/jemalloc${install_suffix}.h:include/jemalloc/jemalloc.h.in\"\ncfgoutputs_tup=\"${cfgoutputs_tup} include/jemalloc/internal/jemalloc_internal.h\"\ncfgoutputs_tup=\"${cfgoutputs_tup} test/jemalloc_test.h:test/jemalloc_test.h.in\"\n\ncfghdrs_in=\"${srcroot}include/jemalloc/jemalloc_defs.h.in\"\ncfghdrs_in=\"${cfghdrs_in} ${srcroot}include/jemalloc/internal/size_classes.sh\"\n\ncfghdrs_out=\"include/jemalloc/jemalloc_defs${install_suffix}.h\"\ncfghdrs_out=\"${cfghdrs_out} include/jemalloc/internal/size_classes.h\"\n\ncfghdrs_tup=\"include/jemalloc/jemalloc_defs${install_suffix}.h:include/jemalloc/jemalloc_defs.h.in\"\n\n# Check whether --enable-cc-silence was given.\nif test \"${enable_cc_silence+set}\" = set; then :\n  enableval=$enable_cc_silence; if test \"x$enable_cc_silence\" = \"xno\" ; then\n  enable_cc_silence=\"0\"\nelse\n  enable_cc_silence=\"1\"\nfi\n\nelse\n  enable_cc_silence=\"0\"\n\nfi\n\nif test \"x$enable_cc_silence\" = \"x1\" ; then\n  $as_echo \"#define JEMALLOC_CC_SILENCE  \" >>confdefs.h\n\nfi\n\n# Check whether --enable-debug was given.\nif test \"${enable_debug+set}\" = set; then :\n  enableval=$enable_debug; if test \"x$enable_debug\" = \"xno\" ; then\n  enable_debug=\"0\"\nelse\n  enable_debug=\"1\"\nfi\n\nelse\n  enable_debug=\"0\"\n\nfi\n\nif test \"x$enable_debug\" = \"x1\" ; then\n  $as_echo \"#define JEMALLOC_DEBUG  \" >>confdefs.h\n\n  $as_echo \"#define JEMALLOC_IVSALLOC  \" >>confdefs.h\n\nfi\n\n\nif test \"x$enable_debug\" = \"x0\" -a \"x$no_CFLAGS\" = \"xyes\" ; then\n    optimize=\"no\"\n  echo \"$EXTRA_CFLAGS\" | grep \"\\-O\" >/dev/null || optimize=\"yes\"\n  if test \"x${optimize}\" = \"xyes\" ; then\n    if test \"x$GCC\" = \"xyes\" ; then\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether compiler supports -O3\" >&5\n$as_echo_n \"checking whether compiler supports -O3... \" >&6; }\nTCFLAGS=\"${CFLAGS}\"\nif test \"x${CFLAGS}\" = \"x\" ; then\n  CFLAGS=\"-O3\"\nelse\n  CFLAGS=\"${CFLAGS} -O3\"\nfi\ncat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\n\nint\nmain ()\n{\n\n    return 0;\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: yes\" >&5\n$as_echo \"yes\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\n              CFLAGS=\"${TCFLAGS}\"\n\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\n\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether compiler supports -funroll-loops\" >&5\n$as_echo_n \"checking whether compiler supports -funroll-loops... \" >&6; }\nTCFLAGS=\"${CFLAGS}\"\nif test \"x${CFLAGS}\" = \"x\" ; then\n  CFLAGS=\"-funroll-loops\"\nelse\n  CFLAGS=\"${CFLAGS} -funroll-loops\"\nfi\ncat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\n\nint\nmain ()\n{\n\n    return 0;\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: yes\" >&5\n$as_echo \"yes\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\n              CFLAGS=\"${TCFLAGS}\"\n\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\n\n    elif test \"x$je_cv_msvc\" = \"xyes\" ; then\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether compiler supports -O2\" >&5\n$as_echo_n \"checking whether compiler supports -O2... \" >&6; }\nTCFLAGS=\"${CFLAGS}\"\nif test \"x${CFLAGS}\" = \"x\" ; then\n  CFLAGS=\"-O2\"\nelse\n  CFLAGS=\"${CFLAGS} -O2\"\nfi\ncat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\n\nint\nmain ()\n{\n\n    return 0;\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: yes\" >&5\n$as_echo \"yes\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\n              CFLAGS=\"${TCFLAGS}\"\n\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\n\n    else\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether compiler supports -O\" >&5\n$as_echo_n \"checking whether compiler supports -O... \" >&6; }\nTCFLAGS=\"${CFLAGS}\"\nif test \"x${CFLAGS}\" = \"x\" ; then\n  CFLAGS=\"-O\"\nelse\n  CFLAGS=\"${CFLAGS} -O\"\nfi\ncat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\n\nint\nmain ()\n{\n\n    return 0;\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: yes\" >&5\n$as_echo \"yes\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\n              CFLAGS=\"${TCFLAGS}\"\n\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\n\n    fi\n  fi\nfi\n\n# Check whether --enable-stats was given.\nif test \"${enable_stats+set}\" = set; then :\n  enableval=$enable_stats; if test \"x$enable_stats\" = \"xno\" ; then\n  enable_stats=\"0\"\nelse\n  enable_stats=\"1\"\nfi\n\nelse\n  enable_stats=\"1\"\n\nfi\n\nif test \"x$enable_stats\" = \"x1\" ; then\n  $as_echo \"#define JEMALLOC_STATS  \" >>confdefs.h\n\nfi\n\n\n# Check whether --enable-prof was given.\nif test \"${enable_prof+set}\" = set; then :\n  enableval=$enable_prof; if test \"x$enable_prof\" = \"xno\" ; then\n  enable_prof=\"0\"\nelse\n  enable_prof=\"1\"\nfi\n\nelse\n  enable_prof=\"0\"\n\nfi\n\nif test \"x$enable_prof\" = \"x1\" ; then\n  backtrace_method=\"\"\nelse\n  backtrace_method=\"N/A\"\nfi\n\n# Check whether --enable-prof-libunwind was given.\nif test \"${enable_prof_libunwind+set}\" = set; then :\n  enableval=$enable_prof_libunwind; if test \"x$enable_prof_libunwind\" = \"xno\" ; then\n  enable_prof_libunwind=\"0\"\nelse\n  enable_prof_libunwind=\"1\"\nfi\n\nelse\n  enable_prof_libunwind=\"0\"\n\nfi\n\n\n# Check whether --with-static_libunwind was given.\nif test \"${with_static_libunwind+set}\" = set; then :\n  withval=$with_static_libunwind; if test \"x$with_static_libunwind\" = \"xno\" ; then\n  LUNWIND=\"-lunwind\"\nelse\n  if test ! -f \"$with_static_libunwind\" ; then\n    as_fn_error $? \"Static libunwind not found: $with_static_libunwind\" \"$LINENO\" 5\n  fi\n  LUNWIND=\"$with_static_libunwind\"\nfi\nelse\n  LUNWIND=\"-lunwind\"\n\nfi\n\nif test \"x$backtrace_method\" = \"x\" -a \"x$enable_prof_libunwind\" = \"x1\" ; then\n  for ac_header in libunwind.h\ndo :\n  ac_fn_c_check_header_mongrel \"$LINENO\" \"libunwind.h\" \"ac_cv_header_libunwind_h\" \"$ac_includes_default\"\nif test \"x$ac_cv_header_libunwind_h\" = xyes; then :\n  cat >>confdefs.h <<_ACEOF\n#define HAVE_LIBUNWIND_H 1\n_ACEOF\n\nelse\n  enable_prof_libunwind=\"0\"\nfi\n\ndone\n\n  if test \"x$LUNWIND\" = \"x-lunwind\" ; then\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: checking for backtrace in -lunwind\" >&5\n$as_echo_n \"checking for backtrace in -lunwind... \" >&6; }\nif ${ac_cv_lib_unwind_backtrace+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  ac_check_lib_save_LIBS=$LIBS\nLIBS=\"-lunwind  $LIBS\"\ncat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\n/* Override any GCC internal prototype to avoid an error.\n   Use char because int might match the return type of a GCC\n   builtin and then its argument prototype would still apply.  */\n#ifdef __cplusplus\nextern \"C\"\n#endif\nchar backtrace ();\nint\nmain ()\n{\nreturn backtrace ();\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_link \"$LINENO\"; then :\n  ac_cv_lib_unwind_backtrace=yes\nelse\n  ac_cv_lib_unwind_backtrace=no\nfi\nrm -f core conftest.err conftest.$ac_objext \\\n    conftest$ac_exeext conftest.$ac_ext\nLIBS=$ac_check_lib_save_LIBS\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_unwind_backtrace\" >&5\n$as_echo \"$ac_cv_lib_unwind_backtrace\" >&6; }\nif test \"x$ac_cv_lib_unwind_backtrace\" = xyes; then :\n  LIBS=\"$LIBS $LUNWIND\"\nelse\n  enable_prof_libunwind=\"0\"\nfi\n\n  else\n    LIBS=\"$LIBS $LUNWIND\"\n  fi\n  if test \"x${enable_prof_libunwind}\" = \"x1\" ; then\n    backtrace_method=\"libunwind\"\n    $as_echo \"#define JEMALLOC_PROF_LIBUNWIND  \" >>confdefs.h\n\n  fi\nfi\n\n# Check whether --enable-prof-libgcc was given.\nif test \"${enable_prof_libgcc+set}\" = set; then :\n  enableval=$enable_prof_libgcc; if test \"x$enable_prof_libgcc\" = \"xno\" ; then\n  enable_prof_libgcc=\"0\"\nelse\n  enable_prof_libgcc=\"1\"\nfi\n\nelse\n  enable_prof_libgcc=\"1\"\n\nfi\n\nif test \"x$backtrace_method\" = \"x\" -a \"x$enable_prof_libgcc\" = \"x1\" \\\n     -a \"x$GCC\" = \"xyes\" ; then\n  for ac_header in unwind.h\ndo :\n  ac_fn_c_check_header_mongrel \"$LINENO\" \"unwind.h\" \"ac_cv_header_unwind_h\" \"$ac_includes_default\"\nif test \"x$ac_cv_header_unwind_h\" = xyes; then :\n  cat >>confdefs.h <<_ACEOF\n#define HAVE_UNWIND_H 1\n_ACEOF\n\nelse\n  enable_prof_libgcc=\"0\"\nfi\n\ndone\n\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: checking for _Unwind_Backtrace in -lgcc\" >&5\n$as_echo_n \"checking for _Unwind_Backtrace in -lgcc... \" >&6; }\nif ${ac_cv_lib_gcc__Unwind_Backtrace+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  ac_check_lib_save_LIBS=$LIBS\nLIBS=\"-lgcc  $LIBS\"\ncat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\n/* Override any GCC internal prototype to avoid an error.\n   Use char because int might match the return type of a GCC\n   builtin and then its argument prototype would still apply.  */\n#ifdef __cplusplus\nextern \"C\"\n#endif\nchar _Unwind_Backtrace ();\nint\nmain ()\n{\nreturn _Unwind_Backtrace ();\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_link \"$LINENO\"; then :\n  ac_cv_lib_gcc__Unwind_Backtrace=yes\nelse\n  ac_cv_lib_gcc__Unwind_Backtrace=no\nfi\nrm -f core conftest.err conftest.$ac_objext \\\n    conftest$ac_exeext conftest.$ac_ext\nLIBS=$ac_check_lib_save_LIBS\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_gcc__Unwind_Backtrace\" >&5\n$as_echo \"$ac_cv_lib_gcc__Unwind_Backtrace\" >&6; }\nif test \"x$ac_cv_lib_gcc__Unwind_Backtrace\" = xyes; then :\n  LIBS=\"$LIBS -lgcc\"\nelse\n  enable_prof_libgcc=\"0\"\nfi\n\n      { $as_echo \"$as_me:${as_lineno-$LINENO}: checking libgcc-based backtracing reliability on ${host_cpu}\" >&5\n$as_echo_n \"checking libgcc-based backtracing reliability on ${host_cpu}... \" >&6; }\n  case \"${host_cpu}\" in\n    i[3456]86)\n      { $as_echo \"$as_me:${as_lineno-$LINENO}: result: unreliable\" >&5\n$as_echo \"unreliable\" >&6; }\n      enable_prof_libgcc=\"0\";\n      ;;\n    x86_64)\n      { $as_echo \"$as_me:${as_lineno-$LINENO}: result: reliable\" >&5\n$as_echo \"reliable\" >&6; }\n      ;;\n    *)\n      { $as_echo \"$as_me:${as_lineno-$LINENO}: result: unreliable\" >&5\n$as_echo \"unreliable\" >&6; }\n      enable_prof_libgcc=\"0\";\n      ;;\n  esac\n  if test \"x${enable_prof_libgcc}\" = \"x1\" ; then\n    backtrace_method=\"libgcc\"\n    $as_echo \"#define JEMALLOC_PROF_LIBGCC  \" >>confdefs.h\n\n  fi\nelse\n  enable_prof_libgcc=\"0\"\nfi\n\n# Check whether --enable-prof-gcc was given.\nif test \"${enable_prof_gcc+set}\" = set; then :\n  enableval=$enable_prof_gcc; if test \"x$enable_prof_gcc\" = \"xno\" ; then\n  enable_prof_gcc=\"0\"\nelse\n  enable_prof_gcc=\"1\"\nfi\n\nelse\n  enable_prof_gcc=\"1\"\n\nfi\n\nif test \"x$backtrace_method\" = \"x\" -a \"x$enable_prof_gcc\" = \"x1\" \\\n     -a \"x$GCC\" = \"xyes\" ; then\n  backtrace_method=\"gcc intrinsics\"\n  $as_echo \"#define JEMALLOC_PROF_GCC  \" >>confdefs.h\n\nelse\n  enable_prof_gcc=\"0\"\nfi\n\nif test \"x$backtrace_method\" = \"x\" ; then\n  backtrace_method=\"none (disabling profiling)\"\n  enable_prof=\"0\"\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking configured backtracing method\" >&5\n$as_echo_n \"checking configured backtracing method... \" >&6; }\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $backtrace_method\" >&5\n$as_echo \"$backtrace_method\" >&6; }\nif test \"x$enable_prof\" = \"x1\" ; then\n  if test \"x${force_tls}\" = \"x0\" ; then\n    as_fn_error $? \"Heap profiling requires TLS\" \"$LINENO\" 5;\n  fi\n  force_tls=\"1\"\n  $as_echo \"#define JEMALLOC_PROF  \" >>confdefs.h\n\nfi\n\n\n# Check whether --enable-tcache was given.\nif test \"${enable_tcache+set}\" = set; then :\n  enableval=$enable_tcache; if test \"x$enable_tcache\" = \"xno\" ; then\n  enable_tcache=\"0\"\nelse\n  enable_tcache=\"1\"\nfi\n\nelse\n  enable_tcache=\"1\"\n\nfi\n\nif test \"x$enable_tcache\" = \"x1\" ; then\n  $as_echo \"#define JEMALLOC_TCACHE  \" >>confdefs.h\n\nfi\n\n\n# Check whether --enable-mremap was given.\nif test \"${enable_mremap+set}\" = set; then :\n  enableval=$enable_mremap; if test \"x$enable_mremap\" = \"xno\" ; then\n  enable_mremap=\"0\"\nelse\n  enable_mremap=\"1\"\nfi\n\nelse\n  enable_mremap=\"0\"\n\nfi\n\nif test \"x$enable_mremap\" = \"x1\" ; then\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether mremap(...MREMAP_FIXED...) is compilable\" >&5\n$as_echo_n \"checking whether mremap(...MREMAP_FIXED...) is compilable... \" >&6; }\nif ${je_cv_mremap_fixed+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\n#define _GNU_SOURCE\n#include <sys/mman.h>\n\nint\nmain ()\n{\n\nvoid *p = mremap((void *)0, 0, 0, MREMAP_MAYMOVE|MREMAP_FIXED, (void *)0);\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_link \"$LINENO\"; then :\n  je_cv_mremap_fixed=yes\nelse\n  je_cv_mremap_fixed=no\nfi\nrm -f core conftest.err conftest.$ac_objext \\\n    conftest$ac_exeext conftest.$ac_ext\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $je_cv_mremap_fixed\" >&5\n$as_echo \"$je_cv_mremap_fixed\" >&6; }\n\n  if test \"x${je_cv_mremap_fixed}\" = \"xno\" ; then\n    enable_mremap=\"0\"\n  fi\nfi\nif test \"x$enable_mremap\" = \"x1\" ; then\n  $as_echo \"#define JEMALLOC_MREMAP  \" >>confdefs.h\n\nfi\n\n\n# Check whether --enable-munmap was given.\nif test \"${enable_munmap+set}\" = set; then :\n  enableval=$enable_munmap; if test \"x$enable_munmap\" = \"xno\" ; then\n  enable_munmap=\"0\"\nelse\n  enable_munmap=\"1\"\nfi\n\nelse\n  enable_munmap=\"${default_munmap}\"\n\nfi\n\nif test \"x$enable_munmap\" = \"x1\" ; then\n  $as_echo \"#define JEMALLOC_MUNMAP  \" >>confdefs.h\n\nfi\n\n\n# Check whether --enable-dss was given.\nif test \"${enable_dss+set}\" = set; then :\n  enableval=$enable_dss; if test \"x$enable_dss\" = \"xno\" ; then\n  enable_dss=\"0\"\nelse\n  enable_dss=\"1\"\nfi\n\nelse\n  enable_dss=\"0\"\n\nfi\n\nac_fn_c_check_func \"$LINENO\" \"sbrk\" \"ac_cv_func_sbrk\"\nif test \"x$ac_cv_func_sbrk\" = xyes; then :\n  have_sbrk=\"1\"\nelse\n  have_sbrk=\"0\"\nfi\n\nif test \"x$have_sbrk\" = \"x1\" ; then\n  $as_echo \"#define JEMALLOC_HAVE_SBRK  \" >>confdefs.h\n\nelse\n  enable_dss=\"0\"\nfi\n\nif test \"x$enable_dss\" = \"x1\" ; then\n  $as_echo \"#define JEMALLOC_DSS  \" >>confdefs.h\n\nfi\n\n\n# Check whether --enable-fill was given.\nif test \"${enable_fill+set}\" = set; then :\n  enableval=$enable_fill; if test \"x$enable_fill\" = \"xno\" ; then\n  enable_fill=\"0\"\nelse\n  enable_fill=\"1\"\nfi\n\nelse\n  enable_fill=\"1\"\n\nfi\n\nif test \"x$enable_fill\" = \"x1\" ; then\n  $as_echo \"#define JEMALLOC_FILL  \" >>confdefs.h\n\nfi\n\n\n# Check whether --enable-utrace was given.\nif test \"${enable_utrace+set}\" = set; then :\n  enableval=$enable_utrace; if test \"x$enable_utrace\" = \"xno\" ; then\n  enable_utrace=\"0\"\nelse\n  enable_utrace=\"1\"\nfi\n\nelse\n  enable_utrace=\"0\"\n\nfi\n\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether utrace(2) is compilable\" >&5\n$as_echo_n \"checking whether utrace(2) is compilable... \" >&6; }\nif ${je_cv_utrace+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\n#include <sys/types.h>\n#include <sys/param.h>\n#include <sys/time.h>\n#include <sys/uio.h>\n#include <sys/ktrace.h>\n\nint\nmain ()\n{\n\n\tutrace((void *)0, 0);\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_link \"$LINENO\"; then :\n  je_cv_utrace=yes\nelse\n  je_cv_utrace=no\nfi\nrm -f core conftest.err conftest.$ac_objext \\\n    conftest$ac_exeext conftest.$ac_ext\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $je_cv_utrace\" >&5\n$as_echo \"$je_cv_utrace\" >&6; }\n\nif test \"x${je_cv_utrace}\" = \"xno\" ; then\n  enable_utrace=\"0\"\nfi\nif test \"x$enable_utrace\" = \"x1\" ; then\n  $as_echo \"#define JEMALLOC_UTRACE  \" >>confdefs.h\n\nfi\n\n\n# Check whether --enable-valgrind was given.\nif test \"${enable_valgrind+set}\" = set; then :\n  enableval=$enable_valgrind; if test \"x$enable_valgrind\" = \"xno\" ; then\n  enable_valgrind=\"0\"\nelse\n  enable_valgrind=\"1\"\nfi\n\nelse\n  enable_valgrind=\"1\"\n\nfi\n\nif test \"x$enable_valgrind\" = \"x1\" ; then\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether valgrind is compilable\" >&5\n$as_echo_n \"checking whether valgrind is compilable... \" >&6; }\nif ${je_cv_valgrind+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\n#include <valgrind/valgrind.h>\n#include <valgrind/memcheck.h>\n\n#if !defined(VALGRIND_RESIZEINPLACE_BLOCK)\n#  error \"Incompatible Valgrind version\"\n#endif\n\nint\nmain ()\n{\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_link \"$LINENO\"; then :\n  je_cv_valgrind=yes\nelse\n  je_cv_valgrind=no\nfi\nrm -f core conftest.err conftest.$ac_objext \\\n    conftest$ac_exeext conftest.$ac_ext\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $je_cv_valgrind\" >&5\n$as_echo \"$je_cv_valgrind\" >&6; }\n\n  if test \"x${je_cv_valgrind}\" = \"xno\" ; then\n    enable_valgrind=\"0\"\n  fi\n  if test \"x$enable_valgrind\" = \"x1\" ; then\n    $as_echo \"#define JEMALLOC_VALGRIND  \" >>confdefs.h\n\n  fi\nfi\n\n\n# Check whether --enable-xmalloc was given.\nif test \"${enable_xmalloc+set}\" = set; then :\n  enableval=$enable_xmalloc; if test \"x$enable_xmalloc\" = \"xno\" ; then\n  enable_xmalloc=\"0\"\nelse\n  enable_xmalloc=\"1\"\nfi\n\nelse\n  enable_xmalloc=\"0\"\n\nfi\n\nif test \"x$enable_xmalloc\" = \"x1\" ; then\n  $as_echo \"#define JEMALLOC_XMALLOC  \" >>confdefs.h\n\nfi\n\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking STATIC_PAGE_SHIFT\" >&5\n$as_echo_n \"checking STATIC_PAGE_SHIFT... \" >&6; }\nif ${je_cv_static_page_shift+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if test \"$cross_compiling\" = yes; then :\n  { { $as_echo \"$as_me:${as_lineno-$LINENO}: error: in \\`$ac_pwd':\" >&5\n$as_echo \"$as_me: error: in \\`$ac_pwd':\" >&2;}\nas_fn_error $? \"cannot run test program while cross compiling\nSee \\`config.log' for more details\" \"$LINENO\" 5; }\nelse\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\n#include <strings.h>\n#ifdef _WIN32\n#include <windows.h>\n#else\n#include <unistd.h>\n#endif\n#include <stdio.h>\n\nint\nmain ()\n{\n\n    long result;\n    FILE *f;\n\n#ifdef _WIN32\n    SYSTEM_INFO si;\n    GetSystemInfo(&si);\n    result = si.dwPageSize;\n#else\n    result = sysconf(_SC_PAGESIZE);\n#endif\n    if (result == -1) {\n\treturn 1;\n    }\n    result = ffsl(result) - 1;\n\n    f = fopen(\"conftest.out\", \"w\");\n    if (f == NULL) {\n\treturn 1;\n    }\n    fprintf(f, \"%u\\n\", result);\n    fclose(f);\n\n    return 0;\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_run \"$LINENO\"; then :\n  je_cv_static_page_shift=`cat conftest.out`\nelse\n  je_cv_static_page_shift=undefined\nfi\nrm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \\\n  conftest.$ac_objext conftest.beam conftest.$ac_ext\nfi\n\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $je_cv_static_page_shift\" >&5\n$as_echo \"$je_cv_static_page_shift\" >&6; }\n\nif test \"x$je_cv_static_page_shift\" != \"xundefined\"; then\n   cat >>confdefs.h <<_ACEOF\n#define STATIC_PAGE_SHIFT $je_cv_static_page_shift\n_ACEOF\n\nelse\n   as_fn_error $? \"cannot determine value for STATIC_PAGE_SHIFT\" \"$LINENO\" 5\nfi\n\n\nif test -d \"${srcroot}.git\" ; then\n  git describe --long --abbrev=40 > ${srcroot}VERSION\nfi\njemalloc_version=`cat ${srcroot}VERSION`\njemalloc_version_major=`echo ${jemalloc_version} | tr \".g-\" \" \" | awk '{print $1}'`\njemalloc_version_minor=`echo ${jemalloc_version} | tr \".g-\" \" \" | awk '{print $2}'`\njemalloc_version_bugfix=`echo ${jemalloc_version} | tr \".g-\" \" \" | awk '{print $3}'`\njemalloc_version_nrev=`echo ${jemalloc_version} | tr \".g-\" \" \" | awk '{print $4}'`\njemalloc_version_gid=`echo ${jemalloc_version} | tr \".g-\" \" \" | awk '{print $5}'`\n\n\n\n\n\n\n\n\nif test \"x$abi\" != \"xpecoff\" ; then\n  for ac_header in pthread.h\ndo :\n  ac_fn_c_check_header_mongrel \"$LINENO\" \"pthread.h\" \"ac_cv_header_pthread_h\" \"$ac_includes_default\"\nif test \"x$ac_cv_header_pthread_h\" = xyes; then :\n  cat >>confdefs.h <<_ACEOF\n#define HAVE_PTHREAD_H 1\n_ACEOF\n\nelse\n  as_fn_error $? \"pthread.h is missing\" \"$LINENO\" 5\nfi\n\ndone\n\n      { $as_echo \"$as_me:${as_lineno-$LINENO}: checking for pthread_create in -lpthread\" >&5\n$as_echo_n \"checking for pthread_create in -lpthread... \" >&6; }\nif ${ac_cv_lib_pthread_pthread_create+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  ac_check_lib_save_LIBS=$LIBS\nLIBS=\"-lpthread  $LIBS\"\ncat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\n/* Override any GCC internal prototype to avoid an error.\n   Use char because int might match the return type of a GCC\n   builtin and then its argument prototype would still apply.  */\n#ifdef __cplusplus\nextern \"C\"\n#endif\nchar pthread_create ();\nint\nmain ()\n{\nreturn pthread_create ();\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_link \"$LINENO\"; then :\n  ac_cv_lib_pthread_pthread_create=yes\nelse\n  ac_cv_lib_pthread_pthread_create=no\nfi\nrm -f core conftest.err conftest.$ac_objext \\\n    conftest$ac_exeext conftest.$ac_ext\nLIBS=$ac_check_lib_save_LIBS\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pthread_pthread_create\" >&5\n$as_echo \"$ac_cv_lib_pthread_pthread_create\" >&6; }\nif test \"x$ac_cv_lib_pthread_pthread_create\" = xyes; then :\n  LIBS=\"$LIBS -lpthread\"\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: checking for library containing pthread_create\" >&5\n$as_echo_n \"checking for library containing pthread_create... \" >&6; }\nif ${ac_cv_search_pthread_create+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  ac_func_search_save_LIBS=$LIBS\ncat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\n/* Override any GCC internal prototype to avoid an error.\n   Use char because int might match the return type of a GCC\n   builtin and then its argument prototype would still apply.  */\n#ifdef __cplusplus\nextern \"C\"\n#endif\nchar pthread_create ();\nint\nmain ()\n{\nreturn pthread_create ();\n  ;\n  return 0;\n}\n_ACEOF\nfor ac_lib in '' ; do\n  if test -z \"$ac_lib\"; then\n    ac_res=\"none required\"\n  else\n    ac_res=-l$ac_lib\n    LIBS=\"-l$ac_lib  $ac_func_search_save_LIBS\"\n  fi\n  if ac_fn_c_try_link \"$LINENO\"; then :\n  ac_cv_search_pthread_create=$ac_res\nfi\nrm -f core conftest.err conftest.$ac_objext \\\n    conftest$ac_exeext\n  if ${ac_cv_search_pthread_create+:} false; then :\n  break\nfi\ndone\nif ${ac_cv_search_pthread_create+:} false; then :\n\nelse\n  ac_cv_search_pthread_create=no\nfi\nrm conftest.$ac_ext\nLIBS=$ac_func_search_save_LIBS\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_pthread_create\" >&5\n$as_echo \"$ac_cv_search_pthread_create\" >&6; }\nac_res=$ac_cv_search_pthread_create\nif test \"$ac_res\" != no; then :\n  test \"$ac_res\" = \"none required\" || LIBS=\"$ac_res $LIBS\"\n\nelse\n  as_fn_error $? \"libpthread is missing\" \"$LINENO\" 5\nfi\n\nfi\n\nfi\n\nCPPFLAGS=\"$CPPFLAGS -D_REENTRANT\"\n\nac_fn_c_check_func \"$LINENO\" \"_malloc_thread_cleanup\" \"ac_cv_func__malloc_thread_cleanup\"\nif test \"x$ac_cv_func__malloc_thread_cleanup\" = xyes; then :\n  have__malloc_thread_cleanup=\"1\"\nelse\n  have__malloc_thread_cleanup=\"0\"\n\nfi\n\nif test \"x$have__malloc_thread_cleanup\" = \"x1\" ; then\n  $as_echo \"#define JEMALLOC_MALLOC_THREAD_CLEANUP  \" >>confdefs.h\n\n  force_tls=\"1\"\nfi\n\nac_fn_c_check_func \"$LINENO\" \"_pthread_mutex_init_calloc_cb\" \"ac_cv_func__pthread_mutex_init_calloc_cb\"\nif test \"x$ac_cv_func__pthread_mutex_init_calloc_cb\" = xyes; then :\n  have__pthread_mutex_init_calloc_cb=\"1\"\nelse\n  have__pthread_mutex_init_calloc_cb=\"0\"\n\nfi\n\nif test \"x$have__pthread_mutex_init_calloc_cb\" = \"x1\" ; then\n  $as_echo \"#define JEMALLOC_MUTEX_INIT_CB 1\" >>confdefs.h\n\nfi\n\n# Check whether --enable-lazy_lock was given.\nif test \"${enable_lazy_lock+set}\" = set; then :\n  enableval=$enable_lazy_lock; if test \"x$enable_lazy_lock\" = \"xno\" ; then\n  enable_lazy_lock=\"0\"\nelse\n  enable_lazy_lock=\"1\"\nfi\n\nelse\n  enable_lazy_lock=\"0\"\n\nfi\n\nif test \"x$enable_lazy_lock\" = \"x0\" -a \"x${force_lazy_lock}\" = \"x1\" ; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: Forcing lazy-lock to avoid allocator/threading bootstrap issues\" >&5\n$as_echo \"Forcing lazy-lock to avoid allocator/threading bootstrap issues\" >&6; }\n  enable_lazy_lock=\"1\"\nfi\nif test \"x$enable_lazy_lock\" = \"x1\" ; then\n  if test \"x$abi\" != \"xpecoff\" ; then\n    for ac_header in dlfcn.h\ndo :\n  ac_fn_c_check_header_mongrel \"$LINENO\" \"dlfcn.h\" \"ac_cv_header_dlfcn_h\" \"$ac_includes_default\"\nif test \"x$ac_cv_header_dlfcn_h\" = xyes; then :\n  cat >>confdefs.h <<_ACEOF\n#define HAVE_DLFCN_H 1\n_ACEOF\n\nelse\n  as_fn_error $? \"dlfcn.h is missing\" \"$LINENO\" 5\nfi\n\ndone\n\n    ac_fn_c_check_func \"$LINENO\" \"dlsym\" \"ac_cv_func_dlsym\"\nif test \"x$ac_cv_func_dlsym\" = xyes; then :\n\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: checking for dlsym in -ldl\" >&5\n$as_echo_n \"checking for dlsym in -ldl... \" >&6; }\nif ${ac_cv_lib_dl_dlsym+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  ac_check_lib_save_LIBS=$LIBS\nLIBS=\"-ldl  $LIBS\"\ncat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\n/* Override any GCC internal prototype to avoid an error.\n   Use char because int might match the return type of a GCC\n   builtin and then its argument prototype would still apply.  */\n#ifdef __cplusplus\nextern \"C\"\n#endif\nchar dlsym ();\nint\nmain ()\n{\nreturn dlsym ();\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_link \"$LINENO\"; then :\n  ac_cv_lib_dl_dlsym=yes\nelse\n  ac_cv_lib_dl_dlsym=no\nfi\nrm -f core conftest.err conftest.$ac_objext \\\n    conftest$ac_exeext conftest.$ac_ext\nLIBS=$ac_check_lib_save_LIBS\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlsym\" >&5\n$as_echo \"$ac_cv_lib_dl_dlsym\" >&6; }\nif test \"x$ac_cv_lib_dl_dlsym\" = xyes; then :\n  LIBS=\"$LIBS -ldl\"\nelse\n  as_fn_error $? \"libdl is missing\" \"$LINENO\" 5\nfi\n\n\nfi\n\n  fi\n  $as_echo \"#define JEMALLOC_LAZY_LOCK  \" >>confdefs.h\n\nfi\n\n\n# Check whether --enable-tls was given.\nif test \"${enable_tls+set}\" = set; then :\n  enableval=$enable_tls; if test \"x$enable_tls\" = \"xno\" ; then\n  enable_tls=\"0\"\nelse\n  enable_tls=\"1\"\nfi\n\nelse\n  enable_tls=\"1\"\n\nfi\n\nif test \"x${enable_tls}\" = \"x0\" -a \"x${force_tls}\" = \"x1\" ; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: Forcing TLS to avoid allocator/threading bootstrap issues\" >&5\n$as_echo \"Forcing TLS to avoid allocator/threading bootstrap issues\" >&6; }\n  enable_tls=\"1\"\nfi\nif test \"x${enable_tls}\" = \"x1\" -a \"x${force_tls}\" = \"x0\" ; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: Forcing no TLS to avoid allocator/threading bootstrap issues\" >&5\n$as_echo \"Forcing no TLS to avoid allocator/threading bootstrap issues\" >&6; }\n  enable_tls=\"0\"\nfi\nif test \"x${enable_tls}\" = \"x1\" ; then\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for TLS\" >&5\n$as_echo_n \"checking for TLS... \" >&6; }\ncat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\n    __thread int x;\n\nint\nmain ()\n{\n\n    x = 42;\n\n    return 0;\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: yes\" >&5\n$as_echo \"yes\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\n              enable_tls=\"0\"\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\nfi\n\nif test \"x${enable_tls}\" = \"x1\" ; then\n  cat >>confdefs.h <<_ACEOF\n#define JEMALLOC_TLS\n_ACEOF\n\nelif test \"x${force_tls}\" = \"x1\" ; then\n  as_fn_error $? \"Failed to configure TLS, which is mandatory for correct function\" \"$LINENO\" 5\nfi\n\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether a program using ffsl is compilable\" >&5\n$as_echo_n \"checking whether a program using ffsl is compilable... \" >&6; }\nif ${je_cv_function_ffsl+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\n#include <strings.h>\n#include <string.h>\n\nint\nmain ()\n{\n\n\t{\n\t\tint rv = ffsl(0x08);\n\t}\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_link \"$LINENO\"; then :\n  je_cv_function_ffsl=yes\nelse\n  je_cv_function_ffsl=no\nfi\nrm -f core conftest.err conftest.$ac_objext \\\n    conftest$ac_exeext conftest.$ac_ext\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $je_cv_function_ffsl\" >&5\n$as_echo \"$je_cv_function_ffsl\" >&6; }\n\nif test \"x${je_cv_function_ffsl}\" != \"xyes\" ; then\n   as_fn_error $? \"Cannot build without ffsl(3)\" \"$LINENO\" 5\nfi\n\n\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether atomic(9) is compilable\" >&5\n$as_echo_n \"checking whether atomic(9) is compilable... \" >&6; }\nif ${je_cv_atomic9+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\n#include <sys/types.h>\n#include <machine/atomic.h>\n#include <inttypes.h>\n\nint\nmain ()\n{\n\n\t{\n\t\tuint32_t x32 = 0;\n\t\tvolatile uint32_t *x32p = &x32;\n\t\tatomic_fetchadd_32(x32p, 1);\n\t}\n\t{\n\t\tunsigned long xlong = 0;\n\t\tvolatile unsigned long *xlongp = &xlong;\n\t\tatomic_fetchadd_long(xlongp, 1);\n\t}\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_link \"$LINENO\"; then :\n  je_cv_atomic9=yes\nelse\n  je_cv_atomic9=no\nfi\nrm -f core conftest.err conftest.$ac_objext \\\n    conftest$ac_exeext conftest.$ac_ext\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $je_cv_atomic9\" >&5\n$as_echo \"$je_cv_atomic9\" >&6; }\n\nif test \"x${je_cv_atomic9}\" = \"xyes\" ; then\n  $as_echo \"#define JEMALLOC_ATOMIC9 1\" >>confdefs.h\n\nfi\n\n\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether Darwin OSAtomic*() is compilable\" >&5\n$as_echo_n \"checking whether Darwin OSAtomic*() is compilable... \" >&6; }\nif ${je_cv_osatomic+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\n#include <libkern/OSAtomic.h>\n#include <inttypes.h>\n\nint\nmain ()\n{\n\n\t{\n\t\tint32_t x32 = 0;\n\t\tvolatile int32_t *x32p = &x32;\n\t\tOSAtomicAdd32(1, x32p);\n\t}\n\t{\n\t\tint64_t x64 = 0;\n\t\tvolatile int64_t *x64p = &x64;\n\t\tOSAtomicAdd64(1, x64p);\n\t}\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_link \"$LINENO\"; then :\n  je_cv_osatomic=yes\nelse\n  je_cv_osatomic=no\nfi\nrm -f core conftest.err conftest.$ac_objext \\\n    conftest$ac_exeext conftest.$ac_ext\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $je_cv_osatomic\" >&5\n$as_echo \"$je_cv_osatomic\" >&6; }\n\nif test \"x${je_cv_osatomic}\" = \"xyes\" ; then\n  $as_echo \"#define JEMALLOC_OSATOMIC  \" >>confdefs.h\n\nfi\n\n\n\n\nif test \"x${je_cv_atomic9}\" != \"xyes\" -a \"x${je_cv_osatomic}\" != \"xyes\" ; then\n\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether to force 32-bit __sync_{add,sub}_and_fetch()\" >&5\n$as_echo_n \"checking whether to force 32-bit __sync_{add,sub}_and_fetch()... \" >&6; }\nif ${je_cv_sync_compare_and_swap_4+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\n                                                 #include <stdint.h>\n\nint\nmain ()\n{\n\n                                                 #ifndef __GCC_HAVE_SYNC_COMPARE_AND_SWAP_4\n                                                 {\n                                                    uint32_t x32 = 0;\n                                                    __sync_add_and_fetch(&x32, 42);\n                                                    __sync_sub_and_fetch(&x32, 1);\n                                                 }\n                                                 #else\n                                                 #error __GCC_HAVE_SYNC_COMPARE_AND_SWAP_4 is defined, no need to force\n                                                 #endif\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_link \"$LINENO\"; then :\n  je_cv_sync_compare_and_swap_4=yes\nelse\n  je_cv_sync_compare_and_swap_4=no\nfi\nrm -f core conftest.err conftest.$ac_objext \\\n    conftest$ac_exeext conftest.$ac_ext\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $je_cv_sync_compare_and_swap_4\" >&5\n$as_echo \"$je_cv_sync_compare_and_swap_4\" >&6; }\n\n  if test \"x${je_cv_sync_compare_and_swap_4}\" = \"xyes\" ; then\n    $as_echo \"#define JE_FORCE_SYNC_COMPARE_AND_SWAP_4  \" >>confdefs.h\n\n  fi\n\n\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether to force 64-bit __sync_{add,sub}_and_fetch()\" >&5\n$as_echo_n \"checking whether to force 64-bit __sync_{add,sub}_and_fetch()... \" >&6; }\nif ${je_cv_sync_compare_and_swap_8+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\n                                                 #include <stdint.h>\n\nint\nmain ()\n{\n\n                                                 #ifndef __GCC_HAVE_SYNC_COMPARE_AND_SWAP_8\n                                                 {\n                                                    uint64_t x64 = 0;\n                                                    __sync_add_and_fetch(&x64, 42);\n                                                    __sync_sub_and_fetch(&x64, 1);\n                                                 }\n                                                 #else\n                                                 #error __GCC_HAVE_SYNC_COMPARE_AND_SWAP_8 is defined, no need to force\n                                                 #endif\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_link \"$LINENO\"; then :\n  je_cv_sync_compare_and_swap_8=yes\nelse\n  je_cv_sync_compare_and_swap_8=no\nfi\nrm -f core conftest.err conftest.$ac_objext \\\n    conftest$ac_exeext conftest.$ac_ext\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $je_cv_sync_compare_and_swap_8\" >&5\n$as_echo \"$je_cv_sync_compare_and_swap_8\" >&6; }\n\n  if test \"x${je_cv_sync_compare_and_swap_8}\" = \"xyes\" ; then\n    $as_echo \"#define JE_FORCE_SYNC_COMPARE_AND_SWAP_8  \" >>confdefs.h\n\n  fi\n\nfi\n\n\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether Darwin OSSpin*() is compilable\" >&5\n$as_echo_n \"checking whether Darwin OSSpin*() is compilable... \" >&6; }\nif ${je_cv_osspin+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\n#include <libkern/OSAtomic.h>\n#include <inttypes.h>\n\nint\nmain ()\n{\n\n\tOSSpinLock lock = 0;\n\tOSSpinLockLock(&lock);\n\tOSSpinLockUnlock(&lock);\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_link \"$LINENO\"; then :\n  je_cv_osspin=yes\nelse\n  je_cv_osspin=no\nfi\nrm -f core conftest.err conftest.$ac_objext \\\n    conftest$ac_exeext conftest.$ac_ext\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $je_cv_osspin\" >&5\n$as_echo \"$je_cv_osspin\" >&6; }\n\nif test \"x${je_cv_osspin}\" = \"xyes\" ; then\n  $as_echo \"#define JEMALLOC_OSSPIN  \" >>confdefs.h\n\nfi\n\n\nif test \"x${abi}\" = \"xmacho\" ; then\n  $as_echo \"#define JEMALLOC_IVSALLOC  \" >>confdefs.h\n\n  $as_echo \"#define JEMALLOC_ZONE  \" >>confdefs.h\n\n\n        { $as_echo \"$as_me:${as_lineno-$LINENO}: checking malloc zone version\" >&5\n$as_echo_n \"checking malloc zone version... \" >&6; }\n\n\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n#include <malloc/malloc.h>\nint\nmain ()\n{\nstatic foo[sizeof(malloc_zone_t) == sizeof(void *) * 14 ? 1 : -1]\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  JEMALLOC_ZONE_VERSION=3\nelse\n\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n#include <malloc/malloc.h>\nint\nmain ()\n{\nstatic foo[sizeof(malloc_zone_t) == sizeof(void *) * 15 ? 1 : -1]\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  JEMALLOC_ZONE_VERSION=5\nelse\n\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n#include <malloc/malloc.h>\nint\nmain ()\n{\nstatic foo[sizeof(malloc_zone_t) == sizeof(void *) * 16 ? 1 : -1]\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n\n    cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n#include <malloc/malloc.h>\nint\nmain ()\n{\nstatic foo[sizeof(malloc_introspection_t) == sizeof(void *) * 9 ? 1 : -1]\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  JEMALLOC_ZONE_VERSION=6\nelse\n\n    cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n#include <malloc/malloc.h>\nint\nmain ()\n{\nstatic foo[sizeof(malloc_introspection_t) == sizeof(void *) * 13 ? 1 : -1]\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  JEMALLOC_ZONE_VERSION=7\nelse\n  JEMALLOC_ZONE_VERSION=\n\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\nelse\n\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n#include <malloc/malloc.h>\nint\nmain ()\n{\nstatic foo[sizeof(malloc_zone_t) == sizeof(void *) * 17 ? 1 : -1]\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  JEMALLOC_ZONE_VERSION=8\nelse\n\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n#include <malloc/malloc.h>\nint\nmain ()\n{\nstatic foo[sizeof(malloc_zone_t) > sizeof(void *) * 17 ? 1 : -1]\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  JEMALLOC_ZONE_VERSION=9\nelse\n  JEMALLOC_ZONE_VERSION=\n\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\n  if test \"x${JEMALLOC_ZONE_VERSION}\" = \"x\"; then\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: result: unsupported\" >&5\n$as_echo \"unsupported\" >&6; }\n    as_fn_error $? \"Unsupported malloc zone version\" \"$LINENO\" 5\n  fi\n  if test \"${JEMALLOC_ZONE_VERSION}\" = 9; then\n    JEMALLOC_ZONE_VERSION=8\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: result: > 8\" >&5\n$as_echo \"> 8\" >&6; }\n  else\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $JEMALLOC_ZONE_VERSION\" >&5\n$as_echo \"$JEMALLOC_ZONE_VERSION\" >&6; }\n  fi\n  cat >>confdefs.h <<_ACEOF\n#define JEMALLOC_ZONE_VERSION $JEMALLOC_ZONE_VERSION\n_ACEOF\n\nfi\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for stdbool.h that conforms to C99\" >&5\n$as_echo_n \"checking for stdbool.h that conforms to C99... \" >&6; }\nif ${ac_cv_header_stdbool_h+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\n#include <stdbool.h>\n#ifndef bool\n \"error: bool is not defined\"\n#endif\n#ifndef false\n \"error: false is not defined\"\n#endif\n#if false\n \"error: false is not 0\"\n#endif\n#ifndef true\n \"error: true is not defined\"\n#endif\n#if true != 1\n \"error: true is not 1\"\n#endif\n#ifndef __bool_true_false_are_defined\n \"error: __bool_true_false_are_defined is not defined\"\n#endif\n\n\tstruct s { _Bool s: 1; _Bool t; } s;\n\n\tchar a[true == 1 ? 1 : -1];\n\tchar b[false == 0 ? 1 : -1];\n\tchar c[__bool_true_false_are_defined == 1 ? 1 : -1];\n\tchar d[(bool) 0.5 == true ? 1 : -1];\n\t/* See body of main program for 'e'.  */\n\tchar f[(_Bool) 0.0 == false ? 1 : -1];\n\tchar g[true];\n\tchar h[sizeof (_Bool)];\n\tchar i[sizeof s.t];\n\tenum { j = false, k = true, l = false * true, m = true * 256 };\n\t/* The following fails for\n\t   HP aC++/ANSI C B3910B A.05.55 [Dec 04 2003]. */\n\t_Bool n[m];\n\tchar o[sizeof n == m * sizeof n[0] ? 1 : -1];\n\tchar p[-1 - (_Bool) 0 < 0 && -1 - (bool) 0 < 0 ? 1 : -1];\n\t/* Catch a bug in an HP-UX C compiler.  See\n\t   http://gcc.gnu.org/ml/gcc-patches/2003-12/msg02303.html\n\t   http://lists.gnu.org/archive/html/bug-coreutils/2005-11/msg00161.html\n\t */\n\t_Bool q = true;\n\t_Bool *pq = &q;\n\nint\nmain ()\n{\n\n\tbool e = &s;\n\t*pq |= q;\n\t*pq |= ! q;\n\t/* Refer to every declared value, to avoid compiler optimizations.  */\n\treturn (!a + !b + !c + !d + !e + !f + !g + !h + !i + !!j + !k + !!l\n\t\t+ !m + !n + !o + !p + !q + !pq);\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  ac_cv_header_stdbool_h=yes\nelse\n  ac_cv_header_stdbool_h=no\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdbool_h\" >&5\n$as_echo \"$ac_cv_header_stdbool_h\" >&6; }\nac_fn_c_check_type \"$LINENO\" \"_Bool\" \"ac_cv_type__Bool\" \"$ac_includes_default\"\nif test \"x$ac_cv_type__Bool\" = xyes; then :\n\ncat >>confdefs.h <<_ACEOF\n#define HAVE__BOOL 1\n_ACEOF\n\n\nfi\n\nif test $ac_cv_header_stdbool_h = yes; then\n\n$as_echo \"#define HAVE_STDBOOL_H 1\" >>confdefs.h\n\nfi\n\n\nac_config_commands=\"$ac_config_commands include/jemalloc/internal/size_classes.h\"\n\n\n\n\nac_config_headers=\"$ac_config_headers $cfghdrs_tup\"\n\n\nac_config_files=\"$ac_config_files $cfgoutputs_tup config.stamp bin/jemalloc.sh\"\n\n\n\ncat >confcache <<\\_ACEOF\n# This file is a shell script that caches the results of configure\n# tests run on this system so they can be shared between configure\n# scripts and configure runs, see configure's option --config-cache.\n# It is not useful on other systems.  If it contains results you don't\n# want to keep, you may remove or edit it.\n#\n# config.status only pays attention to the cache file if you give it\n# the --recheck option to rerun configure.\n#\n# `ac_cv_env_foo' variables (set or unset) will be overridden when\n# loading this file, other *unset* `ac_cv_foo' will be assigned the\n# following values.\n\n_ACEOF\n\n# The following way of writing the cache mishandles newlines in values,\n# but we know of no workaround that is simple, portable, and efficient.\n# So, we kill variables containing newlines.\n# Ultrix sh set writes to stderr and can't be redirected directly,\n# and sets the high bit in the cache file unless we assign to the vars.\n(\n  for ac_var in `(set) 2>&1 | sed -n 's/^\\([a-zA-Z_][a-zA-Z0-9_]*\\)=.*/\\1/p'`; do\n    eval ac_val=\\$$ac_var\n    case $ac_val in #(\n    *${as_nl}*)\n      case $ac_var in #(\n      *_cv_*) { $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline\" >&5\n$as_echo \"$as_me: WARNING: cache variable $ac_var contains a newline\" >&2;} ;;\n      esac\n      case $ac_var in #(\n      _ | IFS | as_nl) ;; #(\n      BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(\n      *) { eval $ac_var=; unset $ac_var;} ;;\n      esac ;;\n    esac\n  done\n\n  (set) 2>&1 |\n    case $as_nl`(ac_space=' '; set) 2>&1` in #(\n    *${as_nl}ac_space=\\ *)\n      # `set' does not quote correctly, so add quotes: double-quote\n      # substitution turns \\\\\\\\ into \\\\, and sed turns \\\\ into \\.\n      sed -n \\\n\t\"s/'/'\\\\\\\\''/g;\n\t  s/^\\\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\\\)=\\\\(.*\\\\)/\\\\1='\\\\2'/p\"\n      ;; #(\n    *)\n      # `set' quotes correctly as required by POSIX, so do not add quotes.\n      sed -n \"/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p\"\n      ;;\n    esac |\n    sort\n) |\n  sed '\n     /^ac_cv_env_/b end\n     t clear\n     :clear\n     s/^\\([^=]*\\)=\\(.*[{}].*\\)$/test \"${\\1+set}\" = set || &/\n     t end\n     s/^\\([^=]*\\)=\\(.*\\)$/\\1=${\\1=\\2}/\n     :end' >>confcache\nif diff \"$cache_file\" confcache >/dev/null 2>&1; then :; else\n  if test -w \"$cache_file\"; then\n    if test \"x$cache_file\" != \"x/dev/null\"; then\n      { $as_echo \"$as_me:${as_lineno-$LINENO}: updating cache $cache_file\" >&5\n$as_echo \"$as_me: updating cache $cache_file\" >&6;}\n      if test ! -f \"$cache_file\" || test -h \"$cache_file\"; then\n\tcat confcache >\"$cache_file\"\n      else\n        case $cache_file in #(\n        */* | ?:*)\n\t  mv -f confcache \"$cache_file\"$$ &&\n\t  mv -f \"$cache_file\"$$ \"$cache_file\" ;; #(\n        *)\n\t  mv -f confcache \"$cache_file\" ;;\n\tesac\n      fi\n    fi\n  else\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file\" >&5\n$as_echo \"$as_me: not updating unwritable cache $cache_file\" >&6;}\n  fi\nfi\nrm -f confcache\n\ntest \"x$prefix\" = xNONE && prefix=$ac_default_prefix\n# Let make expand exec_prefix.\ntest \"x$exec_prefix\" = xNONE && exec_prefix='${prefix}'\n\nDEFS=-DHAVE_CONFIG_H\n\nac_libobjs=\nac_ltlibobjs=\nU=\nfor ac_i in : $LIBOBJS; do test \"x$ac_i\" = x: && continue\n  # 1. Remove the extension, and $U if already installed.\n  ac_script='s/\\$U\\././;s/\\.o$//;s/\\.obj$//'\n  ac_i=`$as_echo \"$ac_i\" | sed \"$ac_script\"`\n  # 2. Prepend LIBOBJDIR.  When used with automake>=1.10 LIBOBJDIR\n  #    will be set to the directory where LIBOBJS objects are built.\n  as_fn_append ac_libobjs \" \\${LIBOBJDIR}$ac_i\\$U.$ac_objext\"\n  as_fn_append ac_ltlibobjs \" \\${LIBOBJDIR}$ac_i\"'$U.lo'\ndone\nLIBOBJS=$ac_libobjs\n\nLTLIBOBJS=$ac_ltlibobjs\n\n\n\n: \"${CONFIG_STATUS=./config.status}\"\nac_write_fail=0\nac_clean_files_save=$ac_clean_files\nac_clean_files=\"$ac_clean_files $CONFIG_STATUS\"\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS\" >&5\n$as_echo \"$as_me: creating $CONFIG_STATUS\" >&6;}\nas_write_fail=0\ncat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1\n#! $SHELL\n# Generated by $as_me.\n# Run this file to recreate the current configuration.\n# Compiler output produced by configure, useful for debugging\n# configure, is in config.log if it exists.\n\ndebug=false\nac_cs_recheck=false\nac_cs_silent=false\n\nSHELL=\\${CONFIG_SHELL-$SHELL}\nexport SHELL\n_ASEOF\ncat >>$CONFIG_STATUS <<\\_ASEOF || as_write_fail=1\n## -------------------- ##\n## M4sh Initialization. ##\n## -------------------- ##\n\n# Be more Bourne compatible\nDUALCASE=1; export DUALCASE # for MKS sh\nif test -n \"${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :\n  emulate sh\n  NULLCMD=:\n  # Pre-4.2 versions of Zsh do word splitting on ${1+\"$@\"}, which\n  # is contrary to our usage.  Disable this feature.\n  alias -g '${1+\"$@\"}'='\"$@\"'\n  setopt NO_GLOB_SUBST\nelse\n  case `(set -o) 2>/dev/null` in #(\n  *posix*) :\n    set -o posix ;; #(\n  *) :\n     ;;\nesac\nfi\n\n\nas_nl='\n'\nexport as_nl\n# Printing a long string crashes Solaris 7 /usr/bin/printf.\nas_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'\nas_echo=$as_echo$as_echo$as_echo$as_echo$as_echo\nas_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo\n# Prefer a ksh shell builtin over an external printf program on Solaris,\n# but without wasting forks for bash or zsh.\nif test -z \"$BASH_VERSION$ZSH_VERSION\" \\\n    && (test \"X`print -r -- $as_echo`\" = \"X$as_echo\") 2>/dev/null; then\n  as_echo='print -r --'\n  as_echo_n='print -rn --'\nelif (test \"X`printf %s $as_echo`\" = \"X$as_echo\") 2>/dev/null; then\n  as_echo='printf %s\\n'\n  as_echo_n='printf %s'\nelse\n  if test \"X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`\" = \"X-n $as_echo\"; then\n    as_echo_body='eval /usr/ucb/echo -n \"$1$as_nl\"'\n    as_echo_n='/usr/ucb/echo -n'\n  else\n    as_echo_body='eval expr \"X$1\" : \"X\\\\(.*\\\\)\"'\n    as_echo_n_body='eval\n      arg=$1;\n      case $arg in #(\n      *\"$as_nl\"*)\n\texpr \"X$arg\" : \"X\\\\(.*\\\\)$as_nl\";\n\targ=`expr \"X$arg\" : \".*$as_nl\\\\(.*\\\\)\"`;;\n      esac;\n      expr \"X$arg\" : \"X\\\\(.*\\\\)\" | tr -d \"$as_nl\"\n    '\n    export as_echo_n_body\n    as_echo_n='sh -c $as_echo_n_body as_echo'\n  fi\n  export as_echo_body\n  as_echo='sh -c $as_echo_body as_echo'\nfi\n\n# The user is always right.\nif test \"${PATH_SEPARATOR+set}\" != set; then\n  PATH_SEPARATOR=:\n  (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {\n    (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||\n      PATH_SEPARATOR=';'\n  }\nfi\n\n\n# IFS\n# We need space, tab and new line, in precisely that order.  Quoting is\n# there to prevent editors from complaining about space-tab.\n# (If _AS_PATH_WALK were called with IFS unset, it would disable word\n# splitting by setting IFS to empty value.)\nIFS=\" \"\"\t$as_nl\"\n\n# Find who we are.  Look in the path if we contain no directory separator.\nas_myself=\ncase $0 in #((\n  *[\\\\/]* ) as_myself=$0 ;;\n  *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    test -r \"$as_dir/$0\" && as_myself=$as_dir/$0 && break\n  done\nIFS=$as_save_IFS\n\n     ;;\nesac\n# We did not find ourselves, most probably we were run as `sh COMMAND'\n# in which case we are not to be found in the path.\nif test \"x$as_myself\" = x; then\n  as_myself=$0\nfi\nif test ! -f \"$as_myself\"; then\n  $as_echo \"$as_myself: error: cannot find myself; rerun with an absolute file name\" >&2\n  exit 1\nfi\n\n# Unset variables that we do not need and which cause bugs (e.g. in\n# pre-3.0 UWIN ksh).  But do not cause bugs in bash 2.01; the \"|| exit 1\"\n# suppresses any \"Segmentation fault\" message there.  '((' could\n# trigger a bug in pdksh 5.2.14.\nfor as_var in BASH_ENV ENV MAIL MAILPATH\ndo eval test x\\${$as_var+set} = xset \\\n  && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :\ndone\nPS1='$ '\nPS2='> '\nPS4='+ '\n\n# NLS nuisances.\nLC_ALL=C\nexport LC_ALL\nLANGUAGE=C\nexport LANGUAGE\n\n# CDPATH.\n(unset CDPATH) >/dev/null 2>&1 && unset CDPATH\n\n\n# as_fn_error STATUS ERROR [LINENO LOG_FD]\n# ----------------------------------------\n# Output \"`basename $0`: error: ERROR\" to stderr. If LINENO and LOG_FD are\n# provided, also output the error to LOG_FD, referencing LINENO. Then exit the\n# script with STATUS, using 1 if that was 0.\nas_fn_error ()\n{\n  as_status=$1; test $as_status -eq 0 && as_status=1\n  if test \"$4\"; then\n    as_lineno=${as_lineno-\"$3\"} as_lineno_stack=as_lineno_stack=$as_lineno_stack\n    $as_echo \"$as_me:${as_lineno-$LINENO}: error: $2\" >&$4\n  fi\n  $as_echo \"$as_me: error: $2\" >&2\n  as_fn_exit $as_status\n} # as_fn_error\n\n\n# as_fn_set_status STATUS\n# -----------------------\n# Set $? to STATUS, without forking.\nas_fn_set_status ()\n{\n  return $1\n} # as_fn_set_status\n\n# as_fn_exit STATUS\n# -----------------\n# Exit the shell with STATUS, even in a \"trap 0\" or \"set -e\" context.\nas_fn_exit ()\n{\n  set +e\n  as_fn_set_status $1\n  exit $1\n} # as_fn_exit\n\n# as_fn_unset VAR\n# ---------------\n# Portably unset VAR.\nas_fn_unset ()\n{\n  { eval $1=; unset $1;}\n}\nas_unset=as_fn_unset\n# as_fn_append VAR VALUE\n# ----------------------\n# Append the text in VALUE to the end of the definition contained in VAR. Take\n# advantage of any shell optimizations that allow amortized linear growth over\n# repeated appends, instead of the typical quadratic growth present in naive\n# implementations.\nif (eval \"as_var=1; as_var+=2; test x\\$as_var = x12\") 2>/dev/null; then :\n  eval 'as_fn_append ()\n  {\n    eval $1+=\\$2\n  }'\nelse\n  as_fn_append ()\n  {\n    eval $1=\\$$1\\$2\n  }\nfi # as_fn_append\n\n# as_fn_arith ARG...\n# ------------------\n# Perform arithmetic evaluation on the ARGs, and store the result in the\n# global $as_val. Take advantage of shells that can avoid forks. The arguments\n# must be portable across $(()) and expr.\nif (eval \"test \\$(( 1 + 1 )) = 2\") 2>/dev/null; then :\n  eval 'as_fn_arith ()\n  {\n    as_val=$(( $* ))\n  }'\nelse\n  as_fn_arith ()\n  {\n    as_val=`expr \"$@\" || test $? -eq 1`\n  }\nfi # as_fn_arith\n\n\nif expr a : '\\(a\\)' >/dev/null 2>&1 &&\n   test \"X`expr 00001 : '.*\\(...\\)'`\" = X001; then\n  as_expr=expr\nelse\n  as_expr=false\nfi\n\nif (basename -- /) >/dev/null 2>&1 && test \"X`basename -- / 2>&1`\" = \"X/\"; then\n  as_basename=basename\nelse\n  as_basename=false\nfi\n\nif (as_dir=`dirname -- /` && test \"X$as_dir\" = X/) >/dev/null 2>&1; then\n  as_dirname=dirname\nelse\n  as_dirname=false\nfi\n\nas_me=`$as_basename -- \"$0\" ||\n$as_expr X/\"$0\" : '.*/\\([^/][^/]*\\)/*$' \\| \\\n\t X\"$0\" : 'X\\(//\\)$' \\| \\\n\t X\"$0\" : 'X\\(/\\)' \\| . 2>/dev/null ||\n$as_echo X/\"$0\" |\n    sed '/^.*\\/\\([^/][^/]*\\)\\/*$/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  /^X\\/\\(\\/\\/\\)$/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  /^X\\/\\(\\/\\).*/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  s/.*/./; q'`\n\n# Avoid depending upon Character Ranges.\nas_cr_letters='abcdefghijklmnopqrstuvwxyz'\nas_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'\nas_cr_Letters=$as_cr_letters$as_cr_LETTERS\nas_cr_digits='0123456789'\nas_cr_alnum=$as_cr_Letters$as_cr_digits\n\nECHO_C= ECHO_N= ECHO_T=\ncase `echo -n x` in #(((((\n-n*)\n  case `echo 'xy\\c'` in\n  *c*) ECHO_T='\t';;\t# ECHO_T is single tab character.\n  xy)  ECHO_C='\\c';;\n  *)   echo `echo ksh88 bug on AIX 6.1` > /dev/null\n       ECHO_T='\t';;\n  esac;;\n*)\n  ECHO_N='-n';;\nesac\n\nrm -f conf$$ conf$$.exe conf$$.file\nif test -d conf$$.dir; then\n  rm -f conf$$.dir/conf$$.file\nelse\n  rm -f conf$$.dir\n  mkdir conf$$.dir 2>/dev/null\nfi\nif (echo >conf$$.file) 2>/dev/null; then\n  if ln -s conf$$.file conf$$ 2>/dev/null; then\n    as_ln_s='ln -s'\n    # ... but there are two gotchas:\n    # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.\n    # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.\n    # In both cases, we have to default to `cp -p'.\n    ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||\n      as_ln_s='cp -p'\n  elif ln conf$$.file conf$$ 2>/dev/null; then\n    as_ln_s=ln\n  else\n    as_ln_s='cp -p'\n  fi\nelse\n  as_ln_s='cp -p'\nfi\nrm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file\nrmdir conf$$.dir 2>/dev/null\n\n\n# as_fn_mkdir_p\n# -------------\n# Create \"$as_dir\" as a directory, including parents if necessary.\nas_fn_mkdir_p ()\n{\n\n  case $as_dir in #(\n  -*) as_dir=./$as_dir;;\n  esac\n  test -d \"$as_dir\" || eval $as_mkdir_p || {\n    as_dirs=\n    while :; do\n      case $as_dir in #(\n      *\\'*) as_qdir=`$as_echo \"$as_dir\" | sed \"s/'/'\\\\\\\\\\\\\\\\''/g\"`;; #'(\n      *) as_qdir=$as_dir;;\n      esac\n      as_dirs=\"'$as_qdir' $as_dirs\"\n      as_dir=`$as_dirname -- \"$as_dir\" ||\n$as_expr X\"$as_dir\" : 'X\\(.*[^/]\\)//*[^/][^/]*/*$' \\| \\\n\t X\"$as_dir\" : 'X\\(//\\)[^/]' \\| \\\n\t X\"$as_dir\" : 'X\\(//\\)$' \\| \\\n\t X\"$as_dir\" : 'X\\(/\\)' \\| . 2>/dev/null ||\n$as_echo X\"$as_dir\" |\n    sed '/^X\\(.*[^/]\\)\\/\\/*[^/][^/]*\\/*$/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  /^X\\(\\/\\/\\)[^/].*/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  /^X\\(\\/\\/\\)$/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  /^X\\(\\/\\).*/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  s/.*/./; q'`\n      test -d \"$as_dir\" && break\n    done\n    test -z \"$as_dirs\" || eval \"mkdir $as_dirs\"\n  } || test -d \"$as_dir\" || as_fn_error $? \"cannot create directory $as_dir\"\n\n\n} # as_fn_mkdir_p\nif mkdir -p . 2>/dev/null; then\n  as_mkdir_p='mkdir -p \"$as_dir\"'\nelse\n  test -d ./-p && rmdir ./-p\n  as_mkdir_p=false\nfi\n\nif test -x / >/dev/null 2>&1; then\n  as_test_x='test -x'\nelse\n  if ls -dL / >/dev/null 2>&1; then\n    as_ls_L_option=L\n  else\n    as_ls_L_option=\n  fi\n  as_test_x='\n    eval sh -c '\\''\n      if test -d \"$1\"; then\n\ttest -d \"$1/.\";\n      else\n\tcase $1 in #(\n\t-*)set \"./$1\";;\n\tesac;\n\tcase `ls -ld'$as_ls_L_option' \"$1\" 2>/dev/null` in #((\n\t???[sx]*):;;*)false;;esac;fi\n    '\\'' sh\n  '\nfi\nas_executable_p=$as_test_x\n\n# Sed expression to map a string onto a valid CPP name.\nas_tr_cpp=\"eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'\"\n\n# Sed expression to map a string onto a valid variable name.\nas_tr_sh=\"eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'\"\n\n\nexec 6>&1\n## ----------------------------------- ##\n## Main body of $CONFIG_STATUS script. ##\n## ----------------------------------- ##\n_ASEOF\ntest $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1\n\ncat >>$CONFIG_STATUS <<\\_ACEOF || ac_write_fail=1\n# Save the log message, to keep $0 and so on meaningful, and to\n# report actual input values of CONFIG_FILES etc. instead of their\n# values after options handling.\nac_log=\"\nThis file was extended by $as_me, which was\ngenerated by GNU Autoconf 2.68.  Invocation command line was\n\n  CONFIG_FILES    = $CONFIG_FILES\n  CONFIG_HEADERS  = $CONFIG_HEADERS\n  CONFIG_LINKS    = $CONFIG_LINKS\n  CONFIG_COMMANDS = $CONFIG_COMMANDS\n  $ $0 $@\n\non `(hostname || uname -n) 2>/dev/null | sed 1q`\n\"\n\n_ACEOF\n\ncase $ac_config_files in *\"\n\"*) set x $ac_config_files; shift; ac_config_files=$*;;\nesac\n\ncase $ac_config_headers in *\"\n\"*) set x $ac_config_headers; shift; ac_config_headers=$*;;\nesac\n\n\ncat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1\n# Files that config.status was made for.\nconfig_files=\"$ac_config_files\"\nconfig_headers=\"$ac_config_headers\"\nconfig_commands=\"$ac_config_commands\"\n\n_ACEOF\n\ncat >>$CONFIG_STATUS <<\\_ACEOF || ac_write_fail=1\nac_cs_usage=\"\\\n\\`$as_me' instantiates files and other configuration actions\nfrom templates according to the current configuration.  Unless the files\nand actions are specified as TAGs, all are instantiated by default.\n\nUsage: $0 [OPTION]... [TAG]...\n\n  -h, --help       print this help, then exit\n  -V, --version    print version number and configuration settings, then exit\n      --config     print configuration, then exit\n  -q, --quiet, --silent\n                   do not print progress messages\n  -d, --debug      don't remove temporary files\n      --recheck    update $as_me by reconfiguring in the same conditions\n      --file=FILE[:TEMPLATE]\n                   instantiate the configuration file FILE\n      --header=FILE[:TEMPLATE]\n                   instantiate the configuration header FILE\n\nConfiguration files:\n$config_files\n\nConfiguration headers:\n$config_headers\n\nConfiguration commands:\n$config_commands\n\nReport bugs to the package provider.\"\n\n_ACEOF\ncat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1\nac_cs_config=\"`$as_echo \"$ac_configure_args\" | sed 's/^ //; s/[\\\\\"\"\\`\\$]/\\\\\\\\&/g'`\"\nac_cs_version=\"\\\\\nconfig.status\nconfigured by $0, generated by GNU Autoconf 2.68,\n  with options \\\\\"\\$ac_cs_config\\\\\"\n\nCopyright (C) 2010 Free Software Foundation, Inc.\nThis config.status script is free software; the Free Software Foundation\ngives unlimited permission to copy, distribute and modify it.\"\n\nac_pwd='$ac_pwd'\nsrcdir='$srcdir'\nINSTALL='$INSTALL'\ntest -n \"\\$AWK\" || AWK=awk\n_ACEOF\n\ncat >>$CONFIG_STATUS <<\\_ACEOF || ac_write_fail=1\n# The default lists apply if the user does not specify any file.\nac_need_defaults=:\nwhile test $# != 0\ndo\n  case $1 in\n  --*=?*)\n    ac_option=`expr \"X$1\" : 'X\\([^=]*\\)='`\n    ac_optarg=`expr \"X$1\" : 'X[^=]*=\\(.*\\)'`\n    ac_shift=:\n    ;;\n  --*=)\n    ac_option=`expr \"X$1\" : 'X\\([^=]*\\)='`\n    ac_optarg=\n    ac_shift=:\n    ;;\n  *)\n    ac_option=$1\n    ac_optarg=$2\n    ac_shift=shift\n    ;;\n  esac\n\n  case $ac_option in\n  # Handling of the options.\n  -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)\n    ac_cs_recheck=: ;;\n  --version | --versio | --versi | --vers | --ver | --ve | --v | -V )\n    $as_echo \"$ac_cs_version\"; exit ;;\n  --config | --confi | --conf | --con | --co | --c )\n    $as_echo \"$ac_cs_config\"; exit ;;\n  --debug | --debu | --deb | --de | --d | -d )\n    debug=: ;;\n  --file | --fil | --fi | --f )\n    $ac_shift\n    case $ac_optarg in\n    *\\'*) ac_optarg=`$as_echo \"$ac_optarg\" | sed \"s/'/'\\\\\\\\\\\\\\\\''/g\"` ;;\n    '') as_fn_error $? \"missing file argument\" ;;\n    esac\n    as_fn_append CONFIG_FILES \" '$ac_optarg'\"\n    ac_need_defaults=false;;\n  --header | --heade | --head | --hea )\n    $ac_shift\n    case $ac_optarg in\n    *\\'*) ac_optarg=`$as_echo \"$ac_optarg\" | sed \"s/'/'\\\\\\\\\\\\\\\\''/g\"` ;;\n    esac\n    as_fn_append CONFIG_HEADERS \" '$ac_optarg'\"\n    ac_need_defaults=false;;\n  --he | --h)\n    # Conflict between --help and --header\n    as_fn_error $? \"ambiguous option: \\`$1'\nTry \\`$0 --help' for more information.\";;\n  --help | --hel | -h )\n    $as_echo \"$ac_cs_usage\"; exit ;;\n  -q | -quiet | --quiet | --quie | --qui | --qu | --q \\\n  | -silent | --silent | --silen | --sile | --sil | --si | --s)\n    ac_cs_silent=: ;;\n\n  # This is an error.\n  -*) as_fn_error $? \"unrecognized option: \\`$1'\nTry \\`$0 --help' for more information.\" ;;\n\n  *) as_fn_append ac_config_targets \" $1\"\n     ac_need_defaults=false ;;\n\n  esac\n  shift\ndone\n\nac_configure_extra_args=\n\nif $ac_cs_silent; then\n  exec 6>/dev/null\n  ac_configure_extra_args=\"$ac_configure_extra_args --silent\"\nfi\n\n_ACEOF\ncat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1\nif \\$ac_cs_recheck; then\n  set X '$SHELL' '$0' $ac_configure_args \\$ac_configure_extra_args --no-create --no-recursion\n  shift\n  \\$as_echo \"running CONFIG_SHELL=$SHELL \\$*\" >&6\n  CONFIG_SHELL='$SHELL'\n  export CONFIG_SHELL\n  exec \"\\$@\"\nfi\n\n_ACEOF\ncat >>$CONFIG_STATUS <<\\_ACEOF || ac_write_fail=1\nexec 5>>config.log\n{\n  echo\n  sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX\n## Running $as_me. ##\n_ASBOX\n  $as_echo \"$ac_log\"\n} >&5\n\n_ACEOF\ncat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1\n_ACEOF\n\ncat >>$CONFIG_STATUS <<\\_ACEOF || ac_write_fail=1\n\n# Handling of arguments.\nfor ac_config_target in $ac_config_targets\ndo\n  case $ac_config_target in\n    \"include/jemalloc/internal/size_classes.h\") CONFIG_COMMANDS=\"$CONFIG_COMMANDS include/jemalloc/internal/size_classes.h\" ;;\n    \"$cfghdrs_tup\") CONFIG_HEADERS=\"$CONFIG_HEADERS $cfghdrs_tup\" ;;\n    \"$cfgoutputs_tup\") CONFIG_FILES=\"$CONFIG_FILES $cfgoutputs_tup\" ;;\n    \"config.stamp\") CONFIG_FILES=\"$CONFIG_FILES config.stamp\" ;;\n    \"bin/jemalloc.sh\") CONFIG_FILES=\"$CONFIG_FILES bin/jemalloc.sh\" ;;\n\n  *) as_fn_error $? \"invalid argument: \\`$ac_config_target'\" \"$LINENO\" 5;;\n  esac\ndone\n\n\n# If the user did not use the arguments to specify the items to instantiate,\n# then the envvar interface is used.  Set only those that are not.\n# We use the long form for the default assignment because of an extremely\n# bizarre bug on SunOS 4.1.3.\nif $ac_need_defaults; then\n  test \"${CONFIG_FILES+set}\" = set || CONFIG_FILES=$config_files\n  test \"${CONFIG_HEADERS+set}\" = set || CONFIG_HEADERS=$config_headers\n  test \"${CONFIG_COMMANDS+set}\" = set || CONFIG_COMMANDS=$config_commands\nfi\n\n# Have a temporary directory for convenience.  Make it in the build tree\n# simply because there is no reason against having it here, and in addition,\n# creating and moving files from /tmp can sometimes cause problems.\n# Hook for its removal unless debugging.\n# Note that there is a small window in which the directory will not be cleaned:\n# after its creation but before its name has been assigned to `$tmp'.\n$debug ||\n{\n  tmp= ac_tmp=\n  trap 'exit_status=$?\n  : \"${ac_tmp:=$tmp}\"\n  { test ! -d \"$ac_tmp\" || rm -fr \"$ac_tmp\"; } && exit $exit_status\n' 0\n  trap 'as_fn_exit 1' 1 2 13 15\n}\n# Create a (secure) tmp directory for tmp files.\n\n{\n  tmp=`(umask 077 && mktemp -d \"./confXXXXXX\") 2>/dev/null` &&\n  test -d \"$tmp\"\n}  ||\n{\n  tmp=./conf$$-$RANDOM\n  (umask 077 && mkdir \"$tmp\")\n} || as_fn_error $? \"cannot create a temporary directory in .\" \"$LINENO\" 5\nac_tmp=$tmp\n\n# Set up the scripts for CONFIG_FILES section.\n# No need to generate them if there are no CONFIG_FILES.\n# This happens for instance with `./config.status config.h'.\nif test -n \"$CONFIG_FILES\"; then\n\n\nac_cr=`echo X | tr X '\\015'`\n# On cygwin, bash can eat \\r inside `` if the user requested igncr.\n# But we know of no other shell where ac_cr would be empty at this\n# point, so we can use a bashism as a fallback.\nif test \"x$ac_cr\" = x; then\n  eval ac_cr=\\$\\'\\\\r\\'\nfi\nac_cs_awk_cr=`$AWK 'BEGIN { print \"a\\rb\" }' </dev/null 2>/dev/null`\nif test \"$ac_cs_awk_cr\" = \"a${ac_cr}b\"; then\n  ac_cs_awk_cr='\\\\r'\nelse\n  ac_cs_awk_cr=$ac_cr\nfi\n\necho 'BEGIN {' >\"$ac_tmp/subs1.awk\" &&\n_ACEOF\n\n\n{\n  echo \"cat >conf$$subs.awk <<_ACEOF\" &&\n  echo \"$ac_subst_vars\" | sed 's/.*/&!$&$ac_delim/' &&\n  echo \"_ACEOF\"\n} >conf$$subs.sh ||\n  as_fn_error $? \"could not make $CONFIG_STATUS\" \"$LINENO\" 5\nac_delim_num=`echo \"$ac_subst_vars\" | grep -c '^'`\nac_delim='%!_!# '\nfor ac_last_try in false false false false false :; do\n  . ./conf$$subs.sh ||\n    as_fn_error $? \"could not make $CONFIG_STATUS\" \"$LINENO\" 5\n\n  ac_delim_n=`sed -n \"s/.*$ac_delim\\$/X/p\" conf$$subs.awk | grep -c X`\n  if test $ac_delim_n = $ac_delim_num; then\n    break\n  elif $ac_last_try; then\n    as_fn_error $? \"could not make $CONFIG_STATUS\" \"$LINENO\" 5\n  else\n    ac_delim=\"$ac_delim!$ac_delim _$ac_delim!! \"\n  fi\ndone\nrm -f conf$$subs.sh\n\ncat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1\ncat >>\"\\$ac_tmp/subs1.awk\" <<\\\\_ACAWK &&\n_ACEOF\nsed -n '\nh\ns/^/S[\"/; s/!.*/\"]=/\np\ng\ns/^[^!]*!//\n:repl\nt repl\ns/'\"$ac_delim\"'$//\nt delim\n:nl\nh\ns/\\(.\\{148\\}\\)..*/\\1/\nt more1\ns/[\"\\\\]/\\\\&/g; s/^/\"/; s/$/\\\\n\"\\\\/\np\nn\nb repl\n:more1\ns/[\"\\\\]/\\\\&/g; s/^/\"/; s/$/\"\\\\/\np\ng\ns/.\\{148\\}//\nt nl\n:delim\nh\ns/\\(.\\{148\\}\\)..*/\\1/\nt more2\ns/[\"\\\\]/\\\\&/g; s/^/\"/; s/$/\"/\np\nb\n:more2\ns/[\"\\\\]/\\\\&/g; s/^/\"/; s/$/\"\\\\/\np\ng\ns/.\\{148\\}//\nt delim\n' <conf$$subs.awk | sed '\n/^[^\"\"]/{\n  N\n  s/\\n//\n}\n' >>$CONFIG_STATUS || ac_write_fail=1\nrm -f conf$$subs.awk\ncat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1\n_ACAWK\ncat >>\"\\$ac_tmp/subs1.awk\" <<_ACAWK &&\n  for (key in S) S_is_set[key] = 1\n  FS = \"\u0007\"\n\n}\n{\n  line = $ 0\n  nfields = split(line, field, \"@\")\n  substed = 0\n  len = length(field[1])\n  for (i = 2; i < nfields; i++) {\n    key = field[i]\n    keylen = length(key)\n    if (S_is_set[key]) {\n      value = S[key]\n      line = substr(line, 1, len) \"\" value \"\" substr(line, len + keylen + 3)\n      len += length(value) + length(field[++i])\n      substed = 1\n    } else\n      len += 1 + keylen\n  }\n\n  print line\n}\n\n_ACAWK\n_ACEOF\ncat >>$CONFIG_STATUS <<\\_ACEOF || ac_write_fail=1\nif sed \"s/$ac_cr//\" < /dev/null > /dev/null 2>&1; then\n  sed \"s/$ac_cr\\$//; s/$ac_cr/$ac_cs_awk_cr/g\"\nelse\n  cat\nfi < \"$ac_tmp/subs1.awk\" > \"$ac_tmp/subs.awk\" \\\n  || as_fn_error $? \"could not setup config files machinery\" \"$LINENO\" 5\n_ACEOF\n\n# VPATH may cause trouble with some makes, so we remove sole $(srcdir),\n# ${srcdir} and @srcdir@ entries from VPATH if srcdir is \".\", strip leading and\n# trailing colons and then remove the whole line if VPATH becomes empty\n# (actually we leave an empty line to preserve line numbers).\nif test \"x$srcdir\" = x.; then\n  ac_vpsub='/^[\t ]*VPATH[\t ]*=[\t ]*/{\nh\ns///\ns/^/:/\ns/[\t ]*$/:/\ns/:\\$(srcdir):/:/g\ns/:\\${srcdir}:/:/g\ns/:@srcdir@:/:/g\ns/^:*//\ns/:*$//\nx\ns/\\(=[\t ]*\\).*/\\1/\nG\ns/\\n//\ns/^[^=]*=[\t ]*$//\n}'\nfi\n\ncat >>$CONFIG_STATUS <<\\_ACEOF || ac_write_fail=1\nfi # test -n \"$CONFIG_FILES\"\n\n# Set up the scripts for CONFIG_HEADERS section.\n# No need to generate them if there are no CONFIG_HEADERS.\n# This happens for instance with `./config.status Makefile'.\nif test -n \"$CONFIG_HEADERS\"; then\ncat >\"$ac_tmp/defines.awk\" <<\\_ACAWK ||\nBEGIN {\n_ACEOF\n\n# Transform confdefs.h into an awk script `defines.awk', embedded as\n# here-document in config.status, that substitutes the proper values into\n# config.h.in to produce config.h.\n\n# Create a delimiter string that does not exist in confdefs.h, to ease\n# handling of long lines.\nac_delim='%!_!# '\nfor ac_last_try in false false :; do\n  ac_tt=`sed -n \"/$ac_delim/p\" confdefs.h`\n  if test -z \"$ac_tt\"; then\n    break\n  elif $ac_last_try; then\n    as_fn_error $? \"could not make $CONFIG_HEADERS\" \"$LINENO\" 5\n  else\n    ac_delim=\"$ac_delim!$ac_delim _$ac_delim!! \"\n  fi\ndone\n\n# For the awk script, D is an array of macro values keyed by name,\n# likewise P contains macro parameters if any.  Preserve backslash\n# newline sequences.\n\nac_word_re=[_$as_cr_Letters][_$as_cr_alnum]*\nsed -n '\ns/.\\{148\\}/&'\"$ac_delim\"'/g\nt rset\n:rset\ns/^[\t ]*#[\t ]*define[\t ][\t ]*/ /\nt def\nd\n:def\ns/\\\\$//\nt bsnl\ns/[\"\\\\]/\\\\&/g\ns/^ \\('\"$ac_word_re\"'\\)\\(([^()]*)\\)[\t ]*\\(.*\\)/P[\"\\1\"]=\"\\2\"\\\nD[\"\\1\"]=\" \\3\"/p\ns/^ \\('\"$ac_word_re\"'\\)[\t ]*\\(.*\\)/D[\"\\1\"]=\" \\2\"/p\nd\n:bsnl\ns/[\"\\\\]/\\\\&/g\ns/^ \\('\"$ac_word_re\"'\\)\\(([^()]*)\\)[\t ]*\\(.*\\)/P[\"\\1\"]=\"\\2\"\\\nD[\"\\1\"]=\" \\3\\\\\\\\\\\\n\"\\\\/p\nt cont\ns/^ \\('\"$ac_word_re\"'\\)[\t ]*\\(.*\\)/D[\"\\1\"]=\" \\2\\\\\\\\\\\\n\"\\\\/p\nt cont\nd\n:cont\nn\ns/.\\{148\\}/&'\"$ac_delim\"'/g\nt clear\n:clear\ns/\\\\$//\nt bsnlc\ns/[\"\\\\]/\\\\&/g; s/^/\"/; s/$/\"/p\nd\n:bsnlc\ns/[\"\\\\]/\\\\&/g; s/^/\"/; s/$/\\\\\\\\\\\\n\"\\\\/p\nb cont\n' <confdefs.h | sed '\ns/'\"$ac_delim\"'/\"\\\\\\\n\"/g' >>$CONFIG_STATUS || ac_write_fail=1\n\ncat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1\n  for (key in D) D_is_set[key] = 1\n  FS = \"\u0007\"\n}\n/^[\\t ]*#[\\t ]*(define|undef)[\\t ]+$ac_word_re([\\t (]|\\$)/ {\n  line = \\$ 0\n  split(line, arg, \" \")\n  if (arg[1] == \"#\") {\n    defundef = arg[2]\n    mac1 = arg[3]\n  } else {\n    defundef = substr(arg[1], 2)\n    mac1 = arg[2]\n  }\n  split(mac1, mac2, \"(\") #)\n  macro = mac2[1]\n  prefix = substr(line, 1, index(line, defundef) - 1)\n  if (D_is_set[macro]) {\n    # Preserve the white space surrounding the \"#\".\n    print prefix \"define\", macro P[macro] D[macro]\n    next\n  } else {\n    # Replace #undef with comments.  This is necessary, for example,\n    # in the case of _POSIX_SOURCE, which is predefined and required\n    # on some systems where configure will not decide to define it.\n    if (defundef == \"undef\") {\n      print \"/*\", prefix defundef, macro, \"*/\"\n      next\n    }\n  }\n}\n{ print }\n_ACAWK\n_ACEOF\ncat >>$CONFIG_STATUS <<\\_ACEOF || ac_write_fail=1\n  as_fn_error $? \"could not setup config headers machinery\" \"$LINENO\" 5\nfi # test -n \"$CONFIG_HEADERS\"\n\n\neval set X \"  :F $CONFIG_FILES  :H $CONFIG_HEADERS    :C $CONFIG_COMMANDS\"\nshift\nfor ac_tag\ndo\n  case $ac_tag in\n  :[FHLC]) ac_mode=$ac_tag; continue;;\n  esac\n  case $ac_mode$ac_tag in\n  :[FHL]*:*);;\n  :L* | :C*:*) as_fn_error $? \"invalid tag \\`$ac_tag'\" \"$LINENO\" 5;;\n  :[FH]-) ac_tag=-:-;;\n  :[FH]*) ac_tag=$ac_tag:$ac_tag.in;;\n  esac\n  ac_save_IFS=$IFS\n  IFS=:\n  set x $ac_tag\n  IFS=$ac_save_IFS\n  shift\n  ac_file=$1\n  shift\n\n  case $ac_mode in\n  :L) ac_source=$1;;\n  :[FH])\n    ac_file_inputs=\n    for ac_f\n    do\n      case $ac_f in\n      -) ac_f=\"$ac_tmp/stdin\";;\n      *) # Look for the file first in the build tree, then in the source tree\n\t # (if the path is not absolute).  The absolute path cannot be DOS-style,\n\t # because $ac_f cannot contain `:'.\n\t test -f \"$ac_f\" ||\n\t   case $ac_f in\n\t   [\\\\/$]*) false;;\n\t   *) test -f \"$srcdir/$ac_f\" && ac_f=\"$srcdir/$ac_f\";;\n\t   esac ||\n\t   as_fn_error 1 \"cannot find input file: \\`$ac_f'\" \"$LINENO\" 5;;\n      esac\n      case $ac_f in *\\'*) ac_f=`$as_echo \"$ac_f\" | sed \"s/'/'\\\\\\\\\\\\\\\\''/g\"`;; esac\n      as_fn_append ac_file_inputs \" '$ac_f'\"\n    done\n\n    # Let's still pretend it is `configure' which instantiates (i.e., don't\n    # use $as_me), people would be surprised to read:\n    #    /* config.h.  Generated by config.status.  */\n    configure_input='Generated from '`\n\t  $as_echo \"$*\" | sed 's|^[^:]*/||;s|:[^:]*/|, |g'\n\t`' by configure.'\n    if test x\"$ac_file\" != x-; then\n      configure_input=\"$ac_file.  $configure_input\"\n      { $as_echo \"$as_me:${as_lineno-$LINENO}: creating $ac_file\" >&5\n$as_echo \"$as_me: creating $ac_file\" >&6;}\n    fi\n    # Neutralize special characters interpreted by sed in replacement strings.\n    case $configure_input in #(\n    *\\&* | *\\|* | *\\\\* )\n       ac_sed_conf_input=`$as_echo \"$configure_input\" |\n       sed 's/[\\\\\\\\&|]/\\\\\\\\&/g'`;; #(\n    *) ac_sed_conf_input=$configure_input;;\n    esac\n\n    case $ac_tag in\n    *:-:* | *:-) cat >\"$ac_tmp/stdin\" \\\n      || as_fn_error $? \"could not create $ac_file\" \"$LINENO\" 5 ;;\n    esac\n    ;;\n  esac\n\n  ac_dir=`$as_dirname -- \"$ac_file\" ||\n$as_expr X\"$ac_file\" : 'X\\(.*[^/]\\)//*[^/][^/]*/*$' \\| \\\n\t X\"$ac_file\" : 'X\\(//\\)[^/]' \\| \\\n\t X\"$ac_file\" : 'X\\(//\\)$' \\| \\\n\t X\"$ac_file\" : 'X\\(/\\)' \\| . 2>/dev/null ||\n$as_echo X\"$ac_file\" |\n    sed '/^X\\(.*[^/]\\)\\/\\/*[^/][^/]*\\/*$/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  /^X\\(\\/\\/\\)[^/].*/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  /^X\\(\\/\\/\\)$/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  /^X\\(\\/\\).*/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  s/.*/./; q'`\n  as_dir=\"$ac_dir\"; as_fn_mkdir_p\n  ac_builddir=.\n\ncase \"$ac_dir\" in\n.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;\n*)\n  ac_dir_suffix=/`$as_echo \"$ac_dir\" | sed 's|^\\.[\\\\/]||'`\n  # A \"..\" for each directory in $ac_dir_suffix.\n  ac_top_builddir_sub=`$as_echo \"$ac_dir_suffix\" | sed 's|/[^\\\\/]*|/..|g;s|/||'`\n  case $ac_top_builddir_sub in\n  \"\") ac_top_builddir_sub=. ac_top_build_prefix= ;;\n  *)  ac_top_build_prefix=$ac_top_builddir_sub/ ;;\n  esac ;;\nesac\nac_abs_top_builddir=$ac_pwd\nac_abs_builddir=$ac_pwd$ac_dir_suffix\n# for backward compatibility:\nac_top_builddir=$ac_top_build_prefix\n\ncase $srcdir in\n  .)  # We are building in place.\n    ac_srcdir=.\n    ac_top_srcdir=$ac_top_builddir_sub\n    ac_abs_top_srcdir=$ac_pwd ;;\n  [\\\\/]* | ?:[\\\\/]* )  # Absolute name.\n    ac_srcdir=$srcdir$ac_dir_suffix;\n    ac_top_srcdir=$srcdir\n    ac_abs_top_srcdir=$srcdir ;;\n  *) # Relative name.\n    ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix\n    ac_top_srcdir=$ac_top_build_prefix$srcdir\n    ac_abs_top_srcdir=$ac_pwd/$srcdir ;;\nesac\nac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix\n\n\n  case $ac_mode in\n  :F)\n  #\n  # CONFIG_FILE\n  #\n\n  case $INSTALL in\n  [\\\\/$]* | ?:[\\\\/]* ) ac_INSTALL=$INSTALL ;;\n  *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;;\n  esac\n_ACEOF\n\ncat >>$CONFIG_STATUS <<\\_ACEOF || ac_write_fail=1\n# If the template does not know about datarootdir, expand it.\n# FIXME: This hack should be removed a few years after 2.60.\nac_datarootdir_hack=; ac_datarootdir_seen=\nac_sed_dataroot='\n/datarootdir/ {\n  p\n  q\n}\n/@datadir@/p\n/@docdir@/p\n/@infodir@/p\n/@localedir@/p\n/@mandir@/p'\ncase `eval \"sed -n \\\"\\$ac_sed_dataroot\\\" $ac_file_inputs\"` in\n*datarootdir*) ac_datarootdir_seen=yes;;\n*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*)\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting\" >&5\n$as_echo \"$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting\" >&2;}\n_ACEOF\ncat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1\n  ac_datarootdir_hack='\n  s&@datadir@&$datadir&g\n  s&@docdir@&$docdir&g\n  s&@infodir@&$infodir&g\n  s&@localedir@&$localedir&g\n  s&@mandir@&$mandir&g\n  s&\\\\\\${datarootdir}&$datarootdir&g' ;;\nesac\n_ACEOF\n\n# Neutralize VPATH when `$srcdir' = `.'.\n# Shell code in configure.ac might set extrasub.\n# FIXME: do we really want to maintain this feature?\ncat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1\nac_sed_extra=\"$ac_vpsub\n$extrasub\n_ACEOF\ncat >>$CONFIG_STATUS <<\\_ACEOF || ac_write_fail=1\n:t\n/@[a-zA-Z_][a-zA-Z_0-9]*@/!b\ns|@configure_input@|$ac_sed_conf_input|;t t\ns&@top_builddir@&$ac_top_builddir_sub&;t t\ns&@top_build_prefix@&$ac_top_build_prefix&;t t\ns&@srcdir@&$ac_srcdir&;t t\ns&@abs_srcdir@&$ac_abs_srcdir&;t t\ns&@top_srcdir@&$ac_top_srcdir&;t t\ns&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t\ns&@builddir@&$ac_builddir&;t t\ns&@abs_builddir@&$ac_abs_builddir&;t t\ns&@abs_top_builddir@&$ac_abs_top_builddir&;t t\ns&@INSTALL@&$ac_INSTALL&;t t\n$ac_datarootdir_hack\n\"\neval sed \\\"\\$ac_sed_extra\\\" \"$ac_file_inputs\" | $AWK -f \"$ac_tmp/subs.awk\" \\\n  >$ac_tmp/out || as_fn_error $? \"could not create $ac_file\" \"$LINENO\" 5\n\ntest -z \"$ac_datarootdir_hack$ac_datarootdir_seen\" &&\n  { ac_out=`sed -n '/\\${datarootdir}/p' \"$ac_tmp/out\"`; test -n \"$ac_out\"; } &&\n  { ac_out=`sed -n '/^[\t ]*datarootdir[\t ]*:*=/p' \\\n      \"$ac_tmp/out\"`; test -z \"$ac_out\"; } &&\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \\`datarootdir'\nwhich seems to be undefined.  Please make sure it is defined\" >&5\n$as_echo \"$as_me: WARNING: $ac_file contains a reference to the variable \\`datarootdir'\nwhich seems to be undefined.  Please make sure it is defined\" >&2;}\n\n  rm -f \"$ac_tmp/stdin\"\n  case $ac_file in\n  -) cat \"$ac_tmp/out\" && rm -f \"$ac_tmp/out\";;\n  *) rm -f \"$ac_file\" && mv \"$ac_tmp/out\" \"$ac_file\";;\n  esac \\\n  || as_fn_error $? \"could not create $ac_file\" \"$LINENO\" 5\n ;;\n  :H)\n  #\n  # CONFIG_HEADER\n  #\n  if test x\"$ac_file\" != x-; then\n    {\n      $as_echo \"/* $configure_input  */\" \\\n      && eval '$AWK -f \"$ac_tmp/defines.awk\"' \"$ac_file_inputs\"\n    } >\"$ac_tmp/config.h\" \\\n      || as_fn_error $? \"could not create $ac_file\" \"$LINENO\" 5\n    if diff \"$ac_file\" \"$ac_tmp/config.h\" >/dev/null 2>&1; then\n      { $as_echo \"$as_me:${as_lineno-$LINENO}: $ac_file is unchanged\" >&5\n$as_echo \"$as_me: $ac_file is unchanged\" >&6;}\n    else\n      rm -f \"$ac_file\"\n      mv \"$ac_tmp/config.h\" \"$ac_file\" \\\n\t|| as_fn_error $? \"could not create $ac_file\" \"$LINENO\" 5\n    fi\n  else\n    $as_echo \"/* $configure_input  */\" \\\n      && eval '$AWK -f \"$ac_tmp/defines.awk\"' \"$ac_file_inputs\" \\\n      || as_fn_error $? \"could not create -\" \"$LINENO\" 5\n  fi\n ;;\n\n  :C)  { $as_echo \"$as_me:${as_lineno-$LINENO}: executing $ac_file commands\" >&5\n$as_echo \"$as_me: executing $ac_file commands\" >&6;}\n ;;\n  esac\n\n\n  case $ac_file$ac_mode in\n    \"include/jemalloc/internal/size_classes.h\":C)\n  mkdir -p \"include/jemalloc/internal\"\n  \"${srcdir}/include/jemalloc/internal/size_classes.sh\" > \"${objroot}include/jemalloc/internal/size_classes.h\"\n ;;\n\n  esac\ndone # for ac_tag\n\n\nas_fn_exit 0\n_ACEOF\nac_clean_files=$ac_clean_files_save\n\ntest $ac_write_fail = 0 ||\n  as_fn_error $? \"write failure creating $CONFIG_STATUS\" \"$LINENO\" 5\n\n\n# configure is writing to config.log, and then calls config.status.\n# config.status does its own redirection, appending to config.log.\n# Unfortunately, on DOS this fails, as config.log is still kept open\n# by configure, so config.status won't be able to write to it; its\n# output is simply discarded.  So we exec the FD to /dev/null,\n# effectively closing config.log, so it can be properly (re)opened and\n# appended to by config.status.  When coming back to configure, we\n# need to make the FD available again.\nif test \"$no_create\" != yes; then\n  ac_cs_success=:\n  ac_config_status_args=\n  test \"$silent\" = yes &&\n    ac_config_status_args=\"$ac_config_status_args --quiet\"\n  exec 5>/dev/null\n  $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false\n  exec 5>>config.log\n  # Use ||, not &&, to avoid exiting from the if with $? = 1, which\n  # would make configure fail if this is the last instruction.\n  $ac_cs_success || as_fn_exit 1\nfi\nif test -n \"$ac_unrecognized_opts\" && test \"$enable_option_checking\" != no; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts\" >&5\n$as_echo \"$as_me: WARNING: unrecognized options: $ac_unrecognized_opts\" >&2;}\nfi\n\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: ===============================================================================\" >&5\n$as_echo \"===============================================================================\" >&6; }\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: jemalloc version   : ${jemalloc_version}\" >&5\n$as_echo \"jemalloc version   : ${jemalloc_version}\" >&6; }\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: library revision   : ${rev}\" >&5\n$as_echo \"library revision   : ${rev}\" >&6; }\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: \" >&5\n$as_echo \"\" >&6; }\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: CC                 : ${CC}\" >&5\n$as_echo \"CC                 : ${CC}\" >&6; }\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: CPPFLAGS           : ${CPPFLAGS}\" >&5\n$as_echo \"CPPFLAGS           : ${CPPFLAGS}\" >&6; }\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: CFLAGS             : ${CFLAGS}\" >&5\n$as_echo \"CFLAGS             : ${CFLAGS}\" >&6; }\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: LDFLAGS            : ${LDFLAGS}\" >&5\n$as_echo \"LDFLAGS            : ${LDFLAGS}\" >&6; }\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: LIBS               : ${LIBS}\" >&5\n$as_echo \"LIBS               : ${LIBS}\" >&6; }\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: RPATH_EXTRA        : ${RPATH_EXTRA}\" >&5\n$as_echo \"RPATH_EXTRA        : ${RPATH_EXTRA}\" >&6; }\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: \" >&5\n$as_echo \"\" >&6; }\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: XSLTPROC           : ${XSLTPROC}\" >&5\n$as_echo \"XSLTPROC           : ${XSLTPROC}\" >&6; }\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: XSLROOT            : ${XSLROOT}\" >&5\n$as_echo \"XSLROOT            : ${XSLROOT}\" >&6; }\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: \" >&5\n$as_echo \"\" >&6; }\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: PREFIX             : ${PREFIX}\" >&5\n$as_echo \"PREFIX             : ${PREFIX}\" >&6; }\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: BINDIR             : ${BINDIR}\" >&5\n$as_echo \"BINDIR             : ${BINDIR}\" >&6; }\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: INCLUDEDIR         : ${INCLUDEDIR}\" >&5\n$as_echo \"INCLUDEDIR         : ${INCLUDEDIR}\" >&6; }\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: LIBDIR             : ${LIBDIR}\" >&5\n$as_echo \"LIBDIR             : ${LIBDIR}\" >&6; }\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: DATADIR            : ${DATADIR}\" >&5\n$as_echo \"DATADIR            : ${DATADIR}\" >&6; }\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: MANDIR             : ${MANDIR}\" >&5\n$as_echo \"MANDIR             : ${MANDIR}\" >&6; }\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: \" >&5\n$as_echo \"\" >&6; }\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: srcroot            : ${srcroot}\" >&5\n$as_echo \"srcroot            : ${srcroot}\" >&6; }\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: abs_srcroot        : ${abs_srcroot}\" >&5\n$as_echo \"abs_srcroot        : ${abs_srcroot}\" >&6; }\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: objroot            : ${objroot}\" >&5\n$as_echo \"objroot            : ${objroot}\" >&6; }\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: abs_objroot        : ${abs_objroot}\" >&5\n$as_echo \"abs_objroot        : ${abs_objroot}\" >&6; }\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: \" >&5\n$as_echo \"\" >&6; }\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: JEMALLOC_PREFIX    : ${JEMALLOC_PREFIX}\" >&5\n$as_echo \"JEMALLOC_PREFIX    : ${JEMALLOC_PREFIX}\" >&6; }\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: JEMALLOC_PRIVATE_NAMESPACE\" >&5\n$as_echo \"JEMALLOC_PRIVATE_NAMESPACE\" >&6; }\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result:                    : ${JEMALLOC_PRIVATE_NAMESPACE}\" >&5\n$as_echo \"                   : ${JEMALLOC_PRIVATE_NAMESPACE}\" >&6; }\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: install_suffix     : ${install_suffix}\" >&5\n$as_echo \"install_suffix     : ${install_suffix}\" >&6; }\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: autogen            : ${enable_autogen}\" >&5\n$as_echo \"autogen            : ${enable_autogen}\" >&6; }\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: experimental       : ${enable_experimental}\" >&5\n$as_echo \"experimental       : ${enable_experimental}\" >&6; }\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: cc-silence         : ${enable_cc_silence}\" >&5\n$as_echo \"cc-silence         : ${enable_cc_silence}\" >&6; }\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: debug              : ${enable_debug}\" >&5\n$as_echo \"debug              : ${enable_debug}\" >&6; }\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: stats              : ${enable_stats}\" >&5\n$as_echo \"stats              : ${enable_stats}\" >&6; }\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: prof               : ${enable_prof}\" >&5\n$as_echo \"prof               : ${enable_prof}\" >&6; }\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: prof-libunwind     : ${enable_prof_libunwind}\" >&5\n$as_echo \"prof-libunwind     : ${enable_prof_libunwind}\" >&6; }\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: prof-libgcc        : ${enable_prof_libgcc}\" >&5\n$as_echo \"prof-libgcc        : ${enable_prof_libgcc}\" >&6; }\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: prof-gcc           : ${enable_prof_gcc}\" >&5\n$as_echo \"prof-gcc           : ${enable_prof_gcc}\" >&6; }\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: tcache             : ${enable_tcache}\" >&5\n$as_echo \"tcache             : ${enable_tcache}\" >&6; }\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: fill               : ${enable_fill}\" >&5\n$as_echo \"fill               : ${enable_fill}\" >&6; }\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: utrace             : ${enable_utrace}\" >&5\n$as_echo \"utrace             : ${enable_utrace}\" >&6; }\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: valgrind           : ${enable_valgrind}\" >&5\n$as_echo \"valgrind           : ${enable_valgrind}\" >&6; }\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: xmalloc            : ${enable_xmalloc}\" >&5\n$as_echo \"xmalloc            : ${enable_xmalloc}\" >&6; }\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: mremap             : ${enable_mremap}\" >&5\n$as_echo \"mremap             : ${enable_mremap}\" >&6; }\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: munmap             : ${enable_munmap}\" >&5\n$as_echo \"munmap             : ${enable_munmap}\" >&6; }\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: dss                : ${enable_dss}\" >&5\n$as_echo \"dss                : ${enable_dss}\" >&6; }\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: lazy_lock          : ${enable_lazy_lock}\" >&5\n$as_echo \"lazy_lock          : ${enable_lazy_lock}\" >&6; }\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: tls                : ${enable_tls}\" >&5\n$as_echo \"tls                : ${enable_tls}\" >&6; }\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: ===============================================================================\" >&5\n$as_echo \"===============================================================================\" >&6; }\n"
  },
  {
    "path": "deps/jemalloc/configure.ac",
    "content": "dnl Process this file with autoconf to produce a configure script.\nAC_INIT([Makefile.in])\n\ndnl ============================================================================\ndnl Custom macro definitions.\n\ndnl JE_CFLAGS_APPEND(cflag)\nAC_DEFUN([JE_CFLAGS_APPEND],\n[\nAC_MSG_CHECKING([whether compiler supports $1])\nTCFLAGS=\"${CFLAGS}\"\nif test \"x${CFLAGS}\" = \"x\" ; then\n  CFLAGS=\"$1\"\nelse\n  CFLAGS=\"${CFLAGS} $1\"\nfi\nAC_COMPILE_IFELSE([AC_LANG_PROGRAM(\n[[\n]], [[\n    return 0;\n]])],\n              AC_MSG_RESULT([yes]),\n              AC_MSG_RESULT([no])\n              [CFLAGS=\"${TCFLAGS}\"]\n)\n])\n\ndnl JE_COMPILABLE(label, hcode, mcode, rvar)\ndnl \ndnl Use AC_LINK_IFELSE() rather than AC_COMPILE_IFELSE() so that linker errors\ndnl cause failure.\nAC_DEFUN([JE_COMPILABLE],\n[\nAC_CACHE_CHECK([whether $1 is compilable],\n               [$4],\n               [AC_LINK_IFELSE([AC_LANG_PROGRAM([$2],\n                                                [$3])],\n                               [$4=yes],\n                               [$4=no])])\n])\n\ndnl ============================================================================\n\ndnl Library revision.\nrev=1\nAC_SUBST([rev])\n\nsrcroot=$srcdir\nif test \"x${srcroot}\" = \"x.\" ; then\n  srcroot=\"\"\nelse\n  srcroot=\"${srcroot}/\"\nfi\nAC_SUBST([srcroot])\nabs_srcroot=\"`cd \\\"${srcdir}\\\"; pwd`/\"\nAC_SUBST([abs_srcroot])\n\nobjroot=\"\"\nAC_SUBST([objroot])\nabs_objroot=\"`pwd`/\"\nAC_SUBST([abs_objroot])\n\ndnl Munge install path variables.\nif test \"x$prefix\" = \"xNONE\" ; then\n  prefix=\"/usr/local\"\nfi\nif test \"x$exec_prefix\" = \"xNONE\" ; then\n  exec_prefix=$prefix\nfi\nPREFIX=$prefix\nAC_SUBST([PREFIX])\nBINDIR=`eval echo $bindir`\nBINDIR=`eval echo $BINDIR`\nAC_SUBST([BINDIR])\nINCLUDEDIR=`eval echo $includedir`\nINCLUDEDIR=`eval echo $INCLUDEDIR`\nAC_SUBST([INCLUDEDIR])\nLIBDIR=`eval echo $libdir`\nLIBDIR=`eval echo $LIBDIR`\nAC_SUBST([LIBDIR])\nDATADIR=`eval echo $datadir`\nDATADIR=`eval echo $DATADIR`\nAC_SUBST([DATADIR])\nMANDIR=`eval echo $mandir`\nMANDIR=`eval echo $MANDIR`\nAC_SUBST([MANDIR])\n\ndnl Support for building documentation.\nAC_PATH_PROG([XSLTPROC], [xsltproc], , [$PATH])\nif test -d \"/usr/share/xml/docbook/stylesheet/docbook-xsl\" ; then\n  DEFAULT_XSLROOT=\"/usr/share/xml/docbook/stylesheet/docbook-xsl\"\nelif test -d \"/usr/share/sgml/docbook/xsl-stylesheets\" ; then\n  DEFAULT_XSLROOT=\"/usr/share/sgml/docbook/xsl-stylesheets\"\nelse\n  dnl Documentation building will fail if this default gets used.\n  DEFAULT_XSLROOT=\"\"\nfi\nAC_ARG_WITH([xslroot],\n  [AS_HELP_STRING([--with-xslroot=<path>], [XSL stylesheet root path])], [\nif test \"x$with_xslroot\" = \"xno\" ; then\n  XSLROOT=\"${DEFAULT_XSLROOT}\"\nelse\n  XSLROOT=\"${with_xslroot}\"\nfi\n],\n  XSLROOT=\"${DEFAULT_XSLROOT}\"\n)\nAC_SUBST([XSLROOT])\n\ndnl If CFLAGS isn't defined, set CFLAGS to something reasonable.  Otherwise,\ndnl just prevent autoconf from molesting CFLAGS.\nCFLAGS=$CFLAGS\nAC_PROG_CC\nif test \"x$GCC\" != \"xyes\" ; then\n  AC_CACHE_CHECK([whether compiler is MSVC],\n                 [je_cv_msvc],\n                 [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],\n                                                     [\n#ifndef _MSC_VER\n  int fail[-1];\n#endif\n])],\n                               [je_cv_msvc=yes],\n                               [je_cv_msvc=no])])\nfi\n\nif test \"x$CFLAGS\" = \"x\" ; then\n  no_CFLAGS=\"yes\"\n  if test \"x$GCC\" = \"xyes\" ; then\n    JE_CFLAGS_APPEND([-std=gnu99])\n    JE_CFLAGS_APPEND([-Wall])\n    JE_CFLAGS_APPEND([-pipe])\n    JE_CFLAGS_APPEND([-g3])\n  elif test \"x$je_cv_msvc\" = \"xyes\" ; then\n    CC=\"$CC -nologo\"\n    JE_CFLAGS_APPEND([-Zi])\n    JE_CFLAGS_APPEND([-MT])\n    JE_CFLAGS_APPEND([-W3])\n    CPPFLAGS=\"$CPPFLAGS -I${srcroot}/include/msvc_compat\"\n  fi\nfi\ndnl Append EXTRA_CFLAGS to CFLAGS, if defined.\nif test \"x$EXTRA_CFLAGS\" != \"x\" ; then\n  JE_CFLAGS_APPEND([$EXTRA_CFLAGS])\nfi\nAC_PROG_CPP\n\nAC_CHECK_SIZEOF([void *])\nif test \"x${ac_cv_sizeof_void_p}\" = \"x8\" ; then\n  LG_SIZEOF_PTR=3\nelif test \"x${ac_cv_sizeof_void_p}\" = \"x4\" ; then\n  LG_SIZEOF_PTR=2\nelse\n  AC_MSG_ERROR([Unsupported pointer size: ${ac_cv_sizeof_void_p}])\nfi\nAC_DEFINE_UNQUOTED([LG_SIZEOF_PTR], [$LG_SIZEOF_PTR])\n\nAC_CHECK_SIZEOF([int])\nif test \"x${ac_cv_sizeof_int}\" = \"x8\" ; then\n  LG_SIZEOF_INT=3\nelif test \"x${ac_cv_sizeof_int}\" = \"x4\" ; then\n  LG_SIZEOF_INT=2\nelse\n  AC_MSG_ERROR([Unsupported int size: ${ac_cv_sizeof_int}])\nfi\nAC_DEFINE_UNQUOTED([LG_SIZEOF_INT], [$LG_SIZEOF_INT])\n\nAC_CHECK_SIZEOF([long])\nif test \"x${ac_cv_sizeof_long}\" = \"x8\" ; then\n  LG_SIZEOF_LONG=3\nelif test \"x${ac_cv_sizeof_long}\" = \"x4\" ; then\n  LG_SIZEOF_LONG=2\nelse\n  AC_MSG_ERROR([Unsupported long size: ${ac_cv_sizeof_long}])\nfi\nAC_DEFINE_UNQUOTED([LG_SIZEOF_LONG], [$LG_SIZEOF_LONG])\n\nAC_CHECK_SIZEOF([intmax_t])\nif test \"x${ac_cv_sizeof_intmax_t}\" = \"x16\" ; then\n  LG_SIZEOF_INTMAX_T=4\nelif test \"x${ac_cv_sizeof_intmax_t}\" = \"x8\" ; then\n  LG_SIZEOF_INTMAX_T=3\nelif test \"x${ac_cv_sizeof_intmax_t}\" = \"x4\" ; then\n  LG_SIZEOF_INTMAX_T=2\nelse\n  AC_MSG_ERROR([Unsupported intmax_t size: ${ac_cv_sizeof_intmax_t}])\nfi\nAC_DEFINE_UNQUOTED([LG_SIZEOF_INTMAX_T], [$LG_SIZEOF_INTMAX_T])\n\nAC_CANONICAL_HOST\ndnl CPU-specific settings.\nCPU_SPINWAIT=\"\"\ncase \"${host_cpu}\" in\n  i[[345]]86)\n\t;;\n  i686)\n\tJE_COMPILABLE([__asm__], [], [[__asm__ volatile(\"pause\"); return 0;]],\n\t              [je_cv_asm])\n\tif test \"x${je_cv_asm}\" = \"xyes\" ; then\n\t    CPU_SPINWAIT='__asm__ volatile(\"pause\")'\n\tfi\n\t;;\n  x86_64)\n\tJE_COMPILABLE([__asm__ syntax], [],\n\t              [[__asm__ volatile(\"pause\"); return 0;]], [je_cv_asm])\n\tif test \"x${je_cv_asm}\" = \"xyes\" ; then\n\t    CPU_SPINWAIT='__asm__ volatile(\"pause\")'\n\tfi\n\t;;\n  *)\n\t;;\nesac\nAC_DEFINE_UNQUOTED([CPU_SPINWAIT], [$CPU_SPINWAIT])\n\nLD_PRELOAD_VAR=\"LD_PRELOAD\"\nso=\"so\"\nimportlib=\"${so}\"\no=\"$ac_objext\"\na=\"a\"\nexe=\"$ac_exeext\"\nlibprefix=\"lib\"\nDSO_LDFLAGS='-shared -Wl,-soname,$(@F)'\nRPATH='-Wl,-rpath,$(1)'\nSOREV=\"${so}.${rev}\"\nPIC_CFLAGS='-fPIC -DPIC'\nCTARGET='-o $@'\nLDTARGET='-o $@'\nEXTRA_LDFLAGS=\nMKLIB='ar crus $@'\nCC_MM=1\n\ndnl Platform-specific settings.  abi and RPATH can probably be determined\ndnl programmatically, but doing so is error-prone, which makes it generally\ndnl not worth the trouble.\ndnl \ndnl Define cpp macros in CPPFLAGS, rather than doing AC_DEFINE(macro), since the\ndnl definitions need to be seen before any headers are included, which is a pain\ndnl to make happen otherwise.\ndefault_munmap=\"1\"\nJEMALLOC_USABLE_SIZE_CONST=\"const\"\ncase \"${host}\" in\n  *-*-darwin*)\n\tCFLAGS=\"$CFLAGS\"\n\tabi=\"macho\"\n\tAC_DEFINE([JEMALLOC_PURGE_MADVISE_FREE], [ ])\n\tRPATH=\"\"\n\tLD_PRELOAD_VAR=\"DYLD_INSERT_LIBRARIES\"\n\tso=\"dylib\"\n\timportlib=\"${so}\"\n\tforce_tls=\"0\"\n\tDSO_LDFLAGS='-shared -Wl,-dylib_install_name,$(@F)'\n\tSOREV=\"${rev}.${so}\"\n\t;;\n  *-*-freebsd*)\n\tCFLAGS=\"$CFLAGS\"\n\tabi=\"elf\"\n\tAC_DEFINE([JEMALLOC_PURGE_MADVISE_FREE], [ ])\n\tforce_lazy_lock=\"1\"\n\t;;\n  *-*-linux*)\n\tCFLAGS=\"$CFLAGS\"\n\tCPPFLAGS=\"$CPPFLAGS -D_GNU_SOURCE\"\n\tabi=\"elf\"\n\tAC_DEFINE([JEMALLOC_PURGE_MADVISE_DONTNEED], [ ])\n\tAC_DEFINE([JEMALLOC_THREADED_INIT], [ ])\n\tJEMALLOC_USABLE_SIZE_CONST=\"\"\n\tdefault_munmap=\"0\"\n\t;;\n  *-*-netbsd*)\n\tAC_MSG_CHECKING([ABI])\n        AC_COMPILE_IFELSE([AC_LANG_PROGRAM(\n[[#ifdef __ELF__\n/* ELF */\n#else\n#error aout\n#endif\n]])],\n                          [CFLAGS=\"$CFLAGS\"; abi=\"elf\"],\n                          [abi=\"aout\"])\n\tAC_MSG_RESULT([$abi])\n\tAC_DEFINE([JEMALLOC_PURGE_MADVISE_FREE], [ ])\n\t;;\n  *-*-solaris2*)\n\tCFLAGS=\"$CFLAGS\"\n\tabi=\"elf\"\n\tRPATH='-Wl,-R,$(1)'\n\tdnl Solaris needs this for sigwait().\n\tCPPFLAGS=\"$CPPFLAGS -D_POSIX_PTHREAD_SEMANTICS\"\n\tLIBS=\"$LIBS -lposix4 -lsocket -lnsl\"\n\t;;\n  *-ibm-aix*)\n\tif \"$LG_SIZEOF_PTR\" = \"8\"; then\n\t  dnl 64bit AIX\n\t  LD_PRELOAD_VAR=\"LDR_PRELOAD64\"\n\telse\n\t  dnl 32bit AIX\n\t  LD_PRELOAD_VAR=\"LDR_PRELOAD\"\n\tfi\n\tabi=\"xcoff\"\n\t;;\n  *-*-mingw*)\n\tabi=\"pecoff\"\n\tforce_tls=\"0\"\n\tRPATH=\"\"\n\tso=\"dll\"\n\tif test \"x$je_cv_msvc\" = \"xyes\" ; then\n\t  importlib=\"lib\"\n\t  DSO_LDFLAGS=\"-LD\"\n\t  EXTRA_LDFLAGS=\"-link -DEBUG\"\n\t  CTARGET='-Fo$@'\n\t  LDTARGET='-Fe$@'\n\t  MKLIB='lib -nologo -out:$@'\n\t  CC_MM=\n        else\n\t  importlib=\"${so}\"\n\t  DSO_LDFLAGS=\"-shared\"\n\tfi\n\ta=\"lib\"\n\tlibprefix=\"\"\n\tSOREV=\"${so}\"\n\tPIC_CFLAGS=\"\"\n\t;;\n  *)\n\tAC_MSG_RESULT([Unsupported operating system: ${host}])\n\tabi=\"elf\"\n\t;;\nesac\nAC_DEFINE_UNQUOTED([JEMALLOC_USABLE_SIZE_CONST], [$JEMALLOC_USABLE_SIZE_CONST])\nAC_SUBST([abi])\nAC_SUBST([RPATH])\nAC_SUBST([LD_PRELOAD_VAR])\nAC_SUBST([so])\nAC_SUBST([importlib])\nAC_SUBST([o])\nAC_SUBST([a])\nAC_SUBST([exe])\nAC_SUBST([libprefix])\nAC_SUBST([DSO_LDFLAGS])\nAC_SUBST([EXTRA_LDFLAGS])\nAC_SUBST([SOREV])\nAC_SUBST([PIC_CFLAGS])\nAC_SUBST([CTARGET])\nAC_SUBST([LDTARGET])\nAC_SUBST([MKLIB])\nAC_SUBST([CC_MM])\n\nif test \"x$abi\" != \"xpecoff\"; then\n  dnl Heap profiling uses the log(3) function.\n  LIBS=\"$LIBS -lm\"\nfi\n\nJE_COMPILABLE([__attribute__ syntax],\n              [static __attribute__((unused)) void foo(void){}],\n              [],\n              [je_cv_attribute])\nif test \"x${je_cv_attribute}\" = \"xyes\" ; then\n  AC_DEFINE([JEMALLOC_HAVE_ATTR], [ ])\n  if test \"x${GCC}\" = \"xyes\" -a \"x${abi}\" = \"xelf\"; then\n    JE_CFLAGS_APPEND([-fvisibility=hidden])\n  fi\nfi\ndnl Check for tls_model attribute support (clang 3.0 still lacks support).\nSAVED_CFLAGS=\"${CFLAGS}\"\nJE_CFLAGS_APPEND([-Werror])\nJE_COMPILABLE([tls_model attribute], [],\n              [static __thread int\n               __attribute__((tls_model(\"initial-exec\"))) foo;\n               foo = 0;],\n              [je_cv_tls_model])\nCFLAGS=\"${SAVED_CFLAGS}\"\nif test \"x${je_cv_tls_model}\" = \"xyes\" ; then\n  AC_DEFINE([JEMALLOC_TLS_MODEL],\n            [__attribute__((tls_model(\"initial-exec\")))])\nelse\n  AC_DEFINE([JEMALLOC_TLS_MODEL], [ ])\nfi\n\ndnl Support optional additions to rpath.\nAC_ARG_WITH([rpath],\n  [AS_HELP_STRING([--with-rpath=<rpath>], [Colon-separated rpath (ELF systems only)])],\nif test \"x$with_rpath\" = \"xno\" ; then\n  RPATH_EXTRA=\nelse\n  RPATH_EXTRA=\"`echo $with_rpath | tr \\\":\\\" \\\" \\\"`\"\nfi,\n  RPATH_EXTRA=\n)\nAC_SUBST([RPATH_EXTRA])\n\ndnl Disable rules that do automatic regeneration of configure output by default.\nAC_ARG_ENABLE([autogen],\n  [AS_HELP_STRING([--enable-autogen], [Automatically regenerate configure output])],\nif test \"x$enable_autogen\" = \"xno\" ; then\n  enable_autogen=\"0\"\nelse\n  enable_autogen=\"1\"\nfi\n,\nenable_autogen=\"0\"\n)\nAC_SUBST([enable_autogen])\n\nAC_PROG_INSTALL\nAC_PROG_RANLIB\nAC_PATH_PROG([AR], [ar], , [$PATH])\nAC_PATH_PROG([LD], [ld], , [$PATH])\nAC_PATH_PROG([AUTOCONF], [autoconf], , [$PATH])\n\npublic_syms=\"malloc_conf malloc_message malloc calloc posix_memalign aligned_alloc realloc free malloc_usable_size malloc_stats_print mallctl mallctlnametomib mallctlbymib\"\n\ndnl Check for allocator-related functions that should be wrapped.\nAC_CHECK_FUNC([memalign],\n\t      [AC_DEFINE([JEMALLOC_OVERRIDE_MEMALIGN], [ ])\n\t       public_syms=\"${public_syms} memalign\"])\nAC_CHECK_FUNC([valloc],\n\t      [AC_DEFINE([JEMALLOC_OVERRIDE_VALLOC], [ ])\n\t       public_syms=\"${public_syms} valloc\"])\n\ndnl Support the experimental API by default.\nAC_ARG_ENABLE([experimental],\n  [AS_HELP_STRING([--disable-experimental],\n   [Disable support for the experimental API])],\n[if test \"x$enable_experimental\" = \"xno\" ; then\n  enable_experimental=\"0\"\nelse\n  enable_experimental=\"1\"\nfi\n],\n[enable_experimental=\"1\"]\n)\nif test \"x$enable_experimental\" = \"x1\" ; then\n  AC_DEFINE([JEMALLOC_EXPERIMENTAL], [ ])\n  public_syms=\"${public_syms} allocm dallocm nallocm rallocm sallocm\"\nfi\nAC_SUBST([enable_experimental])\n\ndnl Perform no name mangling by default.\nAC_ARG_WITH([mangling],\n  [AS_HELP_STRING([--with-mangling=<map>], [Mangle symbols in <map>])],\n  [mangling_map=\"$with_mangling\"], [mangling_map=\"\"])\nfor nm in `echo ${mangling_map} |tr ',' ' '` ; do\n  k=\"`echo ${nm} |tr ':' ' ' |awk '{print $1}'`\"\n  n=\"je_${k}\"\n  m=`echo ${nm} |tr ':' ' ' |awk '{print $2}'`\n  AC_DEFINE_UNQUOTED([${n}], [${m}])\n  dnl Remove key from public_syms so that it isn't redefined later.\n  public_syms=`for sym in ${public_syms}; do echo \"${sym}\"; done |grep -v \"^${k}\\$\" |tr '\\n' ' '`\ndone\n\ndnl Do not prefix public APIs by default.\nAC_ARG_WITH([jemalloc_prefix],\n  [AS_HELP_STRING([--with-jemalloc-prefix=<prefix>], [Prefix to prepend to all public APIs])],\n  [JEMALLOC_PREFIX=\"$with_jemalloc_prefix\"],\n  [if test \"x$abi\" != \"xmacho\" -a \"x$abi\" != \"xpecoff\"; then\n  JEMALLOC_PREFIX=\"\"\nelse\n  JEMALLOC_PREFIX=\"je_\"\nfi]\n)\nif test \"x$JEMALLOC_PREFIX\" != \"x\" ; then\n  JEMALLOC_CPREFIX=`echo ${JEMALLOC_PREFIX} | tr \"a-z\" \"A-Z\"`\n  AC_DEFINE_UNQUOTED([JEMALLOC_PREFIX], [\"$JEMALLOC_PREFIX\"])\n  AC_DEFINE_UNQUOTED([JEMALLOC_CPREFIX], [\"$JEMALLOC_CPREFIX\"])\nfi\ndnl Generate macros to rename public symbols.  All public symbols are prefixed\ndnl with je_ in the source code, so these macro definitions are needed even if\ndnl --with-jemalloc-prefix wasn't specified.\nfor stem in ${public_syms}; do\n  n=\"je_${stem}\"\n  m=\"${JEMALLOC_PREFIX}${stem}\"\n  AC_DEFINE_UNQUOTED([${n}], [${m}])\ndone\n\ndnl Do not mangle library-private APIs by default.\nAC_ARG_WITH([private_namespace],\n  [AS_HELP_STRING([--with-private-namespace=<prefix>], [Prefix to prepend to all library-private APIs])],\n  [JEMALLOC_PRIVATE_NAMESPACE=\"$with_private_namespace\"],\n  [JEMALLOC_PRIVATE_NAMESPACE=\"\"]\n)\nAC_DEFINE_UNQUOTED([JEMALLOC_PRIVATE_NAMESPACE], [\"$JEMALLOC_PRIVATE_NAMESPACE\"])\nif test \"x$JEMALLOC_PRIVATE_NAMESPACE\" != \"x\" ; then\n  AC_DEFINE_UNQUOTED([JEMALLOC_N(string_that_no_one_should_want_to_use_as_a_jemalloc_private_namespace_prefix)], [${JEMALLOC_PRIVATE_NAMESPACE}##string_that_no_one_should_want_to_use_as_a_jemalloc_private_namespace_prefix])\nelse\n  AC_DEFINE_UNQUOTED([JEMALLOC_N(string_that_no_one_should_want_to_use_as_a_jemalloc_private_namespace_prefix)], [string_that_no_one_should_want_to_use_as_a_jemalloc_private_namespace_prefix])\nfi\n\ndnl Do not add suffix to installed files by default.\nAC_ARG_WITH([install_suffix],\n  [AS_HELP_STRING([--with-install-suffix=<suffix>], [Suffix to append to all installed files])],\n  [INSTALL_SUFFIX=\"$with_install_suffix\"],\n  [INSTALL_SUFFIX=]\n)\ninstall_suffix=\"$INSTALL_SUFFIX\"\nAC_SUBST([install_suffix])\n\ncfgoutputs_in=\"${srcroot}Makefile.in\"\ncfgoutputs_in=\"${cfgoutputs_in} ${srcroot}doc/html.xsl.in\"\ncfgoutputs_in=\"${cfgoutputs_in} ${srcroot}doc/manpages.xsl.in\"\ncfgoutputs_in=\"${cfgoutputs_in} ${srcroot}doc/jemalloc.xml.in\"\ncfgoutputs_in=\"${cfgoutputs_in} ${srcroot}include/jemalloc/jemalloc.h.in\"\ncfgoutputs_in=\"${cfgoutputs_in} ${srcroot}include/jemalloc/internal/jemalloc_internal.h.in\"\ncfgoutputs_in=\"${cfgoutputs_in} ${srcroot}test/jemalloc_test.h.in\"\n\ncfgoutputs_out=\"Makefile\"\ncfgoutputs_out=\"${cfgoutputs_out} doc/html.xsl\"\ncfgoutputs_out=\"${cfgoutputs_out} doc/manpages.xsl\"\ncfgoutputs_out=\"${cfgoutputs_out} doc/jemalloc${install_suffix}.xml\"\ncfgoutputs_out=\"${cfgoutputs_out} include/jemalloc/jemalloc${install_suffix}.h\"\ncfgoutputs_out=\"${cfgoutputs_out} include/jemalloc/internal/jemalloc_internal.h\"\ncfgoutputs_out=\"${cfgoutputs_out} test/jemalloc_test.h\"\n\ncfgoutputs_tup=\"Makefile\"\ncfgoutputs_tup=\"${cfgoutputs_tup} doc/html.xsl:doc/html.xsl.in\"\ncfgoutputs_tup=\"${cfgoutputs_tup} doc/manpages.xsl:doc/manpages.xsl.in\"\ncfgoutputs_tup=\"${cfgoutputs_tup} doc/jemalloc${install_suffix}.xml:doc/jemalloc.xml.in\"\ncfgoutputs_tup=\"${cfgoutputs_tup} include/jemalloc/jemalloc${install_suffix}.h:include/jemalloc/jemalloc.h.in\"\ncfgoutputs_tup=\"${cfgoutputs_tup} include/jemalloc/internal/jemalloc_internal.h\"\ncfgoutputs_tup=\"${cfgoutputs_tup} test/jemalloc_test.h:test/jemalloc_test.h.in\"\n\ncfghdrs_in=\"${srcroot}include/jemalloc/jemalloc_defs.h.in\"\ncfghdrs_in=\"${cfghdrs_in} ${srcroot}include/jemalloc/internal/size_classes.sh\"\n\ncfghdrs_out=\"include/jemalloc/jemalloc_defs${install_suffix}.h\"\ncfghdrs_out=\"${cfghdrs_out} include/jemalloc/internal/size_classes.h\"\n\ncfghdrs_tup=\"include/jemalloc/jemalloc_defs${install_suffix}.h:include/jemalloc/jemalloc_defs.h.in\"\n\ndnl Do not silence irrelevant compiler warnings by default, since enabling this\ndnl option incurs a performance penalty.\nAC_ARG_ENABLE([cc-silence],\n  [AS_HELP_STRING([--enable-cc-silence],\n                  [Silence irrelevant compiler warnings])],\n[if test \"x$enable_cc_silence\" = \"xno\" ; then\n  enable_cc_silence=\"0\"\nelse\n  enable_cc_silence=\"1\"\nfi\n],\n[enable_cc_silence=\"0\"]\n)\nif test \"x$enable_cc_silence\" = \"x1\" ; then\n  AC_DEFINE([JEMALLOC_CC_SILENCE], [ ])\nfi\n\ndnl Do not compile with debugging by default.\nAC_ARG_ENABLE([debug],\n  [AS_HELP_STRING([--enable-debug], [Build debugging code])],\n[if test \"x$enable_debug\" = \"xno\" ; then\n  enable_debug=\"0\"\nelse\n  enable_debug=\"1\"\nfi\n],\n[enable_debug=\"0\"]\n)\nif test \"x$enable_debug\" = \"x1\" ; then\n  AC_DEFINE([JEMALLOC_DEBUG], [ ])\n  AC_DEFINE([JEMALLOC_IVSALLOC], [ ])\nfi\nAC_SUBST([enable_debug])\n\ndnl Only optimize if not debugging.\nif test \"x$enable_debug\" = \"x0\" -a \"x$no_CFLAGS\" = \"xyes\" ; then\n  dnl Make sure that an optimization flag was not specified in EXTRA_CFLAGS.\n  optimize=\"no\"\n  echo \"$EXTRA_CFLAGS\" | grep \"\\-O\" >/dev/null || optimize=\"yes\"\n  if test \"x${optimize}\" = \"xyes\" ; then\n    if test \"x$GCC\" = \"xyes\" ; then\n      JE_CFLAGS_APPEND([-O3])\n      JE_CFLAGS_APPEND([-funroll-loops])\n    elif test \"x$je_cv_msvc\" = \"xyes\" ; then\n      JE_CFLAGS_APPEND([-O2])\n    else\n      JE_CFLAGS_APPEND([-O])\n    fi\n  fi\nfi\n\ndnl Enable statistics calculation by default.\nAC_ARG_ENABLE([stats],\n  [AS_HELP_STRING([--disable-stats],\n                  [Disable statistics calculation/reporting])],\n[if test \"x$enable_stats\" = \"xno\" ; then\n  enable_stats=\"0\"\nelse\n  enable_stats=\"1\"\nfi\n],\n[enable_stats=\"1\"]\n)\nif test \"x$enable_stats\" = \"x1\" ; then\n  AC_DEFINE([JEMALLOC_STATS], [ ])\nfi\nAC_SUBST([enable_stats])\n\ndnl Do not enable profiling by default.\nAC_ARG_ENABLE([prof],\n  [AS_HELP_STRING([--enable-prof], [Enable allocation profiling])],\n[if test \"x$enable_prof\" = \"xno\" ; then\n  enable_prof=\"0\"\nelse\n  enable_prof=\"1\"\nfi\n],\n[enable_prof=\"0\"]\n)\nif test \"x$enable_prof\" = \"x1\" ; then\n  backtrace_method=\"\"\nelse\n  backtrace_method=\"N/A\"\nfi\n\nAC_ARG_ENABLE([prof-libunwind],\n  [AS_HELP_STRING([--enable-prof-libunwind], [Use libunwind for backtracing])],\n[if test \"x$enable_prof_libunwind\" = \"xno\" ; then\n  enable_prof_libunwind=\"0\"\nelse\n  enable_prof_libunwind=\"1\"\nfi\n],\n[enable_prof_libunwind=\"0\"]\n)\nAC_ARG_WITH([static_libunwind],\n  [AS_HELP_STRING([--with-static-libunwind=<libunwind.a>],\n  [Path to static libunwind library; use rather than dynamically linking])],\nif test \"x$with_static_libunwind\" = \"xno\" ; then\n  LUNWIND=\"-lunwind\"\nelse\n  if test ! -f \"$with_static_libunwind\" ; then\n    AC_MSG_ERROR([Static libunwind not found: $with_static_libunwind])\n  fi\n  LUNWIND=\"$with_static_libunwind\"\nfi,\n  LUNWIND=\"-lunwind\"\n)\nif test \"x$backtrace_method\" = \"x\" -a \"x$enable_prof_libunwind\" = \"x1\" ; then\n  AC_CHECK_HEADERS([libunwind.h], , [enable_prof_libunwind=\"0\"])\n  if test \"x$LUNWIND\" = \"x-lunwind\" ; then\n    AC_CHECK_LIB([unwind], [backtrace], [LIBS=\"$LIBS $LUNWIND\"],\n                 [enable_prof_libunwind=\"0\"])\n  else\n    LIBS=\"$LIBS $LUNWIND\"\n  fi\n  if test \"x${enable_prof_libunwind}\" = \"x1\" ; then\n    backtrace_method=\"libunwind\"\n    AC_DEFINE([JEMALLOC_PROF_LIBUNWIND], [ ])\n  fi\nfi\n\nAC_ARG_ENABLE([prof-libgcc],\n  [AS_HELP_STRING([--disable-prof-libgcc],\n  [Do not use libgcc for backtracing])],\n[if test \"x$enable_prof_libgcc\" = \"xno\" ; then\n  enable_prof_libgcc=\"0\"\nelse\n  enable_prof_libgcc=\"1\"\nfi\n],\n[enable_prof_libgcc=\"1\"]\n)\nif test \"x$backtrace_method\" = \"x\" -a \"x$enable_prof_libgcc\" = \"x1\" \\\n     -a \"x$GCC\" = \"xyes\" ; then\n  AC_CHECK_HEADERS([unwind.h], , [enable_prof_libgcc=\"0\"])\n  AC_CHECK_LIB([gcc], [_Unwind_Backtrace], [LIBS=\"$LIBS -lgcc\"], [enable_prof_libgcc=\"0\"])\n  dnl The following is conservative, in that it only has entries for CPUs on\n  dnl which jemalloc has been tested.\n  AC_MSG_CHECKING([libgcc-based backtracing reliability on ${host_cpu}])\n  case \"${host_cpu}\" in\n    i[[3456]]86)\n      AC_MSG_RESULT([unreliable])\n      enable_prof_libgcc=\"0\";\n      ;;\n    x86_64)\n      AC_MSG_RESULT([reliable])\n      ;;\n    *)\n      AC_MSG_RESULT([unreliable])\n      enable_prof_libgcc=\"0\";\n      ;;\n  esac\n  if test \"x${enable_prof_libgcc}\" = \"x1\" ; then\n    backtrace_method=\"libgcc\"\n    AC_DEFINE([JEMALLOC_PROF_LIBGCC], [ ])\n  fi\nelse\n  enable_prof_libgcc=\"0\"\nfi\n\nAC_ARG_ENABLE([prof-gcc],\n  [AS_HELP_STRING([--disable-prof-gcc],\n  [Do not use gcc intrinsics for backtracing])],\n[if test \"x$enable_prof_gcc\" = \"xno\" ; then\n  enable_prof_gcc=\"0\"\nelse\n  enable_prof_gcc=\"1\"\nfi\n],\n[enable_prof_gcc=\"1\"]\n)\nif test \"x$backtrace_method\" = \"x\" -a \"x$enable_prof_gcc\" = \"x1\" \\\n     -a \"x$GCC\" = \"xyes\" ; then\n  backtrace_method=\"gcc intrinsics\"\n  AC_DEFINE([JEMALLOC_PROF_GCC], [ ])\nelse\n  enable_prof_gcc=\"0\"\nfi\n\nif test \"x$backtrace_method\" = \"x\" ; then\n  backtrace_method=\"none (disabling profiling)\"\n  enable_prof=\"0\"\nfi\nAC_MSG_CHECKING([configured backtracing method])\nAC_MSG_RESULT([$backtrace_method])\nif test \"x$enable_prof\" = \"x1\" ; then\n  if test \"x${force_tls}\" = \"x0\" ; then\n    AC_MSG_ERROR([Heap profiling requires TLS]);\n  fi\n  force_tls=\"1\"\n  AC_DEFINE([JEMALLOC_PROF], [ ])\nfi\nAC_SUBST([enable_prof])\n\ndnl Enable thread-specific caching by default.\nAC_ARG_ENABLE([tcache],\n  [AS_HELP_STRING([--disable-tcache], [Disable per thread caches])],\n[if test \"x$enable_tcache\" = \"xno\" ; then\n  enable_tcache=\"0\"\nelse\n  enable_tcache=\"1\"\nfi\n],\n[enable_tcache=\"1\"]\n)\nif test \"x$enable_tcache\" = \"x1\" ; then\n  AC_DEFINE([JEMALLOC_TCACHE], [ ])\nfi\nAC_SUBST([enable_tcache])\n\ndnl Disable mremap() for huge realloc() by default.\nAC_ARG_ENABLE([mremap],\n  [AS_HELP_STRING([--enable-mremap], [Enable mremap(2) for huge realloc()])],\n[if test \"x$enable_mremap\" = \"xno\" ; then\n  enable_mremap=\"0\"\nelse\n  enable_mremap=\"1\"\nfi\n],\n[enable_mremap=\"0\"]\n)\nif test \"x$enable_mremap\" = \"x1\" ; then\n  JE_COMPILABLE([mremap(...MREMAP_FIXED...)], [\n#define _GNU_SOURCE\n#include <sys/mman.h>\n], [\nvoid *p = mremap((void *)0, 0, 0, MREMAP_MAYMOVE|MREMAP_FIXED, (void *)0);\n], [je_cv_mremap_fixed])\n  if test \"x${je_cv_mremap_fixed}\" = \"xno\" ; then\n    enable_mremap=\"0\"\n  fi\nfi\nif test \"x$enable_mremap\" = \"x1\" ; then\n  AC_DEFINE([JEMALLOC_MREMAP], [ ])\nfi\nAC_SUBST([enable_mremap])\n\ndnl Enable VM deallocation via munmap() by default.\nAC_ARG_ENABLE([munmap],\n  [AS_HELP_STRING([--disable-munmap], [Disable VM deallocation via munmap(2)])],\n[if test \"x$enable_munmap\" = \"xno\" ; then\n  enable_munmap=\"0\"\nelse\n  enable_munmap=\"1\"\nfi\n],\n[enable_munmap=\"${default_munmap}\"]\n)\nif test \"x$enable_munmap\" = \"x1\" ; then\n  AC_DEFINE([JEMALLOC_MUNMAP], [ ])\nfi\nAC_SUBST([enable_munmap])\n\ndnl Do not enable allocation from DSS by default.\nAC_ARG_ENABLE([dss],\n  [AS_HELP_STRING([--enable-dss], [Enable allocation from DSS])],\n[if test \"x$enable_dss\" = \"xno\" ; then\n  enable_dss=\"0\"\nelse\n  enable_dss=\"1\"\nfi\n],\n[enable_dss=\"0\"]\n)\ndnl Check whether the BSD/SUSv1 sbrk() exists.  If not, disable DSS support.\nAC_CHECK_FUNC([sbrk], [have_sbrk=\"1\"], [have_sbrk=\"0\"])\nif test \"x$have_sbrk\" = \"x1\" ; then\n  AC_DEFINE([JEMALLOC_HAVE_SBRK], [ ])\nelse\n  enable_dss=\"0\"\nfi\n\nif test \"x$enable_dss\" = \"x1\" ; then\n  AC_DEFINE([JEMALLOC_DSS], [ ])\nfi\nAC_SUBST([enable_dss])\n\ndnl Support the junk/zero filling option by default.\nAC_ARG_ENABLE([fill],\n  [AS_HELP_STRING([--disable-fill],\n                  [Disable support for junk/zero filling, quarantine, and redzones])],\n[if test \"x$enable_fill\" = \"xno\" ; then\n  enable_fill=\"0\"\nelse\n  enable_fill=\"1\"\nfi\n],\n[enable_fill=\"1\"]\n)\nif test \"x$enable_fill\" = \"x1\" ; then\n  AC_DEFINE([JEMALLOC_FILL], [ ])\nfi\nAC_SUBST([enable_fill])\n\ndnl Disable utrace(2)-based tracing by default.\nAC_ARG_ENABLE([utrace],\n  [AS_HELP_STRING([--enable-utrace], [Enable utrace(2)-based tracing])],\n[if test \"x$enable_utrace\" = \"xno\" ; then\n  enable_utrace=\"0\"\nelse\n  enable_utrace=\"1\"\nfi\n],\n[enable_utrace=\"0\"]\n)\nJE_COMPILABLE([utrace(2)], [\n#include <sys/types.h>\n#include <sys/param.h>\n#include <sys/time.h>\n#include <sys/uio.h>\n#include <sys/ktrace.h>\n], [\n\tutrace((void *)0, 0);\n], [je_cv_utrace])\nif test \"x${je_cv_utrace}\" = \"xno\" ; then\n  enable_utrace=\"0\"\nfi\nif test \"x$enable_utrace\" = \"x1\" ; then\n  AC_DEFINE([JEMALLOC_UTRACE], [ ])\nfi\nAC_SUBST([enable_utrace])\n\ndnl Support Valgrind by default.\nAC_ARG_ENABLE([valgrind],\n  [AS_HELP_STRING([--disable-valgrind], [Disable support for Valgrind])],\n[if test \"x$enable_valgrind\" = \"xno\" ; then\n  enable_valgrind=\"0\"\nelse\n  enable_valgrind=\"1\"\nfi\n],\n[enable_valgrind=\"1\"]\n)\nif test \"x$enable_valgrind\" = \"x1\" ; then\n  JE_COMPILABLE([valgrind], [\n#include <valgrind/valgrind.h>\n#include <valgrind/memcheck.h>\n\n#if !defined(VALGRIND_RESIZEINPLACE_BLOCK)\n#  error \"Incompatible Valgrind version\"\n#endif\n], [], [je_cv_valgrind])\n  if test \"x${je_cv_valgrind}\" = \"xno\" ; then\n    enable_valgrind=\"0\"\n  fi\n  if test \"x$enable_valgrind\" = \"x1\" ; then\n    AC_DEFINE([JEMALLOC_VALGRIND], [ ])\n  fi\nfi\nAC_SUBST([enable_valgrind])\n\ndnl Do not support the xmalloc option by default.\nAC_ARG_ENABLE([xmalloc],\n  [AS_HELP_STRING([--enable-xmalloc], [Support xmalloc option])],\n[if test \"x$enable_xmalloc\" = \"xno\" ; then\n  enable_xmalloc=\"0\"\nelse\n  enable_xmalloc=\"1\"\nfi\n],\n[enable_xmalloc=\"0\"]\n)\nif test \"x$enable_xmalloc\" = \"x1\" ; then\n  AC_DEFINE([JEMALLOC_XMALLOC], [ ])\nfi\nAC_SUBST([enable_xmalloc])\n\nAC_CACHE_CHECK([STATIC_PAGE_SHIFT],\n               [je_cv_static_page_shift],\n               AC_RUN_IFELSE([AC_LANG_PROGRAM(\n[[\n#include <strings.h>\n#ifdef _WIN32\n#include <windows.h>\n#else\n#include <unistd.h>\n#endif\n#include <stdio.h>\n]],\n[[\n    long result;\n    FILE *f;\n\n#ifdef _WIN32\n    SYSTEM_INFO si;\n    GetSystemInfo(&si);\n    result = si.dwPageSize;\n#else\n    result = sysconf(_SC_PAGESIZE);\n#endif\n    if (result == -1) {\n\treturn 1;\n    }\n    result = ffsl(result) - 1;\n\n    f = fopen(\"conftest.out\", \"w\");\n    if (f == NULL) {\n\treturn 1;\n    }\n    fprintf(f, \"%u\\n\", result);\n    fclose(f);\n\n    return 0;\n]])],\n                             [je_cv_static_page_shift=`cat conftest.out`],\n                             [je_cv_static_page_shift=undefined]))\n\nif test \"x$je_cv_static_page_shift\" != \"xundefined\"; then\n   AC_DEFINE_UNQUOTED([STATIC_PAGE_SHIFT], [$je_cv_static_page_shift])\nelse\n   AC_MSG_ERROR([cannot determine value for STATIC_PAGE_SHIFT])\nfi\n\ndnl ============================================================================\ndnl jemalloc configuration.\ndnl \n\ndnl Set VERSION if source directory has an embedded git repository.\nif test -d \"${srcroot}.git\" ; then\n  git describe --long --abbrev=40 > ${srcroot}VERSION\nfi\njemalloc_version=`cat ${srcroot}VERSION`\njemalloc_version_major=`echo ${jemalloc_version} | tr \".g-\" \" \" | awk '{print [$]1}'`\njemalloc_version_minor=`echo ${jemalloc_version} | tr \".g-\" \" \" | awk '{print [$]2}'`\njemalloc_version_bugfix=`echo ${jemalloc_version} | tr \".g-\" \" \" | awk '{print [$]3}'`\njemalloc_version_nrev=`echo ${jemalloc_version} | tr \".g-\" \" \" | awk '{print [$]4}'`\njemalloc_version_gid=`echo ${jemalloc_version} | tr \".g-\" \" \" | awk '{print [$]5}'`\nAC_SUBST([jemalloc_version])\nAC_SUBST([jemalloc_version_major])\nAC_SUBST([jemalloc_version_minor])\nAC_SUBST([jemalloc_version_bugfix])\nAC_SUBST([jemalloc_version_nrev])\nAC_SUBST([jemalloc_version_gid])\n\ndnl ============================================================================\ndnl Configure pthreads.\n\nif test \"x$abi\" != \"xpecoff\" ; then\n  AC_CHECK_HEADERS([pthread.h], , [AC_MSG_ERROR([pthread.h is missing])])\n  dnl Some systems may embed pthreads functionality in libc; check for libpthread\n  dnl first, but try libc too before failing.\n  AC_CHECK_LIB([pthread], [pthread_create], [LIBS=\"$LIBS -lpthread\"],\n               [AC_SEARCH_LIBS([pthread_create], , ,\n                               AC_MSG_ERROR([libpthread is missing]))])\nfi\n\nCPPFLAGS=\"$CPPFLAGS -D_REENTRANT\"\n\ndnl Check whether the BSD-specific _malloc_thread_cleanup() exists.  If so, use\ndnl it rather than pthreads TSD cleanup functions to support cleanup during\ndnl thread exit, in order to avoid pthreads library recursion during\ndnl bootstrapping.\nAC_CHECK_FUNC([_malloc_thread_cleanup],\n              [have__malloc_thread_cleanup=\"1\"],\n              [have__malloc_thread_cleanup=\"0\"]\n             )\nif test \"x$have__malloc_thread_cleanup\" = \"x1\" ; then\n  AC_DEFINE([JEMALLOC_MALLOC_THREAD_CLEANUP], [ ])\n  force_tls=\"1\"\nfi\n\ndnl Check whether the BSD-specific _pthread_mutex_init_calloc_cb() exists.  If\ndnl so, mutex initialization causes allocation, and we need to implement this\ndnl callback function in order to prevent recursive allocation.\nAC_CHECK_FUNC([_pthread_mutex_init_calloc_cb],\n              [have__pthread_mutex_init_calloc_cb=\"1\"],\n              [have__pthread_mutex_init_calloc_cb=\"0\"]\n             )\nif test \"x$have__pthread_mutex_init_calloc_cb\" = \"x1\" ; then\n  AC_DEFINE([JEMALLOC_MUTEX_INIT_CB])\nfi\n\ndnl Disable lazy locking by default.\nAC_ARG_ENABLE([lazy_lock],\n  [AS_HELP_STRING([--enable-lazy-lock],\n  [Enable lazy locking (only lock when multi-threaded)])],\n[if test \"x$enable_lazy_lock\" = \"xno\" ; then\n  enable_lazy_lock=\"0\"\nelse\n  enable_lazy_lock=\"1\"\nfi\n],\n[enable_lazy_lock=\"0\"]\n)\nif test \"x$enable_lazy_lock\" = \"x0\" -a \"x${force_lazy_lock}\" = \"x1\" ; then\n  AC_MSG_RESULT([Forcing lazy-lock to avoid allocator/threading bootstrap issues])\n  enable_lazy_lock=\"1\"\nfi\nif test \"x$enable_lazy_lock\" = \"x1\" ; then\n  if test \"x$abi\" != \"xpecoff\" ; then\n    AC_CHECK_HEADERS([dlfcn.h], , [AC_MSG_ERROR([dlfcn.h is missing])])\n    AC_CHECK_FUNC([dlsym], [],\n      [AC_CHECK_LIB([dl], [dlsym], [LIBS=\"$LIBS -ldl\"],\n                    [AC_MSG_ERROR([libdl is missing])])\n      ])\n  fi\n  AC_DEFINE([JEMALLOC_LAZY_LOCK], [ ])\nfi\nAC_SUBST([enable_lazy_lock])\n\nAC_ARG_ENABLE([tls],\n  [AS_HELP_STRING([--disable-tls], [Disable thread-local storage (__thread keyword)])],\nif test \"x$enable_tls\" = \"xno\" ; then\n  enable_tls=\"0\"\nelse\n  enable_tls=\"1\"\nfi\n,\nenable_tls=\"1\"\n)\nif test \"x${enable_tls}\" = \"x0\" -a \"x${force_tls}\" = \"x1\" ; then\n  AC_MSG_RESULT([Forcing TLS to avoid allocator/threading bootstrap issues])\n  enable_tls=\"1\"\nfi\nif test \"x${enable_tls}\" = \"x1\" -a \"x${force_tls}\" = \"x0\" ; then\n  AC_MSG_RESULT([Forcing no TLS to avoid allocator/threading bootstrap issues])\n  enable_tls=\"0\"\nfi\nif test \"x${enable_tls}\" = \"x1\" ; then\nAC_MSG_CHECKING([for TLS])\nAC_COMPILE_IFELSE([AC_LANG_PROGRAM(\n[[\n    __thread int x;\n]], [[\n    x = 42;\n\n    return 0;\n]])],\n              AC_MSG_RESULT([yes]),\n              AC_MSG_RESULT([no])\n              enable_tls=\"0\")\nfi\nAC_SUBST([enable_tls])\nif test \"x${enable_tls}\" = \"x1\" ; then\n  AC_DEFINE_UNQUOTED([JEMALLOC_TLS], [ ])\nelif test \"x${force_tls}\" = \"x1\" ; then\n  AC_MSG_ERROR([Failed to configure TLS, which is mandatory for correct function])\nfi\n\ndnl ============================================================================\ndnl Check for ffsl(3), and fail if not found.  This function exists on all\ndnl platforms that jemalloc currently has a chance of functioning on without\ndnl modification.\nJE_COMPILABLE([a program using ffsl], [\n#include <strings.h>\n#include <string.h>\n], [\n\t{\n\t\tint rv = ffsl(0x08);\n\t}\n], [je_cv_function_ffsl])\nif test \"x${je_cv_function_ffsl}\" != \"xyes\" ; then\n   AC_MSG_ERROR([Cannot build without ffsl(3)])\nfi\n\ndnl ============================================================================\ndnl Check for atomic(9) operations as provided on FreeBSD.\n\nJE_COMPILABLE([atomic(9)], [\n#include <sys/types.h>\n#include <machine/atomic.h>\n#include <inttypes.h>\n], [\n\t{\n\t\tuint32_t x32 = 0;\n\t\tvolatile uint32_t *x32p = &x32;\n\t\tatomic_fetchadd_32(x32p, 1);\n\t}\n\t{\n\t\tunsigned long xlong = 0;\n\t\tvolatile unsigned long *xlongp = &xlong;\n\t\tatomic_fetchadd_long(xlongp, 1);\n\t}\n], [je_cv_atomic9])\nif test \"x${je_cv_atomic9}\" = \"xyes\" ; then\n  AC_DEFINE([JEMALLOC_ATOMIC9])\nfi\n\ndnl ============================================================================\ndnl Check for atomic(3) operations as provided on Darwin.\n\nJE_COMPILABLE([Darwin OSAtomic*()], [\n#include <libkern/OSAtomic.h>\n#include <inttypes.h>\n], [\n\t{\n\t\tint32_t x32 = 0;\n\t\tvolatile int32_t *x32p = &x32;\n\t\tOSAtomicAdd32(1, x32p);\n\t}\n\t{\n\t\tint64_t x64 = 0;\n\t\tvolatile int64_t *x64p = &x64;\n\t\tOSAtomicAdd64(1, x64p);\n\t}\n], [je_cv_osatomic])\nif test \"x${je_cv_osatomic}\" = \"xyes\" ; then\n  AC_DEFINE([JEMALLOC_OSATOMIC], [ ])\nfi\n\ndnl ============================================================================\ndnl Check whether __sync_{add,sub}_and_fetch() are available despite\ndnl __GCC_HAVE_SYNC_COMPARE_AND_SWAP_n macros being undefined.\n\nAC_DEFUN([JE_SYNC_COMPARE_AND_SWAP_CHECK],[\n  AC_CACHE_CHECK([whether to force $1-bit __sync_{add,sub}_and_fetch()],\n               [je_cv_sync_compare_and_swap_$2],\n               [AC_LINK_IFELSE([AC_LANG_PROGRAM([\n                                                 #include <stdint.h>\n                                                ],\n                                                [\n                                                 #ifndef __GCC_HAVE_SYNC_COMPARE_AND_SWAP_$2\n                                                 {\n                                                    uint$1_t x$1 = 0;\n                                                    __sync_add_and_fetch(&x$1, 42);\n                                                    __sync_sub_and_fetch(&x$1, 1);\n                                                 }\n                                                 #else\n                                                 #error __GCC_HAVE_SYNC_COMPARE_AND_SWAP_$2 is defined, no need to force\n                                                 #endif\n                                                ])],\n                               [je_cv_sync_compare_and_swap_$2=yes],\n                               [je_cv_sync_compare_and_swap_$2=no])])\n\n  if test \"x${je_cv_sync_compare_and_swap_$2}\" = \"xyes\" ; then\n    AC_DEFINE([JE_FORCE_SYNC_COMPARE_AND_SWAP_$2], [ ])\n  fi\n])\n\nif test \"x${je_cv_atomic9}\" != \"xyes\" -a \"x${je_cv_osatomic}\" != \"xyes\" ; then\n  JE_SYNC_COMPARE_AND_SWAP_CHECK(32, 4)\n  JE_SYNC_COMPARE_AND_SWAP_CHECK(64, 8)\nfi\n\ndnl ============================================================================\ndnl Check for spinlock(3) operations as provided on Darwin.\n\nJE_COMPILABLE([Darwin OSSpin*()], [\n#include <libkern/OSAtomic.h>\n#include <inttypes.h>\n], [\n\tOSSpinLock lock = 0;\n\tOSSpinLockLock(&lock);\n\tOSSpinLockUnlock(&lock);\n], [je_cv_osspin])\nif test \"x${je_cv_osspin}\" = \"xyes\" ; then\n  AC_DEFINE([JEMALLOC_OSSPIN], [ ])\nfi\n\ndnl ============================================================================\ndnl Darwin-related configuration.\n\nif test \"x${abi}\" = \"xmacho\" ; then\n  AC_DEFINE([JEMALLOC_IVSALLOC], [ ])\n  AC_DEFINE([JEMALLOC_ZONE], [ ])\n\n  dnl The szone version jumped from 3 to 6 between the OS X 10.5.x and 10.6\n  dnl releases.  malloc_zone_t and malloc_introspection_t have new fields in\n  dnl 10.6, which is the only source-level indication of the change.\n  AC_MSG_CHECKING([malloc zone version])\n  AC_DEFUN([JE_ZONE_PROGRAM],\n    [AC_LANG_PROGRAM(\n      [#include <malloc/malloc.h>],\n      [static foo[[sizeof($1) $2 sizeof(void *) * $3 ? 1 : -1]]]\n    )])\n\n  AC_COMPILE_IFELSE([JE_ZONE_PROGRAM(malloc_zone_t,==,14)],[JEMALLOC_ZONE_VERSION=3],[\n  AC_COMPILE_IFELSE([JE_ZONE_PROGRAM(malloc_zone_t,==,15)],[JEMALLOC_ZONE_VERSION=5],[\n  AC_COMPILE_IFELSE([JE_ZONE_PROGRAM(malloc_zone_t,==,16)],[\n    AC_COMPILE_IFELSE([JE_ZONE_PROGRAM(malloc_introspection_t,==,9)],[JEMALLOC_ZONE_VERSION=6],[\n    AC_COMPILE_IFELSE([JE_ZONE_PROGRAM(malloc_introspection_t,==,13)],[JEMALLOC_ZONE_VERSION=7],[JEMALLOC_ZONE_VERSION=]\n  )])],[\n  AC_COMPILE_IFELSE([JE_ZONE_PROGRAM(malloc_zone_t,==,17)],[JEMALLOC_ZONE_VERSION=8],[\n  AC_COMPILE_IFELSE([JE_ZONE_PROGRAM(malloc_zone_t,>,17)],[JEMALLOC_ZONE_VERSION=9],[JEMALLOC_ZONE_VERSION=]\n  )])])])])\n  if test \"x${JEMALLOC_ZONE_VERSION}\" = \"x\"; then\n    AC_MSG_RESULT([unsupported])\n    AC_MSG_ERROR([Unsupported malloc zone version])\n  fi\n  if test \"${JEMALLOC_ZONE_VERSION}\" = 9; then\n    JEMALLOC_ZONE_VERSION=8\n    AC_MSG_RESULT([> 8])\n  else\n    AC_MSG_RESULT([$JEMALLOC_ZONE_VERSION])\n  fi\n  AC_DEFINE_UNQUOTED(JEMALLOC_ZONE_VERSION, [$JEMALLOC_ZONE_VERSION])\nfi\n\ndnl ============================================================================\ndnl Check for typedefs, structures, and compiler characteristics.\nAC_HEADER_STDBOOL\n\nAC_CONFIG_COMMANDS([include/jemalloc/internal/size_classes.h], [\n  mkdir -p \"include/jemalloc/internal\"\n  \"${srcdir}/include/jemalloc/internal/size_classes.sh\" > \"${objroot}include/jemalloc/internal/size_classes.h\"\n])\n\ndnl Process .in files.\nAC_SUBST([cfghdrs_in])\nAC_SUBST([cfghdrs_out])\nAC_CONFIG_HEADERS([$cfghdrs_tup])\n\ndnl ============================================================================\ndnl Generate outputs.\nAC_CONFIG_FILES([$cfgoutputs_tup config.stamp bin/jemalloc.sh])\nAC_SUBST([cfgoutputs_in])\nAC_SUBST([cfgoutputs_out])\nAC_OUTPUT\n\ndnl ============================================================================\ndnl Print out the results of configuration.\nAC_MSG_RESULT([===============================================================================])\nAC_MSG_RESULT([jemalloc version   : ${jemalloc_version}])\nAC_MSG_RESULT([library revision   : ${rev}])\nAC_MSG_RESULT([])\nAC_MSG_RESULT([CC                 : ${CC}])\nAC_MSG_RESULT([CPPFLAGS           : ${CPPFLAGS}])\nAC_MSG_RESULT([CFLAGS             : ${CFLAGS}])\nAC_MSG_RESULT([LDFLAGS            : ${LDFLAGS}])\nAC_MSG_RESULT([LIBS               : ${LIBS}])\nAC_MSG_RESULT([RPATH_EXTRA        : ${RPATH_EXTRA}])\nAC_MSG_RESULT([])\nAC_MSG_RESULT([XSLTPROC           : ${XSLTPROC}])\nAC_MSG_RESULT([XSLROOT            : ${XSLROOT}])\nAC_MSG_RESULT([])\nAC_MSG_RESULT([PREFIX             : ${PREFIX}])\nAC_MSG_RESULT([BINDIR             : ${BINDIR}])\nAC_MSG_RESULT([INCLUDEDIR         : ${INCLUDEDIR}])\nAC_MSG_RESULT([LIBDIR             : ${LIBDIR}])\nAC_MSG_RESULT([DATADIR            : ${DATADIR}])\nAC_MSG_RESULT([MANDIR             : ${MANDIR}])\nAC_MSG_RESULT([])\nAC_MSG_RESULT([srcroot            : ${srcroot}])\nAC_MSG_RESULT([abs_srcroot        : ${abs_srcroot}])\nAC_MSG_RESULT([objroot            : ${objroot}])\nAC_MSG_RESULT([abs_objroot        : ${abs_objroot}])\nAC_MSG_RESULT([])\nAC_MSG_RESULT([JEMALLOC_PREFIX    : ${JEMALLOC_PREFIX}])\nAC_MSG_RESULT([JEMALLOC_PRIVATE_NAMESPACE])\nAC_MSG_RESULT([                   : ${JEMALLOC_PRIVATE_NAMESPACE}])\nAC_MSG_RESULT([install_suffix     : ${install_suffix}])\nAC_MSG_RESULT([autogen            : ${enable_autogen}])\nAC_MSG_RESULT([experimental       : ${enable_experimental}])\nAC_MSG_RESULT([cc-silence         : ${enable_cc_silence}])\nAC_MSG_RESULT([debug              : ${enable_debug}])\nAC_MSG_RESULT([stats              : ${enable_stats}])\nAC_MSG_RESULT([prof               : ${enable_prof}])\nAC_MSG_RESULT([prof-libunwind     : ${enable_prof_libunwind}])\nAC_MSG_RESULT([prof-libgcc        : ${enable_prof_libgcc}])\nAC_MSG_RESULT([prof-gcc           : ${enable_prof_gcc}])\nAC_MSG_RESULT([tcache             : ${enable_tcache}])\nAC_MSG_RESULT([fill               : ${enable_fill}])\nAC_MSG_RESULT([utrace             : ${enable_utrace}])\nAC_MSG_RESULT([valgrind           : ${enable_valgrind}])\nAC_MSG_RESULT([xmalloc            : ${enable_xmalloc}])\nAC_MSG_RESULT([mremap             : ${enable_mremap}])\nAC_MSG_RESULT([munmap             : ${enable_munmap}])\nAC_MSG_RESULT([dss                : ${enable_dss}])\nAC_MSG_RESULT([lazy_lock          : ${enable_lazy_lock}])\nAC_MSG_RESULT([tls                : ${enable_tls}])\nAC_MSG_RESULT([===============================================================================])\n"
  },
  {
    "path": "deps/jemalloc/doc/html.xsl.in",
    "content": "<xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" version=\"1.0\">\n  <xsl:import href=\"@XSLROOT@/html/docbook.xsl\"/>\n  <xsl:import href=\"@abs_srcroot@doc/stylesheet.xsl\"/>\n</xsl:stylesheet>\n"
  },
  {
    "path": "deps/jemalloc/doc/jemalloc.3",
    "content": "'\\\" t\n.\\\"     Title: JEMALLOC\n.\\\"    Author: Jason Evans\n.\\\" Generator: DocBook XSL Stylesheets v1.76.1 <http://docbook.sf.net/>\n.\\\"      Date: 11/09/2012\n.\\\"    Manual: User Manual\n.\\\"    Source: jemalloc 3.2.0-0-g87499f6748ebe4817571e817e9f680ccb5bf54a9\n.\\\"  Language: English\n.\\\"\n.TH \"JEMALLOC\" \"3\" \"11/09/2012\" \"jemalloc 3.2.0-0-g87499f6748eb\" \"User Manual\"\n.\\\" -----------------------------------------------------------------\n.\\\" * Define some portability stuff\n.\\\" -----------------------------------------------------------------\n.\\\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n.\\\" http://bugs.debian.org/507673\n.\\\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html\n.\\\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n.ie \\n(.g .ds Aq \\(aq\n.el       .ds Aq '\n.\\\" -----------------------------------------------------------------\n.\\\" * set default formatting\n.\\\" -----------------------------------------------------------------\n.\\\" disable hyphenation\n.nh\n.\\\" disable justification (adjust text to left margin only)\n.ad l\n.\\\" -----------------------------------------------------------------\n.\\\" * MAIN CONTENT STARTS HERE *\n.\\\" -----------------------------------------------------------------\n.SH \"NAME\"\njemalloc \\- general purpose memory allocation functions\n.SH \"LIBRARY\"\n.PP\nThis manual describes jemalloc 3\\&.2\\&.0\\-0\\-g87499f6748ebe4817571e817e9f680ccb5bf54a9\\&. More information can be found at the\n\\m[blue]\\fBjemalloc website\\fR\\m[]\\&\\s-2\\u[1]\\d\\s+2\\&.\n.SH \"SYNOPSIS\"\n.sp\n.ft B\n.nf\n#include <stdlib\\&.h>\n#include <jemalloc/jemalloc\\&.h>\n.fi\n.ft\n.SS \"Standard API\"\n.HP \\w'void\\ *malloc('u\n.BI \"void *malloc(size_t\\ \" \"size\" \");\"\n.HP \\w'void\\ *calloc('u\n.BI \"void *calloc(size_t\\ \" \"number\" \", size_t\\ \" \"size\" \");\"\n.HP \\w'int\\ posix_memalign('u\n.BI \"int posix_memalign(void\\ **\" \"ptr\" \", size_t\\ \" \"alignment\" \", size_t\\ \" \"size\" \");\"\n.HP \\w'void\\ *aligned_alloc('u\n.BI \"void *aligned_alloc(size_t\\ \" \"alignment\" \", size_t\\ \" \"size\" \");\"\n.HP \\w'void\\ *realloc('u\n.BI \"void *realloc(void\\ *\" \"ptr\" \", size_t\\ \" \"size\" \");\"\n.HP \\w'void\\ free('u\n.BI \"void free(void\\ *\" \"ptr\" \");\"\n.SS \"Non\\-standard API\"\n.HP \\w'size_t\\ malloc_usable_size('u\n.BI \"size_t malloc_usable_size(const\\ void\\ *\" \"ptr\" \");\"\n.HP \\w'void\\ malloc_stats_print('u\n.BI \"void malloc_stats_print(void\\ \" \"(*write_cb)\" \"\\ (void\\ *,\\ const\\ char\\ *), void\\ *\" \"cbopaque\" \", const\\ char\\ *\" \"opts\" \");\"\n.HP \\w'int\\ mallctl('u\n.BI \"int mallctl(const\\ char\\ *\" \"name\" \", void\\ *\" \"oldp\" \", size_t\\ *\" \"oldlenp\" \", void\\ *\" \"newp\" \", size_t\\ \" \"newlen\" \");\"\n.HP \\w'int\\ mallctlnametomib('u\n.BI \"int mallctlnametomib(const\\ char\\ *\" \"name\" \", size_t\\ *\" \"mibp\" \", size_t\\ *\" \"miblenp\" \");\"\n.HP \\w'int\\ mallctlbymib('u\n.BI \"int mallctlbymib(const\\ size_t\\ *\" \"mib\" \", size_t\\ \" \"miblen\" \", void\\ *\" \"oldp\" \", size_t\\ *\" \"oldlenp\" \", void\\ *\" \"newp\" \", size_t\\ \" \"newlen\" \");\"\n.HP \\w'void\\ (*malloc_message)('u\n.BI \"void (*malloc_message)(void\\ *\" \"cbopaque\" \", const\\ char\\ *\" \"s\" \");\"\n.PP\nconst char *\\fImalloc_conf\\fR;\n.SS \"Experimental API\"\n.HP \\w'int\\ allocm('u\n.BI \"int allocm(void\\ **\" \"ptr\" \", size_t\\ *\" \"rsize\" \", size_t\\ \" \"size\" \", int\\ \" \"flags\" \");\"\n.HP \\w'int\\ rallocm('u\n.BI \"int rallocm(void\\ **\" \"ptr\" \", size_t\\ *\" \"rsize\" \", size_t\\ \" \"size\" \", size_t\\ \" \"extra\" \", int\\ \" \"flags\" \");\"\n.HP \\w'int\\ sallocm('u\n.BI \"int sallocm(const\\ void\\ *\" \"ptr\" \", size_t\\ *\" \"rsize\" \", int\\ \" \"flags\" \");\"\n.HP \\w'int\\ dallocm('u\n.BI \"int dallocm(void\\ *\" \"ptr\" \", int\\ \" \"flags\" \");\"\n.HP \\w'int\\ nallocm('u\n.BI \"int nallocm(size_t\\ *\" \"rsize\" \", size_t\\ \" \"size\" \", int\\ \" \"flags\" \");\"\n.SH \"DESCRIPTION\"\n.SS \"Standard API\"\n.PP\nThe\n\\fBmalloc\\fR\\fB\\fR\nfunction allocates\n\\fIsize\\fR\nbytes of uninitialized memory\\&. The allocated space is suitably aligned (after possible pointer coercion) for storage of any type of object\\&.\n.PP\nThe\n\\fBcalloc\\fR\\fB\\fR\nfunction allocates space for\n\\fInumber\\fR\nobjects, each\n\\fIsize\\fR\nbytes in length\\&. The result is identical to calling\n\\fBmalloc\\fR\\fB\\fR\nwith an argument of\n\\fInumber\\fR\n*\n\\fIsize\\fR, with the exception that the allocated memory is explicitly initialized to zero bytes\\&.\n.PP\nThe\n\\fBposix_memalign\\fR\\fB\\fR\nfunction allocates\n\\fIsize\\fR\nbytes of memory such that the allocation\\*(Aqs base address is an even multiple of\n\\fIalignment\\fR, and returns the allocation in the value pointed to by\n\\fIptr\\fR\\&. The requested\n\\fIalignment\\fR\nmust be a power of 2 at least as large as\nsizeof(\\fBvoid *\\fR)\\&.\n.PP\nThe\n\\fBaligned_alloc\\fR\\fB\\fR\nfunction allocates\n\\fIsize\\fR\nbytes of memory such that the allocation\\*(Aqs base address is an even multiple of\n\\fIalignment\\fR\\&. The requested\n\\fIalignment\\fR\nmust be a power of 2\\&. Behavior is undefined if\n\\fIsize\\fR\nis not an integral multiple of\n\\fIalignment\\fR\\&.\n.PP\nThe\n\\fBrealloc\\fR\\fB\\fR\nfunction changes the size of the previously allocated memory referenced by\n\\fIptr\\fR\nto\n\\fIsize\\fR\nbytes\\&. The contents of the memory are unchanged up to the lesser of the new and old sizes\\&. If the new size is larger, the contents of the newly allocated portion of the memory are undefined\\&. Upon success, the memory referenced by\n\\fIptr\\fR\nis freed and a pointer to the newly allocated memory is returned\\&. Note that\n\\fBrealloc\\fR\\fB\\fR\nmay move the memory allocation, resulting in a different return value than\n\\fIptr\\fR\\&. If\n\\fIptr\\fR\nis\n\\fBNULL\\fR, the\n\\fBrealloc\\fR\\fB\\fR\nfunction behaves identically to\n\\fBmalloc\\fR\\fB\\fR\nfor the specified size\\&.\n.PP\nThe\n\\fBfree\\fR\\fB\\fR\nfunction causes the allocated memory referenced by\n\\fIptr\\fR\nto be made available for future allocations\\&. If\n\\fIptr\\fR\nis\n\\fBNULL\\fR, no action occurs\\&.\n.SS \"Non\\-standard API\"\n.PP\nThe\n\\fBmalloc_usable_size\\fR\\fB\\fR\nfunction returns the usable size of the allocation pointed to by\n\\fIptr\\fR\\&. The return value may be larger than the size that was requested during allocation\\&. The\n\\fBmalloc_usable_size\\fR\\fB\\fR\nfunction is not a mechanism for in\\-place\n\\fBrealloc\\fR\\fB\\fR; rather it is provided solely as a tool for introspection purposes\\&. Any discrepancy between the requested allocation size and the size reported by\n\\fBmalloc_usable_size\\fR\\fB\\fR\nshould not be depended on, since such behavior is entirely implementation\\-dependent\\&.\n.PP\nThe\n\\fBmalloc_stats_print\\fR\\fB\\fR\nfunction writes human\\-readable summary statistics via the\n\\fIwrite_cb\\fR\ncallback function pointer and\n\\fIcbopaque\\fR\ndata passed to\n\\fIwrite_cb\\fR, or\n\\fBmalloc_message\\fR\\fB\\fR\nif\n\\fIwrite_cb\\fR\nis\n\\fBNULL\\fR\\&. This function can be called repeatedly\\&. General information that never changes during execution can be omitted by specifying \"g\" as a character within the\n\\fIopts\\fR\nstring\\&. Note that\n\\fBmalloc_message\\fR\\fB\\fR\nuses the\n\\fBmallctl*\\fR\\fB\\fR\nfunctions internally, so inconsistent statistics can be reported if multiple threads use these functions simultaneously\\&. If\n\\fB\\-\\-enable\\-stats\\fR\nis specified during configuration, \\(lqm\\(rq and \\(lqa\\(rq can be specified to omit merged arena and per arena statistics, respectively; \\(lqb\\(rq and \\(lql\\(rq can be specified to omit per size class statistics for bins and large objects, respectively\\&. Unrecognized characters are silently ignored\\&. Note that thread caching may prevent some statistics from being completely up to date, since extra locking would be required to merge counters that track thread cache operations\\&.\n.PP\nThe\n\\fBmallctl\\fR\\fB\\fR\nfunction provides a general interface for introspecting the memory allocator, as well as setting modifiable parameters and triggering actions\\&. The period\\-separated\n\\fIname\\fR\nargument specifies a location in a tree\\-structured namespace; see the\nMALLCTL NAMESPACE\nsection for documentation on the tree contents\\&. To read a value, pass a pointer via\n\\fIoldp\\fR\nto adequate space to contain the value, and a pointer to its length via\n\\fIoldlenp\\fR; otherwise pass\n\\fBNULL\\fR\nand\n\\fBNULL\\fR\\&. Similarly, to write a value, pass a pointer to the value via\n\\fInewp\\fR, and its length via\n\\fInewlen\\fR; otherwise pass\n\\fBNULL\\fR\nand\n\\fB0\\fR\\&.\n.PP\nThe\n\\fBmallctlnametomib\\fR\\fB\\fR\nfunction provides a way to avoid repeated name lookups for applications that repeatedly query the same portion of the namespace, by translating a name to a \\(lqManagement Information Base\\(rq (MIB) that can be passed repeatedly to\n\\fBmallctlbymib\\fR\\fB\\fR\\&. Upon successful return from\n\\fBmallctlnametomib\\fR\\fB\\fR,\n\\fImibp\\fR\ncontains an array of\n\\fI*miblenp\\fR\nintegers, where\n\\fI*miblenp\\fR\nis the lesser of the number of components in\n\\fIname\\fR\nand the input value of\n\\fI*miblenp\\fR\\&. Thus it is possible to pass a\n\\fI*miblenp\\fR\nthat is smaller than the number of period\\-separated name components, which results in a partial MIB that can be used as the basis for constructing a complete MIB\\&. For name components that are integers (e\\&.g\\&. the 2 in\n\"arenas\\&.bin\\&.2\\&.size\"), the corresponding MIB component will always be that integer\\&. Therefore, it is legitimate to construct code like the following:\n.sp\n.if n \\{\\\n.RS 4\n.\\}\n.nf\nunsigned nbins, i;\n\nint mib[4];\nsize_t len, miblen;\n\nlen = sizeof(nbins);\nmallctl(\"arenas\\&.nbins\", &nbins, &len, NULL, 0);\n\nmiblen = 4;\nmallnametomib(\"arenas\\&.bin\\&.0\\&.size\", mib, &miblen);\nfor (i = 0; i < nbins; i++) {\n\tsize_t bin_size;\n\n\tmib[2] = i;\n\tlen = sizeof(bin_size);\n\tmallctlbymib(mib, miblen, &bin_size, &len, NULL, 0);\n\t/* Do something with bin_size\\&.\\&.\\&. */\n}\n.fi\n.if n \\{\\\n.RE\n.\\}\n.SS \"Experimental API\"\n.PP\nThe experimental API is subject to change or removal without regard for backward compatibility\\&. If\n\\fB\\-\\-disable\\-experimental\\fR\nis specified during configuration, the experimental API is omitted\\&.\n.PP\nThe\n\\fBallocm\\fR\\fB\\fR,\n\\fBrallocm\\fR\\fB\\fR,\n\\fBsallocm\\fR\\fB\\fR,\n\\fBdallocm\\fR\\fB\\fR, and\n\\fBnallocm\\fR\\fB\\fR\nfunctions all have a\n\\fIflags\\fR\nargument that can be used to specify options\\&. The functions only check the options that are contextually relevant\\&. Use bitwise or (|) operations to specify one or more of the following:\n.PP\n\\fBALLOCM_LG_ALIGN(\\fR\\fB\\fIla\\fR\\fR\\fB) \\fR\n.RS 4\nAlign the memory allocation to start at an address that is a multiple of\n(1 << \\fIla\\fR)\\&. This macro does not validate that\n\\fIla\\fR\nis within the valid range\\&.\n.RE\n.PP\n\\fBALLOCM_ALIGN(\\fR\\fB\\fIa\\fR\\fR\\fB) \\fR\n.RS 4\nAlign the memory allocation to start at an address that is a multiple of\n\\fIa\\fR, where\n\\fIa\\fR\nis a power of two\\&. This macro does not validate that\n\\fIa\\fR\nis a power of 2\\&.\n.RE\n.PP\n\\fBALLOCM_ZERO\\fR\n.RS 4\nInitialize newly allocated memory to contain zero bytes\\&. In the growing reallocation case, the real size prior to reallocation defines the boundary between untouched bytes and those that are initialized to contain zero bytes\\&. If this option is absent, newly allocated memory is uninitialized\\&.\n.RE\n.PP\n\\fBALLOCM_NO_MOVE\\fR\n.RS 4\nFor reallocation, fail rather than moving the object\\&. This constraint can apply to both growth and shrinkage\\&.\n.RE\n.PP\n\\fBALLOCM_ARENA(\\fR\\fB\\fIa\\fR\\fR\\fB) \\fR\n.RS 4\nUse the arena specified by the index\n\\fIa\\fR\\&. This macro does not validate that\n\\fIa\\fR\nspecifies an arena in the valid range\\&.\n.RE\n.PP\nThe\n\\fBallocm\\fR\\fB\\fR\nfunction allocates at least\n\\fIsize\\fR\nbytes of memory, sets\n\\fI*ptr\\fR\nto the base address of the allocation, and sets\n\\fI*rsize\\fR\nto the real size of the allocation if\n\\fIrsize\\fR\nis not\n\\fBNULL\\fR\\&. Behavior is undefined if\n\\fIsize\\fR\nis\n\\fB0\\fR\\&.\n.PP\nThe\n\\fBrallocm\\fR\\fB\\fR\nfunction resizes the allocation at\n\\fI*ptr\\fR\nto be at least\n\\fIsize\\fR\nbytes, sets\n\\fI*ptr\\fR\nto the base address of the allocation if it moved, and sets\n\\fI*rsize\\fR\nto the real size of the allocation if\n\\fIrsize\\fR\nis not\n\\fBNULL\\fR\\&. If\n\\fIextra\\fR\nis non\\-zero, an attempt is made to resize the allocation to be at least\n\\fIsize\\fR + \\fIextra\\fR)\nbytes, though inability to allocate the extra byte(s) will not by itself result in failure\\&. Behavior is undefined if\n\\fIsize\\fR\nis\n\\fB0\\fR, or if\n(\\fIsize\\fR + \\fIextra\\fR > \\fBSIZE_T_MAX\\fR)\\&.\n.PP\nThe\n\\fBsallocm\\fR\\fB\\fR\nfunction sets\n\\fI*rsize\\fR\nto the real size of the allocation\\&.\n.PP\nThe\n\\fBdallocm\\fR\\fB\\fR\nfunction causes the memory referenced by\n\\fIptr\\fR\nto be made available for future allocations\\&.\n.PP\nThe\n\\fBnallocm\\fR\\fB\\fR\nfunction allocates no memory, but it performs the same size computation as the\n\\fBallocm\\fR\\fB\\fR\nfunction, and if\n\\fIrsize\\fR\nis not\n\\fBNULL\\fR\nit sets\n\\fI*rsize\\fR\nto the real size of the allocation that would result from the equivalent\n\\fBallocm\\fR\\fB\\fR\nfunction call\\&. Behavior is undefined if\n\\fIsize\\fR\nis\n\\fB0\\fR\\&.\n.SH \"TUNING\"\n.PP\nOnce, when the first call is made to one of the memory allocation routines, the allocator initializes its internals based in part on various options that can be specified at compile\\- or run\\-time\\&.\n.PP\nThe string pointed to by the global variable\n\\fImalloc_conf\\fR, the \\(lqname\\(rq of the file referenced by the symbolic link named\n/etc/malloc\\&.conf, and the value of the environment variable\n\\fBMALLOC_CONF\\fR, will be interpreted, in that order, from left to right as options\\&.\n.PP\nAn options string is a comma\\-separated list of option:value pairs\\&. There is one key corresponding to each\n\"opt\\&.*\"\nmallctl (see the\nMALLCTL NAMESPACE\nsection for options documentation)\\&. For example,\nabort:true,narenas:1\nsets the\n\"opt\\&.abort\"\nand\n\"opt\\&.narenas\"\noptions\\&. Some options have boolean values (true/false), others have integer values (base 8, 10, or 16, depending on prefix), and yet others have raw string values\\&.\n.SH \"IMPLEMENTATION NOTES\"\n.PP\nTraditionally, allocators have used\n\\fBsbrk\\fR(2)\nto obtain memory, which is suboptimal for several reasons, including race conditions, increased fragmentation, and artificial limitations on maximum usable memory\\&. If\n\\fB\\-\\-enable\\-dss\\fR\nis specified during configuration, this allocator uses both\n\\fBmmap\\fR(2)\nand\n\\fBsbrk\\fR(2), in that order of preference; otherwise only\n\\fBmmap\\fR(2)\nis used\\&.\n.PP\nThis allocator uses multiple arenas in order to reduce lock contention for threaded programs on multi\\-processor systems\\&. This works well with regard to threading scalability, but incurs some costs\\&. There is a small fixed per\\-arena overhead, and additionally, arenas manage memory completely independently of each other, which means a small fixed increase in overall memory fragmentation\\&. These overheads are not generally an issue, given the number of arenas normally used\\&. Note that using substantially more arenas than the default is not likely to improve performance, mainly due to reduced cache performance\\&. However, it may make sense to reduce the number of arenas if an application does not make much use of the allocation functions\\&.\n.PP\nIn addition to multiple arenas, unless\n\\fB\\-\\-disable\\-tcache\\fR\nis specified during configuration, this allocator supports thread\\-specific caching for small and large objects, in order to make it possible to completely avoid synchronization for most allocation requests\\&. Such caching allows very fast allocation in the common case, but it increases memory usage and fragmentation, since a bounded number of objects can remain allocated in each thread cache\\&.\n.PP\nMemory is conceptually broken into equal\\-sized chunks, where the chunk size is a power of two that is greater than the page size\\&. Chunks are always aligned to multiples of the chunk size\\&. This alignment makes it possible to find metadata for user objects very quickly\\&.\n.PP\nUser objects are broken into three categories according to size: small, large, and huge\\&. Small objects are smaller than one page\\&. Large objects are smaller than the chunk size\\&. Huge objects are a multiple of the chunk size\\&. Small and large objects are managed by arenas; huge objects are managed separately in a single data structure that is shared by all threads\\&. Huge objects are used by applications infrequently enough that this single data structure is not a scalability issue\\&.\n.PP\nEach chunk that is managed by an arena tracks its contents as runs of contiguous pages (unused, backing a set of small objects, or backing one large object)\\&. The combination of chunk alignment and chunk page maps makes it possible to determine all metadata regarding small and large allocations in constant time\\&.\n.PP\nSmall objects are managed in groups by page runs\\&. Each run maintains a frontier and free list to track which regions are in use\\&. Allocation requests that are no more than half the quantum (8 or 16, depending on architecture) are rounded up to the nearest power of two that is at least\nsizeof(\\fBdouble\\fR)\\&. All other small object size classes are multiples of the quantum, spaced such that internal fragmentation is limited to approximately 25% for all but the smallest size classes\\&. Allocation requests that are larger than the maximum small size class, but small enough to fit in an arena\\-managed chunk (see the\n\"opt\\&.lg_chunk\"\noption), are rounded up to the nearest run size\\&. Allocation requests that are too large to fit in an arena\\-managed chunk are rounded up to the nearest multiple of the chunk size\\&.\n.PP\nAllocations are packed tightly together, which can be an issue for multi\\-threaded applications\\&. If you need to assure that allocations do not suffer from cacheline sharing, round your allocation requests up to the nearest multiple of the cacheline size, or specify cacheline alignment when allocating\\&.\n.PP\nAssuming 4 MiB chunks, 4 KiB pages, and a 16\\-byte quantum on a 64\\-bit system, the size classes in each category are as shown in\nTable 1\\&.\n.sp\n.it 1 an-trap\n.nr an-no-space-flag 1\n.nr an-break-flag 1\n.br\n.B Table\\ \\&1.\\ \\&Size classes\n.TS\nallbox tab(:);\nlB rB lB.\nT{\nCategory\nT}:T{\nSpacing\nT}:T{\nSize\nT}\n.T&\nl r l\n^ r l\n^ r l\n^ r l\n^ r l\n^ r l\n^ r l\nl r l\nl r l.\nT{\nSmall\nT}:T{\nlg\nT}:T{\n[8]\nT}\n:T{\n16\nT}:T{\n[16, 32, 48, \\&.\\&.\\&., 128]\nT}\n:T{\n32\nT}:T{\n[160, 192, 224, 256]\nT}\n:T{\n64\nT}:T{\n[320, 384, 448, 512]\nT}\n:T{\n128\nT}:T{\n[640, 768, 896, 1024]\nT}\n:T{\n256\nT}:T{\n[1280, 1536, 1792, 2048]\nT}\n:T{\n512\nT}:T{\n[2560, 3072, 3584]\nT}\nT{\nLarge\nT}:T{\n4 KiB\nT}:T{\n[4 KiB, 8 KiB, 12 KiB, \\&.\\&.\\&., 4072 KiB]\nT}\nT{\nHuge\nT}:T{\n4 MiB\nT}:T{\n[4 MiB, 8 MiB, 12 MiB, \\&.\\&.\\&.]\nT}\n.TE\n.sp 1\n.SH \"MALLCTL NAMESPACE\"\n.PP\nThe following names are defined in the namespace accessible via the\n\\fBmallctl*\\fR\\fB\\fR\nfunctions\\&. Value types are specified in parentheses, their readable/writable statuses are encoded as\nrw,\nr\\-,\n\\-w, or\n\\-\\-, and required build configuration flags follow, if any\\&. A name element encoded as\n<i>\nor\n<j>\nindicates an integer component, where the integer varies from 0 to some upper value that must be determined via introspection\\&. In the case of\n\"stats\\&.arenas\\&.<i>\\&.*\",\n<i>\nequal to\n\"arenas\\&.narenas\"\ncan be used to access the summation of statistics from all arenas\\&. Take special note of the\n\"epoch\"\nmallctl, which controls refreshing of cached dynamic statistics\\&.\n.PP\n\"version\" (\\fBconst char *\\fR) r\\-\n.RS 4\nReturn the jemalloc version string\\&.\n.RE\n.PP\n\"epoch\" (\\fBuint64_t\\fR) rw\n.RS 4\nIf a value is passed in, refresh the data from which the\n\\fBmallctl*\\fR\\fB\\fR\nfunctions report values, and increment the epoch\\&. Return the current epoch\\&. This is useful for detecting whether another thread caused a refresh\\&.\n.RE\n.PP\n\"config\\&.debug\" (\\fBbool\\fR) r\\-\n.RS 4\n\\fB\\-\\-enable\\-debug\\fR\nwas specified during build configuration\\&.\n.RE\n.PP\n\"config\\&.dss\" (\\fBbool\\fR) r\\-\n.RS 4\n\\fB\\-\\-enable\\-dss\\fR\nwas specified during build configuration\\&.\n.RE\n.PP\n\"config\\&.fill\" (\\fBbool\\fR) r\\-\n.RS 4\n\\fB\\-\\-enable\\-fill\\fR\nwas specified during build configuration\\&.\n.RE\n.PP\n\"config\\&.lazy_lock\" (\\fBbool\\fR) r\\-\n.RS 4\n\\fB\\-\\-enable\\-lazy\\-lock\\fR\nwas specified during build configuration\\&.\n.RE\n.PP\n\"config\\&.mremap\" (\\fBbool\\fR) r\\-\n.RS 4\n\\fB\\-\\-enable\\-mremap\\fR\nwas specified during build configuration\\&.\n.RE\n.PP\n\"config\\&.munmap\" (\\fBbool\\fR) r\\-\n.RS 4\n\\fB\\-\\-enable\\-munmap\\fR\nwas specified during build configuration\\&.\n.RE\n.PP\n\"config\\&.prof\" (\\fBbool\\fR) r\\-\n.RS 4\n\\fB\\-\\-enable\\-prof\\fR\nwas specified during build configuration\\&.\n.RE\n.PP\n\"config\\&.prof_libgcc\" (\\fBbool\\fR) r\\-\n.RS 4\n\\fB\\-\\-disable\\-prof\\-libgcc\\fR\nwas not specified during build configuration\\&.\n.RE\n.PP\n\"config\\&.prof_libunwind\" (\\fBbool\\fR) r\\-\n.RS 4\n\\fB\\-\\-enable\\-prof\\-libunwind\\fR\nwas specified during build configuration\\&.\n.RE\n.PP\n\"config\\&.stats\" (\\fBbool\\fR) r\\-\n.RS 4\n\\fB\\-\\-enable\\-stats\\fR\nwas specified during build configuration\\&.\n.RE\n.PP\n\"config\\&.tcache\" (\\fBbool\\fR) r\\-\n.RS 4\n\\fB\\-\\-disable\\-tcache\\fR\nwas not specified during build configuration\\&.\n.RE\n.PP\n\"config\\&.tls\" (\\fBbool\\fR) r\\-\n.RS 4\n\\fB\\-\\-disable\\-tls\\fR\nwas not specified during build configuration\\&.\n.RE\n.PP\n\"config\\&.utrace\" (\\fBbool\\fR) r\\-\n.RS 4\n\\fB\\-\\-enable\\-utrace\\fR\nwas specified during build configuration\\&.\n.RE\n.PP\n\"config\\&.valgrind\" (\\fBbool\\fR) r\\-\n.RS 4\n\\fB\\-\\-enable\\-valgrind\\fR\nwas specified during build configuration\\&.\n.RE\n.PP\n\"config\\&.xmalloc\" (\\fBbool\\fR) r\\-\n.RS 4\n\\fB\\-\\-enable\\-xmalloc\\fR\nwas specified during build configuration\\&.\n.RE\n.PP\n\"opt\\&.abort\" (\\fBbool\\fR) r\\-\n.RS 4\nAbort\\-on\\-warning enabled/disabled\\&. If true, most warnings are fatal\\&. The process will call\n\\fBabort\\fR(3)\nin these cases\\&. This option is disabled by default unless\n\\fB\\-\\-enable\\-debug\\fR\nis specified during configuration, in which case it is enabled by default\\&.\n.RE\n.PP\n\"opt\\&.lg_chunk\" (\\fBsize_t\\fR) r\\-\n.RS 4\nVirtual memory chunk size (log base 2)\\&. The default chunk size is 4 MiB (2^22)\\&.\n.RE\n.PP\n\"opt\\&.dss\" (\\fBconst char *\\fR) r\\-\n.RS 4\ndss (\\fBsbrk\\fR(2)) allocation precedence as related to\n\\fBmmap\\fR(2)\nallocation\\&. The following settings are supported: \\(lqdisabled\\(rq, \\(lqprimary\\(rq, and \\(lqsecondary\\(rq (default)\\&.\n.RE\n.PP\n\"opt\\&.narenas\" (\\fBsize_t\\fR) r\\-\n.RS 4\nMaximum number of arenas to use for automatic multiplexing of threads and arenas\\&. The default is four times the number of CPUs, or one if there is a single CPU\\&.\n.RE\n.PP\n\"opt\\&.lg_dirty_mult\" (\\fBssize_t\\fR) r\\-\n.RS 4\nPer\\-arena minimum ratio (log base 2) of active to dirty pages\\&. Some dirty unused pages may be allowed to accumulate, within the limit set by the ratio (or one chunk worth of dirty pages, whichever is greater), before informing the kernel about some of those pages via\n\\fBmadvise\\fR(2)\nor a similar system call\\&. This provides the kernel with sufficient information to recycle dirty pages if physical memory becomes scarce and the pages remain unused\\&. The default minimum ratio is 8:1 (2^3:1); an option value of \\-1 will disable dirty page purging\\&.\n.RE\n.PP\n\"opt\\&.stats_print\" (\\fBbool\\fR) r\\-\n.RS 4\nEnable/disable statistics printing at exit\\&. If enabled, the\n\\fBmalloc_stats_print\\fR\\fB\\fR\nfunction is called at program exit via an\n\\fBatexit\\fR(3)\nfunction\\&. If\n\\fB\\-\\-enable\\-stats\\fR\nis specified during configuration, this has the potential to cause deadlock for a multi\\-threaded process that exits while one or more threads are executing in the memory allocation functions\\&. Therefore, this option should only be used with care; it is primarily intended as a performance tuning aid during application development\\&. This option is disabled by default\\&.\n.RE\n.PP\n\"opt\\&.junk\" (\\fBbool\\fR) r\\- [\\fB\\-\\-enable\\-fill\\fR]\n.RS 4\nJunk filling enabled/disabled\\&. If enabled, each byte of uninitialized allocated memory will be initialized to\n0xa5\\&. All deallocated memory will be initialized to\n0x5a\\&. This is intended for debugging and will impact performance negatively\\&. This option is disabled by default unless\n\\fB\\-\\-enable\\-debug\\fR\nis specified during configuration, in which case it is enabled by default unless running inside\n\\m[blue]\\fBValgrind\\fR\\m[]\\&\\s-2\\u[2]\\d\\s+2\\&.\n.RE\n.PP\n\"opt\\&.quarantine\" (\\fBsize_t\\fR) r\\- [\\fB\\-\\-enable\\-fill\\fR]\n.RS 4\nPer thread quarantine size in bytes\\&. If non\\-zero, each thread maintains a FIFO object quarantine that stores up to the specified number of bytes of memory\\&. The quarantined memory is not freed until it is released from quarantine, though it is immediately junk\\-filled if the\n\"opt\\&.junk\"\noption is enabled\\&. This feature is of particular use in combination with\n\\m[blue]\\fBValgrind\\fR\\m[]\\&\\s-2\\u[2]\\d\\s+2, which can detect attempts to access quarantined objects\\&. This is intended for debugging and will impact performance negatively\\&. The default quarantine size is 0 unless running inside Valgrind, in which case the default is 16 MiB\\&.\n.RE\n.PP\n\"opt\\&.redzone\" (\\fBbool\\fR) r\\- [\\fB\\-\\-enable\\-fill\\fR]\n.RS 4\nRedzones enabled/disabled\\&. If enabled, small allocations have redzones before and after them\\&. Furthermore, if the\n\"opt\\&.junk\"\noption is enabled, the redzones are checked for corruption during deallocation\\&. However, the primary intended purpose of this feature is to be used in combination with\n\\m[blue]\\fBValgrind\\fR\\m[]\\&\\s-2\\u[2]\\d\\s+2, which needs redzones in order to do effective buffer overflow/underflow detection\\&. This option is intended for debugging and will impact performance negatively\\&. This option is disabled by default unless running inside Valgrind\\&.\n.RE\n.PP\n\"opt\\&.zero\" (\\fBbool\\fR) r\\- [\\fB\\-\\-enable\\-fill\\fR]\n.RS 4\nZero filling enabled/disabled\\&. If enabled, each byte of uninitialized allocated memory will be initialized to 0\\&. Note that this initialization only happens once for each byte, so\n\\fBrealloc\\fR\\fB\\fR\nand\n\\fBrallocm\\fR\\fB\\fR\ncalls do not zero memory that was previously allocated\\&. This is intended for debugging and will impact performance negatively\\&. This option is disabled by default\\&.\n.RE\n.PP\n\"opt\\&.utrace\" (\\fBbool\\fR) r\\- [\\fB\\-\\-enable\\-utrace\\fR]\n.RS 4\nAllocation tracing based on\n\\fButrace\\fR(2)\nenabled/disabled\\&. This option is disabled by default\\&.\n.RE\n.PP\n\"opt\\&.valgrind\" (\\fBbool\\fR) r\\- [\\fB\\-\\-enable\\-valgrind\\fR]\n.RS 4\n\\m[blue]\\fBValgrind\\fR\\m[]\\&\\s-2\\u[2]\\d\\s+2\nsupport enabled/disabled\\&. This option is vestigal because jemalloc auto\\-detects whether it is running inside Valgrind\\&. This option is disabled by default, unless running inside Valgrind\\&.\n.RE\n.PP\n\"opt\\&.xmalloc\" (\\fBbool\\fR) r\\- [\\fB\\-\\-enable\\-xmalloc\\fR]\n.RS 4\nAbort\\-on\\-out\\-of\\-memory enabled/disabled\\&. If enabled, rather than returning failure for any allocation function, display a diagnostic message on\n\\fBSTDERR_FILENO\\fR\nand cause the program to drop core (using\n\\fBabort\\fR(3))\\&. If an application is designed to depend on this behavior, set the option at compile time by including the following in the source code:\n.sp\n.if n \\{\\\n.RS 4\n.\\}\n.nf\nmalloc_conf = \"xmalloc:true\";\n.fi\n.if n \\{\\\n.RE\n.\\}\n.sp\nThis option is disabled by default\\&.\n.RE\n.PP\n\"opt\\&.tcache\" (\\fBbool\\fR) r\\- [\\fB\\-\\-enable\\-tcache\\fR]\n.RS 4\nThread\\-specific caching enabled/disabled\\&. When there are multiple threads, each thread uses a thread\\-specific cache for objects up to a certain size\\&. Thread\\-specific caching allows many allocations to be satisfied without performing any thread synchronization, at the cost of increased memory use\\&. See the\n\"opt\\&.lg_tcache_max\"\noption for related tuning information\\&. This option is enabled by default unless running inside\n\\m[blue]\\fBValgrind\\fR\\m[]\\&\\s-2\\u[2]\\d\\s+2\\&.\n.RE\n.PP\n\"opt\\&.lg_tcache_max\" (\\fBsize_t\\fR) r\\- [\\fB\\-\\-enable\\-tcache\\fR]\n.RS 4\nMaximum size class (log base 2) to cache in the thread\\-specific cache\\&. At a minimum, all small size classes are cached, and at a maximum all large size classes are cached\\&. The default maximum is 32 KiB (2^15)\\&.\n.RE\n.PP\n\"opt\\&.prof\" (\\fBbool\\fR) r\\- [\\fB\\-\\-enable\\-prof\\fR]\n.RS 4\nMemory profiling enabled/disabled\\&. If enabled, profile memory allocation activity\\&. See the\n\"opt\\&.prof_active\"\noption for on\\-the\\-fly activation/deactivation\\&. See the\n\"opt\\&.lg_prof_sample\"\noption for probabilistic sampling control\\&. See the\n\"opt\\&.prof_accum\"\noption for control of cumulative sample reporting\\&. See the\n\"opt\\&.lg_prof_interval\"\noption for information on interval\\-triggered profile dumping, the\n\"opt\\&.prof_gdump\"\noption for information on high\\-water\\-triggered profile dumping, and the\n\"opt\\&.prof_final\"\noption for final profile dumping\\&. Profile output is compatible with the included\n\\fBpprof\\fR\nPerl script, which originates from the\n\\m[blue]\\fBgperftools package\\fR\\m[]\\&\\s-2\\u[3]\\d\\s+2\\&.\n.RE\n.PP\n\"opt\\&.prof_prefix\" (\\fBconst char *\\fR) r\\- [\\fB\\-\\-enable\\-prof\\fR]\n.RS 4\nFilename prefix for profile dumps\\&. If the prefix is set to the empty string, no automatic dumps will occur; this is primarily useful for disabling the automatic final heap dump (which also disables leak reporting, if enabled)\\&. The default prefix is\njeprof\\&.\n.RE\n.PP\n\"opt\\&.prof_active\" (\\fBbool\\fR) r\\- [\\fB\\-\\-enable\\-prof\\fR]\n.RS 4\nProfiling activated/deactivated\\&. This is a secondary control mechanism that makes it possible to start the application with profiling enabled (see the\n\"opt\\&.prof\"\noption) but inactive, then toggle profiling at any time during program execution with the\n\"prof\\&.active\"\nmallctl\\&. This option is enabled by default\\&.\n.RE\n.PP\n\"opt\\&.lg_prof_sample\" (\\fBssize_t\\fR) r\\- [\\fB\\-\\-enable\\-prof\\fR]\n.RS 4\nAverage interval (log base 2) between allocation samples, as measured in bytes of allocation activity\\&. Increasing the sampling interval decreases profile fidelity, but also decreases the computational overhead\\&. The default sample interval is 512 KiB (2^19 B)\\&.\n.RE\n.PP\n\"opt\\&.prof_accum\" (\\fBbool\\fR) r\\- [\\fB\\-\\-enable\\-prof\\fR]\n.RS 4\nReporting of cumulative object/byte counts in profile dumps enabled/disabled\\&. If this option is enabled, every unique backtrace must be stored for the duration of execution\\&. Depending on the application, this can impose a large memory overhead, and the cumulative counts are not always of interest\\&. This option is disabled by default\\&.\n.RE\n.PP\n\"opt\\&.lg_prof_interval\" (\\fBssize_t\\fR) r\\- [\\fB\\-\\-enable\\-prof\\fR]\n.RS 4\nAverage interval (log base 2) between memory profile dumps, as measured in bytes of allocation activity\\&. The actual interval between dumps may be sporadic because decentralized allocation counters are used to avoid synchronization bottlenecks\\&. Profiles are dumped to files named according to the pattern\n<prefix>\\&.<pid>\\&.<seq>\\&.i<iseq>\\&.heap, where\n<prefix>\nis controlled by the\n\"opt\\&.prof_prefix\"\noption\\&. By default, interval\\-triggered profile dumping is disabled (encoded as \\-1)\\&.\n.RE\n.PP\n\"opt\\&.prof_gdump\" (\\fBbool\\fR) r\\- [\\fB\\-\\-enable\\-prof\\fR]\n.RS 4\nTrigger a memory profile dump every time the total virtual memory exceeds the previous maximum\\&. Profiles are dumped to files named according to the pattern\n<prefix>\\&.<pid>\\&.<seq>\\&.u<useq>\\&.heap, where\n<prefix>\nis controlled by the\n\"opt\\&.prof_prefix\"\noption\\&. This option is disabled by default\\&.\n.RE\n.PP\n\"opt\\&.prof_final\" (\\fBbool\\fR) r\\- [\\fB\\-\\-enable\\-prof\\fR]\n.RS 4\nUse an\n\\fBatexit\\fR(3)\nfunction to dump final memory usage to a file named according to the pattern\n<prefix>\\&.<pid>\\&.<seq>\\&.f\\&.heap, where\n<prefix>\nis controlled by the\n\"opt\\&.prof_prefix\"\noption\\&. This option is enabled by default\\&.\n.RE\n.PP\n\"opt\\&.prof_leak\" (\\fBbool\\fR) r\\- [\\fB\\-\\-enable\\-prof\\fR]\n.RS 4\nLeak reporting enabled/disabled\\&. If enabled, use an\n\\fBatexit\\fR(3)\nfunction to report memory leaks detected by allocation sampling\\&. See the\n\"opt\\&.prof\"\noption for information on analyzing heap profile output\\&. This option is disabled by default\\&.\n.RE\n.PP\n\"thread\\&.arena\" (\\fBunsigned\\fR) rw\n.RS 4\nGet or set the arena associated with the calling thread\\&. If the specified arena was not initialized beforehand (see the\n\"arenas\\&.initialized\"\nmallctl), it will be automatically initialized as a side effect of calling this interface\\&.\n.RE\n.PP\n\"thread\\&.allocated\" (\\fBuint64_t\\fR) r\\- [\\fB\\-\\-enable\\-stats\\fR]\n.RS 4\nGet the total number of bytes ever allocated by the calling thread\\&. This counter has the potential to wrap around; it is up to the application to appropriately interpret the counter in such cases\\&.\n.RE\n.PP\n\"thread\\&.allocatedp\" (\\fBuint64_t *\\fR) r\\- [\\fB\\-\\-enable\\-stats\\fR]\n.RS 4\nGet a pointer to the the value that is returned by the\n\"thread\\&.allocated\"\nmallctl\\&. This is useful for avoiding the overhead of repeated\n\\fBmallctl*\\fR\\fB\\fR\ncalls\\&.\n.RE\n.PP\n\"thread\\&.deallocated\" (\\fBuint64_t\\fR) r\\- [\\fB\\-\\-enable\\-stats\\fR]\n.RS 4\nGet the total number of bytes ever deallocated by the calling thread\\&. This counter has the potential to wrap around; it is up to the application to appropriately interpret the counter in such cases\\&.\n.RE\n.PP\n\"thread\\&.deallocatedp\" (\\fBuint64_t *\\fR) r\\- [\\fB\\-\\-enable\\-stats\\fR]\n.RS 4\nGet a pointer to the the value that is returned by the\n\"thread\\&.deallocated\"\nmallctl\\&. This is useful for avoiding the overhead of repeated\n\\fBmallctl*\\fR\\fB\\fR\ncalls\\&.\n.RE\n.PP\n\"thread\\&.tcache\\&.enabled\" (\\fBbool\\fR) rw [\\fB\\-\\-enable\\-tcache\\fR]\n.RS 4\nEnable/disable calling thread\\*(Aqs tcache\\&. The tcache is implicitly flushed as a side effect of becoming disabled (see\n\"thread\\&.tcache\\&.flush\")\\&.\n.RE\n.PP\n\"thread\\&.tcache\\&.flush\" (\\fBvoid\\fR) \\-\\- [\\fB\\-\\-enable\\-tcache\\fR]\n.RS 4\nFlush calling thread\\*(Aqs tcache\\&. This interface releases all cached objects and internal data structures associated with the calling thread\\*(Aqs thread\\-specific cache\\&. Ordinarily, this interface need not be called, since automatic periodic incremental garbage collection occurs, and the thread cache is automatically discarded when a thread exits\\&. However, garbage collection is triggered by allocation activity, so it is possible for a thread that stops allocating/deallocating to retain its cache indefinitely, in which case the developer may find manual flushing useful\\&.\n.RE\n.PP\n\"arena\\&.<i>\\&.purge\" (\\fBunsigned\\fR) \\-\\-\n.RS 4\nPurge unused dirty pages for arena <i>, or for all arenas if <i> equals\n\"arenas\\&.narenas\"\\&.\n.RE\n.PP\n\"arena\\&.<i>\\&.dss\" (\\fBconst char *\\fR) rw\n.RS 4\nSet the precedence of dss allocation as related to mmap allocation for arena <i>, or for all arenas if <i> equals\n\"arenas\\&.narenas\"\\&. See\n\"opt\\&.dss\"\nfor supported settings\\&.\n.RE\n.PP\n\"arenas\\&.narenas\" (\\fBunsigned\\fR) r\\-\n.RS 4\nCurrent limit on number of arenas\\&.\n.RE\n.PP\n\"arenas\\&.initialized\" (\\fBbool *\\fR) r\\-\n.RS 4\nAn array of\n\"arenas\\&.narenas\"\nbooleans\\&. Each boolean indicates whether the corresponding arena is initialized\\&.\n.RE\n.PP\n\"arenas\\&.quantum\" (\\fBsize_t\\fR) r\\-\n.RS 4\nQuantum size\\&.\n.RE\n.PP\n\"arenas\\&.page\" (\\fBsize_t\\fR) r\\-\n.RS 4\nPage size\\&.\n.RE\n.PP\n\"arenas\\&.tcache_max\" (\\fBsize_t\\fR) r\\- [\\fB\\-\\-enable\\-tcache\\fR]\n.RS 4\nMaximum thread\\-cached size class\\&.\n.RE\n.PP\n\"arenas\\&.nbins\" (\\fBunsigned\\fR) r\\-\n.RS 4\nNumber of bin size classes\\&.\n.RE\n.PP\n\"arenas\\&.nhbins\" (\\fBunsigned\\fR) r\\- [\\fB\\-\\-enable\\-tcache\\fR]\n.RS 4\nTotal number of thread cache bin size classes\\&.\n.RE\n.PP\n\"arenas\\&.bin\\&.<i>\\&.size\" (\\fBsize_t\\fR) r\\-\n.RS 4\nMaximum size supported by size class\\&.\n.RE\n.PP\n\"arenas\\&.bin\\&.<i>\\&.nregs\" (\\fBuint32_t\\fR) r\\-\n.RS 4\nNumber of regions per page run\\&.\n.RE\n.PP\n\"arenas\\&.bin\\&.<i>\\&.run_size\" (\\fBsize_t\\fR) r\\-\n.RS 4\nNumber of bytes per page run\\&.\n.RE\n.PP\n\"arenas\\&.nlruns\" (\\fBsize_t\\fR) r\\-\n.RS 4\nTotal number of large size classes\\&.\n.RE\n.PP\n\"arenas\\&.lrun\\&.<i>\\&.size\" (\\fBsize_t\\fR) r\\-\n.RS 4\nMaximum size supported by this large size class\\&.\n.RE\n.PP\n\"arenas\\&.purge\" (\\fBunsigned\\fR) \\-w\n.RS 4\nPurge unused dirty pages for the specified arena, or for all arenas if none is specified\\&.\n.RE\n.PP\n\"arenas\\&.extend\" (\\fBunsigned\\fR) r\\-\n.RS 4\nExtend the array of arenas by appending a new arena, and returning the new arena index\\&.\n.RE\n.PP\n\"prof\\&.active\" (\\fBbool\\fR) rw [\\fB\\-\\-enable\\-prof\\fR]\n.RS 4\nControl whether sampling is currently active\\&. See the\n\"opt\\&.prof_active\"\noption for additional information\\&.\n.RE\n.PP\n\"prof\\&.dump\" (\\fBconst char *\\fR) \\-w [\\fB\\-\\-enable\\-prof\\fR]\n.RS 4\nDump a memory profile to the specified file, or if NULL is specified, to a file according to the pattern\n<prefix>\\&.<pid>\\&.<seq>\\&.m<mseq>\\&.heap, where\n<prefix>\nis controlled by the\n\"opt\\&.prof_prefix\"\noption\\&.\n.RE\n.PP\n\"prof\\&.interval\" (\\fBuint64_t\\fR) r\\- [\\fB\\-\\-enable\\-prof\\fR]\n.RS 4\nAverage number of bytes allocated between inverval\\-based profile dumps\\&. See the\n\"opt\\&.lg_prof_interval\"\noption for additional information\\&.\n.RE\n.PP\n\"stats\\&.cactive\" (\\fBsize_t *\\fR) r\\- [\\fB\\-\\-enable\\-stats\\fR]\n.RS 4\nPointer to a counter that contains an approximate count of the current number of bytes in active pages\\&. The estimate may be high, but never low, because each arena rounds up to the nearest multiple of the chunk size when computing its contribution to the counter\\&. Note that the\n\"epoch\"\nmallctl has no bearing on this counter\\&. Furthermore, counter consistency is maintained via atomic operations, so it is necessary to use an atomic operation in order to guarantee a consistent read when dereferencing the pointer\\&.\n.RE\n.PP\n\"stats\\&.allocated\" (\\fBsize_t\\fR) r\\- [\\fB\\-\\-enable\\-stats\\fR]\n.RS 4\nTotal number of bytes allocated by the application\\&.\n.RE\n.PP\n\"stats\\&.active\" (\\fBsize_t\\fR) r\\- [\\fB\\-\\-enable\\-stats\\fR]\n.RS 4\nTotal number of bytes in active pages allocated by the application\\&. This is a multiple of the page size, and greater than or equal to\n\"stats\\&.allocated\"\\&. This does not include\n\"stats\\&.arenas\\&.<i>\\&.pdirty\"\nand pages entirely devoted to allocator metadata\\&.\n.RE\n.PP\n\"stats\\&.mapped\" (\\fBsize_t\\fR) r\\- [\\fB\\-\\-enable\\-stats\\fR]\n.RS 4\nTotal number of bytes in chunks mapped on behalf of the application\\&. This is a multiple of the chunk size, and is at least as large as\n\"stats\\&.active\"\\&. This does not include inactive chunks\\&.\n.RE\n.PP\n\"stats\\&.chunks\\&.current\" (\\fBsize_t\\fR) r\\- [\\fB\\-\\-enable\\-stats\\fR]\n.RS 4\nTotal number of chunks actively mapped on behalf of the application\\&. This does not include inactive chunks\\&.\n.RE\n.PP\n\"stats\\&.chunks\\&.total\" (\\fBuint64_t\\fR) r\\- [\\fB\\-\\-enable\\-stats\\fR]\n.RS 4\nCumulative number of chunks allocated\\&.\n.RE\n.PP\n\"stats\\&.chunks\\&.high\" (\\fBsize_t\\fR) r\\- [\\fB\\-\\-enable\\-stats\\fR]\n.RS 4\nMaximum number of active chunks at any time thus far\\&.\n.RE\n.PP\n\"stats\\&.huge\\&.allocated\" (\\fBsize_t\\fR) r\\- [\\fB\\-\\-enable\\-stats\\fR]\n.RS 4\nNumber of bytes currently allocated by huge objects\\&.\n.RE\n.PP\n\"stats\\&.huge\\&.nmalloc\" (\\fBuint64_t\\fR) r\\- [\\fB\\-\\-enable\\-stats\\fR]\n.RS 4\nCumulative number of huge allocation requests\\&.\n.RE\n.PP\n\"stats\\&.huge\\&.ndalloc\" (\\fBuint64_t\\fR) r\\- [\\fB\\-\\-enable\\-stats\\fR]\n.RS 4\nCumulative number of huge deallocation requests\\&.\n.RE\n.PP\n\"stats\\&.arenas\\&.<i>\\&.dss\" (\\fBconst char *\\fR) r\\-\n.RS 4\ndss (\\fBsbrk\\fR(2)) allocation precedence as related to\n\\fBmmap\\fR(2)\nallocation\\&. See\n\"opt\\&.dss\"\nfor details\\&.\n.RE\n.PP\n\"stats\\&.arenas\\&.<i>\\&.nthreads\" (\\fBunsigned\\fR) r\\-\n.RS 4\nNumber of threads currently assigned to arena\\&.\n.RE\n.PP\n\"stats\\&.arenas\\&.<i>\\&.pactive\" (\\fBsize_t\\fR) r\\-\n.RS 4\nNumber of pages in active runs\\&.\n.RE\n.PP\n\"stats\\&.arenas\\&.<i>\\&.pdirty\" (\\fBsize_t\\fR) r\\-\n.RS 4\nNumber of pages within unused runs that are potentially dirty, and for which\n\\fBmadvise\\fR\\fB\\fI\\&.\\&.\\&.\\fR\\fR\\fB \\fR\\fB\\fI\\fBMADV_DONTNEED\\fR\\fR\\fR\nor similar has not been called\\&.\n.RE\n.PP\n\"stats\\&.arenas\\&.<i>\\&.mapped\" (\\fBsize_t\\fR) r\\- [\\fB\\-\\-enable\\-stats\\fR]\n.RS 4\nNumber of mapped bytes\\&.\n.RE\n.PP\n\"stats\\&.arenas\\&.<i>\\&.npurge\" (\\fBuint64_t\\fR) r\\- [\\fB\\-\\-enable\\-stats\\fR]\n.RS 4\nNumber of dirty page purge sweeps performed\\&.\n.RE\n.PP\n\"stats\\&.arenas\\&.<i>\\&.nmadvise\" (\\fBuint64_t\\fR) r\\- [\\fB\\-\\-enable\\-stats\\fR]\n.RS 4\nNumber of\n\\fBmadvise\\fR\\fB\\fI\\&.\\&.\\&.\\fR\\fR\\fB \\fR\\fB\\fI\\fBMADV_DONTNEED\\fR\\fR\\fR\nor similar calls made to purge dirty pages\\&.\n.RE\n.PP\n\"stats\\&.arenas\\&.<i>\\&.npurged\" (\\fBuint64_t\\fR) r\\- [\\fB\\-\\-enable\\-stats\\fR]\n.RS 4\nNumber of pages purged\\&.\n.RE\n.PP\n\"stats\\&.arenas\\&.<i>\\&.small\\&.allocated\" (\\fBsize_t\\fR) r\\- [\\fB\\-\\-enable\\-stats\\fR]\n.RS 4\nNumber of bytes currently allocated by small objects\\&.\n.RE\n.PP\n\"stats\\&.arenas\\&.<i>\\&.small\\&.nmalloc\" (\\fBuint64_t\\fR) r\\- [\\fB\\-\\-enable\\-stats\\fR]\n.RS 4\nCumulative number of allocation requests served by small bins\\&.\n.RE\n.PP\n\"stats\\&.arenas\\&.<i>\\&.small\\&.ndalloc\" (\\fBuint64_t\\fR) r\\- [\\fB\\-\\-enable\\-stats\\fR]\n.RS 4\nCumulative number of small objects returned to bins\\&.\n.RE\n.PP\n\"stats\\&.arenas\\&.<i>\\&.small\\&.nrequests\" (\\fBuint64_t\\fR) r\\- [\\fB\\-\\-enable\\-stats\\fR]\n.RS 4\nCumulative number of small allocation requests\\&.\n.RE\n.PP\n\"stats\\&.arenas\\&.<i>\\&.large\\&.allocated\" (\\fBsize_t\\fR) r\\- [\\fB\\-\\-enable\\-stats\\fR]\n.RS 4\nNumber of bytes currently allocated by large objects\\&.\n.RE\n.PP\n\"stats\\&.arenas\\&.<i>\\&.large\\&.nmalloc\" (\\fBuint64_t\\fR) r\\- [\\fB\\-\\-enable\\-stats\\fR]\n.RS 4\nCumulative number of large allocation requests served directly by the arena\\&.\n.RE\n.PP\n\"stats\\&.arenas\\&.<i>\\&.large\\&.ndalloc\" (\\fBuint64_t\\fR) r\\- [\\fB\\-\\-enable\\-stats\\fR]\n.RS 4\nCumulative number of large deallocation requests served directly by the arena\\&.\n.RE\n.PP\n\"stats\\&.arenas\\&.<i>\\&.large\\&.nrequests\" (\\fBuint64_t\\fR) r\\- [\\fB\\-\\-enable\\-stats\\fR]\n.RS 4\nCumulative number of large allocation requests\\&.\n.RE\n.PP\n\"stats\\&.arenas\\&.<i>\\&.bins\\&.<j>\\&.allocated\" (\\fBsize_t\\fR) r\\- [\\fB\\-\\-enable\\-stats\\fR]\n.RS 4\nCurrent number of bytes allocated by bin\\&.\n.RE\n.PP\n\"stats\\&.arenas\\&.<i>\\&.bins\\&.<j>\\&.nmalloc\" (\\fBuint64_t\\fR) r\\- [\\fB\\-\\-enable\\-stats\\fR]\n.RS 4\nCumulative number of allocations served by bin\\&.\n.RE\n.PP\n\"stats\\&.arenas\\&.<i>\\&.bins\\&.<j>\\&.ndalloc\" (\\fBuint64_t\\fR) r\\- [\\fB\\-\\-enable\\-stats\\fR]\n.RS 4\nCumulative number of allocations returned to bin\\&.\n.RE\n.PP\n\"stats\\&.arenas\\&.<i>\\&.bins\\&.<j>\\&.nrequests\" (\\fBuint64_t\\fR) r\\- [\\fB\\-\\-enable\\-stats\\fR]\n.RS 4\nCumulative number of allocation requests\\&.\n.RE\n.PP\n\"stats\\&.arenas\\&.<i>\\&.bins\\&.<j>\\&.nfills\" (\\fBuint64_t\\fR) r\\- [\\fB\\-\\-enable\\-stats\\fR \\fB\\-\\-enable\\-tcache\\fR]\n.RS 4\nCumulative number of tcache fills\\&.\n.RE\n.PP\n\"stats\\&.arenas\\&.<i>\\&.bins\\&.<j>\\&.nflushes\" (\\fBuint64_t\\fR) r\\- [\\fB\\-\\-enable\\-stats\\fR \\fB\\-\\-enable\\-tcache\\fR]\n.RS 4\nCumulative number of tcache flushes\\&.\n.RE\n.PP\n\"stats\\&.arenas\\&.<i>\\&.bins\\&.<j>\\&.nruns\" (\\fBuint64_t\\fR) r\\- [\\fB\\-\\-enable\\-stats\\fR]\n.RS 4\nCumulative number of runs created\\&.\n.RE\n.PP\n\"stats\\&.arenas\\&.<i>\\&.bins\\&.<j>\\&.nreruns\" (\\fBuint64_t\\fR) r\\- [\\fB\\-\\-enable\\-stats\\fR]\n.RS 4\nCumulative number of times the current run from which to allocate changed\\&.\n.RE\n.PP\n\"stats\\&.arenas\\&.<i>\\&.bins\\&.<j>\\&.curruns\" (\\fBsize_t\\fR) r\\- [\\fB\\-\\-enable\\-stats\\fR]\n.RS 4\nCurrent number of runs\\&.\n.RE\n.PP\n\"stats\\&.arenas\\&.<i>\\&.lruns\\&.<j>\\&.nmalloc\" (\\fBuint64_t\\fR) r\\- [\\fB\\-\\-enable\\-stats\\fR]\n.RS 4\nCumulative number of allocation requests for this size class served directly by the arena\\&.\n.RE\n.PP\n\"stats\\&.arenas\\&.<i>\\&.lruns\\&.<j>\\&.ndalloc\" (\\fBuint64_t\\fR) r\\- [\\fB\\-\\-enable\\-stats\\fR]\n.RS 4\nCumulative number of deallocation requests for this size class served directly by the arena\\&.\n.RE\n.PP\n\"stats\\&.arenas\\&.<i>\\&.lruns\\&.<j>\\&.nrequests\" (\\fBuint64_t\\fR) r\\- [\\fB\\-\\-enable\\-stats\\fR]\n.RS 4\nCumulative number of allocation requests for this size class\\&.\n.RE\n.PP\n\"stats\\&.arenas\\&.<i>\\&.lruns\\&.<j>\\&.curruns\" (\\fBsize_t\\fR) r\\- [\\fB\\-\\-enable\\-stats\\fR]\n.RS 4\nCurrent number of runs for this size class\\&.\n.RE\n.SH \"DEBUGGING MALLOC PROBLEMS\"\n.PP\nWhen debugging, it is a good idea to configure/build jemalloc with the\n\\fB\\-\\-enable\\-debug\\fR\nand\n\\fB\\-\\-enable\\-fill\\fR\noptions, and recompile the program with suitable options and symbols for debugger support\\&. When so configured, jemalloc incorporates a wide variety of run\\-time assertions that catch application errors such as double\\-free, write\\-after\\-free, etc\\&.\n.PP\nPrograms often accidentally depend on \\(lquninitialized\\(rq memory actually being filled with zero bytes\\&. Junk filling (see the\n\"opt\\&.junk\"\noption) tends to expose such bugs in the form of obviously incorrect results and/or coredumps\\&. Conversely, zero filling (see the\n\"opt\\&.zero\"\noption) eliminates the symptoms of such bugs\\&. Between these two options, it is usually possible to quickly detect, diagnose, and eliminate such bugs\\&.\n.PP\nThis implementation does not provide much detail about the problems it detects, because the performance impact for storing such information would be prohibitive\\&. However, jemalloc does integrate with the most excellent\n\\m[blue]\\fBValgrind\\fR\\m[]\\&\\s-2\\u[2]\\d\\s+2\ntool if the\n\\fB\\-\\-enable\\-valgrind\\fR\nconfiguration option is enabled\\&.\n.SH \"DIAGNOSTIC MESSAGES\"\n.PP\nIf any of the memory allocation/deallocation functions detect an error or warning condition, a message will be printed to file descriptor\n\\fBSTDERR_FILENO\\fR\\&. Errors will result in the process dumping core\\&. If the\n\"opt\\&.abort\"\noption is set, most warnings are treated as errors\\&.\n.PP\nThe\n\\fImalloc_message\\fR\nvariable allows the programmer to override the function which emits the text strings forming the errors and warnings if for some reason the\n\\fBSTDERR_FILENO\\fR\nfile descriptor is not suitable for this\\&.\n\\fBmalloc_message\\fR\\fB\\fR\ntakes the\n\\fIcbopaque\\fR\npointer argument that is\n\\fBNULL\\fR\nunless overridden by the arguments in a call to\n\\fBmalloc_stats_print\\fR\\fB\\fR, followed by a string pointer\\&. Please note that doing anything which tries to allocate memory in this function is likely to result in a crash or deadlock\\&.\n.PP\nAll messages are prefixed by \\(lq<jemalloc>:\\(rq\\&.\n.SH \"RETURN VALUES\"\n.SS \"Standard API\"\n.PP\nThe\n\\fBmalloc\\fR\\fB\\fR\nand\n\\fBcalloc\\fR\\fB\\fR\nfunctions return a pointer to the allocated memory if successful; otherwise a\n\\fBNULL\\fR\npointer is returned and\n\\fIerrno\\fR\nis set to\nENOMEM\\&.\n.PP\nThe\n\\fBposix_memalign\\fR\\fB\\fR\nfunction returns the value 0 if successful; otherwise it returns an error value\\&. The\n\\fBposix_memalign\\fR\\fB\\fR\nfunction will fail if:\n.PP\nEINVAL\n.RS 4\nThe\n\\fIalignment\\fR\nparameter is not a power of 2 at least as large as\nsizeof(\\fBvoid *\\fR)\\&.\n.RE\n.PP\nENOMEM\n.RS 4\nMemory allocation error\\&.\n.RE\n.PP\nThe\n\\fBaligned_alloc\\fR\\fB\\fR\nfunction returns a pointer to the allocated memory if successful; otherwise a\n\\fBNULL\\fR\npointer is returned and\n\\fIerrno\\fR\nis set\\&. The\n\\fBaligned_alloc\\fR\\fB\\fR\nfunction will fail if:\n.PP\nEINVAL\n.RS 4\nThe\n\\fIalignment\\fR\nparameter is not a power of 2\\&.\n.RE\n.PP\nENOMEM\n.RS 4\nMemory allocation error\\&.\n.RE\n.PP\nThe\n\\fBrealloc\\fR\\fB\\fR\nfunction returns a pointer, possibly identical to\n\\fIptr\\fR, to the allocated memory if successful; otherwise a\n\\fBNULL\\fR\npointer is returned, and\n\\fIerrno\\fR\nis set to\nENOMEM\nif the error was the result of an allocation failure\\&. The\n\\fBrealloc\\fR\\fB\\fR\nfunction always leaves the original buffer intact when an error occurs\\&.\n.PP\nThe\n\\fBfree\\fR\\fB\\fR\nfunction returns no value\\&.\n.SS \"Non\\-standard API\"\n.PP\nThe\n\\fBmalloc_usable_size\\fR\\fB\\fR\nfunction returns the usable size of the allocation pointed to by\n\\fIptr\\fR\\&.\n.PP\nThe\n\\fBmallctl\\fR\\fB\\fR,\n\\fBmallctlnametomib\\fR\\fB\\fR, and\n\\fBmallctlbymib\\fR\\fB\\fR\nfunctions return 0 on success; otherwise they return an error value\\&. The functions will fail if:\n.PP\nEINVAL\n.RS 4\n\\fInewp\\fR\nis not\n\\fBNULL\\fR, and\n\\fInewlen\\fR\nis too large or too small\\&. Alternatively,\n\\fI*oldlenp\\fR\nis too large or too small; in this case as much data as possible are read despite the error\\&.\n.RE\n.PP\nENOMEM\n.RS 4\n\\fI*oldlenp\\fR\nis too short to hold the requested value\\&.\n.RE\n.PP\nENOENT\n.RS 4\n\\fIname\\fR\nor\n\\fImib\\fR\nspecifies an unknown/invalid value\\&.\n.RE\n.PP\nEPERM\n.RS 4\nAttempt to read or write void value, or attempt to write read\\-only value\\&.\n.RE\n.PP\nEAGAIN\n.RS 4\nA memory allocation failure occurred\\&.\n.RE\n.PP\nEFAULT\n.RS 4\nAn interface with side effects failed in some way not directly related to\n\\fBmallctl*\\fR\\fB\\fR\nread/write processing\\&.\n.RE\n.SS \"Experimental API\"\n.PP\nThe\n\\fBallocm\\fR\\fB\\fR,\n\\fBrallocm\\fR\\fB\\fR,\n\\fBsallocm\\fR\\fB\\fR,\n\\fBdallocm\\fR\\fB\\fR, and\n\\fBnallocm\\fR\\fB\\fR\nfunctions return\n\\fBALLOCM_SUCCESS\\fR\non success; otherwise they return an error value\\&. The\n\\fBallocm\\fR\\fB\\fR,\n\\fBrallocm\\fR\\fB\\fR, and\n\\fBnallocm\\fR\\fB\\fR\nfunctions will fail if:\n.PP\nALLOCM_ERR_OOM\n.RS 4\nOut of memory\\&. Insufficient contiguous memory was available to service the allocation request\\&. The\n\\fBallocm\\fR\\fB\\fR\nfunction additionally sets\n\\fI*ptr\\fR\nto\n\\fBNULL\\fR, whereas the\n\\fBrallocm\\fR\\fB\\fR\nfunction leaves\n\\fB*ptr\\fR\nunmodified\\&.\n.RE\nThe\n\\fBrallocm\\fR\\fB\\fR\nfunction will also fail if:\n.PP\nALLOCM_ERR_NOT_MOVED\n.RS 4\n\\fBALLOCM_NO_MOVE\\fR\nwas specified, but the reallocation request could not be serviced without moving the object\\&.\n.RE\n.SH \"ENVIRONMENT\"\n.PP\nThe following environment variable affects the execution of the allocation functions:\n.PP\n\\fBMALLOC_CONF\\fR\n.RS 4\nIf the environment variable\n\\fBMALLOC_CONF\\fR\nis set, the characters it contains will be interpreted as options\\&.\n.RE\n.SH \"EXAMPLES\"\n.PP\nTo dump core whenever a problem occurs:\n.sp\n.if n \\{\\\n.RS 4\n.\\}\n.nf\nln \\-s \\*(Aqabort:true\\*(Aq /etc/malloc\\&.conf\n.fi\n.if n \\{\\\n.RE\n.\\}\n.PP\nTo specify in the source a chunk size that is 16 MiB:\n.sp\n.if n \\{\\\n.RS 4\n.\\}\n.nf\nmalloc_conf = \"lg_chunk:24\";\n.fi\n.if n \\{\\\n.RE\n.\\}\n.SH \"SEE ALSO\"\n.PP\n\\fBmadvise\\fR(2),\n\\fBmmap\\fR(2),\n\\fBsbrk\\fR(2),\n\\fButrace\\fR(2),\n\\fBalloca\\fR(3),\n\\fBatexit\\fR(3),\n\\fBgetpagesize\\fR(3)\n.SH \"STANDARDS\"\n.PP\nThe\n\\fBmalloc\\fR\\fB\\fR,\n\\fBcalloc\\fR\\fB\\fR,\n\\fBrealloc\\fR\\fB\\fR, and\n\\fBfree\\fR\\fB\\fR\nfunctions conform to ISO/IEC 9899:1990 (\\(lqISO C90\\(rq)\\&.\n.PP\nThe\n\\fBposix_memalign\\fR\\fB\\fR\nfunction conforms to IEEE Std 1003\\&.1\\-2001 (\\(lqPOSIX\\&.1\\(rq)\\&.\n.SH \"AUTHOR\"\n.PP\n\\fBJason Evans\\fR\n.RS 4\n.RE\n.SH \"NOTES\"\n.IP \" 1.\" 4\njemalloc website\n.RS 4\n\\%http://www.canonware.com/jemalloc/\n.RE\n.IP \" 2.\" 4\nValgrind\n.RS 4\n\\%http://valgrind.org/\n.RE\n.IP \" 3.\" 4\ngperftools package\n.RS 4\n\\%http://code.google.com/p/gperftools/\n.RE\n"
  },
  {
    "path": "deps/jemalloc/doc/jemalloc.html",
    "content": "<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=ISO-8859-1\"><title>JEMALLOC</title><meta name=\"generator\" content=\"DocBook XSL Stylesheets V1.76.1\"></head><body bgcolor=\"white\" text=\"black\" link=\"#0000FF\" vlink=\"#840084\" alink=\"#0000FF\"><div class=\"refentry\" title=\"JEMALLOC\"><a name=\"id286949159\"></a><div class=\"titlepage\"></div><div class=\"refnamediv\"><h2>Name</h2><p>jemalloc &#8212; general purpose memory allocation functions</p></div><div class=\"refsect1\" title=\"LIBRARY\"><a name=\"library\"></a><h2>LIBRARY</h2><p>This manual describes jemalloc 3.2.0-0-g87499f6748ebe4817571e817e9f680ccb5bf54a9.  More information\n    can be found at the <a class=\"ulink\" href=\"http://www.canonware.com/jemalloc/\" target=\"_top\">jemalloc website</a>.</p></div><div class=\"refsynopsisdiv\" title=\"SYNOPSIS\"><h2>SYNOPSIS</h2><div class=\"funcsynopsis\"><pre class=\"funcsynopsisinfo\">#include &lt;<code class=\"filename\">stdlib.h</code>&gt;\n#include &lt;<code class=\"filename\">jemalloc/jemalloc.h</code>&gt;</pre><div class=\"refsect2\" title=\"Standard API\"><a name=\"id286901505\"></a><h3>Standard API</h3><table border=\"0\" summary=\"Function synopsis\" cellspacing=\"0\" cellpadding=\"0\" class=\"funcprototype-table\"><tr><td><code class=\"funcdef\">void *<b class=\"fsfunc\">malloc</b>(</code></td><td>size_t <var class=\"pdparam\">size</var><code>)</code>;</td></tr></table><div class=\"funcprototype-spacer\"></div><table border=\"0\" summary=\"Function synopsis\" cellspacing=\"0\" cellpadding=\"0\" class=\"funcprototype-table\"><tr><td><code class=\"funcdef\">void *<b class=\"fsfunc\">calloc</b>(</code></td><td>size_t <var class=\"pdparam\">number</var>, </td></tr><tr><td></td><td>size_t <var class=\"pdparam\">size</var><code>)</code>;</td></tr></table><div class=\"funcprototype-spacer\"></div><table border=\"0\" summary=\"Function synopsis\" cellspacing=\"0\" cellpadding=\"0\" class=\"funcprototype-table\"><tr><td><code class=\"funcdef\">int <b class=\"fsfunc\">posix_memalign</b>(</code></td><td>void **<var class=\"pdparam\">ptr</var>, </td></tr><tr><td></td><td>size_t <var class=\"pdparam\">alignment</var>, </td></tr><tr><td></td><td>size_t <var class=\"pdparam\">size</var><code>)</code>;</td></tr></table><div class=\"funcprototype-spacer\"></div><table border=\"0\" summary=\"Function synopsis\" cellspacing=\"0\" cellpadding=\"0\" class=\"funcprototype-table\"><tr><td><code class=\"funcdef\">void *<b class=\"fsfunc\">aligned_alloc</b>(</code></td><td>size_t <var class=\"pdparam\">alignment</var>, </td></tr><tr><td></td><td>size_t <var class=\"pdparam\">size</var><code>)</code>;</td></tr></table><div class=\"funcprototype-spacer\"></div><table border=\"0\" summary=\"Function synopsis\" cellspacing=\"0\" cellpadding=\"0\" class=\"funcprototype-table\"><tr><td><code class=\"funcdef\">void *<b class=\"fsfunc\">realloc</b>(</code></td><td>void *<var class=\"pdparam\">ptr</var>, </td></tr><tr><td></td><td>size_t <var class=\"pdparam\">size</var><code>)</code>;</td></tr></table><div class=\"funcprototype-spacer\"></div><table border=\"0\" summary=\"Function synopsis\" cellspacing=\"0\" cellpadding=\"0\" class=\"funcprototype-table\"><tr><td><code class=\"funcdef\">void <b class=\"fsfunc\">free</b>(</code></td><td>void *<var class=\"pdparam\">ptr</var><code>)</code>;</td></tr></table><div class=\"funcprototype-spacer\"></div></div><div class=\"refsect2\" title=\"Non-standard API\"><a name=\"id286900549\"></a><h3>Non-standard API</h3><table border=\"0\" summary=\"Function synopsis\" cellspacing=\"0\" cellpadding=\"0\" class=\"funcprototype-table\"><tr><td><code class=\"funcdef\">size_t <b class=\"fsfunc\">malloc_usable_size</b>(</code></td><td>const void *<var class=\"pdparam\">ptr</var><code>)</code>;</td></tr></table><div class=\"funcprototype-spacer\"></div><table border=\"0\" summary=\"Function synopsis\" cellspacing=\"0\" cellpadding=\"0\" class=\"funcprototype-table\"><tr><td><code class=\"funcdef\">void <b class=\"fsfunc\">malloc_stats_print</b>(</code></td><td>void <var class=\"pdparam\">(*write_cb)</var>\n            <code>(</code>void *, const char *<code>)</code>\n          , </td></tr><tr><td></td><td>void *<var class=\"pdparam\">cbopaque</var>, </td></tr><tr><td></td><td>const char *<var class=\"pdparam\">opts</var><code>)</code>;</td></tr></table><div class=\"funcprototype-spacer\"></div><table border=\"0\" summary=\"Function synopsis\" cellspacing=\"0\" cellpadding=\"0\" class=\"funcprototype-table\"><tr><td><code class=\"funcdef\">int <b class=\"fsfunc\">mallctl</b>(</code></td><td>const char *<var class=\"pdparam\">name</var>, </td></tr><tr><td></td><td>void *<var class=\"pdparam\">oldp</var>, </td></tr><tr><td></td><td>size_t *<var class=\"pdparam\">oldlenp</var>, </td></tr><tr><td></td><td>void *<var class=\"pdparam\">newp</var>, </td></tr><tr><td></td><td>size_t <var class=\"pdparam\">newlen</var><code>)</code>;</td></tr></table><div class=\"funcprototype-spacer\"></div><table border=\"0\" summary=\"Function synopsis\" cellspacing=\"0\" cellpadding=\"0\" class=\"funcprototype-table\"><tr><td><code class=\"funcdef\">int <b class=\"fsfunc\">mallctlnametomib</b>(</code></td><td>const char *<var class=\"pdparam\">name</var>, </td></tr><tr><td></td><td>size_t *<var class=\"pdparam\">mibp</var>, </td></tr><tr><td></td><td>size_t *<var class=\"pdparam\">miblenp</var><code>)</code>;</td></tr></table><div class=\"funcprototype-spacer\"></div><table border=\"0\" summary=\"Function synopsis\" cellspacing=\"0\" cellpadding=\"0\" class=\"funcprototype-table\"><tr><td><code class=\"funcdef\">int <b class=\"fsfunc\">mallctlbymib</b>(</code></td><td>const size_t *<var class=\"pdparam\">mib</var>, </td></tr><tr><td></td><td>size_t <var class=\"pdparam\">miblen</var>, </td></tr><tr><td></td><td>void *<var class=\"pdparam\">oldp</var>, </td></tr><tr><td></td><td>size_t *<var class=\"pdparam\">oldlenp</var>, </td></tr><tr><td></td><td>void *<var class=\"pdparam\">newp</var>, </td></tr><tr><td></td><td>size_t <var class=\"pdparam\">newlen</var><code>)</code>;</td></tr></table><div class=\"funcprototype-spacer\"></div><table border=\"0\" summary=\"Function synopsis\" cellspacing=\"0\" cellpadding=\"0\" class=\"funcprototype-table\"><tr><td><code class=\"funcdef\">void <b class=\"fsfunc\">(*malloc_message)</b>(</code></td><td>void *<var class=\"pdparam\">cbopaque</var>, </td></tr><tr><td></td><td>const char *<var class=\"pdparam\">s</var><code>)</code>;</td></tr></table><div class=\"funcprototype-spacer\"></div><p><span class=\"type\">const char *</span><code class=\"varname\">malloc_conf</code>;</p></div><div class=\"refsect2\" title=\"Experimental API\"><a name=\"id286900756\"></a><h3>Experimental API</h3><table border=\"0\" summary=\"Function synopsis\" cellspacing=\"0\" cellpadding=\"0\" class=\"funcprototype-table\"><tr><td><code class=\"funcdef\">int <b class=\"fsfunc\">allocm</b>(</code></td><td>void **<var class=\"pdparam\">ptr</var>, </td></tr><tr><td></td><td>size_t *<var class=\"pdparam\">rsize</var>, </td></tr><tr><td></td><td>size_t <var class=\"pdparam\">size</var>, </td></tr><tr><td></td><td>int <var class=\"pdparam\">flags</var><code>)</code>;</td></tr></table><div class=\"funcprototype-spacer\"></div><table border=\"0\" summary=\"Function synopsis\" cellspacing=\"0\" cellpadding=\"0\" class=\"funcprototype-table\"><tr><td><code class=\"funcdef\">int <b class=\"fsfunc\">rallocm</b>(</code></td><td>void **<var class=\"pdparam\">ptr</var>, </td></tr><tr><td></td><td>size_t *<var class=\"pdparam\">rsize</var>, </td></tr><tr><td></td><td>size_t <var class=\"pdparam\">size</var>, </td></tr><tr><td></td><td>size_t <var class=\"pdparam\">extra</var>, </td></tr><tr><td></td><td>int <var class=\"pdparam\">flags</var><code>)</code>;</td></tr></table><div class=\"funcprototype-spacer\"></div><table border=\"0\" summary=\"Function synopsis\" cellspacing=\"0\" cellpadding=\"0\" class=\"funcprototype-table\"><tr><td><code class=\"funcdef\">int <b class=\"fsfunc\">sallocm</b>(</code></td><td>const void *<var class=\"pdparam\">ptr</var>, </td></tr><tr><td></td><td>size_t *<var class=\"pdparam\">rsize</var>, </td></tr><tr><td></td><td>int <var class=\"pdparam\">flags</var><code>)</code>;</td></tr></table><div class=\"funcprototype-spacer\"></div><table border=\"0\" summary=\"Function synopsis\" cellspacing=\"0\" cellpadding=\"0\" class=\"funcprototype-table\"><tr><td><code class=\"funcdef\">int <b class=\"fsfunc\">dallocm</b>(</code></td><td>void *<var class=\"pdparam\">ptr</var>, </td></tr><tr><td></td><td>int <var class=\"pdparam\">flags</var><code>)</code>;</td></tr></table><div class=\"funcprototype-spacer\"></div><table border=\"0\" summary=\"Function synopsis\" cellspacing=\"0\" cellpadding=\"0\" class=\"funcprototype-table\"><tr><td><code class=\"funcdef\">int <b class=\"fsfunc\">nallocm</b>(</code></td><td>size_t *<var class=\"pdparam\">rsize</var>, </td></tr><tr><td></td><td>size_t <var class=\"pdparam\">size</var>, </td></tr><tr><td></td><td>int <var class=\"pdparam\">flags</var><code>)</code>;</td></tr></table><div class=\"funcprototype-spacer\"></div></div></div></div><div class=\"refsect1\" title=\"DESCRIPTION\"><a name=\"description\"></a><h2>DESCRIPTION</h2><div class=\"refsect2\" title=\"Standard API\"><a name=\"id286949297\"></a><h3>Standard API</h3><p>The <code class=\"function\">malloc</code>(<em class=\"parameter\"><code></code></em>) function allocates\n      <em class=\"parameter\"><code>size</code></em> bytes of uninitialized memory.  The allocated\n      space is suitably aligned (after possible pointer coercion) for storage\n      of any type of object.</p><p>The <code class=\"function\">calloc</code>(<em class=\"parameter\"><code></code></em>) function allocates\n      space for <em class=\"parameter\"><code>number</code></em> objects, each\n      <em class=\"parameter\"><code>size</code></em> bytes in length.  The result is identical to\n      calling <code class=\"function\">malloc</code>(<em class=\"parameter\"><code></code></em>) with an argument of\n      <em class=\"parameter\"><code>number</code></em> * <em class=\"parameter\"><code>size</code></em>, with the\n      exception that the allocated memory is explicitly initialized to zero\n      bytes.</p><p>The <code class=\"function\">posix_memalign</code>(<em class=\"parameter\"><code></code></em>) function\n      allocates <em class=\"parameter\"><code>size</code></em> bytes of memory such that the\n      allocation's base address is an even multiple of\n      <em class=\"parameter\"><code>alignment</code></em>, and returns the allocation in the value\n      pointed to by <em class=\"parameter\"><code>ptr</code></em>.  The requested\n      <em class=\"parameter\"><code>alignment</code></em> must be a power of 2 at least as large\n      as <code class=\"code\">sizeof(<span class=\"type\">void *</span>)</code>.</p><p>The <code class=\"function\">aligned_alloc</code>(<em class=\"parameter\"><code></code></em>) function\n      allocates <em class=\"parameter\"><code>size</code></em> bytes of memory such that the\n      allocation's base address is an even multiple of\n      <em class=\"parameter\"><code>alignment</code></em>.  The requested\n      <em class=\"parameter\"><code>alignment</code></em> must be a power of 2.  Behavior is\n      undefined if <em class=\"parameter\"><code>size</code></em> is not an integral multiple of\n      <em class=\"parameter\"><code>alignment</code></em>.</p><p>The <code class=\"function\">realloc</code>(<em class=\"parameter\"><code></code></em>) function changes the\n      size of the previously allocated memory referenced by\n      <em class=\"parameter\"><code>ptr</code></em> to <em class=\"parameter\"><code>size</code></em> bytes.  The\n      contents of the memory are unchanged up to the lesser of the new and old\n      sizes.  If the new size is larger, the contents of the newly allocated\n      portion of the memory are undefined.  Upon success, the memory referenced\n      by <em class=\"parameter\"><code>ptr</code></em> is freed and a pointer to the newly\n      allocated memory is returned.  Note that\n      <code class=\"function\">realloc</code>(<em class=\"parameter\"><code></code></em>) may move the memory allocation,\n      resulting in a different return value than <em class=\"parameter\"><code>ptr</code></em>.\n      If <em class=\"parameter\"><code>ptr</code></em> is <code class=\"constant\">NULL</code>, the\n      <code class=\"function\">realloc</code>(<em class=\"parameter\"><code></code></em>) function behaves identically to\n      <code class=\"function\">malloc</code>(<em class=\"parameter\"><code></code></em>) for the specified size.</p><p>The <code class=\"function\">free</code>(<em class=\"parameter\"><code></code></em>) function causes the\n      allocated memory referenced by <em class=\"parameter\"><code>ptr</code></em> to be made\n      available for future allocations.  If <em class=\"parameter\"><code>ptr</code></em> is\n      <code class=\"constant\">NULL</code>, no action occurs.</p></div><div class=\"refsect2\" title=\"Non-standard API\"><a name=\"id286949561\"></a><h3>Non-standard API</h3><p>The <code class=\"function\">malloc_usable_size</code>(<em class=\"parameter\"><code></code></em>) function\n      returns the usable size of the allocation pointed to by\n      <em class=\"parameter\"><code>ptr</code></em>.  The return value may be larger than the size\n      that was requested during allocation.  The\n      <code class=\"function\">malloc_usable_size</code>(<em class=\"parameter\"><code></code></em>) function is not a\n      mechanism for in-place <code class=\"function\">realloc</code>(<em class=\"parameter\"><code></code></em>); rather\n      it is provided solely as a tool for introspection purposes.  Any\n      discrepancy between the requested allocation size and the size reported\n      by <code class=\"function\">malloc_usable_size</code>(<em class=\"parameter\"><code></code></em>) should not be\n      depended on, since such behavior is entirely implementation-dependent.\n      </p><p>The <code class=\"function\">malloc_stats_print</code>(<em class=\"parameter\"><code></code></em>) function\n      writes human-readable summary statistics via the\n      <em class=\"parameter\"><code>write_cb</code></em> callback function pointer and\n      <em class=\"parameter\"><code>cbopaque</code></em> data passed to\n      <em class=\"parameter\"><code>write_cb</code></em>, or\n      <code class=\"function\">malloc_message</code>(<em class=\"parameter\"><code></code></em>) if\n      <em class=\"parameter\"><code>write_cb</code></em> is <code class=\"constant\">NULL</code>.  This\n      function can be called repeatedly.  General information that never\n      changes during execution can be omitted by specifying \"g\" as a character\n      within the <em class=\"parameter\"><code>opts</code></em> string.  Note that\n      <code class=\"function\">malloc_message</code>(<em class=\"parameter\"><code></code></em>) uses the\n      <code class=\"function\">mallctl*</code>(<em class=\"parameter\"><code></code></em>) functions internally, so\n      inconsistent statistics can be reported if multiple threads use these\n      functions simultaneously.  If <code class=\"option\">--enable-stats</code> is\n      specified during configuration, &#8220;m&#8221; and &#8220;a&#8221; can\n      be specified to omit merged arena and per arena statistics, respectively;\n      &#8220;b&#8221; and &#8220;l&#8221; can be specified to omit per size\n      class statistics for bins and large objects, respectively.  Unrecognized\n      characters are silently ignored.  Note that thread caching may prevent\n      some statistics from being completely up to date, since extra locking\n      would be required to merge counters that track thread cache operations.\n      </p><p>The <code class=\"function\">mallctl</code>(<em class=\"parameter\"><code></code></em>) function provides a\n      general interface for introspecting the memory allocator, as well as\n      setting modifiable parameters and triggering actions.  The\n      period-separated <em class=\"parameter\"><code>name</code></em> argument specifies a\n      location in a tree-structured namespace; see the <a class=\"xref\" href=\"#mallctl_namespace\" title=\"MALLCTL NAMESPACE\">MALLCTL NAMESPACE</a> section for\n      documentation on the tree contents.  To read a value, pass a pointer via\n      <em class=\"parameter\"><code>oldp</code></em> to adequate space to contain the value, and a\n      pointer to its length via <em class=\"parameter\"><code>oldlenp</code></em>; otherwise pass\n      <code class=\"constant\">NULL</code> and <code class=\"constant\">NULL</code>.  Similarly, to\n      write a value, pass a pointer to the value via\n      <em class=\"parameter\"><code>newp</code></em>, and its length via\n      <em class=\"parameter\"><code>newlen</code></em>; otherwise pass <code class=\"constant\">NULL</code>\n      and <code class=\"constant\">0</code>.</p><p>The <code class=\"function\">mallctlnametomib</code>(<em class=\"parameter\"><code></code></em>) function\n      provides a way to avoid repeated name lookups for applications that\n      repeatedly query the same portion of the namespace, by translating a name\n      to a &#8220;Management Information Base&#8221; (MIB) that can be passed\n      repeatedly to <code class=\"function\">mallctlbymib</code>(<em class=\"parameter\"><code></code></em>).  Upon\n      successful return from <code class=\"function\">mallctlnametomib</code>(<em class=\"parameter\"><code></code></em>),\n      <em class=\"parameter\"><code>mibp</code></em> contains an array of\n      <em class=\"parameter\"><code>*miblenp</code></em> integers, where\n      <em class=\"parameter\"><code>*miblenp</code></em> is the lesser of the number of components\n      in <em class=\"parameter\"><code>name</code></em> and the input value of\n      <em class=\"parameter\"><code>*miblenp</code></em>.  Thus it is possible to pass a\n      <em class=\"parameter\"><code>*miblenp</code></em> that is smaller than the number of\n      period-separated name components, which results in a partial MIB that can\n      be used as the basis for constructing a complete MIB.  For name\n      components that are integers (e.g. the 2 in\n      <a class=\"link\" href=\"#arenas.bin.i.size\">\n    \"<code class=\"mallctl\">arenas.bin.2.size</code>\"\n  </a>),\n      the corresponding MIB component will always be that integer.  Therefore,\n      it is legitimate to construct code like the following: </p><pre class=\"programlisting\">\nunsigned nbins, i;\n\nint mib[4];\nsize_t len, miblen;\n\nlen = sizeof(nbins);\nmallctl(\"arenas.nbins\", &amp;nbins, &amp;len, NULL, 0);\n\nmiblen = 4;\nmallnametomib(\"arenas.bin.0.size\", mib, &amp;miblen);\nfor (i = 0; i &lt; nbins; i++) {\n\tsize_t bin_size;\n\n\tmib[2] = i;\n\tlen = sizeof(bin_size);\n\tmallctlbymib(mib, miblen, &amp;bin_size, &amp;len, NULL, 0);\n\t/* Do something with bin_size... */\n}</pre></div><div class=\"refsect2\" title=\"Experimental API\"><a name=\"id286949870\"></a><h3>Experimental API</h3><p>The experimental API is subject to change or removal without regard\n      for backward compatibility.  If <code class=\"option\">--disable-experimental</code>\n      is specified during configuration, the experimental API is\n      omitted.</p><p>The <code class=\"function\">allocm</code>(<em class=\"parameter\"><code></code></em>),\n      <code class=\"function\">rallocm</code>(<em class=\"parameter\"><code></code></em>),\n      <code class=\"function\">sallocm</code>(<em class=\"parameter\"><code></code></em>),\n      <code class=\"function\">dallocm</code>(<em class=\"parameter\"><code></code></em>), and\n      <code class=\"function\">nallocm</code>(<em class=\"parameter\"><code></code></em>) functions all have a\n      <em class=\"parameter\"><code>flags</code></em> argument that can be used to specify\n      options.  The functions only check the options that are contextually\n      relevant.  Use bitwise or (<code class=\"code\">|</code>) operations to\n      specify one or more of the following:\n        </p><div class=\"variablelist\"><dl><dt><span class=\"term\"><code class=\"constant\">ALLOCM_LG_ALIGN(<em class=\"parameter\"><code>la</code></em>)\n            </code></span></dt><dd><p>Align the memory allocation to start at an address\n            that is a multiple of <code class=\"code\">(1 &lt;&lt;\n            <em class=\"parameter\"><code>la</code></em>)</code>.  This macro does not validate\n            that <em class=\"parameter\"><code>la</code></em> is within the valid\n            range.</p></dd><dt><span class=\"term\"><code class=\"constant\">ALLOCM_ALIGN(<em class=\"parameter\"><code>a</code></em>)\n            </code></span></dt><dd><p>Align the memory allocation to start at an address\n            that is a multiple of <em class=\"parameter\"><code>a</code></em>, where\n            <em class=\"parameter\"><code>a</code></em> is a power of two.  This macro does not\n            validate that <em class=\"parameter\"><code>a</code></em> is a power of 2.\n            </p></dd><dt><span class=\"term\"><code class=\"constant\">ALLOCM_ZERO</code></span></dt><dd><p>Initialize newly allocated memory to contain zero\n            bytes.  In the growing reallocation case, the real size prior to\n            reallocation defines the boundary between untouched bytes and those\n            that are initialized to contain zero bytes.  If this option is\n            absent, newly allocated memory is uninitialized.</p></dd><dt><span class=\"term\"><code class=\"constant\">ALLOCM_NO_MOVE</code></span></dt><dd><p>For reallocation, fail rather than moving the\n            object.  This constraint can apply to both growth and\n            shrinkage.</p></dd><dt><span class=\"term\"><code class=\"constant\">ALLOCM_ARENA(<em class=\"parameter\"><code>a</code></em>)\n            </code></span></dt><dd><p>Use the arena specified by the index\n            <em class=\"parameter\"><code>a</code></em>.  This macro does not validate that\n            <em class=\"parameter\"><code>a</code></em> specifies an arena in the valid\n            range.</p></dd></dl></div><p>\n      </p><p>The <code class=\"function\">allocm</code>(<em class=\"parameter\"><code></code></em>) function allocates at\n      least <em class=\"parameter\"><code>size</code></em> bytes of memory, sets\n      <em class=\"parameter\"><code>*ptr</code></em> to the base address of the allocation, and\n      sets <em class=\"parameter\"><code>*rsize</code></em> to the real size of the allocation if\n      <em class=\"parameter\"><code>rsize</code></em> is not <code class=\"constant\">NULL</code>.  Behavior\n      is undefined if <em class=\"parameter\"><code>size</code></em> is\n      <code class=\"constant\">0</code>.</p><p>The <code class=\"function\">rallocm</code>(<em class=\"parameter\"><code></code></em>) function resizes the\n      allocation at <em class=\"parameter\"><code>*ptr</code></em> to be at least\n      <em class=\"parameter\"><code>size</code></em> bytes, sets <em class=\"parameter\"><code>*ptr</code></em> to\n      the base address of the allocation if it moved, and sets\n      <em class=\"parameter\"><code>*rsize</code></em> to the real size of the allocation if\n      <em class=\"parameter\"><code>rsize</code></em> is not <code class=\"constant\">NULL</code>.  If\n      <em class=\"parameter\"><code>extra</code></em> is non-zero, an attempt is made to resize\n      the allocation to be at least <code class=\"code\"><em class=\"parameter\"><code>size</code></em> +\n      <em class=\"parameter\"><code>extra</code></em>)</code> bytes, though inability to allocate\n      the extra byte(s) will not by itself result in failure.  Behavior is\n      undefined if <em class=\"parameter\"><code>size</code></em> is <code class=\"constant\">0</code>, or if\n      <code class=\"code\">(<em class=\"parameter\"><code>size</code></em> +\n      <em class=\"parameter\"><code>extra</code></em> &gt;\n      <code class=\"constant\">SIZE_T_MAX</code>)</code>.</p><p>The <code class=\"function\">sallocm</code>(<em class=\"parameter\"><code></code></em>) function sets\n      <em class=\"parameter\"><code>*rsize</code></em> to the real size of the allocation.</p><p>The <code class=\"function\">dallocm</code>(<em class=\"parameter\"><code></code></em>) function causes the\n      memory referenced by <em class=\"parameter\"><code>ptr</code></em> to be made available for\n      future allocations.</p><p>The <code class=\"function\">nallocm</code>(<em class=\"parameter\"><code></code></em>) function allocates no\n      memory, but it performs the same size computation as the\n      <code class=\"function\">allocm</code>(<em class=\"parameter\"><code></code></em>) function, and if\n      <em class=\"parameter\"><code>rsize</code></em> is not <code class=\"constant\">NULL</code> it sets\n      <em class=\"parameter\"><code>*rsize</code></em> to the real size of the allocation that\n      would result from the equivalent <code class=\"function\">allocm</code>(<em class=\"parameter\"><code></code></em>)\n      function call.  Behavior is undefined if\n      <em class=\"parameter\"><code>size</code></em> is <code class=\"constant\">0</code>.</p></div></div><div class=\"refsect1\" title=\"TUNING\"><a name=\"tuning\"></a><h2>TUNING</h2><p>Once, when the first call is made to one of the memory allocation\n    routines, the allocator initializes its internals based in part on various\n    options that can be specified at compile- or run-time.</p><p>The string pointed to by the global variable\n    <code class=\"varname\">malloc_conf</code>, the &#8220;name&#8221; of the file\n    referenced by the symbolic link named <code class=\"filename\">/etc/malloc.conf</code>, and the value of the\n    environment variable <code class=\"envar\">MALLOC_CONF</code>, will be interpreted, in\n    that order, from left to right as options.</p><p>An options string is a comma-separated list of option:value pairs.\n    There is one key corresponding to each <a class=\"link\" href=\"#opt.abort\">\n    \"<code class=\"mallctl\">opt.*</code>\"\n  </a> mallctl (see the <a class=\"xref\" href=\"#mallctl_namespace\" title=\"MALLCTL NAMESPACE\">MALLCTL NAMESPACE</a> section for options\n    documentation).  For example, <code class=\"literal\">abort:true,narenas:1</code> sets\n    the <a class=\"link\" href=\"#opt.abort\">\n    \"<code class=\"mallctl\">opt.abort</code>\"\n  </a> and <a class=\"link\" href=\"#opt.narenas\">\n    \"<code class=\"mallctl\">opt.narenas</code>\"\n  </a> options.  Some\n    options have boolean values (true/false), others have integer values (base\n    8, 10, or 16, depending on prefix), and yet others have raw string\n    values.</p></div><div class=\"refsect1\" title=\"IMPLEMENTATION NOTES\"><a name=\"implementation_notes\"></a><h2>IMPLEMENTATION NOTES</h2><p>Traditionally, allocators have used\n    <span class=\"citerefentry\"><span class=\"refentrytitle\">sbrk</span>(2)</span> to obtain memory, which is\n    suboptimal for several reasons, including race conditions, increased\n    fragmentation, and artificial limitations on maximum usable memory.  If\n    <code class=\"option\">--enable-dss</code> is specified during configuration, this\n    allocator uses both <span class=\"citerefentry\"><span class=\"refentrytitle\">mmap</span>(2)</span> and\n    <span class=\"citerefentry\"><span class=\"refentrytitle\">sbrk</span>(2)</span>, in that order of preference;\n    otherwise only <span class=\"citerefentry\"><span class=\"refentrytitle\">mmap</span>(2)</span> is used.</p><p>This allocator uses multiple arenas in order to reduce lock\n    contention for threaded programs on multi-processor systems.  This works\n    well with regard to threading scalability, but incurs some costs.  There is\n    a small fixed per-arena overhead, and additionally, arenas manage memory\n    completely independently of each other, which means a small fixed increase\n    in overall memory fragmentation.  These overheads are not generally an\n    issue, given the number of arenas normally used.  Note that using\n    substantially more arenas than the default is not likely to improve\n    performance, mainly due to reduced cache performance.  However, it may make\n    sense to reduce the number of arenas if an application does not make much\n    use of the allocation functions.</p><p>In addition to multiple arenas, unless\n    <code class=\"option\">--disable-tcache</code> is specified during configuration, this\n    allocator supports thread-specific caching for small and large objects, in\n    order to make it possible to completely avoid synchronization for most\n    allocation requests.  Such caching allows very fast allocation in the\n    common case, but it increases memory usage and fragmentation, since a\n    bounded number of objects can remain allocated in each thread cache.</p><p>Memory is conceptually broken into equal-sized chunks, where the\n    chunk size is a power of two that is greater than the page size.  Chunks\n    are always aligned to multiples of the chunk size.  This alignment makes it\n    possible to find metadata for user objects very quickly.</p><p>User objects are broken into three categories according to size:\n    small, large, and huge.  Small objects are smaller than one page.  Large\n    objects are smaller than the chunk size.  Huge objects are a multiple of\n    the chunk size.  Small and large objects are managed by arenas; huge\n    objects are managed separately in a single data structure that is shared by\n    all threads.  Huge objects are used by applications infrequently enough\n    that this single data structure is not a scalability issue.</p><p>Each chunk that is managed by an arena tracks its contents as runs of\n    contiguous pages (unused, backing a set of small objects, or backing one\n    large object).  The combination of chunk alignment and chunk page maps\n    makes it possible to determine all metadata regarding small and large\n    allocations in constant time.</p><p>Small objects are managed in groups by page runs.  Each run maintains\n    a frontier and free list to track which regions are in use.  Allocation\n    requests that are no more than half the quantum (8 or 16, depending on\n    architecture) are rounded up to the nearest power of two that is at least\n    <code class=\"code\">sizeof(<span class=\"type\">double</span>)</code>.  All other small\n    object size classes are multiples of the quantum, spaced such that internal\n    fragmentation is limited to approximately 25% for all but the smallest size\n    classes.  Allocation requests that are larger than the maximum small size\n    class, but small enough to fit in an arena-managed chunk (see the <a class=\"link\" href=\"#opt.lg_chunk\">\n    \"<code class=\"mallctl\">opt.lg_chunk</code>\"\n  </a> option), are\n    rounded up to the nearest run size.  Allocation requests that are too large\n    to fit in an arena-managed chunk are rounded up to the nearest multiple of\n    the chunk size.</p><p>Allocations are packed tightly together, which can be an issue for\n    multi-threaded applications.  If you need to assure that allocations do not\n    suffer from cacheline sharing, round your allocation requests up to the\n    nearest multiple of the cacheline size, or specify cacheline alignment when\n    allocating.</p><p>Assuming 4 MiB chunks, 4 KiB pages, and a 16-byte quantum on a 64-bit\n    system, the size classes in each category are as shown in <a class=\"xref\" href=\"#size_classes\" title=\"Table1.Size classes\">Table 1</a>.</p><div class=\"table\"><a name=\"size_classes\"></a><p class=\"title\"><b>Table1.Size classes</b></p><div class=\"table-contents\"><table summary=\"Size classes\" border=\"1\"><colgroup><col align=\"left\" class=\"c1\"><col align=\"right\" class=\"c2\"><col align=\"left\" class=\"c3\"></colgroup><thead><tr><th align=\"left\">Category</th><th align=\"right\">Spacing</th><th align=\"left\">Size</th></tr></thead><tbody><tr><td rowspan=\"7\" align=\"left\">Small</td><td align=\"right\">lg</td><td align=\"left\">[8]</td></tr><tr><td align=\"right\">16</td><td align=\"left\">[16, 32, 48, ..., 128]</td></tr><tr><td align=\"right\">32</td><td align=\"left\">[160, 192, 224, 256]</td></tr><tr><td align=\"right\">64</td><td align=\"left\">[320, 384, 448, 512]</td></tr><tr><td align=\"right\">128</td><td align=\"left\">[640, 768, 896, 1024]</td></tr><tr><td align=\"right\">256</td><td align=\"left\">[1280, 1536, 1792, 2048]</td></tr><tr><td align=\"right\">512</td><td align=\"left\">[2560, 3072, 3584]</td></tr><tr><td align=\"left\">Large</td><td align=\"right\">4 KiB</td><td align=\"left\">[4 KiB, 8 KiB, 12 KiB, ..., 4072 KiB]</td></tr><tr><td align=\"left\">Huge</td><td align=\"right\">4 MiB</td><td align=\"left\">[4 MiB, 8 MiB, 12 MiB, ...]</td></tr></tbody></table></div></div><br class=\"table-break\"></div><div class=\"refsect1\" title=\"MALLCTL NAMESPACE\"><a name=\"mallctl_namespace\"></a><h2>MALLCTL NAMESPACE</h2><p>The following names are defined in the namespace accessible via the\n    <code class=\"function\">mallctl*</code>(<em class=\"parameter\"><code></code></em>) functions.  Value types are\n    specified in parentheses, their readable/writable statuses are encoded as\n    <code class=\"literal\">rw</code>, <code class=\"literal\">r-</code>, <code class=\"literal\">-w</code>, or\n    <code class=\"literal\">--</code>, and required build configuration flags follow, if\n    any.  A name element encoded as <code class=\"literal\">&lt;i&gt;</code> or\n    <code class=\"literal\">&lt;j&gt;</code> indicates an integer component, where the\n    integer varies from 0 to some upper value that must be determined via\n    introspection.  In the case of \n    \"<code class=\"mallctl\">stats.arenas.&lt;i&gt;.*</code>\"\n  ,\n    <code class=\"literal\">&lt;i&gt;</code> equal to <a class=\"link\" href=\"#arenas.narenas\">\n    \"<code class=\"mallctl\">arenas.narenas</code>\"\n  </a> can be\n    used to access the summation of statistics from all arenas.  Take special\n    note of the <a class=\"link\" href=\"#epoch\">\n    \"<code class=\"mallctl\">epoch</code>\"\n  </a> mallctl,\n    which controls refreshing of cached dynamic statistics.</p><div class=\"variablelist\"><dl><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">version</code>\"\n  \n          (<span class=\"type\">const char *</span>)\n          <code class=\"literal\">r-</code>\n        </span></dt><dd><p>Return the jemalloc version string.</p></dd><dt><a name=\"epoch\"></a><span class=\"term\">\n          \n    \"<code class=\"mallctl\">epoch</code>\"\n  \n          (<span class=\"type\">uint64_t</span>)\n          <code class=\"literal\">rw</code>\n        </span></dt><dd><p>If a value is passed in, refresh the data from which\n        the <code class=\"function\">mallctl*</code>(<em class=\"parameter\"><code></code></em>) functions report values,\n        and increment the epoch.  Return the current epoch.  This is useful for\n        detecting whether another thread caused a refresh.</p></dd><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">config.debug</code>\"\n  \n          (<span class=\"type\">bool</span>)\n          <code class=\"literal\">r-</code>\n        </span></dt><dd><p><code class=\"option\">--enable-debug</code> was specified during\n        build configuration.</p></dd><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">config.dss</code>\"\n  \n          (<span class=\"type\">bool</span>)\n          <code class=\"literal\">r-</code>\n        </span></dt><dd><p><code class=\"option\">--enable-dss</code> was specified during\n        build configuration.</p></dd><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">config.fill</code>\"\n  \n          (<span class=\"type\">bool</span>)\n          <code class=\"literal\">r-</code>\n        </span></dt><dd><p><code class=\"option\">--enable-fill</code> was specified during\n        build configuration.</p></dd><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">config.lazy_lock</code>\"\n  \n          (<span class=\"type\">bool</span>)\n          <code class=\"literal\">r-</code>\n        </span></dt><dd><p><code class=\"option\">--enable-lazy-lock</code> was specified\n        during build configuration.</p></dd><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">config.mremap</code>\"\n  \n          (<span class=\"type\">bool</span>)\n          <code class=\"literal\">r-</code>\n        </span></dt><dd><p><code class=\"option\">--enable-mremap</code> was specified during\n        build configuration.</p></dd><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">config.munmap</code>\"\n  \n          (<span class=\"type\">bool</span>)\n          <code class=\"literal\">r-</code>\n        </span></dt><dd><p><code class=\"option\">--enable-munmap</code> was specified during\n        build configuration.</p></dd><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">config.prof</code>\"\n  \n          (<span class=\"type\">bool</span>)\n          <code class=\"literal\">r-</code>\n        </span></dt><dd><p><code class=\"option\">--enable-prof</code> was specified during\n        build configuration.</p></dd><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">config.prof_libgcc</code>\"\n  \n          (<span class=\"type\">bool</span>)\n          <code class=\"literal\">r-</code>\n        </span></dt><dd><p><code class=\"option\">--disable-prof-libgcc</code> was not\n        specified during build configuration.</p></dd><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">config.prof_libunwind</code>\"\n  \n          (<span class=\"type\">bool</span>)\n          <code class=\"literal\">r-</code>\n        </span></dt><dd><p><code class=\"option\">--enable-prof-libunwind</code> was specified\n        during build configuration.</p></dd><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">config.stats</code>\"\n  \n          (<span class=\"type\">bool</span>)\n          <code class=\"literal\">r-</code>\n        </span></dt><dd><p><code class=\"option\">--enable-stats</code> was specified during\n        build configuration.</p></dd><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">config.tcache</code>\"\n  \n          (<span class=\"type\">bool</span>)\n          <code class=\"literal\">r-</code>\n        </span></dt><dd><p><code class=\"option\">--disable-tcache</code> was not specified\n        during build configuration.</p></dd><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">config.tls</code>\"\n  \n          (<span class=\"type\">bool</span>)\n          <code class=\"literal\">r-</code>\n        </span></dt><dd><p><code class=\"option\">--disable-tls</code> was not specified during\n        build configuration.</p></dd><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">config.utrace</code>\"\n  \n          (<span class=\"type\">bool</span>)\n          <code class=\"literal\">r-</code>\n        </span></dt><dd><p><code class=\"option\">--enable-utrace</code> was specified during\n        build configuration.</p></dd><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">config.valgrind</code>\"\n  \n          (<span class=\"type\">bool</span>)\n          <code class=\"literal\">r-</code>\n        </span></dt><dd><p><code class=\"option\">--enable-valgrind</code> was specified during\n        build configuration.</p></dd><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">config.xmalloc</code>\"\n  \n          (<span class=\"type\">bool</span>)\n          <code class=\"literal\">r-</code>\n        </span></dt><dd><p><code class=\"option\">--enable-xmalloc</code> was specified during\n        build configuration.</p></dd><dt><a name=\"opt.abort\"></a><span class=\"term\">\n          \n    \"<code class=\"mallctl\">opt.abort</code>\"\n  \n          (<span class=\"type\">bool</span>)\n          <code class=\"literal\">r-</code>\n        </span></dt><dd><p>Abort-on-warning enabled/disabled.  If true, most\n        warnings are fatal.  The process will call\n        <span class=\"citerefentry\"><span class=\"refentrytitle\">abort</span>(3)</span> in these cases.  This option is\n        disabled by default unless <code class=\"option\">--enable-debug</code> is\n        specified during configuration, in which case it is enabled by default.\n        </p></dd><dt><a name=\"opt.lg_chunk\"></a><span class=\"term\">\n          \n    \"<code class=\"mallctl\">opt.lg_chunk</code>\"\n  \n          (<span class=\"type\">size_t</span>)\n          <code class=\"literal\">r-</code>\n        </span></dt><dd><p>Virtual memory chunk size (log base 2).  The default\n        chunk size is 4 MiB (2^22).</p></dd><dt><a name=\"opt.dss\"></a><span class=\"term\">\n          \n    \"<code class=\"mallctl\">opt.dss</code>\"\n  \n          (<span class=\"type\">const char *</span>)\n          <code class=\"literal\">r-</code>\n        </span></dt><dd><p>dss (<span class=\"citerefentry\"><span class=\"refentrytitle\">sbrk</span>(2)</span>) allocation precedence as\n        related to <span class=\"citerefentry\"><span class=\"refentrytitle\">mmap</span>(2)</span> allocation.  The following\n        settings are supported: &#8220;disabled&#8221;, &#8220;primary&#8221;,\n        and &#8220;secondary&#8221; (default).</p></dd><dt><a name=\"opt.narenas\"></a><span class=\"term\">\n          \n    \"<code class=\"mallctl\">opt.narenas</code>\"\n  \n          (<span class=\"type\">size_t</span>)\n          <code class=\"literal\">r-</code>\n        </span></dt><dd><p>Maximum number of arenas to use for automatic\n        multiplexing of threads and arenas.  The default is four times the\n        number of CPUs, or one if there is a single CPU.</p></dd><dt><a name=\"opt.lg_dirty_mult\"></a><span class=\"term\">\n          \n    \"<code class=\"mallctl\">opt.lg_dirty_mult</code>\"\n  \n          (<span class=\"type\">ssize_t</span>)\n          <code class=\"literal\">r-</code>\n        </span></dt><dd><p>Per-arena minimum ratio (log base 2) of active to dirty\n        pages.  Some dirty unused pages may be allowed to accumulate, within\n        the limit set by the ratio (or one chunk worth of dirty pages,\n        whichever is greater), before informing the kernel about some of those\n        pages via <span class=\"citerefentry\"><span class=\"refentrytitle\">madvise</span>(2)</span> or a similar system call.  This\n        provides the kernel with sufficient information to recycle dirty pages\n        if physical memory becomes scarce and the pages remain unused.  The\n        default minimum ratio is 8:1 (2^3:1); an option value of -1 will\n        disable dirty page purging.</p></dd><dt><a name=\"opt.stats_print\"></a><span class=\"term\">\n          \n    \"<code class=\"mallctl\">opt.stats_print</code>\"\n  \n          (<span class=\"type\">bool</span>)\n          <code class=\"literal\">r-</code>\n        </span></dt><dd><p>Enable/disable statistics printing at exit.  If\n        enabled, the <code class=\"function\">malloc_stats_print</code>(<em class=\"parameter\"><code></code></em>)\n        function is called at program exit via an\n        <span class=\"citerefentry\"><span class=\"refentrytitle\">atexit</span>(3)</span> function.  If\n        <code class=\"option\">--enable-stats</code> is specified during configuration, this\n        has the potential to cause deadlock for a multi-threaded process that\n        exits while one or more threads are executing in the memory allocation\n        functions.  Therefore, this option should only be used with care; it is\n        primarily intended as a performance tuning aid during application\n        development.  This option is disabled by default.</p></dd><dt><a name=\"opt.junk\"></a><span class=\"term\">\n          \n    \"<code class=\"mallctl\">opt.junk</code>\"\n  \n          (<span class=\"type\">bool</span>)\n          <code class=\"literal\">r-</code>\n          [<code class=\"option\">--enable-fill</code>]\n        </span></dt><dd><p>Junk filling enabled/disabled.  If enabled, each byte\n        of uninitialized allocated memory will be initialized to\n        <code class=\"literal\">0xa5</code>.  All deallocated memory will be initialized to\n        <code class=\"literal\">0x5a</code>.  This is intended for debugging and will\n        impact performance negatively.  This option is disabled by default\n        unless <code class=\"option\">--enable-debug</code> is specified during\n        configuration, in which case it is enabled by default unless running\n        inside <a class=\"ulink\" href=\"http://valgrind.org/\" target=\"_top\">Valgrind</a>.</p></dd><dt><a name=\"opt.quarantine\"></a><span class=\"term\">\n          \n    \"<code class=\"mallctl\">opt.quarantine</code>\"\n  \n          (<span class=\"type\">size_t</span>)\n          <code class=\"literal\">r-</code>\n          [<code class=\"option\">--enable-fill</code>]\n        </span></dt><dd><p>Per thread quarantine size in bytes.  If non-zero, each\n        thread maintains a FIFO object quarantine that stores up to the\n        specified number of bytes of memory.  The quarantined memory is not\n        freed until it is released from quarantine, though it is immediately\n        junk-filled if the <a class=\"link\" href=\"#opt.junk\">\n    \"<code class=\"mallctl\">opt.junk</code>\"\n  </a> option is\n        enabled.  This feature is of particular use in combination with <a class=\"ulink\" href=\"http://valgrind.org/\" target=\"_top\">Valgrind</a>, which can detect attempts\n        to access quarantined objects.  This is intended for debugging and will\n        impact performance negatively.  The default quarantine size is 0 unless\n        running inside Valgrind, in which case the default is 16\n        MiB.</p></dd><dt><a name=\"opt.redzone\"></a><span class=\"term\">\n          \n    \"<code class=\"mallctl\">opt.redzone</code>\"\n  \n          (<span class=\"type\">bool</span>)\n          <code class=\"literal\">r-</code>\n          [<code class=\"option\">--enable-fill</code>]\n        </span></dt><dd><p>Redzones enabled/disabled.  If enabled, small\n        allocations have redzones before and after them.  Furthermore, if the\n        <a class=\"link\" href=\"#opt.junk\">\n    \"<code class=\"mallctl\">opt.junk</code>\"\n  </a> option is\n        enabled, the redzones are checked for corruption during deallocation.\n        However, the primary intended purpose of this feature is to be used in\n        combination with <a class=\"ulink\" href=\"http://valgrind.org/\" target=\"_top\">Valgrind</a>,\n        which needs redzones in order to do effective buffer overflow/underflow\n        detection.  This option is intended for debugging and will impact\n        performance negatively.  This option is disabled by\n        default unless running inside Valgrind.</p></dd><dt><a name=\"opt.zero\"></a><span class=\"term\">\n          \n    \"<code class=\"mallctl\">opt.zero</code>\"\n  \n          (<span class=\"type\">bool</span>)\n          <code class=\"literal\">r-</code>\n          [<code class=\"option\">--enable-fill</code>]\n        </span></dt><dd><p>Zero filling enabled/disabled.  If enabled, each byte\n        of uninitialized allocated memory will be initialized to 0.  Note that\n        this initialization only happens once for each byte, so\n        <code class=\"function\">realloc</code>(<em class=\"parameter\"><code></code></em>) and\n        <code class=\"function\">rallocm</code>(<em class=\"parameter\"><code></code></em>) calls do not zero memory that\n        was previously allocated.  This is intended for debugging and will\n        impact performance negatively.  This option is disabled by default.\n        </p></dd><dt><a name=\"opt.utrace\"></a><span class=\"term\">\n          \n    \"<code class=\"mallctl\">opt.utrace</code>\"\n  \n          (<span class=\"type\">bool</span>)\n          <code class=\"literal\">r-</code>\n          [<code class=\"option\">--enable-utrace</code>]\n        </span></dt><dd><p>Allocation tracing based on\n        <span class=\"citerefentry\"><span class=\"refentrytitle\">utrace</span>(2)</span> enabled/disabled.  This option\n        is disabled by default.</p></dd><dt><a name=\"opt.valgrind\"></a><span class=\"term\">\n          \n    \"<code class=\"mallctl\">opt.valgrind</code>\"\n  \n          (<span class=\"type\">bool</span>)\n          <code class=\"literal\">r-</code>\n          [<code class=\"option\">--enable-valgrind</code>]\n        </span></dt><dd><p><a class=\"ulink\" href=\"http://valgrind.org/\" target=\"_top\">Valgrind</a>\n        support enabled/disabled.  This option is vestigal because jemalloc\n        auto-detects whether it is running inside Valgrind.  This option is\n        disabled by default, unless running inside Valgrind.</p></dd><dt><a name=\"opt.xmalloc\"></a><span class=\"term\">\n          \n    \"<code class=\"mallctl\">opt.xmalloc</code>\"\n  \n          (<span class=\"type\">bool</span>)\n          <code class=\"literal\">r-</code>\n          [<code class=\"option\">--enable-xmalloc</code>]\n        </span></dt><dd><p>Abort-on-out-of-memory enabled/disabled.  If enabled,\n        rather than returning failure for any allocation function, display a\n        diagnostic message on <code class=\"constant\">STDERR_FILENO</code> and cause the\n        program to drop core (using\n        <span class=\"citerefentry\"><span class=\"refentrytitle\">abort</span>(3)</span>).  If an application is\n        designed to depend on this behavior, set the option at compile time by\n        including the following in the source code:\n        </p><pre class=\"programlisting\">\nmalloc_conf = \"xmalloc:true\";</pre><p>\n        This option is disabled by default.</p></dd><dt><a name=\"opt.tcache\"></a><span class=\"term\">\n          \n    \"<code class=\"mallctl\">opt.tcache</code>\"\n  \n          (<span class=\"type\">bool</span>)\n          <code class=\"literal\">r-</code>\n          [<code class=\"option\">--enable-tcache</code>]\n        </span></dt><dd><p>Thread-specific caching enabled/disabled.  When there\n        are multiple threads, each thread uses a thread-specific cache for\n        objects up to a certain size.  Thread-specific caching allows many\n        allocations to be satisfied without performing any thread\n        synchronization, at the cost of increased memory use.  See the\n        <a class=\"link\" href=\"#opt.lg_tcache_max\">\n    \"<code class=\"mallctl\">opt.lg_tcache_max</code>\"\n  </a>\n        option for related tuning information.  This option is enabled by\n        default unless running inside <a class=\"ulink\" href=\"http://valgrind.org/\" target=\"_top\">Valgrind</a>.</p></dd><dt><a name=\"opt.lg_tcache_max\"></a><span class=\"term\">\n          \n    \"<code class=\"mallctl\">opt.lg_tcache_max</code>\"\n  \n          (<span class=\"type\">size_t</span>)\n          <code class=\"literal\">r-</code>\n          [<code class=\"option\">--enable-tcache</code>]\n        </span></dt><dd><p>Maximum size class (log base 2) to cache in the\n        thread-specific cache.  At a minimum, all small size classes are\n        cached, and at a maximum all large size classes are cached.  The\n        default maximum is 32 KiB (2^15).</p></dd><dt><a name=\"opt.prof\"></a><span class=\"term\">\n          \n    \"<code class=\"mallctl\">opt.prof</code>\"\n  \n          (<span class=\"type\">bool</span>)\n          <code class=\"literal\">r-</code>\n          [<code class=\"option\">--enable-prof</code>]\n        </span></dt><dd><p>Memory profiling enabled/disabled.  If enabled, profile\n        memory allocation activity.  See the <a class=\"link\" href=\"#opt.prof_active\">\n    \"<code class=\"mallctl\">opt.prof_active</code>\"\n  </a>\n        option for on-the-fly activation/deactivation.  See the <a class=\"link\" href=\"#opt.lg_prof_sample\">\n    \"<code class=\"mallctl\">opt.lg_prof_sample</code>\"\n  </a>\n        option for probabilistic sampling control.  See the <a class=\"link\" href=\"#opt.prof_accum\">\n    \"<code class=\"mallctl\">opt.prof_accum</code>\"\n  </a>\n        option for control of cumulative sample reporting.  See the <a class=\"link\" href=\"#opt.lg_prof_interval\">\n    \"<code class=\"mallctl\">opt.lg_prof_interval</code>\"\n  </a>\n        option for information on interval-triggered profile dumping, the <a class=\"link\" href=\"#opt.prof_gdump\">\n    \"<code class=\"mallctl\">opt.prof_gdump</code>\"\n  </a>\n        option for information on high-water-triggered profile dumping, and the\n        <a class=\"link\" href=\"#opt.prof_final\">\n    \"<code class=\"mallctl\">opt.prof_final</code>\"\n  </a>\n        option for final profile dumping.  Profile output is compatible with\n        the included <span class=\"command\"><strong>pprof</strong></span> Perl script, which originates\n        from the <a class=\"ulink\" href=\"http://code.google.com/p/gperftools/\" target=\"_top\">gperftools\n        package</a>.</p></dd><dt><a name=\"opt.prof_prefix\"></a><span class=\"term\">\n          \n    \"<code class=\"mallctl\">opt.prof_prefix</code>\"\n  \n          (<span class=\"type\">const char *</span>)\n          <code class=\"literal\">r-</code>\n          [<code class=\"option\">--enable-prof</code>]\n        </span></dt><dd><p>Filename prefix for profile dumps.  If the prefix is\n        set to the empty string, no automatic dumps will occur; this is\n        primarily useful for disabling the automatic final heap dump (which\n        also disables leak reporting, if enabled).  The default prefix is\n        <code class=\"filename\">jeprof</code>.</p></dd><dt><a name=\"opt.prof_active\"></a><span class=\"term\">\n          \n    \"<code class=\"mallctl\">opt.prof_active</code>\"\n  \n          (<span class=\"type\">bool</span>)\n          <code class=\"literal\">r-</code>\n          [<code class=\"option\">--enable-prof</code>]\n        </span></dt><dd><p>Profiling activated/deactivated.  This is a secondary\n        control mechanism that makes it possible to start the application with\n        profiling enabled (see the <a class=\"link\" href=\"#opt.prof\">\n    \"<code class=\"mallctl\">opt.prof</code>\"\n  </a> option) but\n        inactive, then toggle profiling at any time during program execution\n        with the <a class=\"link\" href=\"#prof.active\">\n    \"<code class=\"mallctl\">prof.active</code>\"\n  </a> mallctl.\n        This option is enabled by default.</p></dd><dt><a name=\"opt.lg_prof_sample\"></a><span class=\"term\">\n          \n    \"<code class=\"mallctl\">opt.lg_prof_sample</code>\"\n  \n          (<span class=\"type\">ssize_t</span>)\n          <code class=\"literal\">r-</code>\n          [<code class=\"option\">--enable-prof</code>]\n        </span></dt><dd><p>Average interval (log base 2) between allocation\n        samples, as measured in bytes of allocation activity.  Increasing the\n        sampling interval decreases profile fidelity, but also decreases the\n        computational overhead.  The default sample interval is 512 KiB (2^19\n        B).</p></dd><dt><a name=\"opt.prof_accum\"></a><span class=\"term\">\n          \n    \"<code class=\"mallctl\">opt.prof_accum</code>\"\n  \n          (<span class=\"type\">bool</span>)\n          <code class=\"literal\">r-</code>\n          [<code class=\"option\">--enable-prof</code>]\n        </span></dt><dd><p>Reporting of cumulative object/byte counts in profile\n        dumps enabled/disabled.  If this option is enabled, every unique\n        backtrace must be stored for the duration of execution.  Depending on\n        the application, this can impose a large memory overhead, and the\n        cumulative counts are not always of interest.  This option is disabled\n        by default.</p></dd><dt><a name=\"opt.lg_prof_interval\"></a><span class=\"term\">\n          \n    \"<code class=\"mallctl\">opt.lg_prof_interval</code>\"\n  \n          (<span class=\"type\">ssize_t</span>)\n          <code class=\"literal\">r-</code>\n          [<code class=\"option\">--enable-prof</code>]\n        </span></dt><dd><p>Average interval (log base 2) between memory profile\n        dumps, as measured in bytes of allocation activity.  The actual\n        interval between dumps may be sporadic because decentralized allocation\n        counters are used to avoid synchronization bottlenecks.  Profiles are\n        dumped to files named according to the pattern\n        <code class=\"filename\">&lt;prefix&gt;.&lt;pid&gt;.&lt;seq&gt;.i&lt;iseq&gt;.heap</code>,\n        where <code class=\"literal\">&lt;prefix&gt;</code> is controlled by the\n        <a class=\"link\" href=\"#opt.prof_prefix\">\n    \"<code class=\"mallctl\">opt.prof_prefix</code>\"\n  </a>\n        option.  By default, interval-triggered profile dumping is disabled\n        (encoded as -1).\n        </p></dd><dt><a name=\"opt.prof_gdump\"></a><span class=\"term\">\n          \n    \"<code class=\"mallctl\">opt.prof_gdump</code>\"\n  \n          (<span class=\"type\">bool</span>)\n          <code class=\"literal\">r-</code>\n          [<code class=\"option\">--enable-prof</code>]\n        </span></dt><dd><p>Trigger a memory profile dump every time the total\n        virtual memory exceeds the previous maximum.  Profiles are dumped to\n        files named according to the pattern\n        <code class=\"filename\">&lt;prefix&gt;.&lt;pid&gt;.&lt;seq&gt;.u&lt;useq&gt;.heap</code>,\n        where <code class=\"literal\">&lt;prefix&gt;</code> is controlled by the <a class=\"link\" href=\"#opt.prof_prefix\">\n    \"<code class=\"mallctl\">opt.prof_prefix</code>\"\n  </a>\n        option.  This option is disabled by default.</p></dd><dt><a name=\"opt.prof_final\"></a><span class=\"term\">\n          \n    \"<code class=\"mallctl\">opt.prof_final</code>\"\n  \n          (<span class=\"type\">bool</span>)\n          <code class=\"literal\">r-</code>\n          [<code class=\"option\">--enable-prof</code>]\n        </span></dt><dd><p>Use an\n        <span class=\"citerefentry\"><span class=\"refentrytitle\">atexit</span>(3)</span> function to dump final memory\n        usage to a file named according to the pattern\n        <code class=\"filename\">&lt;prefix&gt;.&lt;pid&gt;.&lt;seq&gt;.f.heap</code>,\n        where <code class=\"literal\">&lt;prefix&gt;</code> is controlled by the <a class=\"link\" href=\"#opt.prof_prefix\">\n    \"<code class=\"mallctl\">opt.prof_prefix</code>\"\n  </a>\n        option.  This option is enabled by default.</p></dd><dt><a name=\"opt.prof_leak\"></a><span class=\"term\">\n          \n    \"<code class=\"mallctl\">opt.prof_leak</code>\"\n  \n          (<span class=\"type\">bool</span>)\n          <code class=\"literal\">r-</code>\n          [<code class=\"option\">--enable-prof</code>]\n        </span></dt><dd><p>Leak reporting enabled/disabled.  If enabled, use an\n        <span class=\"citerefentry\"><span class=\"refentrytitle\">atexit</span>(3)</span> function to report memory leaks\n        detected by allocation sampling.  See the\n        <a class=\"link\" href=\"#opt.prof\">\n    \"<code class=\"mallctl\">opt.prof</code>\"\n  </a> option for\n        information on analyzing heap profile output.  This option is disabled\n        by default.</p></dd><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">thread.arena</code>\"\n  \n          (<span class=\"type\">unsigned</span>)\n          <code class=\"literal\">rw</code>\n        </span></dt><dd><p>Get or set the arena associated with the calling\n        thread.  If the specified arena was not initialized beforehand (see the\n        <a class=\"link\" href=\"#arenas.initialized\">\n    \"<code class=\"mallctl\">arenas.initialized</code>\"\n  </a>\n        mallctl), it will be automatically initialized as a side effect of\n        calling this interface.</p></dd><dt><a name=\"thread.allocated\"></a><span class=\"term\">\n          \n    \"<code class=\"mallctl\">thread.allocated</code>\"\n  \n          (<span class=\"type\">uint64_t</span>)\n          <code class=\"literal\">r-</code>\n          [<code class=\"option\">--enable-stats</code>]\n        </span></dt><dd><p>Get the total number of bytes ever allocated by the\n        calling thread.  This counter has the potential to wrap around; it is\n        up to the application to appropriately interpret the counter in such\n        cases.</p></dd><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">thread.allocatedp</code>\"\n  \n          (<span class=\"type\">uint64_t *</span>)\n          <code class=\"literal\">r-</code>\n          [<code class=\"option\">--enable-stats</code>]\n        </span></dt><dd><p>Get a pointer to the the value that is returned by the\n        <a class=\"link\" href=\"#thread.allocated\">\n    \"<code class=\"mallctl\">thread.allocated</code>\"\n  </a>\n        mallctl.  This is useful for avoiding the overhead of repeated\n        <code class=\"function\">mallctl*</code>(<em class=\"parameter\"><code></code></em>) calls.</p></dd><dt><a name=\"thread.deallocated\"></a><span class=\"term\">\n          \n    \"<code class=\"mallctl\">thread.deallocated</code>\"\n  \n          (<span class=\"type\">uint64_t</span>)\n          <code class=\"literal\">r-</code>\n          [<code class=\"option\">--enable-stats</code>]\n        </span></dt><dd><p>Get the total number of bytes ever deallocated by the\n        calling thread.  This counter has the potential to wrap around; it is\n        up to the application to appropriately interpret the counter in such\n        cases.</p></dd><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">thread.deallocatedp</code>\"\n  \n          (<span class=\"type\">uint64_t *</span>)\n          <code class=\"literal\">r-</code>\n          [<code class=\"option\">--enable-stats</code>]\n        </span></dt><dd><p>Get a pointer to the the value that is returned by the\n        <a class=\"link\" href=\"#thread.deallocated\">\n    \"<code class=\"mallctl\">thread.deallocated</code>\"\n  </a>\n        mallctl.  This is useful for avoiding the overhead of repeated\n        <code class=\"function\">mallctl*</code>(<em class=\"parameter\"><code></code></em>) calls.</p></dd><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">thread.tcache.enabled</code>\"\n  \n          (<span class=\"type\">bool</span>)\n          <code class=\"literal\">rw</code>\n          [<code class=\"option\">--enable-tcache</code>]\n        </span></dt><dd><p>Enable/disable calling thread's tcache.  The tcache is\n        implicitly flushed as a side effect of becoming\n        disabled (see \n    \"<code class=\"mallctl\">thread.tcache.flush</code>\"\n  ).\n        </p></dd><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">thread.tcache.flush</code>\"\n  \n          (<span class=\"type\">void</span>)\n          <code class=\"literal\">--</code>\n          [<code class=\"option\">--enable-tcache</code>]\n        </span></dt><dd><p>Flush calling thread's tcache.  This interface releases\n        all cached objects and internal data structures associated with the\n        calling thread's thread-specific cache.  Ordinarily, this interface\n        need not be called, since automatic periodic incremental garbage\n        collection occurs, and the thread cache is automatically discarded when\n        a thread exits.  However, garbage collection is triggered by allocation\n        activity, so it is possible for a thread that stops\n        allocating/deallocating to retain its cache indefinitely, in which case\n        the developer may find manual flushing useful.</p></dd><dt><a name=\"arena.i.purge\"></a><span class=\"term\">\n          \n    \"<code class=\"mallctl\">arena.&lt;i&gt;.purge</code>\"\n  \n          (<span class=\"type\">unsigned</span>)\n          <code class=\"literal\">--</code>\n        </span></dt><dd><p>Purge unused dirty pages for arena &lt;i&gt;, or for\n        all arenas if &lt;i&gt; equals <a class=\"link\" href=\"#arenas.narenas\">\n    \"<code class=\"mallctl\">arenas.narenas</code>\"\n  </a>.\n        </p></dd><dt><a name=\"arena.i.dss\"></a><span class=\"term\">\n          \n    \"<code class=\"mallctl\">arena.&lt;i&gt;.dss</code>\"\n  \n          (<span class=\"type\">const char *</span>)\n          <code class=\"literal\">rw</code>\n        </span></dt><dd><p>Set the precedence of dss allocation as related to mmap\n        allocation for arena &lt;i&gt;, or for all arenas if &lt;i&gt; equals\n        <a class=\"link\" href=\"#arenas.narenas\">\n    \"<code class=\"mallctl\">arenas.narenas</code>\"\n  </a>.  See\n        <a class=\"link\" href=\"#opt.dss\">\n    \"<code class=\"mallctl\">opt.dss</code>\"\n  </a> for supported\n        settings.\n        </p></dd><dt><a name=\"arenas.narenas\"></a><span class=\"term\">\n          \n    \"<code class=\"mallctl\">arenas.narenas</code>\"\n  \n          (<span class=\"type\">unsigned</span>)\n          <code class=\"literal\">r-</code>\n        </span></dt><dd><p>Current limit on number of arenas.</p></dd><dt><a name=\"arenas.initialized\"></a><span class=\"term\">\n          \n    \"<code class=\"mallctl\">arenas.initialized</code>\"\n  \n          (<span class=\"type\">bool *</span>)\n          <code class=\"literal\">r-</code>\n        </span></dt><dd><p>An array of <a class=\"link\" href=\"#arenas.narenas\">\n    \"<code class=\"mallctl\">arenas.narenas</code>\"\n  </a>\n        booleans.  Each boolean indicates whether the corresponding arena is\n        initialized.</p></dd><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">arenas.quantum</code>\"\n  \n          (<span class=\"type\">size_t</span>)\n          <code class=\"literal\">r-</code>\n        </span></dt><dd><p>Quantum size.</p></dd><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">arenas.page</code>\"\n  \n          (<span class=\"type\">size_t</span>)\n          <code class=\"literal\">r-</code>\n        </span></dt><dd><p>Page size.</p></dd><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">arenas.tcache_max</code>\"\n  \n          (<span class=\"type\">size_t</span>)\n          <code class=\"literal\">r-</code>\n          [<code class=\"option\">--enable-tcache</code>]\n        </span></dt><dd><p>Maximum thread-cached size class.</p></dd><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">arenas.nbins</code>\"\n  \n          (<span class=\"type\">unsigned</span>)\n          <code class=\"literal\">r-</code>\n        </span></dt><dd><p>Number of bin size classes.</p></dd><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">arenas.nhbins</code>\"\n  \n          (<span class=\"type\">unsigned</span>)\n          <code class=\"literal\">r-</code>\n          [<code class=\"option\">--enable-tcache</code>]\n        </span></dt><dd><p>Total number of thread cache bin size\n        classes.</p></dd><dt><a name=\"arenas.bin.i.size\"></a><span class=\"term\">\n          \n    \"<code class=\"mallctl\">arenas.bin.&lt;i&gt;.size</code>\"\n  \n          (<span class=\"type\">size_t</span>)\n          <code class=\"literal\">r-</code>\n        </span></dt><dd><p>Maximum size supported by size class.</p></dd><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">arenas.bin.&lt;i&gt;.nregs</code>\"\n  \n          (<span class=\"type\">uint32_t</span>)\n          <code class=\"literal\">r-</code>\n        </span></dt><dd><p>Number of regions per page run.</p></dd><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">arenas.bin.&lt;i&gt;.run_size</code>\"\n  \n          (<span class=\"type\">size_t</span>)\n          <code class=\"literal\">r-</code>\n        </span></dt><dd><p>Number of bytes per page run.</p></dd><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">arenas.nlruns</code>\"\n  \n          (<span class=\"type\">size_t</span>)\n          <code class=\"literal\">r-</code>\n        </span></dt><dd><p>Total number of large size classes.</p></dd><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">arenas.lrun.&lt;i&gt;.size</code>\"\n  \n          (<span class=\"type\">size_t</span>)\n          <code class=\"literal\">r-</code>\n        </span></dt><dd><p>Maximum size supported by this large size\n        class.</p></dd><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">arenas.purge</code>\"\n  \n          (<span class=\"type\">unsigned</span>)\n          <code class=\"literal\">-w</code>\n        </span></dt><dd><p>Purge unused dirty pages for the specified arena, or\n        for all arenas if none is specified.</p></dd><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">arenas.extend</code>\"\n  \n          (<span class=\"type\">unsigned</span>)\n          <code class=\"literal\">r-</code>\n        </span></dt><dd><p>Extend the array of arenas by appending a new arena,\n        and returning the new arena index.</p></dd><dt><a name=\"prof.active\"></a><span class=\"term\">\n          \n    \"<code class=\"mallctl\">prof.active</code>\"\n  \n          (<span class=\"type\">bool</span>)\n          <code class=\"literal\">rw</code>\n          [<code class=\"option\">--enable-prof</code>]\n        </span></dt><dd><p>Control whether sampling is currently active.  See the\n        <a class=\"link\" href=\"#opt.prof_active\">\n    \"<code class=\"mallctl\">opt.prof_active</code>\"\n  </a>\n        option for additional information.\n        </p></dd><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">prof.dump</code>\"\n  \n          (<span class=\"type\">const char *</span>)\n          <code class=\"literal\">-w</code>\n          [<code class=\"option\">--enable-prof</code>]\n        </span></dt><dd><p>Dump a memory profile to the specified file, or if NULL\n        is specified, to a file according to the pattern\n        <code class=\"filename\">&lt;prefix&gt;.&lt;pid&gt;.&lt;seq&gt;.m&lt;mseq&gt;.heap</code>,\n        where <code class=\"literal\">&lt;prefix&gt;</code> is controlled by the\n        <a class=\"link\" href=\"#opt.prof_prefix\">\n    \"<code class=\"mallctl\">opt.prof_prefix</code>\"\n  </a>\n        option.</p></dd><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">prof.interval</code>\"\n  \n          (<span class=\"type\">uint64_t</span>)\n          <code class=\"literal\">r-</code>\n          [<code class=\"option\">--enable-prof</code>]\n        </span></dt><dd><p>Average number of bytes allocated between\n        inverval-based profile dumps.  See the\n        <a class=\"link\" href=\"#opt.lg_prof_interval\">\n    \"<code class=\"mallctl\">opt.lg_prof_interval</code>\"\n  </a>\n        option for additional information.</p></dd><dt><a name=\"stats.cactive\"></a><span class=\"term\">\n          \n    \"<code class=\"mallctl\">stats.cactive</code>\"\n  \n          (<span class=\"type\">size_t *</span>)\n          <code class=\"literal\">r-</code>\n          [<code class=\"option\">--enable-stats</code>]\n        </span></dt><dd><p>Pointer to a counter that contains an approximate count\n        of the current number of bytes in active pages.  The estimate may be\n        high, but never low, because each arena rounds up to the nearest\n        multiple of the chunk size when computing its contribution to the\n        counter.  Note that the <a class=\"link\" href=\"#epoch\">\n    \"<code class=\"mallctl\">epoch</code>\"\n  </a> mallctl has no bearing\n        on this counter.  Furthermore, counter consistency is maintained via\n        atomic operations, so it is necessary to use an atomic operation in\n        order to guarantee a consistent read when dereferencing the pointer.\n        </p></dd><dt><a name=\"stats.allocated\"></a><span class=\"term\">\n          \n    \"<code class=\"mallctl\">stats.allocated</code>\"\n  \n          (<span class=\"type\">size_t</span>)\n          <code class=\"literal\">r-</code>\n          [<code class=\"option\">--enable-stats</code>]\n        </span></dt><dd><p>Total number of bytes allocated by the\n        application.</p></dd><dt><a name=\"stats.active\"></a><span class=\"term\">\n          \n    \"<code class=\"mallctl\">stats.active</code>\"\n  \n          (<span class=\"type\">size_t</span>)\n          <code class=\"literal\">r-</code>\n          [<code class=\"option\">--enable-stats</code>]\n        </span></dt><dd><p>Total number of bytes in active pages allocated by the\n        application.  This is a multiple of the page size, and greater than or\n        equal to <a class=\"link\" href=\"#stats.allocated\">\n    \"<code class=\"mallctl\">stats.allocated</code>\"\n  </a>.\n        This does not include <a class=\"link\" href=\"#stats.arenas.i.pdirty\">\n        \n    \"<code class=\"mallctl\">stats.arenas.&lt;i&gt;.pdirty</code>\"\n  </a> and pages\n        entirely devoted to allocator metadata.</p></dd><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">stats.mapped</code>\"\n  \n          (<span class=\"type\">size_t</span>)\n          <code class=\"literal\">r-</code>\n          [<code class=\"option\">--enable-stats</code>]\n        </span></dt><dd><p>Total number of bytes in chunks mapped on behalf of the\n        application.  This is a multiple of the chunk size, and is at least as\n        large as <a class=\"link\" href=\"#stats.active\">\n    \"<code class=\"mallctl\">stats.active</code>\"\n  </a>.  This\n        does not include inactive chunks.</p></dd><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">stats.chunks.current</code>\"\n  \n          (<span class=\"type\">size_t</span>)\n          <code class=\"literal\">r-</code>\n          [<code class=\"option\">--enable-stats</code>]\n        </span></dt><dd><p>Total number of chunks actively mapped on behalf of the\n        application.  This does not include inactive chunks.\n        </p></dd><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">stats.chunks.total</code>\"\n  \n          (<span class=\"type\">uint64_t</span>)\n          <code class=\"literal\">r-</code>\n          [<code class=\"option\">--enable-stats</code>]\n        </span></dt><dd><p>Cumulative number of chunks allocated.</p></dd><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">stats.chunks.high</code>\"\n  \n          (<span class=\"type\">size_t</span>)\n          <code class=\"literal\">r-</code>\n          [<code class=\"option\">--enable-stats</code>]\n        </span></dt><dd><p>Maximum number of active chunks at any time thus far.\n        </p></dd><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">stats.huge.allocated</code>\"\n  \n          (<span class=\"type\">size_t</span>)\n          <code class=\"literal\">r-</code>\n          [<code class=\"option\">--enable-stats</code>]\n        </span></dt><dd><p>Number of bytes currently allocated by huge objects.\n        </p></dd><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">stats.huge.nmalloc</code>\"\n  \n          (<span class=\"type\">uint64_t</span>)\n          <code class=\"literal\">r-</code>\n          [<code class=\"option\">--enable-stats</code>]\n        </span></dt><dd><p>Cumulative number of huge allocation requests.\n        </p></dd><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">stats.huge.ndalloc</code>\"\n  \n          (<span class=\"type\">uint64_t</span>)\n          <code class=\"literal\">r-</code>\n          [<code class=\"option\">--enable-stats</code>]\n        </span></dt><dd><p>Cumulative number of huge deallocation requests.\n        </p></dd><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">stats.arenas.&lt;i&gt;.dss</code>\"\n  \n          (<span class=\"type\">const char *</span>)\n          <code class=\"literal\">r-</code>\n        </span></dt><dd><p>dss (<span class=\"citerefentry\"><span class=\"refentrytitle\">sbrk</span>(2)</span>) allocation precedence as\n        related to <span class=\"citerefentry\"><span class=\"refentrytitle\">mmap</span>(2)</span> allocation.  See <a class=\"link\" href=\"#opt.dss\">\n    \"<code class=\"mallctl\">opt.dss</code>\"\n  </a> for details.\n        </p></dd><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">stats.arenas.&lt;i&gt;.nthreads</code>\"\n  \n          (<span class=\"type\">unsigned</span>)\n          <code class=\"literal\">r-</code>\n        </span></dt><dd><p>Number of threads currently assigned to\n        arena.</p></dd><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">stats.arenas.&lt;i&gt;.pactive</code>\"\n  \n          (<span class=\"type\">size_t</span>)\n          <code class=\"literal\">r-</code>\n        </span></dt><dd><p>Number of pages in active runs.</p></dd><dt><a name=\"stats.arenas.i.pdirty\"></a><span class=\"term\">\n          \n    \"<code class=\"mallctl\">stats.arenas.&lt;i&gt;.pdirty</code>\"\n  \n          (<span class=\"type\">size_t</span>)\n          <code class=\"literal\">r-</code>\n        </span></dt><dd><p>Number of pages within unused runs that are potentially\n        dirty, and for which <code class=\"function\">madvise</code>(<em class=\"parameter\"><code>...</code></em>, \n        <em class=\"parameter\"><code><code class=\"constant\">MADV_DONTNEED</code></code></em>) or\n        similar has not been called.</p></dd><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">stats.arenas.&lt;i&gt;.mapped</code>\"\n  \n          (<span class=\"type\">size_t</span>)\n          <code class=\"literal\">r-</code>\n          [<code class=\"option\">--enable-stats</code>]\n        </span></dt><dd><p>Number of mapped bytes.</p></dd><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">stats.arenas.&lt;i&gt;.npurge</code>\"\n  \n          (<span class=\"type\">uint64_t</span>)\n          <code class=\"literal\">r-</code>\n          [<code class=\"option\">--enable-stats</code>]\n        </span></dt><dd><p>Number of dirty page purge sweeps performed.\n        </p></dd><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">stats.arenas.&lt;i&gt;.nmadvise</code>\"\n  \n          (<span class=\"type\">uint64_t</span>)\n          <code class=\"literal\">r-</code>\n          [<code class=\"option\">--enable-stats</code>]\n        </span></dt><dd><p>Number of <code class=\"function\">madvise</code>(<em class=\"parameter\"><code>...</code></em>, \n        <em class=\"parameter\"><code><code class=\"constant\">MADV_DONTNEED</code></code></em>) or\n        similar calls made to purge dirty pages.</p></dd><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">stats.arenas.&lt;i&gt;.npurged</code>\"\n  \n          (<span class=\"type\">uint64_t</span>)\n          <code class=\"literal\">r-</code>\n          [<code class=\"option\">--enable-stats</code>]\n        </span></dt><dd><p>Number of pages purged.</p></dd><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">stats.arenas.&lt;i&gt;.small.allocated</code>\"\n  \n          (<span class=\"type\">size_t</span>)\n          <code class=\"literal\">r-</code>\n          [<code class=\"option\">--enable-stats</code>]\n        </span></dt><dd><p>Number of bytes currently allocated by small objects.\n        </p></dd><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">stats.arenas.&lt;i&gt;.small.nmalloc</code>\"\n  \n          (<span class=\"type\">uint64_t</span>)\n          <code class=\"literal\">r-</code>\n          [<code class=\"option\">--enable-stats</code>]\n        </span></dt><dd><p>Cumulative number of allocation requests served by\n        small bins.</p></dd><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">stats.arenas.&lt;i&gt;.small.ndalloc</code>\"\n  \n          (<span class=\"type\">uint64_t</span>)\n          <code class=\"literal\">r-</code>\n          [<code class=\"option\">--enable-stats</code>]\n        </span></dt><dd><p>Cumulative number of small objects returned to bins.\n        </p></dd><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">stats.arenas.&lt;i&gt;.small.nrequests</code>\"\n  \n          (<span class=\"type\">uint64_t</span>)\n          <code class=\"literal\">r-</code>\n          [<code class=\"option\">--enable-stats</code>]\n        </span></dt><dd><p>Cumulative number of small allocation requests.\n        </p></dd><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">stats.arenas.&lt;i&gt;.large.allocated</code>\"\n  \n          (<span class=\"type\">size_t</span>)\n          <code class=\"literal\">r-</code>\n          [<code class=\"option\">--enable-stats</code>]\n        </span></dt><dd><p>Number of bytes currently allocated by large objects.\n        </p></dd><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">stats.arenas.&lt;i&gt;.large.nmalloc</code>\"\n  \n          (<span class=\"type\">uint64_t</span>)\n          <code class=\"literal\">r-</code>\n          [<code class=\"option\">--enable-stats</code>]\n        </span></dt><dd><p>Cumulative number of large allocation requests served\n        directly by the arena.</p></dd><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">stats.arenas.&lt;i&gt;.large.ndalloc</code>\"\n  \n          (<span class=\"type\">uint64_t</span>)\n          <code class=\"literal\">r-</code>\n          [<code class=\"option\">--enable-stats</code>]\n        </span></dt><dd><p>Cumulative number of large deallocation requests served\n        directly by the arena.</p></dd><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">stats.arenas.&lt;i&gt;.large.nrequests</code>\"\n  \n          (<span class=\"type\">uint64_t</span>)\n          <code class=\"literal\">r-</code>\n          [<code class=\"option\">--enable-stats</code>]\n        </span></dt><dd><p>Cumulative number of large allocation requests.\n        </p></dd><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">stats.arenas.&lt;i&gt;.bins.&lt;j&gt;.allocated</code>\"\n  \n          (<span class=\"type\">size_t</span>)\n          <code class=\"literal\">r-</code>\n          [<code class=\"option\">--enable-stats</code>]\n        </span></dt><dd><p>Current number of bytes allocated by\n        bin.</p></dd><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">stats.arenas.&lt;i&gt;.bins.&lt;j&gt;.nmalloc</code>\"\n  \n          (<span class=\"type\">uint64_t</span>)\n          <code class=\"literal\">r-</code>\n          [<code class=\"option\">--enable-stats</code>]\n        </span></dt><dd><p>Cumulative number of allocations served by bin.\n        </p></dd><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">stats.arenas.&lt;i&gt;.bins.&lt;j&gt;.ndalloc</code>\"\n  \n          (<span class=\"type\">uint64_t</span>)\n          <code class=\"literal\">r-</code>\n          [<code class=\"option\">--enable-stats</code>]\n        </span></dt><dd><p>Cumulative number of allocations returned to bin.\n        </p></dd><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">stats.arenas.&lt;i&gt;.bins.&lt;j&gt;.nrequests</code>\"\n  \n          (<span class=\"type\">uint64_t</span>)\n          <code class=\"literal\">r-</code>\n          [<code class=\"option\">--enable-stats</code>]\n        </span></dt><dd><p>Cumulative number of allocation\n        requests.</p></dd><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">stats.arenas.&lt;i&gt;.bins.&lt;j&gt;.nfills</code>\"\n  \n          (<span class=\"type\">uint64_t</span>)\n          <code class=\"literal\">r-</code>\n          [<code class=\"option\">--enable-stats</code> <code class=\"option\">--enable-tcache</code>]\n        </span></dt><dd><p>Cumulative number of tcache fills.</p></dd><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">stats.arenas.&lt;i&gt;.bins.&lt;j&gt;.nflushes</code>\"\n  \n          (<span class=\"type\">uint64_t</span>)\n          <code class=\"literal\">r-</code>\n          [<code class=\"option\">--enable-stats</code> <code class=\"option\">--enable-tcache</code>]\n        </span></dt><dd><p>Cumulative number of tcache flushes.</p></dd><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">stats.arenas.&lt;i&gt;.bins.&lt;j&gt;.nruns</code>\"\n  \n          (<span class=\"type\">uint64_t</span>)\n          <code class=\"literal\">r-</code>\n          [<code class=\"option\">--enable-stats</code>]\n        </span></dt><dd><p>Cumulative number of runs created.</p></dd><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">stats.arenas.&lt;i&gt;.bins.&lt;j&gt;.nreruns</code>\"\n  \n          (<span class=\"type\">uint64_t</span>)\n          <code class=\"literal\">r-</code>\n          [<code class=\"option\">--enable-stats</code>]\n        </span></dt><dd><p>Cumulative number of times the current run from which\n        to allocate changed.</p></dd><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">stats.arenas.&lt;i&gt;.bins.&lt;j&gt;.curruns</code>\"\n  \n          (<span class=\"type\">size_t</span>)\n          <code class=\"literal\">r-</code>\n          [<code class=\"option\">--enable-stats</code>]\n        </span></dt><dd><p>Current number of runs.</p></dd><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">stats.arenas.&lt;i&gt;.lruns.&lt;j&gt;.nmalloc</code>\"\n  \n          (<span class=\"type\">uint64_t</span>)\n          <code class=\"literal\">r-</code>\n          [<code class=\"option\">--enable-stats</code>]\n        </span></dt><dd><p>Cumulative number of allocation requests for this size\n        class served directly by the arena.</p></dd><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">stats.arenas.&lt;i&gt;.lruns.&lt;j&gt;.ndalloc</code>\"\n  \n          (<span class=\"type\">uint64_t</span>)\n          <code class=\"literal\">r-</code>\n          [<code class=\"option\">--enable-stats</code>]\n        </span></dt><dd><p>Cumulative number of deallocation requests for this\n        size class served directly by the arena.</p></dd><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">stats.arenas.&lt;i&gt;.lruns.&lt;j&gt;.nrequests</code>\"\n  \n          (<span class=\"type\">uint64_t</span>)\n          <code class=\"literal\">r-</code>\n          [<code class=\"option\">--enable-stats</code>]\n        </span></dt><dd><p>Cumulative number of allocation requests for this size\n        class.</p></dd><dt><span class=\"term\">\n          \n    \"<code class=\"mallctl\">stats.arenas.&lt;i&gt;.lruns.&lt;j&gt;.curruns</code>\"\n  \n          (<span class=\"type\">size_t</span>)\n          <code class=\"literal\">r-</code>\n          [<code class=\"option\">--enable-stats</code>]\n        </span></dt><dd><p>Current number of runs for this size class.\n        </p></dd></dl></div></div><div class=\"refsect1\" title=\"DEBUGGING MALLOC PROBLEMS\"><a name=\"debugging_malloc_problems\"></a><h2>DEBUGGING MALLOC PROBLEMS</h2><p>When debugging, it is a good idea to configure/build jemalloc with\n    the <code class=\"option\">--enable-debug</code> and <code class=\"option\">--enable-fill</code>\n    options, and recompile the program with suitable options and symbols for\n    debugger support.  When so configured, jemalloc incorporates a wide variety\n    of run-time assertions that catch application errors such as double-free,\n    write-after-free, etc.</p><p>Programs often accidentally depend on &#8220;uninitialized&#8221;\n    memory actually being filled with zero bytes.  Junk filling\n    (see the <a class=\"link\" href=\"#opt.junk\">\n    \"<code class=\"mallctl\">opt.junk</code>\"\n  </a>\n    option) tends to expose such bugs in the form of obviously incorrect\n    results and/or coredumps.  Conversely, zero\n    filling (see the <a class=\"link\" href=\"#opt.zero\">\n    \"<code class=\"mallctl\">opt.zero</code>\"\n  </a> option) eliminates\n    the symptoms of such bugs.  Between these two options, it is usually\n    possible to quickly detect, diagnose, and eliminate such bugs.</p><p>This implementation does not provide much detail about the problems\n    it detects, because the performance impact for storing such information\n    would be prohibitive.  However, jemalloc does integrate with the most\n    excellent <a class=\"ulink\" href=\"http://valgrind.org/\" target=\"_top\">Valgrind</a> tool if the\n    <code class=\"option\">--enable-valgrind</code> configuration option is enabled.</p></div><div class=\"refsect1\" title=\"DIAGNOSTIC MESSAGES\"><a name=\"diagnostic_messages\"></a><h2>DIAGNOSTIC MESSAGES</h2><p>If any of the memory allocation/deallocation functions detect an\n    error or warning condition, a message will be printed to file descriptor\n    <code class=\"constant\">STDERR_FILENO</code>.  Errors will result in the process\n    dumping core.  If the <a class=\"link\" href=\"#opt.abort\">\n    \"<code class=\"mallctl\">opt.abort</code>\"\n  </a> option is set, most\n    warnings are treated as errors.</p><p>The <code class=\"varname\">malloc_message</code> variable allows the programmer\n    to override the function which emits the text strings forming the errors\n    and warnings if for some reason the <code class=\"constant\">STDERR_FILENO</code> file\n    descriptor is not suitable for this.\n    <code class=\"function\">malloc_message</code>(<em class=\"parameter\"><code></code></em>) takes the\n    <em class=\"parameter\"><code>cbopaque</code></em> pointer argument that is\n    <code class=\"constant\">NULL</code> unless overridden by the arguments in a call to\n    <code class=\"function\">malloc_stats_print</code>(<em class=\"parameter\"><code></code></em>), followed by a string\n    pointer.  Please note that doing anything which tries to allocate memory in\n    this function is likely to result in a crash or deadlock.</p><p>All messages are prefixed by\n    &#8220;<code class=\"computeroutput\">&lt;jemalloc&gt;: </code>&#8221;.</p></div><div class=\"refsect1\" title=\"RETURN VALUES\"><a name=\"return_values\"></a><h2>RETURN VALUES</h2><div class=\"refsect2\" title=\"Standard API\"><a name=\"id286954473\"></a><h3>Standard API</h3><p>The <code class=\"function\">malloc</code>(<em class=\"parameter\"><code></code></em>) and\n      <code class=\"function\">calloc</code>(<em class=\"parameter\"><code></code></em>) functions return a pointer to the\n      allocated memory if successful; otherwise a <code class=\"constant\">NULL</code>\n      pointer is returned and <code class=\"varname\">errno</code> is set to\n      <span class=\"errorname\">ENOMEM</span>.</p><p>The <code class=\"function\">posix_memalign</code>(<em class=\"parameter\"><code></code></em>) function\n      returns the value 0 if successful; otherwise it returns an error value.\n      The <code class=\"function\">posix_memalign</code>(<em class=\"parameter\"><code></code></em>) function will fail\n      if:\n        </p><div class=\"variablelist\"><dl><dt><span class=\"term\"><span class=\"errorname\">EINVAL</span></span></dt><dd><p>The <em class=\"parameter\"><code>alignment</code></em> parameter is\n            not a power of 2 at least as large as\n            <code class=\"code\">sizeof(<span class=\"type\">void *</span>)</code>.\n            </p></dd><dt><span class=\"term\"><span class=\"errorname\">ENOMEM</span></span></dt><dd><p>Memory allocation error.</p></dd></dl></div><p>\n      </p><p>The <code class=\"function\">aligned_alloc</code>(<em class=\"parameter\"><code></code></em>) function returns\n      a pointer to the allocated memory if successful; otherwise a\n      <code class=\"constant\">NULL</code> pointer is returned and\n      <code class=\"varname\">errno</code> is set.  The\n      <code class=\"function\">aligned_alloc</code>(<em class=\"parameter\"><code></code></em>) function will fail if:\n        </p><div class=\"variablelist\"><dl><dt><span class=\"term\"><span class=\"errorname\">EINVAL</span></span></dt><dd><p>The <em class=\"parameter\"><code>alignment</code></em> parameter is\n            not a power of 2.\n            </p></dd><dt><span class=\"term\"><span class=\"errorname\">ENOMEM</span></span></dt><dd><p>Memory allocation error.</p></dd></dl></div><p>\n      </p><p>The <code class=\"function\">realloc</code>(<em class=\"parameter\"><code></code></em>) function returns a\n      pointer, possibly identical to <em class=\"parameter\"><code>ptr</code></em>, to the\n      allocated memory if successful; otherwise a <code class=\"constant\">NULL</code>\n      pointer is returned, and <code class=\"varname\">errno</code> is set to\n      <span class=\"errorname\">ENOMEM</span> if the error was the result of an\n      allocation failure.  The <code class=\"function\">realloc</code>(<em class=\"parameter\"><code></code></em>)\n      function always leaves the original buffer intact when an error occurs.\n      </p><p>The <code class=\"function\">free</code>(<em class=\"parameter\"><code></code></em>) function returns no\n      value.</p></div><div class=\"refsect2\" title=\"Non-standard API\"><a name=\"id286954690\"></a><h3>Non-standard API</h3><p>The <code class=\"function\">malloc_usable_size</code>(<em class=\"parameter\"><code></code></em>) function\n      returns the usable size of the allocation pointed to by\n      <em class=\"parameter\"><code>ptr</code></em>.  </p><p>The <code class=\"function\">mallctl</code>(<em class=\"parameter\"><code></code></em>),\n      <code class=\"function\">mallctlnametomib</code>(<em class=\"parameter\"><code></code></em>), and\n      <code class=\"function\">mallctlbymib</code>(<em class=\"parameter\"><code></code></em>) functions return 0 on\n      success; otherwise they return an error value.  The functions will fail\n      if:\n        </p><div class=\"variablelist\"><dl><dt><span class=\"term\"><span class=\"errorname\">EINVAL</span></span></dt><dd><p><em class=\"parameter\"><code>newp</code></em> is not\n            <code class=\"constant\">NULL</code>, and <em class=\"parameter\"><code>newlen</code></em> is too\n            large or too small.  Alternatively, <em class=\"parameter\"><code>*oldlenp</code></em>\n            is too large or too small; in this case as much data as possible\n            are read despite the error.</p></dd><dt><span class=\"term\"><span class=\"errorname\">ENOMEM</span></span></dt><dd><p><em class=\"parameter\"><code>*oldlenp</code></em> is too short to\n            hold the requested value.</p></dd><dt><span class=\"term\"><span class=\"errorname\">ENOENT</span></span></dt><dd><p><em class=\"parameter\"><code>name</code></em> or\n            <em class=\"parameter\"><code>mib</code></em> specifies an unknown/invalid\n            value.</p></dd><dt><span class=\"term\"><span class=\"errorname\">EPERM</span></span></dt><dd><p>Attempt to read or write void value, or attempt to\n            write read-only value.</p></dd><dt><span class=\"term\"><span class=\"errorname\">EAGAIN</span></span></dt><dd><p>A memory allocation failure\n            occurred.</p></dd><dt><span class=\"term\"><span class=\"errorname\">EFAULT</span></span></dt><dd><p>An interface with side effects failed in some way\n            not directly related to <code class=\"function\">mallctl*</code>(<em class=\"parameter\"><code></code></em>)\n            read/write processing.</p></dd></dl></div><p>\n      </p></div><div class=\"refsect2\" title=\"Experimental API\"><a name=\"id286954842\"></a><h3>Experimental API</h3><p>The <code class=\"function\">allocm</code>(<em class=\"parameter\"><code></code></em>),\n      <code class=\"function\">rallocm</code>(<em class=\"parameter\"><code></code></em>),\n      <code class=\"function\">sallocm</code>(<em class=\"parameter\"><code></code></em>),\n      <code class=\"function\">dallocm</code>(<em class=\"parameter\"><code></code></em>), and\n      <code class=\"function\">nallocm</code>(<em class=\"parameter\"><code></code></em>) functions return\n      <code class=\"constant\">ALLOCM_SUCCESS</code> on success; otherwise they return an\n      error value.  The <code class=\"function\">allocm</code>(<em class=\"parameter\"><code></code></em>),\n      <code class=\"function\">rallocm</code>(<em class=\"parameter\"><code></code></em>), and\n      <code class=\"function\">nallocm</code>(<em class=\"parameter\"><code></code></em>) functions will fail if:\n        </p><div class=\"variablelist\"><dl><dt><span class=\"term\"><span class=\"errorname\">ALLOCM_ERR_OOM</span></span></dt><dd><p>Out of memory.  Insufficient contiguous memory was\n            available to service the allocation request.  The\n            <code class=\"function\">allocm</code>(<em class=\"parameter\"><code></code></em>) function additionally sets\n            <em class=\"parameter\"><code>*ptr</code></em> to <code class=\"constant\">NULL</code>, whereas\n            the <code class=\"function\">rallocm</code>(<em class=\"parameter\"><code></code></em>) function leaves\n            <code class=\"constant\">*ptr</code> unmodified.</p></dd></dl></div><p>\n      The <code class=\"function\">rallocm</code>(<em class=\"parameter\"><code></code></em>) function will also\n      fail if:\n        </p><div class=\"variablelist\"><dl><dt><span class=\"term\"><span class=\"errorname\">ALLOCM_ERR_NOT_MOVED</span></span></dt><dd><p><code class=\"constant\">ALLOCM_NO_MOVE</code> was specified,\n            but the reallocation request could not be serviced without moving\n            the object.</p></dd></dl></div><p>\n      </p></div></div><div class=\"refsect1\" title=\"ENVIRONMENT\"><a name=\"environment\"></a><h2>ENVIRONMENT</h2><p>The following environment variable affects the execution of the\n    allocation functions:\n      </p><div class=\"variablelist\"><dl><dt><span class=\"term\"><code class=\"envar\">MALLOC_CONF</code></span></dt><dd><p>If the environment variable\n          <code class=\"envar\">MALLOC_CONF</code> is set, the characters it contains\n          will be interpreted as options.</p></dd></dl></div><p>\n    </p></div><div class=\"refsect1\" title=\"EXAMPLES\"><a name=\"examples\"></a><h2>EXAMPLES</h2><p>To dump core whenever a problem occurs:\n      </p><pre class=\"screen\">ln -s 'abort:true' /etc/malloc.conf</pre><p>\n    </p><p>To specify in the source a chunk size that is 16 MiB:\n      </p><pre class=\"programlisting\">\nmalloc_conf = \"lg_chunk:24\";</pre></div><div class=\"refsect1\" title=\"SEE ALSO\"><a name=\"see_also\"></a><h2>SEE ALSO</h2><p><span class=\"citerefentry\"><span class=\"refentrytitle\">madvise</span>(2)</span>,\n    <span class=\"citerefentry\"><span class=\"refentrytitle\">mmap</span>(2)</span>,\n    <span class=\"citerefentry\"><span class=\"refentrytitle\">sbrk</span>(2)</span>,\n    <span class=\"citerefentry\"><span class=\"refentrytitle\">utrace</span>(2)</span>,\n    <span class=\"citerefentry\"><span class=\"refentrytitle\">alloca</span>(3)</span>,\n    <span class=\"citerefentry\"><span class=\"refentrytitle\">atexit</span>(3)</span>,\n    <span class=\"citerefentry\"><span class=\"refentrytitle\">getpagesize</span>(3)</span></p></div><div class=\"refsect1\" title=\"STANDARDS\"><a name=\"standards\"></a><h2>STANDARDS</h2><p>The <code class=\"function\">malloc</code>(<em class=\"parameter\"><code></code></em>),\n    <code class=\"function\">calloc</code>(<em class=\"parameter\"><code></code></em>),\n    <code class=\"function\">realloc</code>(<em class=\"parameter\"><code></code></em>), and\n    <code class=\"function\">free</code>(<em class=\"parameter\"><code></code></em>) functions conform to ISO/IEC\n    9899:1990 (&#8220;ISO C90&#8221;).</p><p>The <code class=\"function\">posix_memalign</code>(<em class=\"parameter\"><code></code></em>) function conforms\n    to IEEE Std 1003.1-2001 (&#8220;POSIX.1&#8221;).</p></div></div></body></html>\n"
  },
  {
    "path": "deps/jemalloc/doc/jemalloc.xml.in",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<?xml-stylesheet type=\"text/xsl\"\n        href=\"http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl\"?>\n<!DOCTYPE refentry PUBLIC \"-//OASIS//DTD DocBook XML V4.4//EN\"\n        \"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd\" [\n]>\n\n<refentry>\n  <refentryinfo>\n    <title>User Manual</title>\n    <productname>jemalloc</productname>\n    <releaseinfo role=\"version\">@jemalloc_version@</releaseinfo>\n    <authorgroup>\n      <author>\n        <firstname>Jason</firstname>\n        <surname>Evans</surname>\n        <personblurb>Author</personblurb>\n      </author>\n    </authorgroup>\n  </refentryinfo>\n  <refmeta>\n    <refentrytitle>JEMALLOC</refentrytitle>\n    <manvolnum>3</manvolnum>\n  </refmeta>\n  <refnamediv>\n    <refdescriptor>jemalloc</refdescriptor>\n    <refname>jemalloc</refname>\n    <!-- Each refname causes a man page file to be created.  Only if this were\n         the system malloc(3) implementation would these files be appropriate.\n    <refname>malloc</refname>\n    <refname>calloc</refname>\n    <refname>posix_memalign</refname>\n    <refname>aligned_alloc</refname>\n    <refname>realloc</refname>\n    <refname>free</refname>\n    <refname>malloc_usable_size</refname>\n    <refname>malloc_stats_print</refname>\n    <refname>mallctl</refname>\n    <refname>mallctlnametomib</refname>\n    <refname>mallctlbymib</refname>\n    <refname>allocm</refname>\n    <refname>rallocm</refname>\n    <refname>sallocm</refname>\n    <refname>dallocm</refname>\n    <refname>nallocm</refname>\n    -->\n    <refpurpose>general purpose memory allocation functions</refpurpose>\n  </refnamediv>\n  <refsect1 id=\"library\">\n    <title>LIBRARY</title>\n    <para>This manual describes jemalloc @jemalloc_version@.  More information\n    can be found at the <ulink\n    url=\"http://www.canonware.com/jemalloc/\">jemalloc website</ulink>.</para>\n  </refsect1>\n  <refsynopsisdiv>\n    <title>SYNOPSIS</title>\n    <funcsynopsis>\n      <funcsynopsisinfo>#include &lt;<filename class=\"headerfile\">stdlib.h</filename>&gt;\n#include &lt;<filename class=\"headerfile\">jemalloc/jemalloc.h</filename>&gt;</funcsynopsisinfo>\n      <refsect2>\n        <title>Standard API</title>\n        <funcprototype>\n          <funcdef>void *<function>malloc</function></funcdef>\n          <paramdef>size_t <parameter>size</parameter></paramdef>\n        </funcprototype>\n        <funcprototype>\n          <funcdef>void *<function>calloc</function></funcdef>\n          <paramdef>size_t <parameter>number</parameter></paramdef>\n          <paramdef>size_t <parameter>size</parameter></paramdef>\n        </funcprototype>\n        <funcprototype>\n          <funcdef>int <function>posix_memalign</function></funcdef>\n          <paramdef>void **<parameter>ptr</parameter></paramdef>\n          <paramdef>size_t <parameter>alignment</parameter></paramdef>\n          <paramdef>size_t <parameter>size</parameter></paramdef>\n        </funcprototype>\n        <funcprototype>\n          <funcdef>void *<function>aligned_alloc</function></funcdef>\n          <paramdef>size_t <parameter>alignment</parameter></paramdef>\n          <paramdef>size_t <parameter>size</parameter></paramdef>\n        </funcprototype>\n        <funcprototype>\n          <funcdef>void *<function>realloc</function></funcdef>\n          <paramdef>void *<parameter>ptr</parameter></paramdef>\n          <paramdef>size_t <parameter>size</parameter></paramdef>\n        </funcprototype>\n        <funcprototype>\n          <funcdef>void <function>free</function></funcdef>\n          <paramdef>void *<parameter>ptr</parameter></paramdef>\n        </funcprototype>\n      </refsect2>\n      <refsect2>\n        <title>Non-standard API</title>\n        <funcprototype>\n          <funcdef>size_t <function>malloc_usable_size</function></funcdef>\n          <paramdef>const void *<parameter>ptr</parameter></paramdef>\n        </funcprototype>\n        <funcprototype>\n          <funcdef>void <function>malloc_stats_print</function></funcdef>\n          <paramdef>void <parameter>(*write_cb)</parameter>\n            <funcparams>void *, const char *</funcparams>\n          </paramdef>\n          <paramdef>void *<parameter>cbopaque</parameter></paramdef>\n          <paramdef>const char *<parameter>opts</parameter></paramdef>\n        </funcprototype>\n        <funcprototype>\n          <funcdef>int <function>mallctl</function></funcdef>\n          <paramdef>const char *<parameter>name</parameter></paramdef>\n          <paramdef>void *<parameter>oldp</parameter></paramdef>\n          <paramdef>size_t *<parameter>oldlenp</parameter></paramdef>\n          <paramdef>void *<parameter>newp</parameter></paramdef>\n          <paramdef>size_t <parameter>newlen</parameter></paramdef>\n        </funcprototype>\n        <funcprototype>\n          <funcdef>int <function>mallctlnametomib</function></funcdef>\n          <paramdef>const char *<parameter>name</parameter></paramdef>\n          <paramdef>size_t *<parameter>mibp</parameter></paramdef>\n          <paramdef>size_t *<parameter>miblenp</parameter></paramdef>\n        </funcprototype>\n        <funcprototype>\n          <funcdef>int <function>mallctlbymib</function></funcdef>\n          <paramdef>const size_t *<parameter>mib</parameter></paramdef>\n          <paramdef>size_t <parameter>miblen</parameter></paramdef>\n          <paramdef>void *<parameter>oldp</parameter></paramdef>\n          <paramdef>size_t *<parameter>oldlenp</parameter></paramdef>\n          <paramdef>void *<parameter>newp</parameter></paramdef>\n          <paramdef>size_t <parameter>newlen</parameter></paramdef>\n        </funcprototype>\n        <funcprototype>\n          <funcdef>void <function>(*malloc_message)</function></funcdef>\n          <paramdef>void *<parameter>cbopaque</parameter></paramdef>\n          <paramdef>const char *<parameter>s</parameter></paramdef>\n        </funcprototype>\n        <para><type>const char *</type><varname>malloc_conf</varname>;</para>\n      </refsect2>\n      <refsect2>\n      <title>Experimental API</title>\n        <funcprototype>\n          <funcdef>int <function>allocm</function></funcdef>\n          <paramdef>void **<parameter>ptr</parameter></paramdef>\n          <paramdef>size_t *<parameter>rsize</parameter></paramdef>\n          <paramdef>size_t <parameter>size</parameter></paramdef>\n          <paramdef>int <parameter>flags</parameter></paramdef>\n        </funcprototype>\n        <funcprototype>\n          <funcdef>int <function>rallocm</function></funcdef>\n          <paramdef>void **<parameter>ptr</parameter></paramdef>\n          <paramdef>size_t *<parameter>rsize</parameter></paramdef>\n          <paramdef>size_t <parameter>size</parameter></paramdef>\n          <paramdef>size_t <parameter>extra</parameter></paramdef>\n          <paramdef>int <parameter>flags</parameter></paramdef>\n        </funcprototype>\n        <funcprototype>\n          <funcdef>int <function>sallocm</function></funcdef>\n          <paramdef>const void *<parameter>ptr</parameter></paramdef>\n          <paramdef>size_t *<parameter>rsize</parameter></paramdef>\n          <paramdef>int <parameter>flags</parameter></paramdef>\n        </funcprototype>\n        <funcprototype>\n          <funcdef>int <function>dallocm</function></funcdef>\n          <paramdef>void *<parameter>ptr</parameter></paramdef>\n          <paramdef>int <parameter>flags</parameter></paramdef>\n        </funcprototype>\n        <funcprototype>\n          <funcdef>int <function>nallocm</function></funcdef>\n          <paramdef>size_t *<parameter>rsize</parameter></paramdef>\n          <paramdef>size_t <parameter>size</parameter></paramdef>\n          <paramdef>int <parameter>flags</parameter></paramdef>\n        </funcprototype>\n      </refsect2>\n    </funcsynopsis>\n  </refsynopsisdiv>\n  <refsect1 id=\"description\">\n    <title>DESCRIPTION</title>\n    <refsect2>\n      <title>Standard API</title>\n\n      <para>The <function>malloc<parameter/></function> function allocates\n      <parameter>size</parameter> bytes of uninitialized memory.  The allocated\n      space is suitably aligned (after possible pointer coercion) for storage\n      of any type of object.</para>\n\n      <para>The <function>calloc<parameter/></function> function allocates\n      space for <parameter>number</parameter> objects, each\n      <parameter>size</parameter> bytes in length.  The result is identical to\n      calling <function>malloc<parameter/></function> with an argument of\n      <parameter>number</parameter> * <parameter>size</parameter>, with the\n      exception that the allocated memory is explicitly initialized to zero\n      bytes.</para>\n\n      <para>The <function>posix_memalign<parameter/></function> function\n      allocates <parameter>size</parameter> bytes of memory such that the\n      allocation's base address is an even multiple of\n      <parameter>alignment</parameter>, and returns the allocation in the value\n      pointed to by <parameter>ptr</parameter>.  The requested\n      <parameter>alignment</parameter> must be a power of 2 at least as large\n      as <code language=\"C\">sizeof(<type>void *</type>)</code>.</para>\n\n      <para>The <function>aligned_alloc<parameter/></function> function\n      allocates <parameter>size</parameter> bytes of memory such that the\n      allocation's base address is an even multiple of\n      <parameter>alignment</parameter>.  The requested\n      <parameter>alignment</parameter> must be a power of 2.  Behavior is\n      undefined if <parameter>size</parameter> is not an integral multiple of\n      <parameter>alignment</parameter>.</para>\n\n      <para>The <function>realloc<parameter/></function> function changes the\n      size of the previously allocated memory referenced by\n      <parameter>ptr</parameter> to <parameter>size</parameter> bytes.  The\n      contents of the memory are unchanged up to the lesser of the new and old\n      sizes.  If the new size is larger, the contents of the newly allocated\n      portion of the memory are undefined.  Upon success, the memory referenced\n      by <parameter>ptr</parameter> is freed and a pointer to the newly\n      allocated memory is returned.  Note that\n      <function>realloc<parameter/></function> may move the memory allocation,\n      resulting in a different return value than <parameter>ptr</parameter>.\n      If <parameter>ptr</parameter> is <constant>NULL</constant>, the\n      <function>realloc<parameter/></function> function behaves identically to\n      <function>malloc<parameter/></function> for the specified size.</para>\n\n      <para>The <function>free<parameter/></function> function causes the\n      allocated memory referenced by <parameter>ptr</parameter> to be made\n      available for future allocations.  If <parameter>ptr</parameter> is\n      <constant>NULL</constant>, no action occurs.</para>\n    </refsect2>\n    <refsect2>\n      <title>Non-standard API</title>\n\n      <para>The <function>malloc_usable_size<parameter/></function> function\n      returns the usable size of the allocation pointed to by\n      <parameter>ptr</parameter>.  The return value may be larger than the size\n      that was requested during allocation.  The\n      <function>malloc_usable_size<parameter/></function> function is not a\n      mechanism for in-place <function>realloc<parameter/></function>; rather\n      it is provided solely as a tool for introspection purposes.  Any\n      discrepancy between the requested allocation size and the size reported\n      by <function>malloc_usable_size<parameter/></function> should not be\n      depended on, since such behavior is entirely implementation-dependent.\n      </para>\n\n      <para>The <function>malloc_stats_print<parameter/></function> function\n      writes human-readable summary statistics via the\n      <parameter>write_cb</parameter> callback function pointer and\n      <parameter>cbopaque</parameter> data passed to\n      <parameter>write_cb</parameter>, or\n      <function>malloc_message<parameter/></function> if\n      <parameter>write_cb</parameter> is <constant>NULL</constant>.  This\n      function can be called repeatedly.  General information that never\n      changes during execution can be omitted by specifying \"g\" as a character\n      within the <parameter>opts</parameter> string.  Note that\n      <function>malloc_message<parameter/></function> uses the\n      <function>mallctl*<parameter/></function> functions internally, so\n      inconsistent statistics can be reported if multiple threads use these\n      functions simultaneously.  If <option>--enable-stats</option> is\n      specified during configuration, &ldquo;m&rdquo; and &ldquo;a&rdquo; can\n      be specified to omit merged arena and per arena statistics, respectively;\n      &ldquo;b&rdquo; and &ldquo;l&rdquo; can be specified to omit per size\n      class statistics for bins and large objects, respectively.  Unrecognized\n      characters are silently ignored.  Note that thread caching may prevent\n      some statistics from being completely up to date, since extra locking\n      would be required to merge counters that track thread cache operations.\n      </para>\n\n      <para>The <function>mallctl<parameter/></function> function provides a\n      general interface for introspecting the memory allocator, as well as\n      setting modifiable parameters and triggering actions.  The\n      period-separated <parameter>name</parameter> argument specifies a\n      location in a tree-structured namespace; see the <xref\n      linkend=\"mallctl_namespace\" xrefstyle=\"template:%t\"/> section for\n      documentation on the tree contents.  To read a value, pass a pointer via\n      <parameter>oldp</parameter> to adequate space to contain the value, and a\n      pointer to its length via <parameter>oldlenp</parameter>; otherwise pass\n      <constant>NULL</constant> and <constant>NULL</constant>.  Similarly, to\n      write a value, pass a pointer to the value via\n      <parameter>newp</parameter>, and its length via\n      <parameter>newlen</parameter>; otherwise pass <constant>NULL</constant>\n      and <constant>0</constant>.</para>\n\n      <para>The <function>mallctlnametomib<parameter/></function> function\n      provides a way to avoid repeated name lookups for applications that\n      repeatedly query the same portion of the namespace, by translating a name\n      to a &ldquo;Management Information Base&rdquo; (MIB) that can be passed\n      repeatedly to <function>mallctlbymib<parameter/></function>.  Upon\n      successful return from <function>mallctlnametomib<parameter/></function>,\n      <parameter>mibp</parameter> contains an array of\n      <parameter>*miblenp</parameter> integers, where\n      <parameter>*miblenp</parameter> is the lesser of the number of components\n      in <parameter>name</parameter> and the input value of\n      <parameter>*miblenp</parameter>.  Thus it is possible to pass a\n      <parameter>*miblenp</parameter> that is smaller than the number of\n      period-separated name components, which results in a partial MIB that can\n      be used as the basis for constructing a complete MIB.  For name\n      components that are integers (e.g. the 2 in\n      <link\n      linkend=\"arenas.bin.i.size\"><mallctl>arenas.bin.2.size</mallctl></link>),\n      the corresponding MIB component will always be that integer.  Therefore,\n      it is legitimate to construct code like the following: <programlisting\n      language=\"C\"><![CDATA[\nunsigned nbins, i;\n\nint mib[4];\nsize_t len, miblen;\n\nlen = sizeof(nbins);\nmallctl(\"arenas.nbins\", &nbins, &len, NULL, 0);\n\nmiblen = 4;\nmallnametomib(\"arenas.bin.0.size\", mib, &miblen);\nfor (i = 0; i < nbins; i++) {\n\tsize_t bin_size;\n\n\tmib[2] = i;\n\tlen = sizeof(bin_size);\n\tmallctlbymib(mib, miblen, &bin_size, &len, NULL, 0);\n\t/* Do something with bin_size... */\n}]]></programlisting></para>\n    </refsect2>\n    <refsect2>\n      <title>Experimental API</title>\n      <para>The experimental API is subject to change or removal without regard\n      for backward compatibility.  If <option>--disable-experimental</option>\n      is specified during configuration, the experimental API is\n      omitted.</para>\n\n      <para>The <function>allocm<parameter/></function>,\n      <function>rallocm<parameter/></function>,\n      <function>sallocm<parameter/></function>,\n      <function>dallocm<parameter/></function>, and\n      <function>nallocm<parameter/></function> functions all have a\n      <parameter>flags</parameter> argument that can be used to specify\n      options.  The functions only check the options that are contextually\n      relevant.  Use bitwise or (<code language=\"C\">|</code>) operations to\n      specify one or more of the following:\n        <variablelist>\n          <varlistentry>\n            <term><constant>ALLOCM_LG_ALIGN(<parameter>la</parameter>)\n            </constant></term>\n\n            <listitem><para>Align the memory allocation to start at an address\n            that is a multiple of <code language=\"C\">(1 &lt;&lt;\n            <parameter>la</parameter>)</code>.  This macro does not validate\n            that <parameter>la</parameter> is within the valid\n            range.</para></listitem>\n          </varlistentry>\n          <varlistentry>\n            <term><constant>ALLOCM_ALIGN(<parameter>a</parameter>)\n            </constant></term>\n\n            <listitem><para>Align the memory allocation to start at an address\n            that is a multiple of <parameter>a</parameter>, where\n            <parameter>a</parameter> is a power of two.  This macro does not\n            validate that <parameter>a</parameter> is a power of 2.\n            </para></listitem>\n          </varlistentry>\n          <varlistentry>\n            <term><constant>ALLOCM_ZERO</constant></term>\n\n            <listitem><para>Initialize newly allocated memory to contain zero\n            bytes.  In the growing reallocation case, the real size prior to\n            reallocation defines the boundary between untouched bytes and those\n            that are initialized to contain zero bytes.  If this option is\n            absent, newly allocated memory is uninitialized.</para></listitem>\n          </varlistentry>\n          <varlistentry>\n            <term><constant>ALLOCM_NO_MOVE</constant></term>\n\n            <listitem><para>For reallocation, fail rather than moving the\n            object.  This constraint can apply to both growth and\n            shrinkage.</para></listitem>\n          </varlistentry>\n          <varlistentry>\n            <term><constant>ALLOCM_ARENA(<parameter>a</parameter>)\n            </constant></term>\n\n            <listitem><para>Use the arena specified by the index\n            <parameter>a</parameter>.  This macro does not validate that\n            <parameter>a</parameter> specifies an arena in the valid\n            range.</para></listitem>\n          </varlistentry>\n        </variablelist>\n      </para>\n\n      <para>The <function>allocm<parameter/></function> function allocates at\n      least <parameter>size</parameter> bytes of memory, sets\n      <parameter>*ptr</parameter> to the base address of the allocation, and\n      sets <parameter>*rsize</parameter> to the real size of the allocation if\n      <parameter>rsize</parameter> is not <constant>NULL</constant>.  Behavior\n      is undefined if <parameter>size</parameter> is\n      <constant>0</constant>.</para>\n\n      <para>The <function>rallocm<parameter/></function> function resizes the\n      allocation at <parameter>*ptr</parameter> to be at least\n      <parameter>size</parameter> bytes, sets <parameter>*ptr</parameter> to\n      the base address of the allocation if it moved, and sets\n      <parameter>*rsize</parameter> to the real size of the allocation if\n      <parameter>rsize</parameter> is not <constant>NULL</constant>.  If\n      <parameter>extra</parameter> is non-zero, an attempt is made to resize\n      the allocation to be at least <code\n      language=\"C\"><parameter>size</parameter> +\n      <parameter>extra</parameter>)</code> bytes, though inability to allocate\n      the extra byte(s) will not by itself result in failure.  Behavior is\n      undefined if <parameter>size</parameter> is <constant>0</constant>, or if\n      <code language=\"C\">(<parameter>size</parameter> +\n      <parameter>extra</parameter> &gt;\n      <constant>SIZE_T_MAX</constant>)</code>.</para>\n\n      <para>The <function>sallocm<parameter/></function> function sets\n      <parameter>*rsize</parameter> to the real size of the allocation.</para>\n\n      <para>The <function>dallocm<parameter/></function> function causes the\n      memory referenced by <parameter>ptr</parameter> to be made available for\n      future allocations.</para>\n\n      <para>The <function>nallocm<parameter/></function> function allocates no\n      memory, but it performs the same size computation as the\n      <function>allocm<parameter/></function> function, and if\n      <parameter>rsize</parameter> is not <constant>NULL</constant> it sets\n      <parameter>*rsize</parameter> to the real size of the allocation that\n      would result from the equivalent <function>allocm<parameter/></function>\n      function call.  Behavior is undefined if\n      <parameter>size</parameter> is <constant>0</constant>.</para>\n    </refsect2>\n  </refsect1>\n  <refsect1 id=\"tuning\">\n    <title>TUNING</title>\n    <para>Once, when the first call is made to one of the memory allocation\n    routines, the allocator initializes its internals based in part on various\n    options that can be specified at compile- or run-time.</para>\n\n    <para>The string pointed to by the global variable\n    <varname>malloc_conf</varname>, the &ldquo;name&rdquo; of the file\n    referenced by the symbolic link named <filename\n    class=\"symlink\">/etc/malloc.conf</filename>, and the value of the\n    environment variable <envar>MALLOC_CONF</envar>, will be interpreted, in\n    that order, from left to right as options.</para>\n\n    <para>An options string is a comma-separated list of option:value pairs.\n    There is one key corresponding to each <link\n    linkend=\"opt.abort\"><mallctl>opt.*</mallctl></link> mallctl (see the <xref\n    linkend=\"mallctl_namespace\" xrefstyle=\"template:%t\"/> section for options\n    documentation).  For example, <literal>abort:true,narenas:1</literal> sets\n    the <link linkend=\"opt.abort\"><mallctl>opt.abort</mallctl></link> and <link\n    linkend=\"opt.narenas\"><mallctl>opt.narenas</mallctl></link> options.  Some\n    options have boolean values (true/false), others have integer values (base\n    8, 10, or 16, depending on prefix), and yet others have raw string\n    values.</para>\n  </refsect1>\n  <refsect1 id=\"implementation_notes\">\n    <title>IMPLEMENTATION NOTES</title>\n    <para>Traditionally, allocators have used\n    <citerefentry><refentrytitle>sbrk</refentrytitle>\n    <manvolnum>2</manvolnum></citerefentry> to obtain memory, which is\n    suboptimal for several reasons, including race conditions, increased\n    fragmentation, and artificial limitations on maximum usable memory.  If\n    <option>--enable-dss</option> is specified during configuration, this\n    allocator uses both <citerefentry><refentrytitle>mmap</refentrytitle>\n    <manvolnum>2</manvolnum></citerefentry> and\n    <citerefentry><refentrytitle>sbrk</refentrytitle>\n    <manvolnum>2</manvolnum></citerefentry>, in that order of preference;\n    otherwise only <citerefentry><refentrytitle>mmap</refentrytitle>\n    <manvolnum>2</manvolnum></citerefentry> is used.</para>\n\n    <para>This allocator uses multiple arenas in order to reduce lock\n    contention for threaded programs on multi-processor systems.  This works\n    well with regard to threading scalability, but incurs some costs.  There is\n    a small fixed per-arena overhead, and additionally, arenas manage memory\n    completely independently of each other, which means a small fixed increase\n    in overall memory fragmentation.  These overheads are not generally an\n    issue, given the number of arenas normally used.  Note that using\n    substantially more arenas than the default is not likely to improve\n    performance, mainly due to reduced cache performance.  However, it may make\n    sense to reduce the number of arenas if an application does not make much\n    use of the allocation functions.</para>\n\n    <para>In addition to multiple arenas, unless\n    <option>--disable-tcache</option> is specified during configuration, this\n    allocator supports thread-specific caching for small and large objects, in\n    order to make it possible to completely avoid synchronization for most\n    allocation requests.  Such caching allows very fast allocation in the\n    common case, but it increases memory usage and fragmentation, since a\n    bounded number of objects can remain allocated in each thread cache.</para>\n\n    <para>Memory is conceptually broken into equal-sized chunks, where the\n    chunk size is a power of two that is greater than the page size.  Chunks\n    are always aligned to multiples of the chunk size.  This alignment makes it\n    possible to find metadata for user objects very quickly.</para>\n\n    <para>User objects are broken into three categories according to size:\n    small, large, and huge.  Small objects are smaller than one page.  Large\n    objects are smaller than the chunk size.  Huge objects are a multiple of\n    the chunk size.  Small and large objects are managed by arenas; huge\n    objects are managed separately in a single data structure that is shared by\n    all threads.  Huge objects are used by applications infrequently enough\n    that this single data structure is not a scalability issue.</para>\n\n    <para>Each chunk that is managed by an arena tracks its contents as runs of\n    contiguous pages (unused, backing a set of small objects, or backing one\n    large object).  The combination of chunk alignment and chunk page maps\n    makes it possible to determine all metadata regarding small and large\n    allocations in constant time.</para>\n\n    <para>Small objects are managed in groups by page runs.  Each run maintains\n    a frontier and free list to track which regions are in use.  Allocation\n    requests that are no more than half the quantum (8 or 16, depending on\n    architecture) are rounded up to the nearest power of two that is at least\n    <code language=\"C\">sizeof(<type>double</type>)</code>.  All other small\n    object size classes are multiples of the quantum, spaced such that internal\n    fragmentation is limited to approximately 25% for all but the smallest size\n    classes.  Allocation requests that are larger than the maximum small size\n    class, but small enough to fit in an arena-managed chunk (see the <link\n    linkend=\"opt.lg_chunk\"><mallctl>opt.lg_chunk</mallctl></link> option), are\n    rounded up to the nearest run size.  Allocation requests that are too large\n    to fit in an arena-managed chunk are rounded up to the nearest multiple of\n    the chunk size.</para>\n\n    <para>Allocations are packed tightly together, which can be an issue for\n    multi-threaded applications.  If you need to assure that allocations do not\n    suffer from cacheline sharing, round your allocation requests up to the\n    nearest multiple of the cacheline size, or specify cacheline alignment when\n    allocating.</para>\n\n    <para>Assuming 4 MiB chunks, 4 KiB pages, and a 16-byte quantum on a 64-bit\n    system, the size classes in each category are as shown in <xref\n    linkend=\"size_classes\" xrefstyle=\"template:Table %n\"/>.</para>\n\n    <table xml:id=\"size_classes\" frame=\"all\">\n      <title>Size classes</title>\n      <tgroup cols=\"3\" colsep=\"1\" rowsep=\"1\">\n      <colspec colname=\"c1\" align=\"left\"/>\n      <colspec colname=\"c2\" align=\"right\"/>\n      <colspec colname=\"c3\" align=\"left\"/>\n      <thead>\n        <row>\n          <entry>Category</entry>\n          <entry>Spacing</entry>\n          <entry>Size</entry>\n        </row>\n      </thead>\n      <tbody>\n        <row>\n          <entry morerows=\"6\">Small</entry>\n          <entry>lg</entry>\n          <entry>[8]</entry>\n        </row>\n        <row>\n          <entry>16</entry>\n          <entry>[16, 32, 48, ..., 128]</entry>\n        </row>\n        <row>\n          <entry>32</entry>\n          <entry>[160, 192, 224, 256]</entry>\n        </row>\n        <row>\n          <entry>64</entry>\n          <entry>[320, 384, 448, 512]</entry>\n        </row>\n        <row>\n          <entry>128</entry>\n          <entry>[640, 768, 896, 1024]</entry>\n        </row>\n        <row>\n          <entry>256</entry>\n          <entry>[1280, 1536, 1792, 2048]</entry>\n        </row>\n        <row>\n          <entry>512</entry>\n          <entry>[2560, 3072, 3584]</entry>\n        </row>\n        <row>\n          <entry>Large</entry>\n          <entry>4 KiB</entry>\n          <entry>[4 KiB, 8 KiB, 12 KiB, ..., 4072 KiB]</entry>\n        </row>\n        <row>\n          <entry>Huge</entry>\n          <entry>4 MiB</entry>\n          <entry>[4 MiB, 8 MiB, 12 MiB, ...]</entry>\n        </row>\n      </tbody>\n      </tgroup>\n    </table>\n  </refsect1>\n  <refsect1 id=\"mallctl_namespace\">\n    <title>MALLCTL NAMESPACE</title>\n    <para>The following names are defined in the namespace accessible via the\n    <function>mallctl*<parameter/></function> functions.  Value types are\n    specified in parentheses, their readable/writable statuses are encoded as\n    <literal>rw</literal>, <literal>r-</literal>, <literal>-w</literal>, or\n    <literal>--</literal>, and required build configuration flags follow, if\n    any.  A name element encoded as <literal>&lt;i&gt;</literal> or\n    <literal>&lt;j&gt;</literal> indicates an integer component, where the\n    integer varies from 0 to some upper value that must be determined via\n    introspection.  In the case of <mallctl>stats.arenas.&lt;i&gt;.*</mallctl>,\n    <literal>&lt;i&gt;</literal> equal to <link\n    linkend=\"arenas.narenas\"><mallctl>arenas.narenas</mallctl></link> can be\n    used to access the summation of statistics from all arenas.  Take special\n    note of the <link linkend=\"epoch\"><mallctl>epoch</mallctl></link> mallctl,\n    which controls refreshing of cached dynamic statistics.</para>\n\n    <variablelist>\n      <varlistentry>\n        <term>\n          <mallctl>version</mallctl>\n          (<type>const char *</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para>Return the jemalloc version string.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"epoch\">\n        <term>\n          <mallctl>epoch</mallctl>\n          (<type>uint64_t</type>)\n          <literal>rw</literal>\n        </term>\n        <listitem><para>If a value is passed in, refresh the data from which\n        the <function>mallctl*<parameter/></function> functions report values,\n        and increment the epoch.  Return the current epoch.  This is useful for\n        detecting whether another thread caused a refresh.</para></listitem>\n      </varlistentry>\n\n      <varlistentry>\n        <term>\n          <mallctl>config.debug</mallctl>\n          (<type>bool</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para><option>--enable-debug</option> was specified during\n        build configuration.</para></listitem>\n      </varlistentry>\n\n      <varlistentry>\n        <term>\n          <mallctl>config.dss</mallctl>\n          (<type>bool</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para><option>--enable-dss</option> was specified during\n        build configuration.</para></listitem>\n      </varlistentry>\n\n      <varlistentry>\n        <term>\n          <mallctl>config.fill</mallctl>\n          (<type>bool</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para><option>--enable-fill</option> was specified during\n        build configuration.</para></listitem>\n      </varlistentry>\n\n      <varlistentry>\n        <term>\n          <mallctl>config.lazy_lock</mallctl>\n          (<type>bool</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para><option>--enable-lazy-lock</option> was specified\n        during build configuration.</para></listitem>\n      </varlistentry>\n\n      <varlistentry>\n        <term>\n          <mallctl>config.mremap</mallctl>\n          (<type>bool</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para><option>--enable-mremap</option> was specified during\n        build configuration.</para></listitem>\n      </varlistentry>\n\n      <varlistentry>\n        <term>\n          <mallctl>config.munmap</mallctl>\n          (<type>bool</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para><option>--enable-munmap</option> was specified during\n        build configuration.</para></listitem>\n      </varlistentry>\n\n      <varlistentry>\n        <term>\n          <mallctl>config.prof</mallctl>\n          (<type>bool</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para><option>--enable-prof</option> was specified during\n        build configuration.</para></listitem>\n      </varlistentry>\n\n      <varlistentry>\n        <term>\n          <mallctl>config.prof_libgcc</mallctl>\n          (<type>bool</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para><option>--disable-prof-libgcc</option> was not\n        specified during build configuration.</para></listitem>\n      </varlistentry>\n\n      <varlistentry>\n        <term>\n          <mallctl>config.prof_libunwind</mallctl>\n          (<type>bool</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para><option>--enable-prof-libunwind</option> was specified\n        during build configuration.</para></listitem>\n      </varlistentry>\n\n      <varlistentry>\n        <term>\n          <mallctl>config.stats</mallctl>\n          (<type>bool</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para><option>--enable-stats</option> was specified during\n        build configuration.</para></listitem>\n      </varlistentry>\n\n      <varlistentry>\n        <term>\n          <mallctl>config.tcache</mallctl>\n          (<type>bool</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para><option>--disable-tcache</option> was not specified\n        during build configuration.</para></listitem>\n      </varlistentry>\n\n      <varlistentry>\n        <term>\n          <mallctl>config.tls</mallctl>\n          (<type>bool</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para><option>--disable-tls</option> was not specified during\n        build configuration.</para></listitem>\n      </varlistentry>\n\n      <varlistentry>\n        <term>\n          <mallctl>config.utrace</mallctl>\n          (<type>bool</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para><option>--enable-utrace</option> was specified during\n        build configuration.</para></listitem>\n      </varlistentry>\n\n      <varlistentry>\n        <term>\n          <mallctl>config.valgrind</mallctl>\n          (<type>bool</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para><option>--enable-valgrind</option> was specified during\n        build configuration.</para></listitem>\n      </varlistentry>\n\n      <varlistentry>\n        <term>\n          <mallctl>config.xmalloc</mallctl>\n          (<type>bool</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para><option>--enable-xmalloc</option> was specified during\n        build configuration.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"opt.abort\">\n        <term>\n          <mallctl>opt.abort</mallctl>\n          (<type>bool</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para>Abort-on-warning enabled/disabled.  If true, most\n        warnings are fatal.  The process will call\n        <citerefentry><refentrytitle>abort</refentrytitle>\n        <manvolnum>3</manvolnum></citerefentry> in these cases.  This option is\n        disabled by default unless <option>--enable-debug</option> is\n        specified during configuration, in which case it is enabled by default.\n        </para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"opt.lg_chunk\">\n        <term>\n          <mallctl>opt.lg_chunk</mallctl>\n          (<type>size_t</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para>Virtual memory chunk size (log base 2).  The default\n        chunk size is 4 MiB (2^22).</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"opt.dss\">\n        <term>\n          <mallctl>opt.dss</mallctl>\n          (<type>const char *</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para>dss (<citerefentry><refentrytitle>sbrk</refentrytitle>\n        <manvolnum>2</manvolnum></citerefentry>) allocation precedence as\n        related to <citerefentry><refentrytitle>mmap</refentrytitle>\n        <manvolnum>2</manvolnum></citerefentry> allocation.  The following\n        settings are supported: &ldquo;disabled&rdquo;, &ldquo;primary&rdquo;,\n        and &ldquo;secondary&rdquo; (default).</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"opt.narenas\">\n        <term>\n          <mallctl>opt.narenas</mallctl>\n          (<type>size_t</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para>Maximum number of arenas to use for automatic\n        multiplexing of threads and arenas.  The default is four times the\n        number of CPUs, or one if there is a single CPU.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"opt.lg_dirty_mult\">\n        <term>\n          <mallctl>opt.lg_dirty_mult</mallctl>\n          (<type>ssize_t</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para>Per-arena minimum ratio (log base 2) of active to dirty\n        pages.  Some dirty unused pages may be allowed to accumulate, within\n        the limit set by the ratio (or one chunk worth of dirty pages,\n        whichever is greater), before informing the kernel about some of those\n        pages via <citerefentry><refentrytitle>madvise</refentrytitle>\n        <manvolnum>2</manvolnum></citerefentry> or a similar system call.  This\n        provides the kernel with sufficient information to recycle dirty pages\n        if physical memory becomes scarce and the pages remain unused.  The\n        default minimum ratio is 8:1 (2^3:1); an option value of -1 will\n        disable dirty page purging.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"opt.stats_print\">\n        <term>\n          <mallctl>opt.stats_print</mallctl>\n          (<type>bool</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para>Enable/disable statistics printing at exit.  If\n        enabled, the <function>malloc_stats_print<parameter/></function>\n        function is called at program exit via an\n        <citerefentry><refentrytitle>atexit</refentrytitle>\n        <manvolnum>3</manvolnum></citerefentry> function.  If\n        <option>--enable-stats</option> is specified during configuration, this\n        has the potential to cause deadlock for a multi-threaded process that\n        exits while one or more threads are executing in the memory allocation\n        functions.  Therefore, this option should only be used with care; it is\n        primarily intended as a performance tuning aid during application\n        development.  This option is disabled by default.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"opt.junk\">\n        <term>\n          <mallctl>opt.junk</mallctl>\n          (<type>bool</type>)\n          <literal>r-</literal>\n          [<option>--enable-fill</option>]\n        </term>\n        <listitem><para>Junk filling enabled/disabled.  If enabled, each byte\n        of uninitialized allocated memory will be initialized to\n        <literal>0xa5</literal>.  All deallocated memory will be initialized to\n        <literal>0x5a</literal>.  This is intended for debugging and will\n        impact performance negatively.  This option is disabled by default\n        unless <option>--enable-debug</option> is specified during\n        configuration, in which case it is enabled by default unless running\n        inside <ulink\n        url=\"http://valgrind.org/\">Valgrind</ulink>.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"opt.quarantine\">\n        <term>\n          <mallctl>opt.quarantine</mallctl>\n          (<type>size_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-fill</option>]\n        </term>\n        <listitem><para>Per thread quarantine size in bytes.  If non-zero, each\n        thread maintains a FIFO object quarantine that stores up to the\n        specified number of bytes of memory.  The quarantined memory is not\n        freed until it is released from quarantine, though it is immediately\n        junk-filled if the <link\n        linkend=\"opt.junk\"><mallctl>opt.junk</mallctl></link> option is\n        enabled.  This feature is of particular use in combination with <ulink\n        url=\"http://valgrind.org/\">Valgrind</ulink>, which can detect attempts\n        to access quarantined objects.  This is intended for debugging and will\n        impact performance negatively.  The default quarantine size is 0 unless\n        running inside Valgrind, in which case the default is 16\n        MiB.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"opt.redzone\">\n        <term>\n          <mallctl>opt.redzone</mallctl>\n          (<type>bool</type>)\n          <literal>r-</literal>\n          [<option>--enable-fill</option>]\n        </term>\n        <listitem><para>Redzones enabled/disabled.  If enabled, small\n        allocations have redzones before and after them.  Furthermore, if the\n        <link linkend=\"opt.junk\"><mallctl>opt.junk</mallctl></link> option is\n        enabled, the redzones are checked for corruption during deallocation.\n        However, the primary intended purpose of this feature is to be used in\n        combination with <ulink url=\"http://valgrind.org/\">Valgrind</ulink>,\n        which needs redzones in order to do effective buffer overflow/underflow\n        detection.  This option is intended for debugging and will impact\n        performance negatively.  This option is disabled by\n        default unless running inside Valgrind.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"opt.zero\">\n        <term>\n          <mallctl>opt.zero</mallctl>\n          (<type>bool</type>)\n          <literal>r-</literal>\n          [<option>--enable-fill</option>]\n        </term>\n        <listitem><para>Zero filling enabled/disabled.  If enabled, each byte\n        of uninitialized allocated memory will be initialized to 0.  Note that\n        this initialization only happens once for each byte, so\n        <function>realloc<parameter/></function> and\n        <function>rallocm<parameter/></function> calls do not zero memory that\n        was previously allocated.  This is intended for debugging and will\n        impact performance negatively.  This option is disabled by default.\n        </para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"opt.utrace\">\n        <term>\n          <mallctl>opt.utrace</mallctl>\n          (<type>bool</type>)\n          <literal>r-</literal>\n          [<option>--enable-utrace</option>]\n        </term>\n        <listitem><para>Allocation tracing based on\n        <citerefentry><refentrytitle>utrace</refentrytitle>\n        <manvolnum>2</manvolnum></citerefentry> enabled/disabled.  This option\n        is disabled by default.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"opt.valgrind\">\n        <term>\n          <mallctl>opt.valgrind</mallctl>\n          (<type>bool</type>)\n          <literal>r-</literal>\n          [<option>--enable-valgrind</option>]\n        </term>\n        <listitem><para><ulink url=\"http://valgrind.org/\">Valgrind</ulink>\n        support enabled/disabled.  This option is vestigal because jemalloc\n        auto-detects whether it is running inside Valgrind.  This option is\n        disabled by default, unless running inside Valgrind.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"opt.xmalloc\">\n        <term>\n          <mallctl>opt.xmalloc</mallctl>\n          (<type>bool</type>)\n          <literal>r-</literal>\n          [<option>--enable-xmalloc</option>]\n        </term>\n        <listitem><para>Abort-on-out-of-memory enabled/disabled.  If enabled,\n        rather than returning failure for any allocation function, display a\n        diagnostic message on <constant>STDERR_FILENO</constant> and cause the\n        program to drop core (using\n        <citerefentry><refentrytitle>abort</refentrytitle>\n        <manvolnum>3</manvolnum></citerefentry>).  If an application is\n        designed to depend on this behavior, set the option at compile time by\n        including the following in the source code:\n        <programlisting language=\"C\"><![CDATA[\nmalloc_conf = \"xmalloc:true\";]]></programlisting>\n        This option is disabled by default.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"opt.tcache\">\n        <term>\n          <mallctl>opt.tcache</mallctl>\n          (<type>bool</type>)\n          <literal>r-</literal>\n          [<option>--enable-tcache</option>]\n        </term>\n        <listitem><para>Thread-specific caching enabled/disabled.  When there\n        are multiple threads, each thread uses a thread-specific cache for\n        objects up to a certain size.  Thread-specific caching allows many\n        allocations to be satisfied without performing any thread\n        synchronization, at the cost of increased memory use.  See the\n        <link\n        linkend=\"opt.lg_tcache_max\"><mallctl>opt.lg_tcache_max</mallctl></link>\n        option for related tuning information.  This option is enabled by\n        default unless running inside <ulink\n        url=\"http://valgrind.org/\">Valgrind</ulink>.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"opt.lg_tcache_max\">\n        <term>\n          <mallctl>opt.lg_tcache_max</mallctl>\n          (<type>size_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-tcache</option>]\n        </term>\n        <listitem><para>Maximum size class (log base 2) to cache in the\n        thread-specific cache.  At a minimum, all small size classes are\n        cached, and at a maximum all large size classes are cached.  The\n        default maximum is 32 KiB (2^15).</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"opt.prof\">\n        <term>\n          <mallctl>opt.prof</mallctl>\n          (<type>bool</type>)\n          <literal>r-</literal>\n          [<option>--enable-prof</option>]\n        </term>\n        <listitem><para>Memory profiling enabled/disabled.  If enabled, profile\n        memory allocation activity.  See the <link\n        linkend=\"opt.prof_active\"><mallctl>opt.prof_active</mallctl></link>\n        option for on-the-fly activation/deactivation.  See the <link\n        linkend=\"opt.lg_prof_sample\"><mallctl>opt.lg_prof_sample</mallctl></link>\n        option for probabilistic sampling control.  See the <link\n        linkend=\"opt.prof_accum\"><mallctl>opt.prof_accum</mallctl></link>\n        option for control of cumulative sample reporting.  See the <link\n        linkend=\"opt.lg_prof_interval\"><mallctl>opt.lg_prof_interval</mallctl></link>\n        option for information on interval-triggered profile dumping, the <link\n        linkend=\"opt.prof_gdump\"><mallctl>opt.prof_gdump</mallctl></link>\n        option for information on high-water-triggered profile dumping, and the\n        <link linkend=\"opt.prof_final\"><mallctl>opt.prof_final</mallctl></link>\n        option for final profile dumping.  Profile output is compatible with\n        the included <command>pprof</command> Perl script, which originates\n        from the <ulink url=\"http://code.google.com/p/gperftools/\">gperftools\n        package</ulink>.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"opt.prof_prefix\">\n        <term>\n          <mallctl>opt.prof_prefix</mallctl>\n          (<type>const char *</type>)\n          <literal>r-</literal>\n          [<option>--enable-prof</option>]\n        </term>\n        <listitem><para>Filename prefix for profile dumps.  If the prefix is\n        set to the empty string, no automatic dumps will occur; this is\n        primarily useful for disabling the automatic final heap dump (which\n        also disables leak reporting, if enabled).  The default prefix is\n        <filename>jeprof</filename>.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"opt.prof_active\">\n        <term>\n          <mallctl>opt.prof_active</mallctl>\n          (<type>bool</type>)\n          <literal>r-</literal>\n          [<option>--enable-prof</option>]\n        </term>\n        <listitem><para>Profiling activated/deactivated.  This is a secondary\n        control mechanism that makes it possible to start the application with\n        profiling enabled (see the <link\n        linkend=\"opt.prof\"><mallctl>opt.prof</mallctl></link> option) but\n        inactive, then toggle profiling at any time during program execution\n        with the <link\n        linkend=\"prof.active\"><mallctl>prof.active</mallctl></link> mallctl.\n        This option is enabled by default.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"opt.lg_prof_sample\">\n        <term>\n          <mallctl>opt.lg_prof_sample</mallctl>\n          (<type>ssize_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-prof</option>]\n        </term>\n        <listitem><para>Average interval (log base 2) between allocation\n        samples, as measured in bytes of allocation activity.  Increasing the\n        sampling interval decreases profile fidelity, but also decreases the\n        computational overhead.  The default sample interval is 512 KiB (2^19\n        B).</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"opt.prof_accum\">\n        <term>\n          <mallctl>opt.prof_accum</mallctl>\n          (<type>bool</type>)\n          <literal>r-</literal>\n          [<option>--enable-prof</option>]\n        </term>\n        <listitem><para>Reporting of cumulative object/byte counts in profile\n        dumps enabled/disabled.  If this option is enabled, every unique\n        backtrace must be stored for the duration of execution.  Depending on\n        the application, this can impose a large memory overhead, and the\n        cumulative counts are not always of interest.  This option is disabled\n        by default.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"opt.lg_prof_interval\">\n        <term>\n          <mallctl>opt.lg_prof_interval</mallctl>\n          (<type>ssize_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-prof</option>]\n        </term>\n        <listitem><para>Average interval (log base 2) between memory profile\n        dumps, as measured in bytes of allocation activity.  The actual\n        interval between dumps may be sporadic because decentralized allocation\n        counters are used to avoid synchronization bottlenecks.  Profiles are\n        dumped to files named according to the pattern\n        <filename>&lt;prefix&gt;.&lt;pid&gt;.&lt;seq&gt;.i&lt;iseq&gt;.heap</filename>,\n        where <literal>&lt;prefix&gt;</literal> is controlled by the\n        <link\n        linkend=\"opt.prof_prefix\"><mallctl>opt.prof_prefix</mallctl></link>\n        option.  By default, interval-triggered profile dumping is disabled\n        (encoded as -1).\n        </para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"opt.prof_gdump\">\n        <term>\n          <mallctl>opt.prof_gdump</mallctl>\n          (<type>bool</type>)\n          <literal>r-</literal>\n          [<option>--enable-prof</option>]\n        </term>\n        <listitem><para>Trigger a memory profile dump every time the total\n        virtual memory exceeds the previous maximum.  Profiles are dumped to\n        files named according to the pattern\n        <filename>&lt;prefix&gt;.&lt;pid&gt;.&lt;seq&gt;.u&lt;useq&gt;.heap</filename>,\n        where <literal>&lt;prefix&gt;</literal> is controlled by the <link\n        linkend=\"opt.prof_prefix\"><mallctl>opt.prof_prefix</mallctl></link>\n        option.  This option is disabled by default.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"opt.prof_final\">\n        <term>\n          <mallctl>opt.prof_final</mallctl>\n          (<type>bool</type>)\n          <literal>r-</literal>\n          [<option>--enable-prof</option>]\n        </term>\n        <listitem><para>Use an\n        <citerefentry><refentrytitle>atexit</refentrytitle>\n        <manvolnum>3</manvolnum></citerefentry> function to dump final memory\n        usage to a file named according to the pattern\n        <filename>&lt;prefix&gt;.&lt;pid&gt;.&lt;seq&gt;.f.heap</filename>,\n        where <literal>&lt;prefix&gt;</literal> is controlled by the <link\n        linkend=\"opt.prof_prefix\"><mallctl>opt.prof_prefix</mallctl></link>\n        option.  This option is enabled by default.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"opt.prof_leak\">\n        <term>\n          <mallctl>opt.prof_leak</mallctl>\n          (<type>bool</type>)\n          <literal>r-</literal>\n          [<option>--enable-prof</option>]\n        </term>\n        <listitem><para>Leak reporting enabled/disabled.  If enabled, use an\n        <citerefentry><refentrytitle>atexit</refentrytitle>\n        <manvolnum>3</manvolnum></citerefentry> function to report memory leaks\n        detected by allocation sampling.  See the\n        <link linkend=\"opt.prof\"><mallctl>opt.prof</mallctl></link> option for\n        information on analyzing heap profile output.  This option is disabled\n        by default.</para></listitem>\n      </varlistentry>\n\n      <varlistentry>\n        <term>\n          <mallctl>thread.arena</mallctl>\n          (<type>unsigned</type>)\n          <literal>rw</literal>\n        </term>\n        <listitem><para>Get or set the arena associated with the calling\n        thread.  If the specified arena was not initialized beforehand (see the\n        <link\n        linkend=\"arenas.initialized\"><mallctl>arenas.initialized</mallctl></link>\n        mallctl), it will be automatically initialized as a side effect of\n        calling this interface.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"thread.allocated\">\n        <term>\n          <mallctl>thread.allocated</mallctl>\n          (<type>uint64_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Get the total number of bytes ever allocated by the\n        calling thread.  This counter has the potential to wrap around; it is\n        up to the application to appropriately interpret the counter in such\n        cases.</para></listitem>\n      </varlistentry>\n\n      <varlistentry>\n        <term>\n          <mallctl>thread.allocatedp</mallctl>\n          (<type>uint64_t *</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Get a pointer to the the value that is returned by the\n        <link\n        linkend=\"thread.allocated\"><mallctl>thread.allocated</mallctl></link>\n        mallctl.  This is useful for avoiding the overhead of repeated\n        <function>mallctl*<parameter/></function> calls.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"thread.deallocated\">\n        <term>\n          <mallctl>thread.deallocated</mallctl>\n          (<type>uint64_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Get the total number of bytes ever deallocated by the\n        calling thread.  This counter has the potential to wrap around; it is\n        up to the application to appropriately interpret the counter in such\n        cases.</para></listitem>\n      </varlistentry>\n\n      <varlistentry>\n        <term>\n          <mallctl>thread.deallocatedp</mallctl>\n          (<type>uint64_t *</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Get a pointer to the the value that is returned by the\n        <link\n        linkend=\"thread.deallocated\"><mallctl>thread.deallocated</mallctl></link>\n        mallctl.  This is useful for avoiding the overhead of repeated\n        <function>mallctl*<parameter/></function> calls.</para></listitem>\n      </varlistentry>\n\n      <varlistentry>\n        <term>\n          <mallctl>thread.tcache.enabled</mallctl>\n          (<type>bool</type>)\n          <literal>rw</literal>\n          [<option>--enable-tcache</option>]\n        </term>\n        <listitem><para>Enable/disable calling thread's tcache.  The tcache is\n        implicitly flushed as a side effect of becoming\n        disabled (see <link\n        lenkend=\"thread.tcache.flush\"><mallctl>thread.tcache.flush</mallctl></link>).\n        </para></listitem>\n      </varlistentry>\n\n      <varlistentry>\n        <term>\n          <mallctl>thread.tcache.flush</mallctl>\n          (<type>void</type>)\n          <literal>--</literal>\n          [<option>--enable-tcache</option>]\n        </term>\n        <listitem><para>Flush calling thread's tcache.  This interface releases\n        all cached objects and internal data structures associated with the\n        calling thread's thread-specific cache.  Ordinarily, this interface\n        need not be called, since automatic periodic incremental garbage\n        collection occurs, and the thread cache is automatically discarded when\n        a thread exits.  However, garbage collection is triggered by allocation\n        activity, so it is possible for a thread that stops\n        allocating/deallocating to retain its cache indefinitely, in which case\n        the developer may find manual flushing useful.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"arena.i.purge\">\n        <term>\n          <mallctl>arena.&lt;i&gt;.purge</mallctl>\n          (<type>unsigned</type>)\n          <literal>--</literal>\n        </term>\n        <listitem><para>Purge unused dirty pages for arena &lt;i&gt;, or for\n        all arenas if &lt;i&gt; equals <link\n        linkend=\"arenas.narenas\"><mallctl>arenas.narenas</mallctl></link>.\n        </para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"arena.i.dss\">\n        <term>\n          <mallctl>arena.&lt;i&gt;.dss</mallctl>\n          (<type>const char *</type>)\n          <literal>rw</literal>\n        </term>\n        <listitem><para>Set the precedence of dss allocation as related to mmap\n        allocation for arena &lt;i&gt;, or for all arenas if &lt;i&gt; equals\n        <link\n        linkend=\"arenas.narenas\"><mallctl>arenas.narenas</mallctl></link>.  See\n        <link linkend=\"opt.dss\"><mallctl>opt.dss</mallctl></link> for supported\n        settings.\n        </para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"arenas.narenas\">\n        <term>\n          <mallctl>arenas.narenas</mallctl>\n          (<type>unsigned</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para>Current limit on number of arenas.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"arenas.initialized\">\n        <term>\n          <mallctl>arenas.initialized</mallctl>\n          (<type>bool *</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para>An array of <link\n        linkend=\"arenas.narenas\"><mallctl>arenas.narenas</mallctl></link>\n        booleans.  Each boolean indicates whether the corresponding arena is\n        initialized.</para></listitem>\n      </varlistentry>\n\n      <varlistentry>\n        <term>\n          <mallctl>arenas.quantum</mallctl>\n          (<type>size_t</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para>Quantum size.</para></listitem>\n      </varlistentry>\n\n      <varlistentry>\n        <term>\n          <mallctl>arenas.page</mallctl>\n          (<type>size_t</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para>Page size.</para></listitem>\n      </varlistentry>\n\n      <varlistentry>\n        <term>\n          <mallctl>arenas.tcache_max</mallctl>\n          (<type>size_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-tcache</option>]\n        </term>\n        <listitem><para>Maximum thread-cached size class.</para></listitem>\n      </varlistentry>\n\n      <varlistentry>\n        <term>\n          <mallctl>arenas.nbins</mallctl>\n          (<type>unsigned</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para>Number of bin size classes.</para></listitem>\n      </varlistentry>\n\n      <varlistentry>\n        <term>\n          <mallctl>arenas.nhbins</mallctl>\n          (<type>unsigned</type>)\n          <literal>r-</literal>\n          [<option>--enable-tcache</option>]\n        </term>\n        <listitem><para>Total number of thread cache bin size\n        classes.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"arenas.bin.i.size\">\n        <term>\n          <mallctl>arenas.bin.&lt;i&gt;.size</mallctl>\n          (<type>size_t</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para>Maximum size supported by size class.</para></listitem>\n      </varlistentry>\n\n      <varlistentry>\n        <term>\n          <mallctl>arenas.bin.&lt;i&gt;.nregs</mallctl>\n          (<type>uint32_t</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para>Number of regions per page run.</para></listitem>\n      </varlistentry>\n\n      <varlistentry>\n        <term>\n          <mallctl>arenas.bin.&lt;i&gt;.run_size</mallctl>\n          (<type>size_t</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para>Number of bytes per page run.</para></listitem>\n      </varlistentry>\n\n      <varlistentry>\n        <term>\n          <mallctl>arenas.nlruns</mallctl>\n          (<type>size_t</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para>Total number of large size classes.</para></listitem>\n      </varlistentry>\n\n      <varlistentry>\n        <term>\n          <mallctl>arenas.lrun.&lt;i&gt;.size</mallctl>\n          (<type>size_t</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para>Maximum size supported by this large size\n        class.</para></listitem>\n      </varlistentry>\n\n      <varlistentry>\n        <term>\n          <mallctl>arenas.purge</mallctl>\n          (<type>unsigned</type>)\n          <literal>-w</literal>\n        </term>\n        <listitem><para>Purge unused dirty pages for the specified arena, or\n        for all arenas if none is specified.</para></listitem>\n      </varlistentry>\n\n      <varlistentry>\n        <term>\n          <mallctl>arenas.extend</mallctl>\n          (<type>unsigned</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para>Extend the array of arenas by appending a new arena,\n        and returning the new arena index.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"prof.active\">\n        <term>\n          <mallctl>prof.active</mallctl>\n          (<type>bool</type>)\n          <literal>rw</literal>\n          [<option>--enable-prof</option>]\n        </term>\n        <listitem><para>Control whether sampling is currently active.  See the\n        <link\n        linkend=\"opt.prof_active\"><mallctl>opt.prof_active</mallctl></link>\n        option for additional information.\n        </para></listitem>\n      </varlistentry>\n\n      <varlistentry>\n        <term>\n          <mallctl>prof.dump</mallctl>\n          (<type>const char *</type>)\n          <literal>-w</literal>\n          [<option>--enable-prof</option>]\n        </term>\n        <listitem><para>Dump a memory profile to the specified file, or if NULL\n        is specified, to a file according to the pattern\n        <filename>&lt;prefix&gt;.&lt;pid&gt;.&lt;seq&gt;.m&lt;mseq&gt;.heap</filename>,\n        where <literal>&lt;prefix&gt;</literal> is controlled by the\n        <link\n        linkend=\"opt.prof_prefix\"><mallctl>opt.prof_prefix</mallctl></link>\n        option.</para></listitem>\n      </varlistentry>\n\n      <varlistentry>\n        <term>\n          <mallctl>prof.interval</mallctl>\n          (<type>uint64_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-prof</option>]\n        </term>\n        <listitem><para>Average number of bytes allocated between\n        inverval-based profile dumps.  See the\n        <link\n        linkend=\"opt.lg_prof_interval\"><mallctl>opt.lg_prof_interval</mallctl></link>\n        option for additional information.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.cactive\">\n        <term>\n          <mallctl>stats.cactive</mallctl>\n          (<type>size_t *</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Pointer to a counter that contains an approximate count\n        of the current number of bytes in active pages.  The estimate may be\n        high, but never low, because each arena rounds up to the nearest\n        multiple of the chunk size when computing its contribution to the\n        counter.  Note that the <link\n        linkend=\"epoch\"><mallctl>epoch</mallctl></link> mallctl has no bearing\n        on this counter.  Furthermore, counter consistency is maintained via\n        atomic operations, so it is necessary to use an atomic operation in\n        order to guarantee a consistent read when dereferencing the pointer.\n        </para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.allocated\">\n        <term>\n          <mallctl>stats.allocated</mallctl>\n          (<type>size_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Total number of bytes allocated by the\n        application.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.active\">\n        <term>\n          <mallctl>stats.active</mallctl>\n          (<type>size_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Total number of bytes in active pages allocated by the\n        application.  This is a multiple of the page size, and greater than or\n        equal to <link\n        linkend=\"stats.allocated\"><mallctl>stats.allocated</mallctl></link>.\n        This does not include <link linkend=\"stats.arenas.i.pdirty\">\n        <mallctl>stats.arenas.&lt;i&gt;.pdirty</mallctl></link> and pages\n        entirely devoted to allocator metadata.</para></listitem>\n      </varlistentry>\n\n      <varlistentry>\n        <term>\n          <mallctl>stats.mapped</mallctl>\n          (<type>size_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Total number of bytes in chunks mapped on behalf of the\n        application.  This is a multiple of the chunk size, and is at least as\n        large as <link\n        linkend=\"stats.active\"><mallctl>stats.active</mallctl></link>.  This\n        does not include inactive chunks.</para></listitem>\n      </varlistentry>\n\n      <varlistentry>\n        <term>\n          <mallctl>stats.chunks.current</mallctl>\n          (<type>size_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Total number of chunks actively mapped on behalf of the\n        application.  This does not include inactive chunks.\n        </para></listitem>\n      </varlistentry>\n\n      <varlistentry>\n        <term>\n          <mallctl>stats.chunks.total</mallctl>\n          (<type>uint64_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Cumulative number of chunks allocated.</para></listitem>\n      </varlistentry>\n\n      <varlistentry>\n        <term>\n          <mallctl>stats.chunks.high</mallctl>\n          (<type>size_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Maximum number of active chunks at any time thus far.\n        </para></listitem>\n      </varlistentry>\n\n      <varlistentry>\n        <term>\n          <mallctl>stats.huge.allocated</mallctl>\n          (<type>size_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Number of bytes currently allocated by huge objects.\n        </para></listitem>\n      </varlistentry>\n\n      <varlistentry>\n        <term>\n          <mallctl>stats.huge.nmalloc</mallctl>\n          (<type>uint64_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Cumulative number of huge allocation requests.\n        </para></listitem>\n      </varlistentry>\n\n      <varlistentry>\n        <term>\n          <mallctl>stats.huge.ndalloc</mallctl>\n          (<type>uint64_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Cumulative number of huge deallocation requests.\n        </para></listitem>\n      </varlistentry>\n\n      <varlistentry>\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.dss</mallctl>\n          (<type>const char *</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para>dss (<citerefentry><refentrytitle>sbrk</refentrytitle>\n        <manvolnum>2</manvolnum></citerefentry>) allocation precedence as\n        related to <citerefentry><refentrytitle>mmap</refentrytitle>\n        <manvolnum>2</manvolnum></citerefentry> allocation.  See <link\n        linkend=\"opt.dss\"><mallctl>opt.dss</mallctl></link> for details.\n        </para></listitem>\n      </varlistentry>\n\n      <varlistentry>\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.nthreads</mallctl>\n          (<type>unsigned</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para>Number of threads currently assigned to\n        arena.</para></listitem>\n      </varlistentry>\n\n      <varlistentry>\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.pactive</mallctl>\n          (<type>size_t</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para>Number of pages in active runs.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.pdirty\">\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.pdirty</mallctl>\n          (<type>size_t</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para>Number of pages within unused runs that are potentially\n        dirty, and for which <function>madvise<parameter>...</parameter>\n        <parameter><constant>MADV_DONTNEED</constant></parameter></function> or\n        similar has not been called.</para></listitem>\n      </varlistentry>\n\n      <varlistentry>\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.mapped</mallctl>\n          (<type>size_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Number of mapped bytes.</para></listitem>\n      </varlistentry>\n\n      <varlistentry>\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.npurge</mallctl>\n          (<type>uint64_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Number of dirty page purge sweeps performed.\n        </para></listitem>\n      </varlistentry>\n\n      <varlistentry>\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.nmadvise</mallctl>\n          (<type>uint64_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Number of <function>madvise<parameter>...</parameter>\n        <parameter><constant>MADV_DONTNEED</constant></parameter></function> or\n        similar calls made to purge dirty pages.</para></listitem>\n      </varlistentry>\n\n      <varlistentry>\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.npurged</mallctl>\n          (<type>uint64_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Number of pages purged.</para></listitem>\n      </varlistentry>\n\n      <varlistentry>\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.small.allocated</mallctl>\n          (<type>size_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Number of bytes currently allocated by small objects.\n        </para></listitem>\n      </varlistentry>\n\n      <varlistentry>\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.small.nmalloc</mallctl>\n          (<type>uint64_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Cumulative number of allocation requests served by\n        small bins.</para></listitem>\n      </varlistentry>\n\n      <varlistentry>\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.small.ndalloc</mallctl>\n          (<type>uint64_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Cumulative number of small objects returned to bins.\n        </para></listitem>\n      </varlistentry>\n\n      <varlistentry>\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.small.nrequests</mallctl>\n          (<type>uint64_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Cumulative number of small allocation requests.\n        </para></listitem>\n      </varlistentry>\n\n      <varlistentry>\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.large.allocated</mallctl>\n          (<type>size_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Number of bytes currently allocated by large objects.\n        </para></listitem>\n      </varlistentry>\n\n      <varlistentry>\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.large.nmalloc</mallctl>\n          (<type>uint64_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Cumulative number of large allocation requests served\n        directly by the arena.</para></listitem>\n      </varlistentry>\n\n      <varlistentry>\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.large.ndalloc</mallctl>\n          (<type>uint64_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Cumulative number of large deallocation requests served\n        directly by the arena.</para></listitem>\n      </varlistentry>\n\n      <varlistentry>\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.large.nrequests</mallctl>\n          (<type>uint64_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Cumulative number of large allocation requests.\n        </para></listitem>\n      </varlistentry>\n\n      <varlistentry>\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.bins.&lt;j&gt;.allocated</mallctl>\n          (<type>size_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Current number of bytes allocated by\n        bin.</para></listitem>\n      </varlistentry>\n\n      <varlistentry>\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.bins.&lt;j&gt;.nmalloc</mallctl>\n          (<type>uint64_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Cumulative number of allocations served by bin.\n        </para></listitem>\n      </varlistentry>\n\n      <varlistentry>\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.bins.&lt;j&gt;.ndalloc</mallctl>\n          (<type>uint64_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Cumulative number of allocations returned to bin.\n        </para></listitem>\n      </varlistentry>\n\n      <varlistentry>\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.bins.&lt;j&gt;.nrequests</mallctl>\n          (<type>uint64_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Cumulative number of allocation\n        requests.</para></listitem>\n      </varlistentry>\n\n      <varlistentry>\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.bins.&lt;j&gt;.nfills</mallctl>\n          (<type>uint64_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option> <option>--enable-tcache</option>]\n        </term>\n        <listitem><para>Cumulative number of tcache fills.</para></listitem>\n      </varlistentry>\n\n      <varlistentry>\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.bins.&lt;j&gt;.nflushes</mallctl>\n          (<type>uint64_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option> <option>--enable-tcache</option>]\n        </term>\n        <listitem><para>Cumulative number of tcache flushes.</para></listitem>\n      </varlistentry>\n\n      <varlistentry>\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.bins.&lt;j&gt;.nruns</mallctl>\n          (<type>uint64_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Cumulative number of runs created.</para></listitem>\n      </varlistentry>\n\n      <varlistentry>\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.bins.&lt;j&gt;.nreruns</mallctl>\n          (<type>uint64_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Cumulative number of times the current run from which\n        to allocate changed.</para></listitem>\n      </varlistentry>\n\n      <varlistentry>\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.bins.&lt;j&gt;.curruns</mallctl>\n          (<type>size_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Current number of runs.</para></listitem>\n      </varlistentry>\n\n      <varlistentry>\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.lruns.&lt;j&gt;.nmalloc</mallctl>\n          (<type>uint64_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Cumulative number of allocation requests for this size\n        class served directly by the arena.</para></listitem>\n      </varlistentry>\n\n      <varlistentry>\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.lruns.&lt;j&gt;.ndalloc</mallctl>\n          (<type>uint64_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Cumulative number of deallocation requests for this\n        size class served directly by the arena.</para></listitem>\n      </varlistentry>\n\n      <varlistentry>\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.lruns.&lt;j&gt;.nrequests</mallctl>\n          (<type>uint64_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Cumulative number of allocation requests for this size\n        class.</para></listitem>\n      </varlistentry>\n\n      <varlistentry>\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.lruns.&lt;j&gt;.curruns</mallctl>\n          (<type>size_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Current number of runs for this size class.\n        </para></listitem>\n      </varlistentry>\n    </variablelist>\n  </refsect1>\n  <refsect1 id=\"debugging_malloc_problems\">\n    <title>DEBUGGING MALLOC PROBLEMS</title>\n    <para>When debugging, it is a good idea to configure/build jemalloc with\n    the <option>--enable-debug</option> and <option>--enable-fill</option>\n    options, and recompile the program with suitable options and symbols for\n    debugger support.  When so configured, jemalloc incorporates a wide variety\n    of run-time assertions that catch application errors such as double-free,\n    write-after-free, etc.</para>\n\n    <para>Programs often accidentally depend on &ldquo;uninitialized&rdquo;\n    memory actually being filled with zero bytes.  Junk filling\n    (see the <link linkend=\"opt.junk\"><mallctl>opt.junk</mallctl></link>\n    option) tends to expose such bugs in the form of obviously incorrect\n    results and/or coredumps.  Conversely, zero\n    filling (see the <link\n    linkend=\"opt.zero\"><mallctl>opt.zero</mallctl></link> option) eliminates\n    the symptoms of such bugs.  Between these two options, it is usually\n    possible to quickly detect, diagnose, and eliminate such bugs.</para>\n\n    <para>This implementation does not provide much detail about the problems\n    it detects, because the performance impact for storing such information\n    would be prohibitive.  However, jemalloc does integrate with the most\n    excellent <ulink url=\"http://valgrind.org/\">Valgrind</ulink> tool if the\n    <option>--enable-valgrind</option> configuration option is enabled.</para>\n  </refsect1>\n  <refsect1 id=\"diagnostic_messages\">\n    <title>DIAGNOSTIC MESSAGES</title>\n    <para>If any of the memory allocation/deallocation functions detect an\n    error or warning condition, a message will be printed to file descriptor\n    <constant>STDERR_FILENO</constant>.  Errors will result in the process\n    dumping core.  If the <link\n    linkend=\"opt.abort\"><mallctl>opt.abort</mallctl></link> option is set, most\n    warnings are treated as errors.</para>\n\n    <para>The <varname>malloc_message</varname> variable allows the programmer\n    to override the function which emits the text strings forming the errors\n    and warnings if for some reason the <constant>STDERR_FILENO</constant> file\n    descriptor is not suitable for this.\n    <function>malloc_message<parameter/></function> takes the\n    <parameter>cbopaque</parameter> pointer argument that is\n    <constant>NULL</constant> unless overridden by the arguments in a call to\n    <function>malloc_stats_print<parameter/></function>, followed by a string\n    pointer.  Please note that doing anything which tries to allocate memory in\n    this function is likely to result in a crash or deadlock.</para>\n\n    <para>All messages are prefixed by\n    &ldquo;<computeroutput>&lt;jemalloc&gt;: </computeroutput>&rdquo;.</para>\n  </refsect1>\n  <refsect1 id=\"return_values\">\n    <title>RETURN VALUES</title>\n    <refsect2>\n      <title>Standard API</title>\n      <para>The <function>malloc<parameter/></function> and\n      <function>calloc<parameter/></function> functions return a pointer to the\n      allocated memory if successful; otherwise a <constant>NULL</constant>\n      pointer is returned and <varname>errno</varname> is set to\n      <errorname>ENOMEM</errorname>.</para>\n\n      <para>The <function>posix_memalign<parameter/></function> function\n      returns the value 0 if successful; otherwise it returns an error value.\n      The <function>posix_memalign<parameter/></function> function will fail\n      if:\n        <variablelist>\n          <varlistentry>\n            <term><errorname>EINVAL</errorname></term>\n\n            <listitem><para>The <parameter>alignment</parameter> parameter is\n            not a power of 2 at least as large as\n            <code language=\"C\">sizeof(<type>void *</type>)</code>.\n            </para></listitem>\n          </varlistentry>\n          <varlistentry>\n            <term><errorname>ENOMEM</errorname></term>\n\n            <listitem><para>Memory allocation error.</para></listitem>\n          </varlistentry>\n        </variablelist>\n      </para>\n\n      <para>The <function>aligned_alloc<parameter/></function> function returns\n      a pointer to the allocated memory if successful; otherwise a\n      <constant>NULL</constant> pointer is returned and\n      <varname>errno</varname> is set.  The\n      <function>aligned_alloc<parameter/></function> function will fail if:\n        <variablelist>\n          <varlistentry>\n            <term><errorname>EINVAL</errorname></term>\n\n            <listitem><para>The <parameter>alignment</parameter> parameter is\n            not a power of 2.\n            </para></listitem>\n          </varlistentry>\n          <varlistentry>\n            <term><errorname>ENOMEM</errorname></term>\n\n            <listitem><para>Memory allocation error.</para></listitem>\n          </varlistentry>\n        </variablelist>\n      </para>\n\n      <para>The <function>realloc<parameter/></function> function returns a\n      pointer, possibly identical to <parameter>ptr</parameter>, to the\n      allocated memory if successful; otherwise a <constant>NULL</constant>\n      pointer is returned, and <varname>errno</varname> is set to\n      <errorname>ENOMEM</errorname> if the error was the result of an\n      allocation failure.  The <function>realloc<parameter/></function>\n      function always leaves the original buffer intact when an error occurs.\n      </para>\n\n      <para>The <function>free<parameter/></function> function returns no\n      value.</para>\n    </refsect2>\n    <refsect2>\n      <title>Non-standard API</title>\n      <para>The <function>malloc_usable_size<parameter/></function> function\n      returns the usable size of the allocation pointed to by\n      <parameter>ptr</parameter>.  </para>\n\n      <para>The <function>mallctl<parameter/></function>,\n      <function>mallctlnametomib<parameter/></function>, and\n      <function>mallctlbymib<parameter/></function> functions return 0 on\n      success; otherwise they return an error value.  The functions will fail\n      if:\n        <variablelist>\n          <varlistentry>\n            <term><errorname>EINVAL</errorname></term>\n\n            <listitem><para><parameter>newp</parameter> is not\n            <constant>NULL</constant>, and <parameter>newlen</parameter> is too\n            large or too small.  Alternatively, <parameter>*oldlenp</parameter>\n            is too large or too small; in this case as much data as possible\n            are read despite the error.</para></listitem>\n          </varlistentry>\n          <varlistentry>\n            <term><errorname>ENOMEM</errorname></term>\n\n            <listitem><para><parameter>*oldlenp</parameter> is too short to\n            hold the requested value.</para></listitem>\n          </varlistentry>\n          <varlistentry>\n            <term><errorname>ENOENT</errorname></term>\n\n            <listitem><para><parameter>name</parameter> or\n            <parameter>mib</parameter> specifies an unknown/invalid\n            value.</para></listitem>\n          </varlistentry>\n          <varlistentry>\n            <term><errorname>EPERM</errorname></term>\n\n            <listitem><para>Attempt to read or write void value, or attempt to\n            write read-only value.</para></listitem>\n          </varlistentry>\n          <varlistentry>\n            <term><errorname>EAGAIN</errorname></term>\n\n            <listitem><para>A memory allocation failure\n            occurred.</para></listitem>\n          </varlistentry>\n          <varlistentry>\n            <term><errorname>EFAULT</errorname></term>\n\n            <listitem><para>An interface with side effects failed in some way\n            not directly related to <function>mallctl*<parameter/></function>\n            read/write processing.</para></listitem>\n          </varlistentry>\n        </variablelist>\n      </para>\n    </refsect2>\n    <refsect2>\n      <title>Experimental API</title>\n      <para>The <function>allocm<parameter/></function>,\n      <function>rallocm<parameter/></function>,\n      <function>sallocm<parameter/></function>,\n      <function>dallocm<parameter/></function>, and\n      <function>nallocm<parameter/></function> functions return\n      <constant>ALLOCM_SUCCESS</constant> on success; otherwise they return an\n      error value.  The <function>allocm<parameter/></function>,\n      <function>rallocm<parameter/></function>, and\n      <function>nallocm<parameter/></function> functions will fail if:\n        <variablelist>\n          <varlistentry>\n            <term><errorname>ALLOCM_ERR_OOM</errorname></term>\n\n            <listitem><para>Out of memory.  Insufficient contiguous memory was\n            available to service the allocation request.  The\n            <function>allocm<parameter/></function> function additionally sets\n            <parameter>*ptr</parameter> to <constant>NULL</constant>, whereas\n            the <function>rallocm<parameter/></function> function leaves\n            <constant>*ptr</constant> unmodified.</para></listitem>\n          </varlistentry>\n        </variablelist>\n      The <function>rallocm<parameter/></function> function will also\n      fail if:\n        <variablelist>\n          <varlistentry>\n            <term><errorname>ALLOCM_ERR_NOT_MOVED</errorname></term>\n\n            <listitem><para><constant>ALLOCM_NO_MOVE</constant> was specified,\n            but the reallocation request could not be serviced without moving\n            the object.</para></listitem>\n          </varlistentry>\n        </variablelist>\n      </para>\n    </refsect2>\n  </refsect1>\n  <refsect1 id=\"environment\">\n    <title>ENVIRONMENT</title>\n    <para>The following environment variable affects the execution of the\n    allocation functions:\n      <variablelist>\n        <varlistentry>\n          <term><envar>MALLOC_CONF</envar></term>\n\n          <listitem><para>If the environment variable\n          <envar>MALLOC_CONF</envar> is set, the characters it contains\n          will be interpreted as options.</para></listitem>\n        </varlistentry>\n      </variablelist>\n    </para>\n  </refsect1>\n  <refsect1 id=\"examples\">\n    <title>EXAMPLES</title>\n    <para>To dump core whenever a problem occurs:\n      <screen>ln -s 'abort:true' /etc/malloc.conf</screen>\n    </para>\n    <para>To specify in the source a chunk size that is 16 MiB:\n      <programlisting language=\"C\"><![CDATA[\nmalloc_conf = \"lg_chunk:24\";]]></programlisting></para>\n  </refsect1>\n  <refsect1 id=\"see_also\">\n    <title>SEE ALSO</title>\n    <para><citerefentry><refentrytitle>madvise</refentrytitle>\n    <manvolnum>2</manvolnum></citerefentry>,\n    <citerefentry><refentrytitle>mmap</refentrytitle>\n    <manvolnum>2</manvolnum></citerefentry>,\n    <citerefentry><refentrytitle>sbrk</refentrytitle>\n    <manvolnum>2</manvolnum></citerefentry>,\n    <citerefentry><refentrytitle>utrace</refentrytitle>\n    <manvolnum>2</manvolnum></citerefentry>,\n    <citerefentry><refentrytitle>alloca</refentrytitle>\n    <manvolnum>3</manvolnum></citerefentry>,\n    <citerefentry><refentrytitle>atexit</refentrytitle>\n    <manvolnum>3</manvolnum></citerefentry>,\n    <citerefentry><refentrytitle>getpagesize</refentrytitle>\n    <manvolnum>3</manvolnum></citerefentry></para>\n  </refsect1>\n  <refsect1 id=\"standards\">\n    <title>STANDARDS</title>\n    <para>The <function>malloc<parameter/></function>,\n    <function>calloc<parameter/></function>,\n    <function>realloc<parameter/></function>, and\n    <function>free<parameter/></function> functions conform to ISO/IEC\n    9899:1990 (&ldquo;ISO C90&rdquo;).</para>\n\n    <para>The <function>posix_memalign<parameter/></function> function conforms\n    to IEEE Std 1003.1-2001 (&ldquo;POSIX.1&rdquo;).</para>\n  </refsect1>\n</refentry>\n"
  },
  {
    "path": "deps/jemalloc/doc/manpages.xsl.in",
    "content": "<xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" version=\"1.0\">\n  <xsl:import href=\"@XSLROOT@/manpages/docbook.xsl\"/>\n  <xsl:import href=\"@abs_srcroot@doc/stylesheet.xsl\"/>\n</xsl:stylesheet>\n"
  },
  {
    "path": "deps/jemalloc/doc/stylesheet.xsl",
    "content": "<xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" version=\"1.0\">\n  <xsl:param name=\"funcsynopsis.style\">ansi</xsl:param>\n  <xsl:param name=\"function.parens\" select=\"1\"/>\n  <xsl:template match=\"mallctl\">\n    \"<xsl:call-template name=\"inline.monoseq\"/>\"\n  </xsl:template>\n</xsl:stylesheet>\n"
  },
  {
    "path": "deps/jemalloc/include/jemalloc/internal/arena.h",
    "content": "/******************************************************************************/\n#ifdef JEMALLOC_H_TYPES\n\n/*\n * RUN_MAX_OVRHD indicates maximum desired run header overhead.  Runs are sized\n * as small as possible such that this setting is still honored, without\n * violating other constraints.  The goal is to make runs as small as possible\n * without exceeding a per run external fragmentation threshold.\n *\n * We use binary fixed point math for overhead computations, where the binary\n * point is implicitly RUN_BFP bits to the left.\n *\n * Note that it is possible to set RUN_MAX_OVRHD low enough that it cannot be\n * honored for some/all object sizes, since when heap profiling is enabled\n * there is one pointer of header overhead per object (plus a constant).  This\n * constraint is relaxed (ignored) for runs that are so small that the\n * per-region overhead is greater than:\n *\n *   (RUN_MAX_OVRHD / (reg_interval << (3+RUN_BFP))\n */\n#define\tRUN_BFP\t\t\t12\n/*                                    \\/   Implicit binary fixed point. */\n#define\tRUN_MAX_OVRHD\t\t0x0000003dU\n#define\tRUN_MAX_OVRHD_RELAX\t0x00001800U\n\n/* Maximum number of regions in one run. */\n#define\tLG_RUN_MAXREGS\t\t11\n#define\tRUN_MAXREGS\t\t(1U << LG_RUN_MAXREGS)\n\n/*\n * Minimum redzone size.  Redzones may be larger than this if necessary to\n * preserve region alignment.\n */\n#define\tREDZONE_MINSIZE\t\t16\n\n/*\n * The minimum ratio of active:dirty pages per arena is computed as:\n *\n *   (nactive >> opt_lg_dirty_mult) >= ndirty\n *\n * So, supposing that opt_lg_dirty_mult is 3, there can be no less than 8 times\n * as many active pages as dirty pages.\n */\n#define\tLG_DIRTY_MULT_DEFAULT\t3\n\ntypedef struct arena_chunk_map_s arena_chunk_map_t;\ntypedef struct arena_chunk_s arena_chunk_t;\ntypedef struct arena_run_s arena_run_t;\ntypedef struct arena_bin_info_s arena_bin_info_t;\ntypedef struct arena_bin_s arena_bin_t;\ntypedef struct arena_s arena_t;\n\n#endif /* JEMALLOC_H_TYPES */\n/******************************************************************************/\n#ifdef JEMALLOC_H_STRUCTS\n\n/* Each element of the chunk map corresponds to one page within the chunk. */\nstruct arena_chunk_map_s {\n#ifndef JEMALLOC_PROF\n\t/*\n\t * Overlay prof_ctx in order to allow it to be referenced by dead code.\n\t * Such antics aren't warranted for per arena data structures, but\n\t * chunk map overhead accounts for a percentage of memory, rather than\n\t * being just a fixed cost.\n\t */\n\tunion {\n#endif\n\tunion {\n\t\t/*\n\t\t * Linkage for run trees.  There are two disjoint uses:\n\t\t *\n\t\t * 1) arena_t's runs_avail tree.\n\t\t * 2) arena_run_t conceptually uses this linkage for in-use\n\t\t *    non-full runs, rather than directly embedding linkage.\n\t\t */\n\t\trb_node(arena_chunk_map_t)\trb_link;\n\t\t/*\n\t\t * List of runs currently in purgatory.  arena_chunk_purge()\n\t\t * temporarily allocates runs that contain dirty pages while\n\t\t * purging, so that other threads cannot use the runs while the\n\t\t * purging thread is operating without the arena lock held.\n\t\t */\n\t\tql_elm(arena_chunk_map_t)\tql_link;\n\t}\t\t\t\tu;\n\n\t/* Profile counters, used for large object runs. */\n\tprof_ctx_t\t\t\t*prof_ctx;\n#ifndef JEMALLOC_PROF\n\t}; /* union { ... }; */\n#endif\n\n\t/*\n\t * Run address (or size) and various flags are stored together.  The bit\n\t * layout looks like (assuming 32-bit system):\n\t *\n\t *   ???????? ???????? ????nnnn nnnndula\n\t *\n\t * ? : Unallocated: Run address for first/last pages, unset for internal\n\t *                  pages.\n\t *     Small: Run page offset.\n\t *     Large: Run size for first page, unset for trailing pages.\n\t * n : binind for small size class, BININD_INVALID for large size class.\n\t * d : dirty?\n\t * u : unzeroed?\n\t * l : large?\n\t * a : allocated?\n\t *\n\t * Following are example bit patterns for the three types of runs.\n\t *\n\t * p : run page offset\n\t * s : run size\n\t * n : binind for size class; large objects set these to BININD_INVALID\n\t *     except for promoted allocations (see prof_promote)\n\t * x : don't care\n\t * - : 0\n\t * + : 1\n\t * [DULA] : bit set\n\t * [dula] : bit unset\n\t *\n\t *   Unallocated (clean):\n\t *     ssssssss ssssssss ssss++++ ++++du-a\n\t *     xxxxxxxx xxxxxxxx xxxxxxxx xxxx-Uxx\n\t *     ssssssss ssssssss ssss++++ ++++dU-a\n\t *\n\t *   Unallocated (dirty):\n\t *     ssssssss ssssssss ssss++++ ++++D--a\n\t *     xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx\n\t *     ssssssss ssssssss ssss++++ ++++D--a\n\t *\n\t *   Small:\n\t *     pppppppp pppppppp ppppnnnn nnnnd--A\n\t *     pppppppp pppppppp ppppnnnn nnnn---A\n\t *     pppppppp pppppppp ppppnnnn nnnnd--A\n\t *\n\t *   Large:\n\t *     ssssssss ssssssss ssss++++ ++++D-LA\n\t *     xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx\n\t *     -------- -------- ----++++ ++++D-LA\n\t *\n\t *   Large (sampled, size <= PAGE):\n\t *     ssssssss ssssssss ssssnnnn nnnnD-LA\n\t *\n\t *   Large (not sampled, size == PAGE):\n\t *     ssssssss ssssssss ssss++++ ++++D-LA\n\t */\n\tsize_t\t\t\t\tbits;\n#define\tCHUNK_MAP_BININD_SHIFT\t4\n#define\tBININD_INVALID\t\t((size_t)0xffU)\n/*     CHUNK_MAP_BININD_MASK == (BININD_INVALID << CHUNK_MAP_BININD_SHIFT) */\n#define\tCHUNK_MAP_BININD_MASK\t((size_t)0xff0U)\n#define\tCHUNK_MAP_BININD_INVALID CHUNK_MAP_BININD_MASK\n#define\tCHUNK_MAP_FLAGS_MASK\t((size_t)0xcU)\n#define\tCHUNK_MAP_DIRTY\t\t((size_t)0x8U)\n#define\tCHUNK_MAP_UNZEROED\t((size_t)0x4U)\n#define\tCHUNK_MAP_LARGE\t\t((size_t)0x2U)\n#define\tCHUNK_MAP_ALLOCATED\t((size_t)0x1U)\n#define\tCHUNK_MAP_KEY\t\tCHUNK_MAP_ALLOCATED\n};\ntypedef rb_tree(arena_chunk_map_t) arena_avail_tree_t;\ntypedef rb_tree(arena_chunk_map_t) arena_run_tree_t;\n\n/* Arena chunk header. */\nstruct arena_chunk_s {\n\t/* Arena that owns the chunk. */\n\tarena_t\t\t\t*arena;\n\n\t/* Linkage for tree of arena chunks that contain dirty runs. */\n\trb_node(arena_chunk_t)\tdirty_link;\n\n\t/* Number of dirty pages. */\n\tsize_t\t\t\tndirty;\n\n\t/* Number of available runs. */\n\tsize_t\t\t\tnruns_avail;\n\n\t/*\n\t * Number of available run adjacencies.  Clean and dirty available runs\n\t * are not coalesced, which causes virtual memory fragmentation.  The\n\t * ratio of (nruns_avail-nruns_adjac):nruns_adjac is used for tracking\n\t * this fragmentation.\n\t * */\n\tsize_t\t\t\tnruns_adjac;\n\n\t/*\n\t * Map of pages within chunk that keeps track of free/large/small.  The\n\t * first map_bias entries are omitted, since the chunk header does not\n\t * need to be tracked in the map.  This omission saves a header page\n\t * for common chunk sizes (e.g. 4 MiB).\n\t */\n\tarena_chunk_map_t\tmap[1]; /* Dynamically sized. */\n};\ntypedef rb_tree(arena_chunk_t) arena_chunk_tree_t;\n\nstruct arena_run_s {\n\t/* Bin this run is associated with. */\n\tarena_bin_t\t*bin;\n\n\t/* Index of next region that has never been allocated, or nregs. */\n\tuint32_t\tnextind;\n\n\t/* Number of free regions in run. */\n\tunsigned\tnfree;\n};\n\n/*\n * Read-only information associated with each element of arena_t's bins array\n * is stored separately, partly to reduce memory usage (only one copy, rather\n * than one per arena), but mainly to avoid false cacheline sharing.\n *\n * Each run has the following layout:\n *\n *               /--------------------\\\n *               | arena_run_t header |\n *               | ...                |\n * bitmap_offset | bitmap             |\n *               | ...                |\n *   ctx0_offset | ctx map            |\n *               | ...                |\n *               |--------------------|\n *               | redzone            |\n *   reg0_offset | region 0           |\n *               | redzone            |\n *               |--------------------| \\\n *               | redzone            | |\n *               | region 1           |  > reg_interval\n *               | redzone            | /\n *               |--------------------|\n *               | ...                |\n *               | ...                |\n *               | ...                |\n *               |--------------------|\n *               | redzone            |\n *               | region nregs-1     |\n *               | redzone            |\n *               |--------------------|\n *               | alignment pad?     |\n *               \\--------------------/\n *\n * reg_interval has at least the same minimum alignment as reg_size; this\n * preserves the alignment constraint that sa2u() depends on.  Alignment pad is\n * either 0 or redzone_size; it is present only if needed to align reg0_offset.\n */\nstruct arena_bin_info_s {\n\t/* Size of regions in a run for this bin's size class. */\n\tsize_t\t\treg_size;\n\n\t/* Redzone size. */\n\tsize_t\t\tredzone_size;\n\n\t/* Interval between regions (reg_size + (redzone_size << 1)). */\n\tsize_t\t\treg_interval;\n\n\t/* Total size of a run for this bin's size class. */\n\tsize_t\t\trun_size;\n\n\t/* Total number of regions in a run for this bin's size class. */\n\tuint32_t\tnregs;\n\n\t/*\n\t * Offset of first bitmap_t element in a run header for this bin's size\n\t * class.\n\t */\n\tuint32_t\tbitmap_offset;\n\n\t/*\n\t * Metadata used to manipulate bitmaps for runs associated with this\n\t * bin.\n\t */\n\tbitmap_info_t\tbitmap_info;\n\n\t/*\n\t * Offset of first (prof_ctx_t *) in a run header for this bin's size\n\t * class, or 0 if (config_prof == false || opt_prof == false).\n\t */\n\tuint32_t\tctx0_offset;\n\n\t/* Offset of first region in a run for this bin's size class. */\n\tuint32_t\treg0_offset;\n};\n\nstruct arena_bin_s {\n\t/*\n\t * All operations on runcur, runs, and stats require that lock be\n\t * locked.  Run allocation/deallocation are protected by the arena lock,\n\t * which may be acquired while holding one or more bin locks, but not\n\t * vise versa.\n\t */\n\tmalloc_mutex_t\tlock;\n\n\t/*\n\t * Current run being used to service allocations of this bin's size\n\t * class.\n\t */\n\tarena_run_t\t*runcur;\n\n\t/*\n\t * Tree of non-full runs.  This tree is used when looking for an\n\t * existing run when runcur is no longer usable.  We choose the\n\t * non-full run that is lowest in memory; this policy tends to keep\n\t * objects packed well, and it can also help reduce the number of\n\t * almost-empty chunks.\n\t */\n\tarena_run_tree_t runs;\n\n\t/* Bin statistics. */\n\tmalloc_bin_stats_t stats;\n};\n\nstruct arena_s {\n\t/* This arena's index within the arenas array. */\n\tunsigned\t\tind;\n\n\t/*\n\t * Number of threads currently assigned to this arena.  This field is\n\t * protected by arenas_lock.\n\t */\n\tunsigned\t\tnthreads;\n\n\t/*\n\t * There are three classes of arena operations from a locking\n\t * perspective:\n\t * 1) Thread asssignment (modifies nthreads) is protected by\n\t *    arenas_lock.\n\t * 2) Bin-related operations are protected by bin locks.\n\t * 3) Chunk- and run-related operations are protected by this mutex.\n\t */\n\tmalloc_mutex_t\t\tlock;\n\n\tarena_stats_t\t\tstats;\n\t/*\n\t * List of tcaches for extant threads associated with this arena.\n\t * Stats from these are merged incrementally, and at exit.\n\t */\n\tql_head(tcache_t)\ttcache_ql;\n\n\tuint64_t\t\tprof_accumbytes;\n\n\tdss_prec_t\t\tdss_prec;\n\n\t/* Tree of dirty-page-containing chunks this arena manages. */\n\tarena_chunk_tree_t\tchunks_dirty;\n\n\t/*\n\t * In order to avoid rapid chunk allocation/deallocation when an arena\n\t * oscillates right on the cusp of needing a new chunk, cache the most\n\t * recently freed chunk.  The spare is left in the arena's chunk trees\n\t * until it is deleted.\n\t *\n\t * There is one spare chunk per arena, rather than one spare total, in\n\t * order to avoid interactions between multiple threads that could make\n\t * a single spare inadequate.\n\t */\n\tarena_chunk_t\t\t*spare;\n\n\t/* Number of pages in active runs. */\n\tsize_t\t\t\tnactive;\n\n\t/*\n\t * Current count of pages within unused runs that are potentially\n\t * dirty, and for which madvise(... MADV_DONTNEED) has not been called.\n\t * By tracking this, we can institute a limit on how much dirty unused\n\t * memory is mapped for each arena.\n\t */\n\tsize_t\t\t\tndirty;\n\n\t/*\n\t * Approximate number of pages being purged.  It is possible for\n\t * multiple threads to purge dirty pages concurrently, and they use\n\t * npurgatory to indicate the total number of pages all threads are\n\t * attempting to purge.\n\t */\n\tsize_t\t\t\tnpurgatory;\n\n\t/*\n\t * Size/address-ordered trees of this arena's available runs.  The trees\n\t * are used for first-best-fit run allocation.\n\t */\n\tarena_avail_tree_t\truns_avail;\n\n\t/* bins is used to store trees of free regions. */\n\tarena_bin_t\t\tbins[NBINS];\n};\n\n#endif /* JEMALLOC_H_STRUCTS */\n/******************************************************************************/\n#ifdef JEMALLOC_H_EXTERNS\n\nextern ssize_t\topt_lg_dirty_mult;\n/*\n * small_size2bin is a compact lookup table that rounds request sizes up to\n * size classes.  In order to reduce cache footprint, the table is compressed,\n * and all accesses are via the SMALL_SIZE2BIN macro.\n */\nextern uint8_t const\tsmall_size2bin[];\n#define\tSMALL_SIZE2BIN(s)\t(small_size2bin[(s-1) >> LG_TINY_MIN])\n\nextern arena_bin_info_t\tarena_bin_info[NBINS];\n\n/* Number of large size classes. */\n#define\t\t\tnlclasses (chunk_npages - map_bias)\n\nvoid\tarena_purge_all(arena_t *arena);\nvoid\tarena_prof_accum(arena_t *arena, uint64_t accumbytes);\nvoid\tarena_tcache_fill_small(arena_t *arena, tcache_bin_t *tbin,\n    size_t binind, uint64_t prof_accumbytes);\nvoid\tarena_alloc_junk_small(void *ptr, arena_bin_info_t *bin_info,\n    bool zero);\nvoid\tarena_dalloc_junk_small(void *ptr, arena_bin_info_t *bin_info);\nvoid\t*arena_malloc_small(arena_t *arena, size_t size, bool zero);\nvoid\t*arena_malloc_large(arena_t *arena, size_t size, bool zero);\nvoid\t*arena_palloc(arena_t *arena, size_t size, size_t alignment, bool zero);\nvoid\tarena_prof_promoted(const void *ptr, size_t size);\nvoid\tarena_dalloc_bin_locked(arena_t *arena, arena_chunk_t *chunk, void *ptr,\n    arena_chunk_map_t *mapelm);\nvoid\tarena_dalloc_bin(arena_t *arena, arena_chunk_t *chunk, void *ptr,\n    size_t pageind, arena_chunk_map_t *mapelm);\nvoid\tarena_dalloc_small(arena_t *arena, arena_chunk_t *chunk, void *ptr,\n    size_t pageind);\nvoid\tarena_dalloc_large_locked(arena_t *arena, arena_chunk_t *chunk,\n    void *ptr);\nvoid\tarena_dalloc_large(arena_t *arena, arena_chunk_t *chunk, void *ptr);\nvoid\t*arena_ralloc_no_move(void *ptr, size_t oldsize, size_t size,\n    size_t extra, bool zero);\nvoid\t*arena_ralloc(arena_t *arena, void *ptr, size_t oldsize, size_t size,\n    size_t extra, size_t alignment, bool zero, bool try_tcache_alloc,\n    bool try_tcache_dalloc);\ndss_prec_t\tarena_dss_prec_get(arena_t *arena);\nvoid\tarena_dss_prec_set(arena_t *arena, dss_prec_t dss_prec);\nvoid\tarena_stats_merge(arena_t *arena, const char **dss, size_t *nactive,\n    size_t *ndirty, arena_stats_t *astats, malloc_bin_stats_t *bstats,\n    malloc_large_stats_t *lstats);\nbool\tarena_new(arena_t *arena, unsigned ind);\nvoid\tarena_boot(void);\nvoid\tarena_prefork(arena_t *arena);\nvoid\tarena_postfork_parent(arena_t *arena);\nvoid\tarena_postfork_child(arena_t *arena);\n\n#endif /* JEMALLOC_H_EXTERNS */\n/******************************************************************************/\n#ifdef JEMALLOC_H_INLINES\n\n#ifndef JEMALLOC_ENABLE_INLINE\narena_chunk_map_t\t*arena_mapp_get(arena_chunk_t *chunk, size_t pageind);\nsize_t\t*arena_mapbitsp_get(arena_chunk_t *chunk, size_t pageind);\nsize_t\tarena_mapbits_get(arena_chunk_t *chunk, size_t pageind);\nsize_t\tarena_mapbits_unallocated_size_get(arena_chunk_t *chunk,\n    size_t pageind);\nsize_t\tarena_mapbits_large_size_get(arena_chunk_t *chunk, size_t pageind);\nsize_t\tarena_mapbits_small_runind_get(arena_chunk_t *chunk, size_t pageind);\nsize_t\tarena_mapbits_binind_get(arena_chunk_t *chunk, size_t pageind);\nsize_t\tarena_mapbits_dirty_get(arena_chunk_t *chunk, size_t pageind);\nsize_t\tarena_mapbits_unzeroed_get(arena_chunk_t *chunk, size_t pageind);\nsize_t\tarena_mapbits_large_get(arena_chunk_t *chunk, size_t pageind);\nsize_t\tarena_mapbits_allocated_get(arena_chunk_t *chunk, size_t pageind);\nvoid\tarena_mapbits_unallocated_set(arena_chunk_t *chunk, size_t pageind,\n    size_t size, size_t flags);\nvoid\tarena_mapbits_unallocated_size_set(arena_chunk_t *chunk, size_t pageind,\n    size_t size);\nvoid\tarena_mapbits_large_set(arena_chunk_t *chunk, size_t pageind,\n    size_t size, size_t flags);\nvoid\tarena_mapbits_large_binind_set(arena_chunk_t *chunk, size_t pageind,\n    size_t binind);\nvoid\tarena_mapbits_small_set(arena_chunk_t *chunk, size_t pageind,\n    size_t runind, size_t binind, size_t flags);\nvoid\tarena_mapbits_unzeroed_set(arena_chunk_t *chunk, size_t pageind,\n    size_t unzeroed);\nsize_t\tarena_ptr_small_binind_get(const void *ptr, size_t mapbits);\nsize_t\tarena_bin_index(arena_t *arena, arena_bin_t *bin);\nunsigned\tarena_run_regind(arena_run_t *run, arena_bin_info_t *bin_info,\n    const void *ptr);\nprof_ctx_t\t*arena_prof_ctx_get(const void *ptr);\nvoid\tarena_prof_ctx_set(const void *ptr, prof_ctx_t *ctx);\nvoid\t*arena_malloc(arena_t *arena, size_t size, bool zero, bool try_tcache);\nsize_t\tarena_salloc(const void *ptr, bool demote);\nvoid\tarena_dalloc(arena_t *arena, arena_chunk_t *chunk, void *ptr,\n    bool try_tcache);\n#endif\n\n#if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_ARENA_C_))\n#  ifdef JEMALLOC_ARENA_INLINE_A\nJEMALLOC_INLINE arena_chunk_map_t *\narena_mapp_get(arena_chunk_t *chunk, size_t pageind)\n{\n\n\tassert(pageind >= map_bias);\n\tassert(pageind < chunk_npages);\n\n\treturn (&chunk->map[pageind-map_bias]);\n}\n\nJEMALLOC_INLINE size_t *\narena_mapbitsp_get(arena_chunk_t *chunk, size_t pageind)\n{\n\n\treturn (&arena_mapp_get(chunk, pageind)->bits);\n}\n\nJEMALLOC_INLINE size_t\narena_mapbits_get(arena_chunk_t *chunk, size_t pageind)\n{\n\n\treturn (*arena_mapbitsp_get(chunk, pageind));\n}\n\nJEMALLOC_INLINE size_t\narena_mapbits_unallocated_size_get(arena_chunk_t *chunk, size_t pageind)\n{\n\tsize_t mapbits;\n\n\tmapbits = arena_mapbits_get(chunk, pageind);\n\tassert((mapbits & (CHUNK_MAP_LARGE|CHUNK_MAP_ALLOCATED)) == 0);\n\treturn (mapbits & ~PAGE_MASK);\n}\n\nJEMALLOC_INLINE size_t\narena_mapbits_large_size_get(arena_chunk_t *chunk, size_t pageind)\n{\n\tsize_t mapbits;\n\n\tmapbits = arena_mapbits_get(chunk, pageind);\n\tassert((mapbits & (CHUNK_MAP_LARGE|CHUNK_MAP_ALLOCATED)) ==\n\t    (CHUNK_MAP_LARGE|CHUNK_MAP_ALLOCATED));\n\treturn (mapbits & ~PAGE_MASK);\n}\n\nJEMALLOC_INLINE size_t\narena_mapbits_small_runind_get(arena_chunk_t *chunk, size_t pageind)\n{\n\tsize_t mapbits;\n\n\tmapbits = arena_mapbits_get(chunk, pageind);\n\tassert((mapbits & (CHUNK_MAP_LARGE|CHUNK_MAP_ALLOCATED)) ==\n\t    CHUNK_MAP_ALLOCATED);\n\treturn (mapbits >> LG_PAGE);\n}\n\nJEMALLOC_INLINE size_t\narena_mapbits_binind_get(arena_chunk_t *chunk, size_t pageind)\n{\n\tsize_t mapbits;\n\tsize_t binind;\n\n\tmapbits = arena_mapbits_get(chunk, pageind);\n\tbinind = (mapbits & CHUNK_MAP_BININD_MASK) >> CHUNK_MAP_BININD_SHIFT;\n\tassert(binind < NBINS || binind == BININD_INVALID);\n\treturn (binind);\n}\n\nJEMALLOC_INLINE size_t\narena_mapbits_dirty_get(arena_chunk_t *chunk, size_t pageind)\n{\n\tsize_t mapbits;\n\n\tmapbits = arena_mapbits_get(chunk, pageind);\n\treturn (mapbits & CHUNK_MAP_DIRTY);\n}\n\nJEMALLOC_INLINE size_t\narena_mapbits_unzeroed_get(arena_chunk_t *chunk, size_t pageind)\n{\n\tsize_t mapbits;\n\n\tmapbits = arena_mapbits_get(chunk, pageind);\n\treturn (mapbits & CHUNK_MAP_UNZEROED);\n}\n\nJEMALLOC_INLINE size_t\narena_mapbits_large_get(arena_chunk_t *chunk, size_t pageind)\n{\n\tsize_t mapbits;\n\n\tmapbits = arena_mapbits_get(chunk, pageind);\n\treturn (mapbits & CHUNK_MAP_LARGE);\n}\n\nJEMALLOC_INLINE size_t\narena_mapbits_allocated_get(arena_chunk_t *chunk, size_t pageind)\n{\n\tsize_t mapbits;\n\n\tmapbits = arena_mapbits_get(chunk, pageind);\n\treturn (mapbits & CHUNK_MAP_ALLOCATED);\n}\n\nJEMALLOC_INLINE void\narena_mapbits_unallocated_set(arena_chunk_t *chunk, size_t pageind, size_t size,\n    size_t flags)\n{\n\tsize_t *mapbitsp;\n\n\tmapbitsp = arena_mapbitsp_get(chunk, pageind);\n\tassert((size & PAGE_MASK) == 0);\n\tassert((flags & ~CHUNK_MAP_FLAGS_MASK) == 0);\n\tassert((flags & (CHUNK_MAP_DIRTY|CHUNK_MAP_UNZEROED)) == flags);\n\t*mapbitsp = size | CHUNK_MAP_BININD_INVALID | flags;\n}\n\nJEMALLOC_INLINE void\narena_mapbits_unallocated_size_set(arena_chunk_t *chunk, size_t pageind,\n    size_t size)\n{\n\tsize_t *mapbitsp;\n\n\tmapbitsp = arena_mapbitsp_get(chunk, pageind);\n\tassert((size & PAGE_MASK) == 0);\n\tassert((*mapbitsp & (CHUNK_MAP_LARGE|CHUNK_MAP_ALLOCATED)) == 0);\n\t*mapbitsp = size | (*mapbitsp & PAGE_MASK);\n}\n\nJEMALLOC_INLINE void\narena_mapbits_large_set(arena_chunk_t *chunk, size_t pageind, size_t size,\n    size_t flags)\n{\n\tsize_t *mapbitsp;\n\tsize_t unzeroed;\n\n\tmapbitsp = arena_mapbitsp_get(chunk, pageind);\n\tassert((size & PAGE_MASK) == 0);\n\tassert((flags & CHUNK_MAP_DIRTY) == flags);\n\tunzeroed = *mapbitsp & CHUNK_MAP_UNZEROED; /* Preserve unzeroed. */\n\t*mapbitsp = size | CHUNK_MAP_BININD_INVALID | flags | unzeroed |\n\t    CHUNK_MAP_LARGE | CHUNK_MAP_ALLOCATED;\n}\n\nJEMALLOC_INLINE void\narena_mapbits_large_binind_set(arena_chunk_t *chunk, size_t pageind,\n    size_t binind)\n{\n\tsize_t *mapbitsp;\n\n\tassert(binind <= BININD_INVALID);\n\tmapbitsp = arena_mapbitsp_get(chunk, pageind);\n\tassert(arena_mapbits_large_size_get(chunk, pageind) == PAGE);\n\t*mapbitsp = (*mapbitsp & ~CHUNK_MAP_BININD_MASK) | (binind <<\n\t    CHUNK_MAP_BININD_SHIFT);\n}\n\nJEMALLOC_INLINE void\narena_mapbits_small_set(arena_chunk_t *chunk, size_t pageind, size_t runind,\n    size_t binind, size_t flags)\n{\n\tsize_t *mapbitsp;\n\tsize_t unzeroed;\n\n\tassert(binind < BININD_INVALID);\n\tmapbitsp = arena_mapbitsp_get(chunk, pageind);\n\tassert(pageind - runind >= map_bias);\n\tassert((flags & CHUNK_MAP_DIRTY) == flags);\n\tunzeroed = *mapbitsp & CHUNK_MAP_UNZEROED; /* Preserve unzeroed. */\n\t*mapbitsp = (runind << LG_PAGE) | (binind << CHUNK_MAP_BININD_SHIFT) |\n\t    flags | unzeroed | CHUNK_MAP_ALLOCATED;\n}\n\nJEMALLOC_INLINE void\narena_mapbits_unzeroed_set(arena_chunk_t *chunk, size_t pageind,\n    size_t unzeroed)\n{\n\tsize_t *mapbitsp;\n\n\tmapbitsp = arena_mapbitsp_get(chunk, pageind);\n\t*mapbitsp = (*mapbitsp & ~CHUNK_MAP_UNZEROED) | unzeroed;\n}\n\nJEMALLOC_INLINE size_t\narena_ptr_small_binind_get(const void *ptr, size_t mapbits)\n{\n\tsize_t binind;\n\n\tbinind = (mapbits & CHUNK_MAP_BININD_MASK) >> CHUNK_MAP_BININD_SHIFT;\n\n\tif (config_debug) {\n\t\tarena_chunk_t *chunk;\n\t\tarena_t *arena;\n\t\tsize_t pageind;\n\t\tsize_t actual_mapbits;\n\t\tarena_run_t *run;\n\t\tarena_bin_t *bin;\n\t\tsize_t actual_binind;\n\t\tarena_bin_info_t *bin_info;\n\n\t\tassert(binind != BININD_INVALID);\n\t\tassert(binind < NBINS);\n\t\tchunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);\n\t\tarena = chunk->arena;\n\t\tpageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> LG_PAGE;\n\t\tactual_mapbits = arena_mapbits_get(chunk, pageind);\n\t\tassert(mapbits == actual_mapbits);\n\t\tassert(arena_mapbits_large_get(chunk, pageind) == 0);\n\t\tassert(arena_mapbits_allocated_get(chunk, pageind) != 0);\n\t\trun = (arena_run_t *)((uintptr_t)chunk + (uintptr_t)((pageind -\n\t\t    (actual_mapbits >> LG_PAGE)) << LG_PAGE));\n\t\tbin = run->bin;\n\t\tactual_binind = bin - arena->bins;\n\t\tassert(binind == actual_binind);\n\t\tbin_info = &arena_bin_info[actual_binind];\n\t\tassert(((uintptr_t)ptr - ((uintptr_t)run +\n\t\t    (uintptr_t)bin_info->reg0_offset)) % bin_info->reg_interval\n\t\t    == 0);\n\t}\n\n\treturn (binind);\n}\n#  endif /* JEMALLOC_ARENA_INLINE_A */\n\n#  ifdef JEMALLOC_ARENA_INLINE_B\nJEMALLOC_INLINE size_t\narena_bin_index(arena_t *arena, arena_bin_t *bin)\n{\n\tsize_t binind = bin - arena->bins;\n\tassert(binind < NBINS);\n\treturn (binind);\n}\n\nJEMALLOC_INLINE unsigned\narena_run_regind(arena_run_t *run, arena_bin_info_t *bin_info, const void *ptr)\n{\n\tunsigned shift, diff, regind;\n\tsize_t interval;\n\n\t/*\n\t * Freeing a pointer lower than region zero can cause assertion\n\t * failure.\n\t */\n\tassert((uintptr_t)ptr >= (uintptr_t)run +\n\t    (uintptr_t)bin_info->reg0_offset);\n\n\t/*\n\t * Avoid doing division with a variable divisor if possible.  Using\n\t * actual division here can reduce allocator throughput by over 20%!\n\t */\n\tdiff = (unsigned)((uintptr_t)ptr - (uintptr_t)run -\n\t    bin_info->reg0_offset);\n\n\t/* Rescale (factor powers of 2 out of the numerator and denominator). */\n\tinterval = bin_info->reg_interval;\n\tshift = ffs(interval) - 1;\n\tdiff >>= shift;\n\tinterval >>= shift;\n\n\tif (interval == 1) {\n\t\t/* The divisor was a power of 2. */\n\t\tregind = diff;\n\t} else {\n\t\t/*\n\t\t * To divide by a number D that is not a power of two we\n\t\t * multiply by (2^21 / D) and then right shift by 21 positions.\n\t\t *\n\t\t *   X / D\n\t\t *\n\t\t * becomes\n\t\t *\n\t\t *   (X * interval_invs[D - 3]) >> SIZE_INV_SHIFT\n\t\t *\n\t\t * We can omit the first three elements, because we never\n\t\t * divide by 0, and 1 and 2 are both powers of two, which are\n\t\t * handled above.\n\t\t */\n#define\tSIZE_INV_SHIFT\t((sizeof(unsigned) << 3) - LG_RUN_MAXREGS)\n#define\tSIZE_INV(s)\t(((1U << SIZE_INV_SHIFT) / (s)) + 1)\n\t\tstatic const unsigned interval_invs[] = {\n\t\t    SIZE_INV(3),\n\t\t    SIZE_INV(4), SIZE_INV(5), SIZE_INV(6), SIZE_INV(7),\n\t\t    SIZE_INV(8), SIZE_INV(9), SIZE_INV(10), SIZE_INV(11),\n\t\t    SIZE_INV(12), SIZE_INV(13), SIZE_INV(14), SIZE_INV(15),\n\t\t    SIZE_INV(16), SIZE_INV(17), SIZE_INV(18), SIZE_INV(19),\n\t\t    SIZE_INV(20), SIZE_INV(21), SIZE_INV(22), SIZE_INV(23),\n\t\t    SIZE_INV(24), SIZE_INV(25), SIZE_INV(26), SIZE_INV(27),\n\t\t    SIZE_INV(28), SIZE_INV(29), SIZE_INV(30), SIZE_INV(31)\n\t\t};\n\n\t\tif (interval <= ((sizeof(interval_invs) / sizeof(unsigned)) +\n\t\t    2)) {\n\t\t\tregind = (diff * interval_invs[interval - 3]) >>\n\t\t\t    SIZE_INV_SHIFT;\n\t\t} else\n\t\t\tregind = diff / interval;\n#undef SIZE_INV\n#undef SIZE_INV_SHIFT\n\t}\n\tassert(diff == regind * interval);\n\tassert(regind < bin_info->nregs);\n\n\treturn (regind);\n}\n\nJEMALLOC_INLINE prof_ctx_t *\narena_prof_ctx_get(const void *ptr)\n{\n\tprof_ctx_t *ret;\n\tarena_chunk_t *chunk;\n\tsize_t pageind, mapbits;\n\n\tcassert(config_prof);\n\tassert(ptr != NULL);\n\tassert(CHUNK_ADDR2BASE(ptr) != ptr);\n\n\tchunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);\n\tpageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> LG_PAGE;\n\tmapbits = arena_mapbits_get(chunk, pageind);\n\tassert((mapbits & CHUNK_MAP_ALLOCATED) != 0);\n\tif ((mapbits & CHUNK_MAP_LARGE) == 0) {\n\t\tif (prof_promote)\n\t\t\tret = (prof_ctx_t *)(uintptr_t)1U;\n\t\telse {\n\t\t\tarena_run_t *run = (arena_run_t *)((uintptr_t)chunk +\n\t\t\t    (uintptr_t)((pageind - (mapbits >> LG_PAGE)) <<\n\t\t\t    LG_PAGE));\n\t\t\tsize_t binind = arena_ptr_small_binind_get(ptr,\n\t\t\t    mapbits);\n\t\t\tarena_bin_info_t *bin_info = &arena_bin_info[binind];\n\t\t\tunsigned regind;\n\n\t\t\tregind = arena_run_regind(run, bin_info, ptr);\n\t\t\tret = *(prof_ctx_t **)((uintptr_t)run +\n\t\t\t    bin_info->ctx0_offset + (regind *\n\t\t\t    sizeof(prof_ctx_t *)));\n\t\t}\n\t} else\n\t\tret = arena_mapp_get(chunk, pageind)->prof_ctx;\n\n\treturn (ret);\n}\n\nJEMALLOC_INLINE void\narena_prof_ctx_set(const void *ptr, prof_ctx_t *ctx)\n{\n\tarena_chunk_t *chunk;\n\tsize_t pageind, mapbits;\n\n\tcassert(config_prof);\n\tassert(ptr != NULL);\n\tassert(CHUNK_ADDR2BASE(ptr) != ptr);\n\n\tchunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);\n\tpageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> LG_PAGE;\n\tmapbits = arena_mapbits_get(chunk, pageind);\n\tassert((mapbits & CHUNK_MAP_ALLOCATED) != 0);\n\tif ((mapbits & CHUNK_MAP_LARGE) == 0) {\n\t\tif (prof_promote == false) {\n\t\t\tarena_run_t *run = (arena_run_t *)((uintptr_t)chunk +\n\t\t\t    (uintptr_t)((pageind - (mapbits >> LG_PAGE)) <<\n\t\t\t    LG_PAGE));\n\t\t\tsize_t binind;\n\t\t\tarena_bin_info_t *bin_info;\n\t\t\tunsigned regind;\n\n\t\t\tbinind = arena_ptr_small_binind_get(ptr, mapbits);\n\t\t\tbin_info = &arena_bin_info[binind];\n\t\t\tregind = arena_run_regind(run, bin_info, ptr);\n\n\t\t\t*((prof_ctx_t **)((uintptr_t)run + bin_info->ctx0_offset\n\t\t\t    + (regind * sizeof(prof_ctx_t *)))) = ctx;\n\t\t} else\n\t\t\tassert((uintptr_t)ctx == (uintptr_t)1U);\n\t} else\n\t\tarena_mapp_get(chunk, pageind)->prof_ctx = ctx;\n}\n\nJEMALLOC_INLINE void *\narena_malloc(arena_t *arena, size_t size, bool zero, bool try_tcache)\n{\n\ttcache_t *tcache;\n\n\tassert(size != 0);\n\tassert(size <= arena_maxclass);\n\n\tif (size <= SMALL_MAXCLASS) {\n\t\tif (try_tcache && (tcache = tcache_get(true)) != NULL)\n\t\t\treturn (tcache_alloc_small(tcache, size, zero));\n\t\telse {\n\t\t\treturn (arena_malloc_small(choose_arena(arena), size,\n\t\t\t    zero));\n\t\t}\n\t} else {\n\t\t/*\n\t\t * Initialize tcache after checking size in order to avoid\n\t\t * infinite recursion during tcache initialization.\n\t\t */\n\t\tif (try_tcache && size <= tcache_maxclass && (tcache =\n\t\t    tcache_get(true)) != NULL)\n\t\t\treturn (tcache_alloc_large(tcache, size, zero));\n\t\telse {\n\t\t\treturn (arena_malloc_large(choose_arena(arena), size,\n\t\t\t    zero));\n\t\t}\n\t}\n}\n\n/* Return the size of the allocation pointed to by ptr. */\nJEMALLOC_INLINE size_t\narena_salloc(const void *ptr, bool demote)\n{\n\tsize_t ret;\n\tarena_chunk_t *chunk;\n\tsize_t pageind, binind;\n\n\tassert(ptr != NULL);\n\tassert(CHUNK_ADDR2BASE(ptr) != ptr);\n\n\tchunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);\n\tpageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> LG_PAGE;\n\tassert(arena_mapbits_allocated_get(chunk, pageind) != 0);\n\tbinind = arena_mapbits_binind_get(chunk, pageind);\n\tif (binind == BININD_INVALID || (config_prof && demote == false &&\n\t    prof_promote && arena_mapbits_large_get(chunk, pageind) != 0)) {\n\t\t/*\n\t\t * Large allocation.  In the common case (demote == true), and\n\t\t * as this is an inline function, most callers will only end up\n\t\t * looking at binind to determine that ptr is a small\n\t\t * allocation.\n\t\t */\n\t\tassert(((uintptr_t)ptr & PAGE_MASK) == 0);\n\t\tret = arena_mapbits_large_size_get(chunk, pageind);\n\t\tassert(ret != 0);\n\t\tassert(pageind + (ret>>LG_PAGE) <= chunk_npages);\n\t\tassert(ret == PAGE || arena_mapbits_large_size_get(chunk,\n\t\t    pageind+(ret>>LG_PAGE)-1) == 0);\n\t\tassert(binind == arena_mapbits_binind_get(chunk,\n\t\t    pageind+(ret>>LG_PAGE)-1));\n\t\tassert(arena_mapbits_dirty_get(chunk, pageind) ==\n\t\t    arena_mapbits_dirty_get(chunk, pageind+(ret>>LG_PAGE)-1));\n\t} else {\n\t\t/*\n\t\t * Small allocation (possibly promoted to a large object due to\n\t\t * prof_promote).\n\t\t */\n\t\tassert(arena_mapbits_large_get(chunk, pageind) != 0 ||\n\t\t    arena_ptr_small_binind_get(ptr, arena_mapbits_get(chunk,\n\t\t    pageind)) == binind);\n\t\tret = arena_bin_info[binind].reg_size;\n\t}\n\n\treturn (ret);\n}\n\nJEMALLOC_INLINE void\narena_dalloc(arena_t *arena, arena_chunk_t *chunk, void *ptr, bool try_tcache)\n{\n\tsize_t pageind, mapbits;\n\ttcache_t *tcache;\n\n\tassert(arena != NULL);\n\tassert(chunk->arena == arena);\n\tassert(ptr != NULL);\n\tassert(CHUNK_ADDR2BASE(ptr) != ptr);\n\n\tpageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> LG_PAGE;\n\tmapbits = arena_mapbits_get(chunk, pageind);\n\tassert(arena_mapbits_allocated_get(chunk, pageind) != 0);\n\tif ((mapbits & CHUNK_MAP_LARGE) == 0) {\n\t\t/* Small allocation. */\n\t\tif (try_tcache && (tcache = tcache_get(false)) != NULL) {\n\t\t\tsize_t binind;\n\n\t\t\tbinind = arena_ptr_small_binind_get(ptr, mapbits);\n\t\t\ttcache_dalloc_small(tcache, ptr, binind);\n\t\t} else\n\t\t\tarena_dalloc_small(arena, chunk, ptr, pageind);\n\t} else {\n\t\tsize_t size = arena_mapbits_large_size_get(chunk, pageind);\n\n\t\tassert(((uintptr_t)ptr & PAGE_MASK) == 0);\n\n\t\tif (try_tcache && size <= tcache_maxclass && (tcache =\n\t\t    tcache_get(false)) != NULL) {\n\t\t\ttcache_dalloc_large(tcache, ptr, size);\n\t\t} else\n\t\t\tarena_dalloc_large(arena, chunk, ptr);\n\t}\n}\n#  endif /* JEMALLOC_ARENA_INLINE_B */\n#endif\n\n#endif /* JEMALLOC_H_INLINES */\n/******************************************************************************/\n"
  },
  {
    "path": "deps/jemalloc/include/jemalloc/internal/atomic.h",
    "content": "/******************************************************************************/\n#ifdef JEMALLOC_H_TYPES\n\n#endif /* JEMALLOC_H_TYPES */\n/******************************************************************************/\n#ifdef JEMALLOC_H_STRUCTS\n\n#endif /* JEMALLOC_H_STRUCTS */\n/******************************************************************************/\n#ifdef JEMALLOC_H_EXTERNS\n\n#define\tatomic_read_uint64(p)\tatomic_add_uint64(p, 0)\n#define\tatomic_read_uint32(p)\tatomic_add_uint32(p, 0)\n#define\tatomic_read_z(p)\tatomic_add_z(p, 0)\n#define\tatomic_read_u(p)\tatomic_add_u(p, 0)\n\n#endif /* JEMALLOC_H_EXTERNS */\n/******************************************************************************/\n#ifdef JEMALLOC_H_INLINES\n\n#ifndef JEMALLOC_ENABLE_INLINE\nuint64_t\tatomic_add_uint64(uint64_t *p, uint64_t x);\nuint64_t\tatomic_sub_uint64(uint64_t *p, uint64_t x);\nuint32_t\tatomic_add_uint32(uint32_t *p, uint32_t x);\nuint32_t\tatomic_sub_uint32(uint32_t *p, uint32_t x);\nsize_t\tatomic_add_z(size_t *p, size_t x);\nsize_t\tatomic_sub_z(size_t *p, size_t x);\nunsigned\tatomic_add_u(unsigned *p, unsigned x);\nunsigned\tatomic_sub_u(unsigned *p, unsigned x);\n#endif\n\n#if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_ATOMIC_C_))\n/******************************************************************************/\n/* 64-bit operations. */\n#if (LG_SIZEOF_PTR == 3 || LG_SIZEOF_INT == 3)\n#  ifdef __GCC_HAVE_SYNC_COMPARE_AND_SWAP_8\nJEMALLOC_INLINE uint64_t\natomic_add_uint64(uint64_t *p, uint64_t x)\n{\n\n\treturn (__sync_add_and_fetch(p, x));\n}\n\nJEMALLOC_INLINE uint64_t\natomic_sub_uint64(uint64_t *p, uint64_t x)\n{\n\n\treturn (__sync_sub_and_fetch(p, x));\n}\n#elif (defined(_MSC_VER))\nJEMALLOC_INLINE uint64_t\natomic_add_uint64(uint64_t *p, uint64_t x)\n{\n\n\treturn (InterlockedExchangeAdd64(p, x));\n}\n\nJEMALLOC_INLINE uint64_t\natomic_sub_uint64(uint64_t *p, uint64_t x)\n{\n\n\treturn (InterlockedExchangeAdd64(p, -((int64_t)x)));\n}\n#elif (defined(JEMALLOC_OSATOMIC))\nJEMALLOC_INLINE uint64_t\natomic_add_uint64(uint64_t *p, uint64_t x)\n{\n\n\treturn (OSAtomicAdd64((int64_t)x, (int64_t *)p));\n}\n\nJEMALLOC_INLINE uint64_t\natomic_sub_uint64(uint64_t *p, uint64_t x)\n{\n\n\treturn (OSAtomicAdd64(-((int64_t)x), (int64_t *)p));\n}\n#  elif (defined(__amd64__) || defined(__x86_64__))\nJEMALLOC_INLINE uint64_t\natomic_add_uint64(uint64_t *p, uint64_t x)\n{\n\n\tasm volatile (\n\t    \"lock; xaddq %0, %1;\"\n\t    : \"+r\" (x), \"=m\" (*p) /* Outputs. */\n\t    : \"m\" (*p) /* Inputs. */\n\t    );\n\n\treturn (x);\n}\n\nJEMALLOC_INLINE uint64_t\natomic_sub_uint64(uint64_t *p, uint64_t x)\n{\n\n\tx = (uint64_t)(-(int64_t)x);\n\tasm volatile (\n\t    \"lock; xaddq %0, %1;\"\n\t    : \"+r\" (x), \"=m\" (*p) /* Outputs. */\n\t    : \"m\" (*p) /* Inputs. */\n\t    );\n\n\treturn (x);\n}\n#  elif (defined(JEMALLOC_ATOMIC9))\nJEMALLOC_INLINE uint64_t\natomic_add_uint64(uint64_t *p, uint64_t x)\n{\n\n\t/*\n\t * atomic_fetchadd_64() doesn't exist, but we only ever use this\n\t * function on LP64 systems, so atomic_fetchadd_long() will do.\n\t */\n\tassert(sizeof(uint64_t) == sizeof(unsigned long));\n\n\treturn (atomic_fetchadd_long(p, (unsigned long)x) + x);\n}\n\nJEMALLOC_INLINE uint64_t\natomic_sub_uint64(uint64_t *p, uint64_t x)\n{\n\n\tassert(sizeof(uint64_t) == sizeof(unsigned long));\n\n\treturn (atomic_fetchadd_long(p, (unsigned long)(-(long)x)) - x);\n}\n#  elif (defined(JE_FORCE_SYNC_COMPARE_AND_SWAP_8))\nJEMALLOC_INLINE uint64_t\natomic_add_uint64(uint64_t *p, uint64_t x)\n{\n\n\treturn (__sync_add_and_fetch(p, x));\n}\n\nJEMALLOC_INLINE uint64_t\natomic_sub_uint64(uint64_t *p, uint64_t x)\n{\n\n\treturn (__sync_sub_and_fetch(p, x));\n}\n#  else\n#    error \"Missing implementation for 64-bit atomic operations\"\n#  endif\n#endif\n\n/******************************************************************************/\n/* 32-bit operations. */\n#ifdef __GCC_HAVE_SYNC_COMPARE_AND_SWAP_4\nJEMALLOC_INLINE uint32_t\natomic_add_uint32(uint32_t *p, uint32_t x)\n{\n\n\treturn (__sync_add_and_fetch(p, x));\n}\n\nJEMALLOC_INLINE uint32_t\natomic_sub_uint32(uint32_t *p, uint32_t x)\n{\n\n\treturn (__sync_sub_and_fetch(p, x));\n}\n#elif (defined(_MSC_VER))\nJEMALLOC_INLINE uint32_t\natomic_add_uint32(uint32_t *p, uint32_t x)\n{\n\n\treturn (InterlockedExchangeAdd(p, x));\n}\n\nJEMALLOC_INLINE uint32_t\natomic_sub_uint32(uint32_t *p, uint32_t x)\n{\n\n\treturn (InterlockedExchangeAdd(p, -((int32_t)x)));\n}\n#elif (defined(JEMALLOC_OSATOMIC))\nJEMALLOC_INLINE uint32_t\natomic_add_uint32(uint32_t *p, uint32_t x)\n{\n\n\treturn (OSAtomicAdd32((int32_t)x, (int32_t *)p));\n}\n\nJEMALLOC_INLINE uint32_t\natomic_sub_uint32(uint32_t *p, uint32_t x)\n{\n\n\treturn (OSAtomicAdd32(-((int32_t)x), (int32_t *)p));\n}\n#elif (defined(__i386__) || defined(__amd64__) || defined(__x86_64__))\nJEMALLOC_INLINE uint32_t\natomic_add_uint32(uint32_t *p, uint32_t x)\n{\n\n\tasm volatile (\n\t    \"lock; xaddl %0, %1;\"\n\t    : \"+r\" (x), \"=m\" (*p) /* Outputs. */\n\t    : \"m\" (*p) /* Inputs. */\n\t    );\n\n\treturn (x);\n}\n\nJEMALLOC_INLINE uint32_t\natomic_sub_uint32(uint32_t *p, uint32_t x)\n{\n\n\tx = (uint32_t)(-(int32_t)x);\n\tasm volatile (\n\t    \"lock; xaddl %0, %1;\"\n\t    : \"+r\" (x), \"=m\" (*p) /* Outputs. */\n\t    : \"m\" (*p) /* Inputs. */\n\t    );\n\n\treturn (x);\n}\n#elif (defined(JEMALLOC_ATOMIC9))\nJEMALLOC_INLINE uint32_t\natomic_add_uint32(uint32_t *p, uint32_t x)\n{\n\n\treturn (atomic_fetchadd_32(p, x) + x);\n}\n\nJEMALLOC_INLINE uint32_t\natomic_sub_uint32(uint32_t *p, uint32_t x)\n{\n\n\treturn (atomic_fetchadd_32(p, (uint32_t)(-(int32_t)x)) - x);\n}\n#elif (defined(JE_FORCE_SYNC_COMPARE_AND_SWAP_4))\nJEMALLOC_INLINE uint32_t\natomic_add_uint32(uint32_t *p, uint32_t x)\n{\n\n\treturn (__sync_add_and_fetch(p, x));\n}\n\nJEMALLOC_INLINE uint32_t\natomic_sub_uint32(uint32_t *p, uint32_t x)\n{\n\n\treturn (__sync_sub_and_fetch(p, x));\n}\n#else\n#  error \"Missing implementation for 32-bit atomic operations\"\n#endif\n\n/******************************************************************************/\n/* size_t operations. */\nJEMALLOC_INLINE size_t\natomic_add_z(size_t *p, size_t x)\n{\n\n#if (LG_SIZEOF_PTR == 3)\n\treturn ((size_t)atomic_add_uint64((uint64_t *)p, (uint64_t)x));\n#elif (LG_SIZEOF_PTR == 2)\n\treturn ((size_t)atomic_add_uint32((uint32_t *)p, (uint32_t)x));\n#endif\n}\n\nJEMALLOC_INLINE size_t\natomic_sub_z(size_t *p, size_t x)\n{\n\n#if (LG_SIZEOF_PTR == 3)\n\treturn ((size_t)atomic_add_uint64((uint64_t *)p,\n\t    (uint64_t)-((int64_t)x)));\n#elif (LG_SIZEOF_PTR == 2)\n\treturn ((size_t)atomic_add_uint32((uint32_t *)p,\n\t    (uint32_t)-((int32_t)x)));\n#endif\n}\n\n/******************************************************************************/\n/* unsigned operations. */\nJEMALLOC_INLINE unsigned\natomic_add_u(unsigned *p, unsigned x)\n{\n\n#if (LG_SIZEOF_INT == 3)\n\treturn ((unsigned)atomic_add_uint64((uint64_t *)p, (uint64_t)x));\n#elif (LG_SIZEOF_INT == 2)\n\treturn ((unsigned)atomic_add_uint32((uint32_t *)p, (uint32_t)x));\n#endif\n}\n\nJEMALLOC_INLINE unsigned\natomic_sub_u(unsigned *p, unsigned x)\n{\n\n#if (LG_SIZEOF_INT == 3)\n\treturn ((unsigned)atomic_add_uint64((uint64_t *)p,\n\t    (uint64_t)-((int64_t)x)));\n#elif (LG_SIZEOF_INT == 2)\n\treturn ((unsigned)atomic_add_uint32((uint32_t *)p,\n\t    (uint32_t)-((int32_t)x)));\n#endif\n}\n/******************************************************************************/\n#endif\n\n#endif /* JEMALLOC_H_INLINES */\n/******************************************************************************/\n"
  },
  {
    "path": "deps/jemalloc/include/jemalloc/internal/base.h",
    "content": "/******************************************************************************/\n#ifdef JEMALLOC_H_TYPES\n\n#endif /* JEMALLOC_H_TYPES */\n/******************************************************************************/\n#ifdef JEMALLOC_H_STRUCTS\n\n#endif /* JEMALLOC_H_STRUCTS */\n/******************************************************************************/\n#ifdef JEMALLOC_H_EXTERNS\n\nvoid\t*base_alloc(size_t size);\nvoid\t*base_calloc(size_t number, size_t size);\nextent_node_t *base_node_alloc(void);\nvoid\tbase_node_dealloc(extent_node_t *node);\nbool\tbase_boot(void);\nvoid\tbase_prefork(void);\nvoid\tbase_postfork_parent(void);\nvoid\tbase_postfork_child(void);\n\n#endif /* JEMALLOC_H_EXTERNS */\n/******************************************************************************/\n#ifdef JEMALLOC_H_INLINES\n\n#endif /* JEMALLOC_H_INLINES */\n/******************************************************************************/\n"
  },
  {
    "path": "deps/jemalloc/include/jemalloc/internal/bitmap.h",
    "content": "/******************************************************************************/\n#ifdef JEMALLOC_H_TYPES\n\n/* Maximum bitmap bit count is 2^LG_BITMAP_MAXBITS. */\n#define\tLG_BITMAP_MAXBITS\tLG_RUN_MAXREGS\n\ntypedef struct bitmap_level_s bitmap_level_t;\ntypedef struct bitmap_info_s bitmap_info_t;\ntypedef unsigned long bitmap_t;\n#define\tLG_SIZEOF_BITMAP\tLG_SIZEOF_LONG\n\n/* Number of bits per group. */\n#define\tLG_BITMAP_GROUP_NBITS\t\t(LG_SIZEOF_BITMAP + 3)\n#define\tBITMAP_GROUP_NBITS\t\t(ZU(1) << LG_BITMAP_GROUP_NBITS)\n#define\tBITMAP_GROUP_NBITS_MASK\t\t(BITMAP_GROUP_NBITS-1)\n\n/* Maximum number of levels possible. */\n#define\tBITMAP_MAX_LEVELS\t\t\t\t\t\t\\\n    (LG_BITMAP_MAXBITS / LG_SIZEOF_BITMAP)\t\t\t\t\\\n    + !!(LG_BITMAP_MAXBITS % LG_SIZEOF_BITMAP)\n\n#endif /* JEMALLOC_H_TYPES */\n/******************************************************************************/\n#ifdef JEMALLOC_H_STRUCTS\n\nstruct bitmap_level_s {\n\t/* Offset of this level's groups within the array of groups. */\n\tsize_t group_offset;\n};\n\nstruct bitmap_info_s {\n\t/* Logical number of bits in bitmap (stored at bottom level). */\n\tsize_t nbits;\n\n\t/* Number of levels necessary for nbits. */\n\tunsigned nlevels;\n\n\t/*\n\t * Only the first (nlevels+1) elements are used, and levels are ordered\n\t * bottom to top (e.g. the bottom level is stored in levels[0]).\n\t */\n\tbitmap_level_t levels[BITMAP_MAX_LEVELS+1];\n};\n\n#endif /* JEMALLOC_H_STRUCTS */\n/******************************************************************************/\n#ifdef JEMALLOC_H_EXTERNS\n\nvoid\tbitmap_info_init(bitmap_info_t *binfo, size_t nbits);\nsize_t\tbitmap_info_ngroups(const bitmap_info_t *binfo);\nsize_t\tbitmap_size(size_t nbits);\nvoid\tbitmap_init(bitmap_t *bitmap, const bitmap_info_t *binfo);\n\n#endif /* JEMALLOC_H_EXTERNS */\n/******************************************************************************/\n#ifdef JEMALLOC_H_INLINES\n\n#ifndef JEMALLOC_ENABLE_INLINE\nbool\tbitmap_full(bitmap_t *bitmap, const bitmap_info_t *binfo);\nbool\tbitmap_get(bitmap_t *bitmap, const bitmap_info_t *binfo, size_t bit);\nvoid\tbitmap_set(bitmap_t *bitmap, const bitmap_info_t *binfo, size_t bit);\nsize_t\tbitmap_sfu(bitmap_t *bitmap, const bitmap_info_t *binfo);\nvoid\tbitmap_unset(bitmap_t *bitmap, const bitmap_info_t *binfo, size_t bit);\n#endif\n\n#if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_BITMAP_C_))\nJEMALLOC_INLINE bool\nbitmap_full(bitmap_t *bitmap, const bitmap_info_t *binfo)\n{\n\tunsigned rgoff = binfo->levels[binfo->nlevels].group_offset - 1;\n\tbitmap_t rg = bitmap[rgoff];\n\t/* The bitmap is full iff the root group is 0. */\n\treturn (rg == 0);\n}\n\nJEMALLOC_INLINE bool\nbitmap_get(bitmap_t *bitmap, const bitmap_info_t *binfo, size_t bit)\n{\n\tsize_t goff;\n\tbitmap_t g;\n\n\tassert(bit < binfo->nbits);\n\tgoff = bit >> LG_BITMAP_GROUP_NBITS;\n\tg = bitmap[goff];\n\treturn (!(g & (1LU << (bit & BITMAP_GROUP_NBITS_MASK))));\n}\n\nJEMALLOC_INLINE void\nbitmap_set(bitmap_t *bitmap, const bitmap_info_t *binfo, size_t bit)\n{\n\tsize_t goff;\n\tbitmap_t *gp;\n\tbitmap_t g;\n\n\tassert(bit < binfo->nbits);\n\tassert(bitmap_get(bitmap, binfo, bit) == false);\n\tgoff = bit >> LG_BITMAP_GROUP_NBITS;\n\tgp = &bitmap[goff];\n\tg = *gp;\n\tassert(g & (1LU << (bit & BITMAP_GROUP_NBITS_MASK)));\n\tg ^= 1LU << (bit & BITMAP_GROUP_NBITS_MASK);\n\t*gp = g;\n\tassert(bitmap_get(bitmap, binfo, bit));\n\t/* Propagate group state transitions up the tree. */\n\tif (g == 0) {\n\t\tunsigned i;\n\t\tfor (i = 1; i < binfo->nlevels; i++) {\n\t\t\tbit = goff;\n\t\t\tgoff = bit >> LG_BITMAP_GROUP_NBITS;\n\t\t\tgp = &bitmap[binfo->levels[i].group_offset + goff];\n\t\t\tg = *gp;\n\t\t\tassert(g & (1LU << (bit & BITMAP_GROUP_NBITS_MASK)));\n\t\t\tg ^= 1LU << (bit & BITMAP_GROUP_NBITS_MASK);\n\t\t\t*gp = g;\n\t\t\tif (g != 0)\n\t\t\t\tbreak;\n\t\t}\n\t}\n}\n\n/* sfu: set first unset. */\nJEMALLOC_INLINE size_t\nbitmap_sfu(bitmap_t *bitmap, const bitmap_info_t *binfo)\n{\n\tsize_t bit;\n\tbitmap_t g;\n\tunsigned i;\n\n\tassert(bitmap_full(bitmap, binfo) == false);\n\n\ti = binfo->nlevels - 1;\n\tg = bitmap[binfo->levels[i].group_offset];\n\tbit = ffsl(g) - 1;\n\twhile (i > 0) {\n\t\ti--;\n\t\tg = bitmap[binfo->levels[i].group_offset + bit];\n\t\tbit = (bit << LG_BITMAP_GROUP_NBITS) + (ffsl(g) - 1);\n\t}\n\n\tbitmap_set(bitmap, binfo, bit);\n\treturn (bit);\n}\n\nJEMALLOC_INLINE void\nbitmap_unset(bitmap_t *bitmap, const bitmap_info_t *binfo, size_t bit)\n{\n\tsize_t goff;\n\tbitmap_t *gp;\n\tbitmap_t g;\n\tbool propagate;\n\n\tassert(bit < binfo->nbits);\n\tassert(bitmap_get(bitmap, binfo, bit));\n\tgoff = bit >> LG_BITMAP_GROUP_NBITS;\n\tgp = &bitmap[goff];\n\tg = *gp;\n\tpropagate = (g == 0);\n\tassert((g & (1LU << (bit & BITMAP_GROUP_NBITS_MASK))) == 0);\n\tg ^= 1LU << (bit & BITMAP_GROUP_NBITS_MASK);\n\t*gp = g;\n\tassert(bitmap_get(bitmap, binfo, bit) == false);\n\t/* Propagate group state transitions up the tree. */\n\tif (propagate) {\n\t\tunsigned i;\n\t\tfor (i = 1; i < binfo->nlevels; i++) {\n\t\t\tbit = goff;\n\t\t\tgoff = bit >> LG_BITMAP_GROUP_NBITS;\n\t\t\tgp = &bitmap[binfo->levels[i].group_offset + goff];\n\t\t\tg = *gp;\n\t\t\tpropagate = (g == 0);\n\t\t\tassert((g & (1LU << (bit & BITMAP_GROUP_NBITS_MASK)))\n\t\t\t    == 0);\n\t\t\tg ^= 1LU << (bit & BITMAP_GROUP_NBITS_MASK);\n\t\t\t*gp = g;\n\t\t\tif (propagate == false)\n\t\t\t\tbreak;\n\t\t}\n\t}\n}\n\n#endif\n\n#endif /* JEMALLOC_H_INLINES */\n/******************************************************************************/\n"
  },
  {
    "path": "deps/jemalloc/include/jemalloc/internal/chunk.h",
    "content": "/******************************************************************************/\n#ifdef JEMALLOC_H_TYPES\n\n/*\n * Size and alignment of memory chunks that are allocated by the OS's virtual\n * memory system.\n */\n#define\tLG_CHUNK_DEFAULT\t22\n\n/* Return the chunk address for allocation address a. */\n#define\tCHUNK_ADDR2BASE(a)\t\t\t\t\t\t\\\n\t((void *)((uintptr_t)(a) & ~chunksize_mask))\n\n/* Return the chunk offset of address a. */\n#define\tCHUNK_ADDR2OFFSET(a)\t\t\t\t\t\t\\\n\t((size_t)((uintptr_t)(a) & chunksize_mask))\n\n/* Return the smallest chunk multiple that is >= s. */\n#define\tCHUNK_CEILING(s)\t\t\t\t\t\t\\\n\t(((s) + chunksize_mask) & ~chunksize_mask)\n\n#endif /* JEMALLOC_H_TYPES */\n/******************************************************************************/\n#ifdef JEMALLOC_H_STRUCTS\n\n#endif /* JEMALLOC_H_STRUCTS */\n/******************************************************************************/\n#ifdef JEMALLOC_H_EXTERNS\n\nextern size_t\t\topt_lg_chunk;\nextern const char\t*opt_dss;\n\n/* Protects stats_chunks; currently not used for any other purpose. */\nextern malloc_mutex_t\tchunks_mtx;\n/* Chunk statistics. */\nextern chunk_stats_t\tstats_chunks;\n\nextern rtree_t\t\t*chunks_rtree;\n\nextern size_t\t\tchunksize;\nextern size_t\t\tchunksize_mask; /* (chunksize - 1). */\nextern size_t\t\tchunk_npages;\nextern size_t\t\tmap_bias; /* Number of arena chunk header pages. */\nextern size_t\t\tarena_maxclass; /* Max size class for arenas. */\n\nvoid\t*chunk_alloc(size_t size, size_t alignment, bool base, bool *zero,\n    dss_prec_t dss_prec);\nvoid\tchunk_unmap(void *chunk, size_t size);\nvoid\tchunk_dealloc(void *chunk, size_t size, bool unmap);\nbool\tchunk_boot(void);\nvoid\tchunk_prefork(void);\nvoid\tchunk_postfork_parent(void);\nvoid\tchunk_postfork_child(void);\n\n#endif /* JEMALLOC_H_EXTERNS */\n/******************************************************************************/\n#ifdef JEMALLOC_H_INLINES\n\n#endif /* JEMALLOC_H_INLINES */\n/******************************************************************************/\n\n#include \"jemalloc/internal/chunk_dss.h\"\n#include \"jemalloc/internal/chunk_mmap.h\"\n"
  },
  {
    "path": "deps/jemalloc/include/jemalloc/internal/chunk_dss.h",
    "content": "/******************************************************************************/\n#ifdef JEMALLOC_H_TYPES\n\ntypedef enum {\n\tdss_prec_disabled  = 0,\n\tdss_prec_primary   = 1,\n\tdss_prec_secondary = 2,\n\n\tdss_prec_limit     = 3\n} dss_prec_t ;\n#define\tDSS_PREC_DEFAULT\tdss_prec_secondary\n#define\tDSS_DEFAULT\t\t\"secondary\"\n\n#endif /* JEMALLOC_H_TYPES */\n/******************************************************************************/\n#ifdef JEMALLOC_H_STRUCTS\n\nextern const char *dss_prec_names[];\n\n#endif /* JEMALLOC_H_STRUCTS */\n/******************************************************************************/\n#ifdef JEMALLOC_H_EXTERNS\n\ndss_prec_t\tchunk_dss_prec_get(void);\nbool\tchunk_dss_prec_set(dss_prec_t dss_prec);\nvoid\t*chunk_alloc_dss(size_t size, size_t alignment, bool *zero);\nbool\tchunk_in_dss(void *chunk);\nbool\tchunk_dss_boot(void);\nvoid\tchunk_dss_prefork(void);\nvoid\tchunk_dss_postfork_parent(void);\nvoid\tchunk_dss_postfork_child(void);\n\n#endif /* JEMALLOC_H_EXTERNS */\n/******************************************************************************/\n#ifdef JEMALLOC_H_INLINES\n\n#endif /* JEMALLOC_H_INLINES */\n/******************************************************************************/\n"
  },
  {
    "path": "deps/jemalloc/include/jemalloc/internal/chunk_mmap.h",
    "content": "/******************************************************************************/\n#ifdef JEMALLOC_H_TYPES\n\n#endif /* JEMALLOC_H_TYPES */\n/******************************************************************************/\n#ifdef JEMALLOC_H_STRUCTS\n\n#endif /* JEMALLOC_H_STRUCTS */\n/******************************************************************************/\n#ifdef JEMALLOC_H_EXTERNS\n\nbool\tpages_purge(void *addr, size_t length);\n\nvoid\t*chunk_alloc_mmap(size_t size, size_t alignment, bool *zero);\nbool\tchunk_dealloc_mmap(void *chunk, size_t size);\n\n#endif /* JEMALLOC_H_EXTERNS */\n/******************************************************************************/\n#ifdef JEMALLOC_H_INLINES\n\n#endif /* JEMALLOC_H_INLINES */\n/******************************************************************************/\n"
  },
  {
    "path": "deps/jemalloc/include/jemalloc/internal/ckh.h",
    "content": "/******************************************************************************/\n#ifdef JEMALLOC_H_TYPES\n\ntypedef struct ckh_s ckh_t;\ntypedef struct ckhc_s ckhc_t;\n\n/* Typedefs to allow easy function pointer passing. */\ntypedef void ckh_hash_t (const void *, unsigned, size_t *, size_t *);\ntypedef bool ckh_keycomp_t (const void *, const void *);\n\n/* Maintain counters used to get an idea of performance. */\n/* #define\tCKH_COUNT */\n/* Print counter values in ckh_delete() (requires CKH_COUNT). */\n/* #define\tCKH_VERBOSE */\n\n/*\n * There are 2^LG_CKH_BUCKET_CELLS cells in each hash table bucket.  Try to fit\n * one bucket per L1 cache line.\n */\n#define LG_CKH_BUCKET_CELLS (LG_CACHELINE - LG_SIZEOF_PTR - 1)\n\n#endif /* JEMALLOC_H_TYPES */\n/******************************************************************************/\n#ifdef JEMALLOC_H_STRUCTS\n\n/* Hash table cell. */\nstruct ckhc_s {\n\tconst void\t*key;\n\tconst void\t*data;\n};\n\nstruct ckh_s {\n#ifdef CKH_COUNT\n\t/* Counters used to get an idea of performance. */\n\tuint64_t\tngrows;\n\tuint64_t\tnshrinks;\n\tuint64_t\tnshrinkfails;\n\tuint64_t\tninserts;\n\tuint64_t\tnrelocs;\n#endif\n\n\t/* Used for pseudo-random number generation. */\n#define\tCKH_A\t\t1103515241\n#define\tCKH_C\t\t12347\n\tuint32_t\tprng_state;\n\n\t/* Total number of items. */\n\tsize_t\t\tcount;\n\n\t/*\n\t * Minimum and current number of hash table buckets.  There are\n\t * 2^LG_CKH_BUCKET_CELLS cells per bucket.\n\t */\n\tunsigned\tlg_minbuckets;\n\tunsigned\tlg_curbuckets;\n\n\t/* Hash and comparison functions. */\n\tckh_hash_t\t*hash;\n\tckh_keycomp_t\t*keycomp;\n\n\t/* Hash table with 2^lg_curbuckets buckets. */\n\tckhc_t\t\t*tab;\n};\n\n#endif /* JEMALLOC_H_STRUCTS */\n/******************************************************************************/\n#ifdef JEMALLOC_H_EXTERNS\n\nbool\tckh_new(ckh_t *ckh, size_t minitems, ckh_hash_t *hash,\n    ckh_keycomp_t *keycomp);\nvoid\tckh_delete(ckh_t *ckh);\nsize_t\tckh_count(ckh_t *ckh);\nbool\tckh_iter(ckh_t *ckh, size_t *tabind, void **key, void **data);\nbool\tckh_insert(ckh_t *ckh, const void *key, const void *data);\nbool\tckh_remove(ckh_t *ckh, const void *searchkey, void **key,\n    void **data);\nbool\tckh_search(ckh_t *ckh, const void *seachkey, void **key, void **data);\nvoid\tckh_string_hash(const void *key, unsigned minbits, size_t *hash1,\n    size_t *hash2);\nbool\tckh_string_keycomp(const void *k1, const void *k2);\nvoid\tckh_pointer_hash(const void *key, unsigned minbits, size_t *hash1,\n    size_t *hash2);\nbool\tckh_pointer_keycomp(const void *k1, const void *k2);\n\n#endif /* JEMALLOC_H_EXTERNS */\n/******************************************************************************/\n#ifdef JEMALLOC_H_INLINES\n\n#endif /* JEMALLOC_H_INLINES */\n/******************************************************************************/\n"
  },
  {
    "path": "deps/jemalloc/include/jemalloc/internal/ctl.h",
    "content": "/******************************************************************************/\n#ifdef JEMALLOC_H_TYPES\n\ntypedef struct ctl_node_s ctl_node_t;\ntypedef struct ctl_named_node_s ctl_named_node_t;\ntypedef struct ctl_indexed_node_s ctl_indexed_node_t;\ntypedef struct ctl_arena_stats_s ctl_arena_stats_t;\ntypedef struct ctl_stats_s ctl_stats_t;\n\n#endif /* JEMALLOC_H_TYPES */\n/******************************************************************************/\n#ifdef JEMALLOC_H_STRUCTS\n\nstruct ctl_node_s {\n\tbool\t\t\tnamed;\n};\n\nstruct ctl_named_node_s {\n\tstruct ctl_node_s\tnode;\n\tconst char\t\t*name;\n\t/* If (nchildren == 0), this is a terminal node. */\n\tunsigned\t\tnchildren;\n\tconst\t\t\tctl_node_t *children;\n\tint\t\t\t(*ctl)(const size_t *, size_t, void *, size_t *,\n\t    void *, size_t);\n};\n\nstruct ctl_indexed_node_s {\n\tstruct ctl_node_s\tnode;\n\tconst ctl_named_node_t\t*(*index)(const size_t *, size_t, size_t);\n};\n\nstruct ctl_arena_stats_s {\n\tbool\t\t\tinitialized;\n\tunsigned\t\tnthreads;\n\tconst char\t\t*dss;\n\tsize_t\t\t\tpactive;\n\tsize_t\t\t\tpdirty;\n\tarena_stats_t\t\tastats;\n\n\t/* Aggregate stats for small size classes, based on bin stats. */\n\tsize_t\t\t\tallocated_small;\n\tuint64_t\t\tnmalloc_small;\n\tuint64_t\t\tndalloc_small;\n\tuint64_t\t\tnrequests_small;\n\n\tmalloc_bin_stats_t\tbstats[NBINS];\n\tmalloc_large_stats_t\t*lstats;\t/* nlclasses elements. */\n};\n\nstruct ctl_stats_s {\n\tsize_t\t\t\tallocated;\n\tsize_t\t\t\tactive;\n\tsize_t\t\t\tmapped;\n\tstruct {\n\t\tsize_t\t\tcurrent;\t/* stats_chunks.curchunks */\n\t\tuint64_t\ttotal;\t\t/* stats_chunks.nchunks */\n\t\tsize_t\t\thigh;\t\t/* stats_chunks.highchunks */\n\t} chunks;\n\tstruct {\n\t\tsize_t\t\tallocated;\t/* huge_allocated */\n\t\tuint64_t\tnmalloc;\t/* huge_nmalloc */\n\t\tuint64_t\tndalloc;\t/* huge_ndalloc */\n\t} huge;\n\tunsigned\t\tnarenas;\n\tctl_arena_stats_t\t*arenas;\t/* (narenas + 1) elements. */\n};\n\n#endif /* JEMALLOC_H_STRUCTS */\n/******************************************************************************/\n#ifdef JEMALLOC_H_EXTERNS\n\nint\tctl_byname(const char *name, void *oldp, size_t *oldlenp, void *newp,\n    size_t newlen);\nint\tctl_nametomib(const char *name, size_t *mibp, size_t *miblenp);\n\nint\tctl_bymib(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,\n    void *newp, size_t newlen);\nbool\tctl_boot(void);\nvoid\tctl_prefork(void);\nvoid\tctl_postfork_parent(void);\nvoid\tctl_postfork_child(void);\n\n#define\txmallctl(name, oldp, oldlenp, newp, newlen) do {\t\t\\\n\tif (je_mallctl(name, oldp, oldlenp, newp, newlen)\t\t\\\n\t    != 0) {\t\t\t\t\t\t\t\\\n\t\tmalloc_printf(\t\t\t\t\t\t\\\n\t\t    \"<jemalloc>: Failure in xmallctl(\\\"%s\\\", ...)\\n\",\t\\\n\t\t    name);\t\t\t\t\t\t\\\n\t\tabort();\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n} while (0)\n\n#define\txmallctlnametomib(name, mibp, miblenp) do {\t\t\t\\\n\tif (je_mallctlnametomib(name, mibp, miblenp) != 0) {\t\t\\\n\t\tmalloc_printf(\"<jemalloc>: Failure in \"\t\t\t\\\n\t\t    \"xmallctlnametomib(\\\"%s\\\", ...)\\n\", name);\t\t\\\n\t\tabort();\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n} while (0)\n\n#define\txmallctlbymib(mib, miblen, oldp, oldlenp, newp, newlen) do {\t\\\n\tif (je_mallctlbymib(mib, miblen, oldp, oldlenp, newp,\t\t\\\n\t    newlen) != 0) {\t\t\t\t\t\t\\\n\t\tmalloc_write(\t\t\t\t\t\t\\\n\t\t    \"<jemalloc>: Failure in xmallctlbymib()\\n\");\t\\\n\t\tabort();\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n} while (0)\n\n#endif /* JEMALLOC_H_EXTERNS */\n/******************************************************************************/\n#ifdef JEMALLOC_H_INLINES\n\n#endif /* JEMALLOC_H_INLINES */\n/******************************************************************************/\n\n"
  },
  {
    "path": "deps/jemalloc/include/jemalloc/internal/extent.h",
    "content": "/******************************************************************************/\n#ifdef JEMALLOC_H_TYPES\n\ntypedef struct extent_node_s extent_node_t;\n\n#endif /* JEMALLOC_H_TYPES */\n/******************************************************************************/\n#ifdef JEMALLOC_H_STRUCTS\n\n/* Tree of extents. */\nstruct extent_node_s {\n\t/* Linkage for the size/address-ordered tree. */\n\trb_node(extent_node_t)\tlink_szad;\n\n\t/* Linkage for the address-ordered tree. */\n\trb_node(extent_node_t)\tlink_ad;\n\n\t/* Profile counters, used for huge objects. */\n\tprof_ctx_t\t\t*prof_ctx;\n\n\t/* Pointer to the extent that this tree node is responsible for. */\n\tvoid\t\t\t*addr;\n\n\t/* Total region size. */\n\tsize_t\t\t\tsize;\n\n\t/* True if zero-filled; used by chunk recycling code. */\n\tbool\t\t\tzeroed;\n};\ntypedef rb_tree(extent_node_t) extent_tree_t;\n\n#endif /* JEMALLOC_H_STRUCTS */\n/******************************************************************************/\n#ifdef JEMALLOC_H_EXTERNS\n\nrb_proto(, extent_tree_szad_, extent_tree_t, extent_node_t)\n\nrb_proto(, extent_tree_ad_, extent_tree_t, extent_node_t)\n\n#endif /* JEMALLOC_H_EXTERNS */\n/******************************************************************************/\n#ifdef JEMALLOC_H_INLINES\n\n#endif /* JEMALLOC_H_INLINES */\n/******************************************************************************/\n\n"
  },
  {
    "path": "deps/jemalloc/include/jemalloc/internal/hash.h",
    "content": "/******************************************************************************/\n#ifdef JEMALLOC_H_TYPES\n\n#endif /* JEMALLOC_H_TYPES */\n/******************************************************************************/\n#ifdef JEMALLOC_H_STRUCTS\n\n#endif /* JEMALLOC_H_STRUCTS */\n/******************************************************************************/\n#ifdef JEMALLOC_H_EXTERNS\n\n#endif /* JEMALLOC_H_EXTERNS */\n/******************************************************************************/\n#ifdef JEMALLOC_H_INLINES\n\n#ifndef JEMALLOC_ENABLE_INLINE\nuint64_t\thash(const void *key, size_t len, uint64_t seed);\n#endif\n\n#if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_HASH_C_))\n/*\n * The following hash function is based on MurmurHash64A(), placed into the\n * public domain by Austin Appleby.  See http://murmurhash.googlepages.com/ for\n * details.\n */\nJEMALLOC_INLINE uint64_t\nhash(const void *key, size_t len, uint64_t seed)\n{\n\tconst uint64_t m = UINT64_C(0xc6a4a7935bd1e995);\n\tconst int r = 47;\n\tuint64_t h = seed ^ (len * m);\n\tconst uint64_t *data = (const uint64_t *)key;\n\tconst uint64_t *end = data + (len/8);\n\tconst unsigned char *data2;\n\n\tassert(((uintptr_t)key & 0x7) == 0);\n\n\twhile(data != end) {\n\t\tuint64_t k = *data++;\n\n\t\tk *= m;\n\t\tk ^= k >> r;\n\t\tk *= m;\n\n\t\th ^= k;\n\t\th *= m;\n\t}\n\n\tdata2 = (const unsigned char *)data;\n\tswitch(len & 7) {\n\tcase 7: h ^= ((uint64_t)(data2[6])) << 48;\n\tcase 6: h ^= ((uint64_t)(data2[5])) << 40;\n\tcase 5: h ^= ((uint64_t)(data2[4])) << 32;\n\tcase 4: h ^= ((uint64_t)(data2[3])) << 24;\n\tcase 3: h ^= ((uint64_t)(data2[2])) << 16;\n\tcase 2: h ^= ((uint64_t)(data2[1])) << 8;\n\tcase 1: h ^= ((uint64_t)(data2[0]));\n\t\th *= m;\n\t}\n\n\th ^= h >> r;\n\th *= m;\n\th ^= h >> r;\n\n\treturn (h);\n}\n#endif\n\n#endif /* JEMALLOC_H_INLINES */\n/******************************************************************************/\n"
  },
  {
    "path": "deps/jemalloc/include/jemalloc/internal/huge.h",
    "content": "/******************************************************************************/\n#ifdef JEMALLOC_H_TYPES\n\n#endif /* JEMALLOC_H_TYPES */\n/******************************************************************************/\n#ifdef JEMALLOC_H_STRUCTS\n\n#endif /* JEMALLOC_H_STRUCTS */\n/******************************************************************************/\n#ifdef JEMALLOC_H_EXTERNS\n\n/* Huge allocation statistics. */\nextern uint64_t\t\thuge_nmalloc;\nextern uint64_t\t\thuge_ndalloc;\nextern size_t\t\thuge_allocated;\n\n/* Protects chunk-related data structures. */\nextern malloc_mutex_t\thuge_mtx;\n\nvoid\t*huge_malloc(size_t size, bool zero);\nvoid\t*huge_palloc(size_t size, size_t alignment, bool zero);\nvoid\t*huge_ralloc_no_move(void *ptr, size_t oldsize, size_t size,\n    size_t extra);\nvoid\t*huge_ralloc(void *ptr, size_t oldsize, size_t size, size_t extra,\n    size_t alignment, bool zero, bool try_tcache_dalloc);\nvoid\thuge_dalloc(void *ptr, bool unmap);\nsize_t\thuge_salloc(const void *ptr);\nprof_ctx_t\t*huge_prof_ctx_get(const void *ptr);\nvoid\thuge_prof_ctx_set(const void *ptr, prof_ctx_t *ctx);\nbool\thuge_boot(void);\nvoid\thuge_prefork(void);\nvoid\thuge_postfork_parent(void);\nvoid\thuge_postfork_child(void);\n\n#endif /* JEMALLOC_H_EXTERNS */\n/******************************************************************************/\n#ifdef JEMALLOC_H_INLINES\n\n#endif /* JEMALLOC_H_INLINES */\n/******************************************************************************/\n"
  },
  {
    "path": "deps/jemalloc/include/jemalloc/internal/jemalloc_internal.h.in",
    "content": "#ifndef JEMALLOC_INTERNAL_H\n#define JEMALLOC_INTERNAL_H\n#include <math.h>\n#ifdef _WIN32\n#  include <windows.h>\n#  define ENOENT ERROR_PATH_NOT_FOUND\n#  define EINVAL ERROR_BAD_ARGUMENTS\n#  define EAGAIN ERROR_OUTOFMEMORY\n#  define EPERM  ERROR_WRITE_FAULT\n#  define EFAULT ERROR_INVALID_ADDRESS\n#  define ENOMEM ERROR_NOT_ENOUGH_MEMORY\n#  undef ERANGE\n#  define ERANGE ERROR_INVALID_DATA\n#else\n#  include <sys/param.h>\n#  include <sys/mman.h>\n#  include <sys/syscall.h>\n#  if !defined(SYS_write) && defined(__NR_write)\n#    define SYS_write __NR_write\n#  endif\n#  include <sys/uio.h>\n#  include <pthread.h>\n#  include <errno.h>\n#endif\n#include <sys/types.h>\n\n#include <limits.h>\n#ifndef SIZE_T_MAX\n#  define SIZE_T_MAX\tSIZE_MAX\n#endif\n#include <stdarg.h>\n#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <stdint.h>\n#include <stddef.h>\n#ifndef offsetof\n#  define offsetof(type, member)\t((size_t)&(((type *)NULL)->member))\n#endif\n#include <inttypes.h>\n#include <string.h>\n#include <strings.h>\n#include <ctype.h>\n#ifdef _MSC_VER\n#  include <io.h>\ntypedef intptr_t ssize_t;\n#  define PATH_MAX 1024\n#  define STDERR_FILENO 2\n#  define __func__ __FUNCTION__\n/* Disable warnings about deprecated system functions */\n#  pragma warning(disable: 4996)\n#else\n#  include <unistd.h>\n#endif\n#include <fcntl.h>\n\n#define\tJEMALLOC_NO_DEMANGLE\n#include \"../jemalloc@install_suffix@.h\"\n\n#ifdef JEMALLOC_UTRACE\n#include <sys/ktrace.h>\n#endif\n\n#ifdef JEMALLOC_VALGRIND\n#include <valgrind/valgrind.h>\n#include <valgrind/memcheck.h>\n#endif\n\n#include \"jemalloc/internal/private_namespace.h\"\n\n#ifdef JEMALLOC_CC_SILENCE\n#define\tUNUSED JEMALLOC_ATTR(unused)\n#else\n#define\tUNUSED\n#endif\n\nstatic const bool config_debug =\n#ifdef JEMALLOC_DEBUG\n    true\n#else\n    false\n#endif\n    ;\nstatic const bool config_dss =\n#ifdef JEMALLOC_DSS\n    true\n#else\n    false\n#endif\n    ;\nstatic const bool config_fill =\n#ifdef JEMALLOC_FILL\n    true\n#else\n    false\n#endif\n    ;\nstatic const bool config_lazy_lock =\n#ifdef JEMALLOC_LAZY_LOCK\n    true\n#else\n    false\n#endif\n    ;\nstatic const bool config_prof =\n#ifdef JEMALLOC_PROF\n    true\n#else\n    false\n#endif\n    ;\nstatic const bool config_prof_libgcc =\n#ifdef JEMALLOC_PROF_LIBGCC\n    true\n#else\n    false\n#endif\n    ;\nstatic const bool config_prof_libunwind =\n#ifdef JEMALLOC_PROF_LIBUNWIND\n    true\n#else\n    false\n#endif\n    ;\nstatic const bool config_mremap =\n#ifdef JEMALLOC_MREMAP\n    true\n#else\n    false\n#endif\n    ;\nstatic const bool config_munmap =\n#ifdef JEMALLOC_MUNMAP\n    true\n#else\n    false\n#endif\n    ;\nstatic const bool config_stats =\n#ifdef JEMALLOC_STATS\n    true\n#else\n    false\n#endif\n    ;\nstatic const bool config_tcache =\n#ifdef JEMALLOC_TCACHE\n    true\n#else\n    false\n#endif\n    ;\nstatic const bool config_tls =\n#ifdef JEMALLOC_TLS\n    true\n#else\n    false\n#endif\n    ;\nstatic const bool config_utrace =\n#ifdef JEMALLOC_UTRACE\n    true\n#else\n    false\n#endif\n    ;\nstatic const bool config_valgrind =\n#ifdef JEMALLOC_VALGRIND\n    true\n#else\n    false\n#endif\n    ;\nstatic const bool config_xmalloc =\n#ifdef JEMALLOC_XMALLOC\n    true\n#else\n    false\n#endif\n    ;\nstatic const bool config_ivsalloc =\n#ifdef JEMALLOC_IVSALLOC\n    true\n#else\n    false\n#endif\n    ;\n\n#ifdef JEMALLOC_ATOMIC9\n#include <machine/atomic.h>\n#endif\n\n#if (defined(JEMALLOC_OSATOMIC) || defined(JEMALLOC_OSSPIN))\n#include <libkern/OSAtomic.h>\n#endif\n\n#ifdef JEMALLOC_ZONE\n#include <mach/mach_error.h>\n#include <mach/mach_init.h>\n#include <mach/vm_map.h>\n#include <malloc/malloc.h>\n#endif\n\n#define\tRB_COMPACT\n#include \"jemalloc/internal/rb.h\"\n#include \"jemalloc/internal/qr.h\"\n#include \"jemalloc/internal/ql.h\"\n\n/*\n * jemalloc can conceptually be broken into components (arena, tcache, etc.),\n * but there are circular dependencies that cannot be broken without\n * substantial performance degradation.  In order to reduce the effect on\n * visual code flow, read the header files in multiple passes, with one of the\n * following cpp variables defined during each pass:\n *\n *   JEMALLOC_H_TYPES   : Preprocessor-defined constants and psuedo-opaque data\n *                        types.\n *   JEMALLOC_H_STRUCTS : Data structures.\n *   JEMALLOC_H_EXTERNS : Extern data declarations and function prototypes.\n *   JEMALLOC_H_INLINES : Inline functions.\n */\n/******************************************************************************/\n#define JEMALLOC_H_TYPES\n\n#define\tALLOCM_LG_ALIGN_MASK\t((int)0x3f)\n\n#define\tZU(z)\t((size_t)z)\n\n#ifndef __DECONST\n#  define\t__DECONST(type, var)\t((type)(uintptr_t)(const void *)(var))\n#endif\n\n#ifdef JEMALLOC_DEBUG\n   /* Disable inlining to make debugging easier. */\n#  define JEMALLOC_INLINE\n#  define inline\n#else\n#  define JEMALLOC_ENABLE_INLINE\n#  define JEMALLOC_INLINE static inline\n#  ifdef _MSC_VER\n#    define inline _inline\n#  endif\n#endif\n\n/* Smallest size class to support. */\n#define\tLG_TINY_MIN\t\t3\n#define\tTINY_MIN\t\t(1U << LG_TINY_MIN)\n\n/*\n * Minimum alignment of allocations is 2^LG_QUANTUM bytes (ignoring tiny size\n * classes).\n */\n#ifndef LG_QUANTUM\n#  if (defined(__i386__) || defined(_M_IX86))\n#    define LG_QUANTUM\t\t4\n#  endif\n#  ifdef __ia64__\n#    define LG_QUANTUM\t\t4\n#  endif\n#  ifdef __alpha__\n#    define LG_QUANTUM\t\t4\n#  endif\n#  ifdef __sparc64__\n#    define LG_QUANTUM\t\t4\n#  endif\n#  if (defined(__amd64__) || defined(__x86_64__) || defined(_M_X64))\n#    define LG_QUANTUM\t\t4\n#  endif\n#  ifdef __arm__\n#    define LG_QUANTUM\t\t3\n#  endif\n#  ifdef __hppa__\n#    define LG_QUANTUM\t\t4\n#  endif\n#  ifdef __mips__\n#    define LG_QUANTUM\t\t3\n#  endif\n#  ifdef __powerpc__\n#    define LG_QUANTUM\t\t4\n#  endif\n#  ifdef __s390x__\n#    define LG_QUANTUM\t\t4\n#  endif\n#  ifdef __SH4__\n#    define LG_QUANTUM\t\t4\n#  endif\n#  ifdef __tile__\n#    define LG_QUANTUM\t\t4\n#  endif\n#  ifndef LG_QUANTUM\n#    error \"No LG_QUANTUM definition for architecture; specify via CPPFLAGS\"\n#  endif\n#endif\n\n#define\tQUANTUM\t\t\t((size_t)(1U << LG_QUANTUM))\n#define\tQUANTUM_MASK\t\t(QUANTUM - 1)\n\n/* Return the smallest quantum multiple that is >= a. */\n#define\tQUANTUM_CEILING(a)\t\t\t\t\t\t\\\n\t(((a) + QUANTUM_MASK) & ~QUANTUM_MASK)\n\n#define\tLONG\t\t\t((size_t)(1U << LG_SIZEOF_LONG))\n#define\tLONG_MASK\t\t(LONG - 1)\n\n/* Return the smallest long multiple that is >= a. */\n#define\tLONG_CEILING(a)\t\t\t\t\t\t\t\\\n\t(((a) + LONG_MASK) & ~LONG_MASK)\n\n#define\tSIZEOF_PTR\t\t(1U << LG_SIZEOF_PTR)\n#define\tPTR_MASK\t\t(SIZEOF_PTR - 1)\n\n/* Return the smallest (void *) multiple that is >= a. */\n#define\tPTR_CEILING(a)\t\t\t\t\t\t\t\\\n\t(((a) + PTR_MASK) & ~PTR_MASK)\n\n/*\n * Maximum size of L1 cache line.  This is used to avoid cache line aliasing.\n * In addition, this controls the spacing of cacheline-spaced size classes.\n *\n * CACHELINE cannot be based on LG_CACHELINE because __declspec(align()) can\n * only handle raw constants.\n */\n#define\tLG_CACHELINE\t\t6\n#define\tCACHELINE\t\t64\n#define\tCACHELINE_MASK\t\t(CACHELINE - 1)\n\n/* Return the smallest cacheline multiple that is >= s. */\n#define\tCACHELINE_CEILING(s)\t\t\t\t\t\t\\\n\t(((s) + CACHELINE_MASK) & ~CACHELINE_MASK)\n\n/* Page size.  STATIC_PAGE_SHIFT is determined by the configure script. */\n#ifdef PAGE_MASK\n#  undef PAGE_MASK\n#endif\n#define\tLG_PAGE\t\tSTATIC_PAGE_SHIFT\n#define\tPAGE\t\t((size_t)(1U << STATIC_PAGE_SHIFT))\n#define\tPAGE_MASK\t((size_t)(PAGE - 1))\n\n/* Return the smallest pagesize multiple that is >= s. */\n#define\tPAGE_CEILING(s)\t\t\t\t\t\t\t\\\n\t(((s) + PAGE_MASK) & ~PAGE_MASK)\n\n/* Return the nearest aligned address at or below a. */\n#define\tALIGNMENT_ADDR2BASE(a, alignment)\t\t\t\t\\\n\t((void *)((uintptr_t)(a) & (-(alignment))))\n\n/* Return the offset between a and the nearest aligned address at or below a. */\n#define\tALIGNMENT_ADDR2OFFSET(a, alignment)\t\t\t\t\\\n\t((size_t)((uintptr_t)(a) & (alignment - 1)))\n\n/* Return the smallest alignment multiple that is >= s. */\n#define\tALIGNMENT_CEILING(s, alignment)\t\t\t\t\t\\\n\t(((s) + (alignment - 1)) & (-(alignment)))\n\n/* Declare a variable length array */\n#if __STDC_VERSION__ < 199901L\n#  ifdef _MSC_VER\n#    include <malloc.h>\n#    define alloca _alloca\n#  else\n#    include <alloca.h>\n#  endif\n#  define VARIABLE_ARRAY(type, name, count) \\\n\ttype *name = alloca(sizeof(type) * count)\n#else\n#  define VARIABLE_ARRAY(type, name, count) type name[count]\n#endif\n\n#ifdef JEMALLOC_VALGRIND\n/*\n * The JEMALLOC_VALGRIND_*() macros must be macros rather than functions\n * so that when Valgrind reports errors, there are no extra stack frames\n * in the backtraces.\n *\n * The size that is reported to valgrind must be consistent through a chain of\n * malloc..realloc..realloc calls.  Request size isn't recorded anywhere in\n * jemalloc, so it is critical that all callers of these macros provide usize\n * rather than request size.  As a result, buffer overflow detection is\n * technically weakened for the standard API, though it is generally accepted\n * practice to consider any extra bytes reported by malloc_usable_size() as\n * usable space.\n */\n#define\tJEMALLOC_VALGRIND_MALLOC(cond, ptr, usize, zero) do {\t\t\\\n\tif (config_valgrind && opt_valgrind && cond)\t\t\t\\\n\t\tVALGRIND_MALLOCLIKE_BLOCK(ptr, usize, p2rz(ptr), zero);\t\\\n} while (0)\n#define\tJEMALLOC_VALGRIND_REALLOC(ptr, usize, old_ptr, old_usize,\t\\\n    old_rzsize, zero)  do {\t\t\t\t\t\t\\\n\tif (config_valgrind && opt_valgrind) {\t\t\t\t\\\n\t\tsize_t rzsize = p2rz(ptr);\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\t\tif (ptr == old_ptr) {\t\t\t\t\t\\\n\t\t\tVALGRIND_RESIZEINPLACE_BLOCK(ptr, old_usize,\t\\\n\t\t\t    usize, rzsize);\t\t\t\t\\\n\t\t\tif (zero && old_usize < usize) {\t\t\\\n\t\t\t\tVALGRIND_MAKE_MEM_DEFINED(\t\t\\\n\t\t\t\t    (void *)((uintptr_t)ptr +\t\t\\\n\t\t\t\t    old_usize), usize - old_usize);\t\\\n\t\t\t}\t\t\t\t\t\t\\\n\t\t} else {\t\t\t\t\t\t\\\n\t\t\tif (old_ptr != NULL) {\t\t\t\t\\\n\t\t\t\tVALGRIND_FREELIKE_BLOCK(old_ptr,\t\\\n\t\t\t\t    old_rzsize);\t\t\t\\\n\t\t\t}\t\t\t\t\t\t\\\n\t\t\tif (ptr != NULL) {\t\t\t\t\\\n\t\t\t\tsize_t copy_size = (old_usize < usize)\t\\\n\t\t\t\t    ?  old_usize : usize;\t\t\\\n\t\t\t\tsize_t tail_size = usize - copy_size;\t\\\n\t\t\t\tVALGRIND_MALLOCLIKE_BLOCK(ptr, usize,\t\\\n\t\t\t\t    rzsize, false);\t\t\t\\\n\t\t\t\tif (copy_size > 0) {\t\t\t\\\n\t\t\t\t\tVALGRIND_MAKE_MEM_DEFINED(ptr,\t\\\n\t\t\t\t\t    copy_size);\t\t\t\\\n\t\t\t\t}\t\t\t\t\t\\\n\t\t\t\tif (zero && tail_size > 0) {\t\t\\\n\t\t\t\t\tVALGRIND_MAKE_MEM_DEFINED(\t\\\n\t\t\t\t\t    (void *)((uintptr_t)ptr +\t\\\n\t\t\t\t\t    copy_size), tail_size);\t\\\n\t\t\t\t}\t\t\t\t\t\\\n\t\t\t}\t\t\t\t\t\t\\\n\t\t}\t\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n} while (0)\n#define\tJEMALLOC_VALGRIND_FREE(ptr, rzsize) do {\t\t\t\\\n\tif (config_valgrind && opt_valgrind)\t\t\t\t\\\n\t\tVALGRIND_FREELIKE_BLOCK(ptr, rzsize);\t\t\t\\\n} while (0)\n#else\n#define\tRUNNING_ON_VALGRIND\t((unsigned)0)\n#define\tVALGRIND_MALLOCLIKE_BLOCK(addr, sizeB, rzB, is_zeroed)\n#define\tVALGRIND_RESIZEINPLACE_BLOCK(addr, oldSizeB, newSizeB, rzB)\n#define\tVALGRIND_FREELIKE_BLOCK(addr, rzB)\n#define\tVALGRIND_MAKE_MEM_UNDEFINED(_qzz_addr, _qzz_len)\n#define\tVALGRIND_MAKE_MEM_DEFINED(_qzz_addr, _qzz_len)\n#define\tJEMALLOC_VALGRIND_MALLOC(cond, ptr, usize, zero)\n#define\tJEMALLOC_VALGRIND_REALLOC(ptr, usize, old_ptr, old_usize,\t\\\n    old_rzsize, zero)\n#define\tJEMALLOC_VALGRIND_FREE(ptr, rzsize)\n#endif\n\n#include \"jemalloc/internal/util.h\"\n#include \"jemalloc/internal/atomic.h\"\n#include \"jemalloc/internal/prng.h\"\n#include \"jemalloc/internal/ckh.h\"\n#include \"jemalloc/internal/size_classes.h\"\n#include \"jemalloc/internal/stats.h\"\n#include \"jemalloc/internal/ctl.h\"\n#include \"jemalloc/internal/mutex.h\"\n#include \"jemalloc/internal/tsd.h\"\n#include \"jemalloc/internal/mb.h\"\n#include \"jemalloc/internal/extent.h\"\n#include \"jemalloc/internal/arena.h\"\n#include \"jemalloc/internal/bitmap.h\"\n#include \"jemalloc/internal/base.h\"\n#include \"jemalloc/internal/chunk.h\"\n#include \"jemalloc/internal/huge.h\"\n#include \"jemalloc/internal/rtree.h\"\n#include \"jemalloc/internal/tcache.h\"\n#include \"jemalloc/internal/hash.h\"\n#include \"jemalloc/internal/quarantine.h\"\n#include \"jemalloc/internal/prof.h\"\n\n#undef JEMALLOC_H_TYPES\n/******************************************************************************/\n#define JEMALLOC_H_STRUCTS\n\n#include \"jemalloc/internal/util.h\"\n#include \"jemalloc/internal/atomic.h\"\n#include \"jemalloc/internal/prng.h\"\n#include \"jemalloc/internal/ckh.h\"\n#include \"jemalloc/internal/size_classes.h\"\n#include \"jemalloc/internal/stats.h\"\n#include \"jemalloc/internal/ctl.h\"\n#include \"jemalloc/internal/mutex.h\"\n#include \"jemalloc/internal/tsd.h\"\n#include \"jemalloc/internal/mb.h\"\n#include \"jemalloc/internal/bitmap.h\"\n#include \"jemalloc/internal/extent.h\"\n#include \"jemalloc/internal/arena.h\"\n#include \"jemalloc/internal/base.h\"\n#include \"jemalloc/internal/chunk.h\"\n#include \"jemalloc/internal/huge.h\"\n#include \"jemalloc/internal/rtree.h\"\n#include \"jemalloc/internal/tcache.h\"\n#include \"jemalloc/internal/hash.h\"\n#include \"jemalloc/internal/quarantine.h\"\n#include \"jemalloc/internal/prof.h\"\n\ntypedef struct {\n\tuint64_t\tallocated;\n\tuint64_t\tdeallocated;\n} thread_allocated_t;\n/*\n * The JEMALLOC_CONCAT() wrapper is necessary to pass {0, 0} via a cpp macro\n * argument.\n */\n#define\tTHREAD_ALLOCATED_INITIALIZER\tJEMALLOC_CONCAT({0, 0})\n\n#undef JEMALLOC_H_STRUCTS\n/******************************************************************************/\n#define JEMALLOC_H_EXTERNS\n\nextern bool\topt_abort;\nextern bool\topt_junk;\nextern size_t\topt_quarantine;\nextern bool\topt_redzone;\nextern bool\topt_utrace;\nextern bool\topt_valgrind;\nextern bool\topt_xmalloc;\nextern bool\topt_zero;\nextern size_t\topt_narenas;\n\n/* Number of CPUs. */\nextern unsigned\t\tncpus;\n\n/* Protects arenas initialization (arenas, arenas_total). */\nextern malloc_mutex_t\tarenas_lock;\n/*\n * Arenas that are used to service external requests.  Not all elements of the\n * arenas array are necessarily used; arenas are created lazily as needed.\n *\n * arenas[0..narenas_auto) are used for automatic multiplexing of threads and\n * arenas.  arenas[narenas_auto..narenas_total) are only used if the application\n * takes some action to create them and allocate from them.\n */\nextern arena_t\t\t**arenas;\nextern unsigned\t\tnarenas_total;\nextern unsigned\t\tnarenas_auto; /* Read-only after initialization. */\n\narena_t\t*arenas_extend(unsigned ind);\nvoid\tarenas_cleanup(void *arg);\narena_t\t*choose_arena_hard(void);\nvoid\tjemalloc_prefork(void);\nvoid\tjemalloc_postfork_parent(void);\nvoid\tjemalloc_postfork_child(void);\n\n#include \"jemalloc/internal/util.h\"\n#include \"jemalloc/internal/atomic.h\"\n#include \"jemalloc/internal/prng.h\"\n#include \"jemalloc/internal/ckh.h\"\n#include \"jemalloc/internal/size_classes.h\"\n#include \"jemalloc/internal/stats.h\"\n#include \"jemalloc/internal/ctl.h\"\n#include \"jemalloc/internal/mutex.h\"\n#include \"jemalloc/internal/tsd.h\"\n#include \"jemalloc/internal/mb.h\"\n#include \"jemalloc/internal/bitmap.h\"\n#include \"jemalloc/internal/extent.h\"\n#include \"jemalloc/internal/arena.h\"\n#include \"jemalloc/internal/base.h\"\n#include \"jemalloc/internal/chunk.h\"\n#include \"jemalloc/internal/huge.h\"\n#include \"jemalloc/internal/rtree.h\"\n#include \"jemalloc/internal/tcache.h\"\n#include \"jemalloc/internal/hash.h\"\n#include \"jemalloc/internal/quarantine.h\"\n#include \"jemalloc/internal/prof.h\"\n\n#undef JEMALLOC_H_EXTERNS\n/******************************************************************************/\n#define JEMALLOC_H_INLINES\n\n#include \"jemalloc/internal/util.h\"\n#include \"jemalloc/internal/atomic.h\"\n#include \"jemalloc/internal/prng.h\"\n#include \"jemalloc/internal/ckh.h\"\n#include \"jemalloc/internal/size_classes.h\"\n#include \"jemalloc/internal/stats.h\"\n#include \"jemalloc/internal/ctl.h\"\n#include \"jemalloc/internal/mutex.h\"\n#include \"jemalloc/internal/tsd.h\"\n#include \"jemalloc/internal/mb.h\"\n#include \"jemalloc/internal/extent.h\"\n#include \"jemalloc/internal/base.h\"\n#include \"jemalloc/internal/chunk.h\"\n#include \"jemalloc/internal/huge.h\"\n\n#ifndef JEMALLOC_ENABLE_INLINE\nmalloc_tsd_protos(JEMALLOC_ATTR(unused), arenas, arena_t *)\n\nsize_t\ts2u(size_t size);\nsize_t\tsa2u(size_t size, size_t alignment);\nunsigned\tnarenas_total_get(void);\narena_t\t*choose_arena(arena_t *arena);\n#endif\n\n#if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_C_))\n/*\n * Map of pthread_self() --> arenas[???], used for selecting an arena to use\n * for allocations.\n */\nmalloc_tsd_externs(arenas, arena_t *)\nmalloc_tsd_funcs(JEMALLOC_INLINE, arenas, arena_t *, NULL, arenas_cleanup)\n\n/*\n * Compute usable size that would result from allocating an object with the\n * specified size.\n */\nJEMALLOC_INLINE size_t\ns2u(size_t size)\n{\n\n\tif (size <= SMALL_MAXCLASS)\n\t\treturn (arena_bin_info[SMALL_SIZE2BIN(size)].reg_size);\n\tif (size <= arena_maxclass)\n\t\treturn (PAGE_CEILING(size));\n\treturn (CHUNK_CEILING(size));\n}\n\n/*\n * Compute usable size that would result from allocating an object with the\n * specified size and alignment.\n */\nJEMALLOC_INLINE size_t\nsa2u(size_t size, size_t alignment)\n{\n\tsize_t usize;\n\n\tassert(alignment != 0 && ((alignment - 1) & alignment) == 0);\n\n\t/*\n\t * Round size up to the nearest multiple of alignment.\n\t *\n\t * This done, we can take advantage of the fact that for each small\n\t * size class, every object is aligned at the smallest power of two\n\t * that is non-zero in the base two representation of the size.  For\n\t * example:\n\t *\n\t *   Size |   Base 2 | Minimum alignment\n\t *   -----+----------+------------------\n\t *     96 |  1100000 |  32\n\t *    144 | 10100000 |  32\n\t *    192 | 11000000 |  64\n\t */\n\tusize = ALIGNMENT_CEILING(size, alignment);\n\t/*\n\t * (usize < size) protects against the combination of maximal\n\t * alignment and size greater than maximal alignment.\n\t */\n\tif (usize < size) {\n\t\t/* size_t overflow. */\n\t\treturn (0);\n\t}\n\n\tif (usize <= arena_maxclass && alignment <= PAGE) {\n\t\tif (usize <= SMALL_MAXCLASS)\n\t\t\treturn (arena_bin_info[SMALL_SIZE2BIN(usize)].reg_size);\n\t\treturn (PAGE_CEILING(usize));\n\t} else {\n\t\tsize_t run_size;\n\n\t\t/*\n\t\t * We can't achieve subpage alignment, so round up alignment\n\t\t * permanently; it makes later calculations simpler.\n\t\t */\n\t\talignment = PAGE_CEILING(alignment);\n\t\tusize = PAGE_CEILING(size);\n\t\t/*\n\t\t * (usize < size) protects against very large sizes within\n\t\t * PAGE of SIZE_T_MAX.\n\t\t *\n\t\t * (usize + alignment < usize) protects against the\n\t\t * combination of maximal alignment and usize large enough\n\t\t * to cause overflow.  This is similar to the first overflow\n\t\t * check above, but it needs to be repeated due to the new\n\t\t * usize value, which may now be *equal* to maximal\n\t\t * alignment, whereas before we only detected overflow if the\n\t\t * original size was *greater* than maximal alignment.\n\t\t */\n\t\tif (usize < size || usize + alignment < usize) {\n\t\t\t/* size_t overflow. */\n\t\t\treturn (0);\n\t\t}\n\n\t\t/*\n\t\t * Calculate the size of the over-size run that arena_palloc()\n\t\t * would need to allocate in order to guarantee the alignment.\n\t\t * If the run wouldn't fit within a chunk, round up to a huge\n\t\t * allocation size.\n\t\t */\n\t\trun_size = usize + alignment - PAGE;\n\t\tif (run_size <= arena_maxclass)\n\t\t\treturn (PAGE_CEILING(usize));\n\t\treturn (CHUNK_CEILING(usize));\n\t}\n}\n\nJEMALLOC_INLINE unsigned\nnarenas_total_get(void)\n{\n\tunsigned narenas;\n\n\tmalloc_mutex_lock(&arenas_lock);\n\tnarenas = narenas_total;\n\tmalloc_mutex_unlock(&arenas_lock);\n\n\treturn (narenas);\n}\n\n/* Choose an arena based on a per-thread value. */\nJEMALLOC_INLINE arena_t *\nchoose_arena(arena_t *arena)\n{\n\tarena_t *ret;\n\n\tif (arena != NULL)\n\t\treturn (arena);\n\n\tif ((ret = *arenas_tsd_get()) == NULL) {\n\t\tret = choose_arena_hard();\n\t\tassert(ret != NULL);\n\t}\n\n\treturn (ret);\n}\n#endif\n\n#include \"jemalloc/internal/bitmap.h\"\n#include \"jemalloc/internal/rtree.h\"\n/*\n * Include arena.h twice in order to resolve circular dependencies with\n * tcache.h.\n */\n#define\tJEMALLOC_ARENA_INLINE_A\n#include \"jemalloc/internal/arena.h\"\n#undef JEMALLOC_ARENA_INLINE_A\n#include \"jemalloc/internal/tcache.h\"\n#define\tJEMALLOC_ARENA_INLINE_B\n#include \"jemalloc/internal/arena.h\"\n#undef JEMALLOC_ARENA_INLINE_B\n#include \"jemalloc/internal/hash.h\"\n#include \"jemalloc/internal/quarantine.h\"\n\n#ifndef JEMALLOC_ENABLE_INLINE\nvoid\t*imallocx(size_t size, bool try_tcache, arena_t *arena);\nvoid\t*imalloc(size_t size);\nvoid\t*icallocx(size_t size, bool try_tcache, arena_t *arena);\nvoid\t*icalloc(size_t size);\nvoid\t*ipallocx(size_t usize, size_t alignment, bool zero, bool try_tcache,\n    arena_t *arena);\nvoid\t*ipalloc(size_t usize, size_t alignment, bool zero);\nsize_t\tisalloc(const void *ptr, bool demote);\nsize_t\tivsalloc(const void *ptr, bool demote);\nsize_t\tu2rz(size_t usize);\nsize_t\tp2rz(const void *ptr);\nvoid\tidallocx(void *ptr, bool try_tcache);\nvoid\tidalloc(void *ptr);\nvoid\tiqallocx(void *ptr, bool try_tcache);\nvoid\tiqalloc(void *ptr);\nvoid\t*irallocx(void *ptr, size_t size, size_t extra, size_t alignment,\n    bool zero, bool no_move, bool try_tcache_alloc, bool try_tcache_dalloc,\n    arena_t *arena);\nvoid\t*iralloc(void *ptr, size_t size, size_t extra, size_t alignment,\n    bool zero, bool no_move);\nmalloc_tsd_protos(JEMALLOC_ATTR(unused), thread_allocated, thread_allocated_t)\n#endif\n\n#if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_C_))\nJEMALLOC_INLINE void *\nimallocx(size_t size, bool try_tcache, arena_t *arena)\n{\n\n\tassert(size != 0);\n\n\tif (size <= arena_maxclass)\n\t\treturn (arena_malloc(arena, size, false, try_tcache));\n\telse\n\t\treturn (huge_malloc(size, false));\n}\n\nJEMALLOC_INLINE void *\nimalloc(size_t size)\n{\n\n\treturn (imallocx(size, true, NULL));\n}\n\nJEMALLOC_INLINE void *\nicallocx(size_t size, bool try_tcache, arena_t *arena)\n{\n\n\tif (size <= arena_maxclass)\n\t\treturn (arena_malloc(arena, size, true, try_tcache));\n\telse\n\t\treturn (huge_malloc(size, true));\n}\n\nJEMALLOC_INLINE void *\nicalloc(size_t size)\n{\n\n\treturn (icallocx(size, true, NULL));\n}\n\nJEMALLOC_INLINE void *\nipallocx(size_t usize, size_t alignment, bool zero, bool try_tcache,\n    arena_t *arena)\n{\n\tvoid *ret;\n\n\tassert(usize != 0);\n\tassert(usize == sa2u(usize, alignment));\n\n\tif (usize <= arena_maxclass && alignment <= PAGE)\n\t\tret = arena_malloc(arena, usize, zero, try_tcache);\n\telse {\n\t\tif (usize <= arena_maxclass) {\n\t\t\tret = arena_palloc(choose_arena(arena), usize,\n\t\t\t    alignment, zero);\n\t\t} else if (alignment <= chunksize)\n\t\t\tret = huge_malloc(usize, zero);\n\t\telse\n\t\t\tret = huge_palloc(usize, alignment, zero);\n\t}\n\n\tassert(ALIGNMENT_ADDR2BASE(ret, alignment) == ret);\n\treturn (ret);\n}\n\nJEMALLOC_INLINE void *\nipalloc(size_t usize, size_t alignment, bool zero)\n{\n\n\treturn (ipallocx(usize, alignment, zero, true, NULL));\n}\n\n/*\n * Typical usage:\n *   void *ptr = [...]\n *   size_t sz = isalloc(ptr, config_prof);\n */\nJEMALLOC_INLINE size_t\nisalloc(const void *ptr, bool demote)\n{\n\tsize_t ret;\n\tarena_chunk_t *chunk;\n\n\tassert(ptr != NULL);\n\t/* Demotion only makes sense if config_prof is true. */\n\tassert(config_prof || demote == false);\n\n\tchunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);\n\tif (chunk != ptr)\n\t\tret = arena_salloc(ptr, demote);\n\telse\n\t\tret = huge_salloc(ptr);\n\n\treturn (ret);\n}\n\nJEMALLOC_INLINE size_t\nivsalloc(const void *ptr, bool demote)\n{\n\n\t/* Return 0 if ptr is not within a chunk managed by jemalloc. */\n\tif (rtree_get(chunks_rtree, (uintptr_t)CHUNK_ADDR2BASE(ptr)) == NULL)\n\t\treturn (0);\n\n\treturn (isalloc(ptr, demote));\n}\n\nJEMALLOC_INLINE size_t\nu2rz(size_t usize)\n{\n\tsize_t ret;\n\n\tif (usize <= SMALL_MAXCLASS) {\n\t\tsize_t binind = SMALL_SIZE2BIN(usize);\n\t\tret = arena_bin_info[binind].redzone_size;\n\t} else\n\t\tret = 0;\n\n\treturn (ret);\n}\n\nJEMALLOC_INLINE size_t\np2rz(const void *ptr)\n{\n\tsize_t usize = isalloc(ptr, false);\n\n\treturn (u2rz(usize));\n}\n\nJEMALLOC_INLINE void\nidallocx(void *ptr, bool try_tcache)\n{\n\tarena_chunk_t *chunk;\n\n\tassert(ptr != NULL);\n\n\tchunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);\n\tif (chunk != ptr)\n\t\tarena_dalloc(chunk->arena, chunk, ptr, try_tcache);\n\telse\n\t\thuge_dalloc(ptr, true);\n}\n\nJEMALLOC_INLINE void\nidalloc(void *ptr)\n{\n\n\tidallocx(ptr, true);\n}\n\nJEMALLOC_INLINE void\niqallocx(void *ptr, bool try_tcache)\n{\n\n\tif (config_fill && opt_quarantine)\n\t\tquarantine(ptr);\n\telse\n\t\tidallocx(ptr, try_tcache);\n}\n\nJEMALLOC_INLINE void\niqalloc(void *ptr)\n{\n\n\tiqallocx(ptr, true);\n}\n\nJEMALLOC_INLINE void *\nirallocx(void *ptr, size_t size, size_t extra, size_t alignment, bool zero,\n    bool no_move, bool try_tcache_alloc, bool try_tcache_dalloc, arena_t *arena)\n{\n\tvoid *ret;\n\tsize_t oldsize;\n\n\tassert(ptr != NULL);\n\tassert(size != 0);\n\n\toldsize = isalloc(ptr, config_prof);\n\n\tif (alignment != 0 && ((uintptr_t)ptr & ((uintptr_t)alignment-1))\n\t    != 0) {\n\t\tsize_t usize, copysize;\n\n\t\t/*\n\t\t * Existing object alignment is inadequate; allocate new space\n\t\t * and copy.\n\t\t */\n\t\tif (no_move)\n\t\t\treturn (NULL);\n\t\tusize = sa2u(size + extra, alignment);\n\t\tif (usize == 0)\n\t\t\treturn (NULL);\n\t\tret = ipallocx(usize, alignment, zero, try_tcache_alloc, arena);\n\t\tif (ret == NULL) {\n\t\t\tif (extra == 0)\n\t\t\t\treturn (NULL);\n\t\t\t/* Try again, without extra this time. */\n\t\t\tusize = sa2u(size, alignment);\n\t\t\tif (usize == 0)\n\t\t\t\treturn (NULL);\n\t\t\tret = ipallocx(usize, alignment, zero, try_tcache_alloc,\n\t\t\t    arena);\n\t\t\tif (ret == NULL)\n\t\t\t\treturn (NULL);\n\t\t}\n\t\t/*\n\t\t * Copy at most size bytes (not size+extra), since the caller\n\t\t * has no expectation that the extra bytes will be reliably\n\t\t * preserved.\n\t\t */\n\t\tcopysize = (size < oldsize) ? size : oldsize;\n\t\tmemcpy(ret, ptr, copysize);\n\t\tiqallocx(ptr, try_tcache_dalloc);\n\t\treturn (ret);\n\t}\n\n\tif (no_move) {\n\t\tif (size <= arena_maxclass) {\n\t\t\treturn (arena_ralloc_no_move(ptr, oldsize, size,\n\t\t\t    extra, zero));\n\t\t} else {\n\t\t\treturn (huge_ralloc_no_move(ptr, oldsize, size,\n\t\t\t    extra));\n\t\t}\n\t} else {\n\t\tif (size + extra <= arena_maxclass) {\n\t\t\treturn (arena_ralloc(arena, ptr, oldsize, size, extra,\n\t\t\t    alignment, zero, try_tcache_alloc,\n\t\t\t    try_tcache_dalloc));\n\t\t} else {\n\t\t\treturn (huge_ralloc(ptr, oldsize, size, extra,\n\t\t\t    alignment, zero, try_tcache_dalloc));\n\t\t}\n\t}\n}\n\nJEMALLOC_INLINE void *\niralloc(void *ptr, size_t size, size_t extra, size_t alignment, bool zero,\n    bool no_move)\n{\n\n\treturn (irallocx(ptr, size, extra, alignment, zero, no_move, true, true,\n\t    NULL));\n}\n\nmalloc_tsd_externs(thread_allocated, thread_allocated_t)\nmalloc_tsd_funcs(JEMALLOC_INLINE, thread_allocated, thread_allocated_t,\n    THREAD_ALLOCATED_INITIALIZER, malloc_tsd_no_cleanup)\n#endif\n\n#include \"jemalloc/internal/prof.h\"\n\n#undef JEMALLOC_H_INLINES\n/******************************************************************************/\n#endif /* JEMALLOC_INTERNAL_H */\n"
  },
  {
    "path": "deps/jemalloc/include/jemalloc/internal/mb.h",
    "content": "/******************************************************************************/\n#ifdef JEMALLOC_H_TYPES\n\n#endif /* JEMALLOC_H_TYPES */\n/******************************************************************************/\n#ifdef JEMALLOC_H_STRUCTS\n\n#endif /* JEMALLOC_H_STRUCTS */\n/******************************************************************************/\n#ifdef JEMALLOC_H_EXTERNS\n\n#endif /* JEMALLOC_H_EXTERNS */\n/******************************************************************************/\n#ifdef JEMALLOC_H_INLINES\n\n#ifndef JEMALLOC_ENABLE_INLINE\nvoid\tmb_write(void);\n#endif\n\n#if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_MB_C_))\n#ifdef __i386__\n/*\n * According to the Intel Architecture Software Developer's Manual, current\n * processors execute instructions in order from the perspective of other\n * processors in a multiprocessor system, but 1) Intel reserves the right to\n * change that, and 2) the compiler's optimizer could re-order instructions if\n * there weren't some form of barrier.  Therefore, even if running on an\n * architecture that does not need memory barriers (everything through at least\n * i686), an \"optimizer barrier\" is necessary.\n */\nJEMALLOC_INLINE void\nmb_write(void)\n{\n\n#  if 0\n\t/* This is a true memory barrier. */\n\tasm volatile (\"pusha;\"\n\t    \"xor  %%eax,%%eax;\"\n\t    \"cpuid;\"\n\t    \"popa;\"\n\t    : /* Outputs. */\n\t    : /* Inputs. */\n\t    : \"memory\" /* Clobbers. */\n\t    );\n#else\n\t/*\n\t * This is hopefully enough to keep the compiler from reordering\n\t * instructions around this one.\n\t */\n\tasm volatile (\"nop;\"\n\t    : /* Outputs. */\n\t    : /* Inputs. */\n\t    : \"memory\" /* Clobbers. */\n\t    );\n#endif\n}\n#elif (defined(__amd64__) || defined(__x86_64__))\nJEMALLOC_INLINE void\nmb_write(void)\n{\n\n\tasm volatile (\"sfence\"\n\t    : /* Outputs. */\n\t    : /* Inputs. */\n\t    : \"memory\" /* Clobbers. */\n\t    );\n}\n#elif defined(__powerpc__)\nJEMALLOC_INLINE void\nmb_write(void)\n{\n\n\tasm volatile (\"eieio\"\n\t    : /* Outputs. */\n\t    : /* Inputs. */\n\t    : \"memory\" /* Clobbers. */\n\t    );\n}\n#elif defined(__sparc64__)\nJEMALLOC_INLINE void\nmb_write(void)\n{\n\n\tasm volatile (\"membar #StoreStore\"\n\t    : /* Outputs. */\n\t    : /* Inputs. */\n\t    : \"memory\" /* Clobbers. */\n\t    );\n}\n#elif defined(__tile__)\nJEMALLOC_INLINE void\nmb_write(void)\n{\n\n\t__sync_synchronize();\n}\n#else\n/*\n * This is much slower than a simple memory barrier, but the semantics of mutex\n * unlock make this work.\n */\nJEMALLOC_INLINE void\nmb_write(void)\n{\n\tmalloc_mutex_t mtx;\n\n\tmalloc_mutex_init(&mtx);\n\tmalloc_mutex_lock(&mtx);\n\tmalloc_mutex_unlock(&mtx);\n}\n#endif\n#endif\n\n#endif /* JEMALLOC_H_INLINES */\n/******************************************************************************/\n"
  },
  {
    "path": "deps/jemalloc/include/jemalloc/internal/mutex.h",
    "content": "/******************************************************************************/\n#ifdef JEMALLOC_H_TYPES\n\ntypedef struct malloc_mutex_s malloc_mutex_t;\n\n#ifdef _WIN32\n#  define MALLOC_MUTEX_INITIALIZER\n#elif (defined(JEMALLOC_OSSPIN))\n#  define MALLOC_MUTEX_INITIALIZER {0}\n#elif (defined(JEMALLOC_MUTEX_INIT_CB))\n#  define MALLOC_MUTEX_INITIALIZER {PTHREAD_MUTEX_INITIALIZER, NULL}\n#else\n#  if (defined(PTHREAD_MUTEX_ADAPTIVE_NP) &&\t\t\t\t\\\n       defined(PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP))\n#    define MALLOC_MUTEX_TYPE PTHREAD_MUTEX_ADAPTIVE_NP\n#    define MALLOC_MUTEX_INITIALIZER {PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP}\n#  else\n#    define MALLOC_MUTEX_TYPE PTHREAD_MUTEX_DEFAULT\n#    define MALLOC_MUTEX_INITIALIZER {PTHREAD_MUTEX_INITIALIZER}\n#  endif\n#endif\n\n#endif /* JEMALLOC_H_TYPES */\n/******************************************************************************/\n#ifdef JEMALLOC_H_STRUCTS\n\nstruct malloc_mutex_s {\n#ifdef _WIN32\n\tCRITICAL_SECTION\tlock;\n#elif (defined(JEMALLOC_OSSPIN))\n\tOSSpinLock\t\tlock;\n#elif (defined(JEMALLOC_MUTEX_INIT_CB))\n\tpthread_mutex_t\t\tlock;\n\tmalloc_mutex_t\t\t*postponed_next;\n#else\n\tpthread_mutex_t\t\tlock;\n#endif\n};\n\n#endif /* JEMALLOC_H_STRUCTS */\n/******************************************************************************/\n#ifdef JEMALLOC_H_EXTERNS\n\n#ifdef JEMALLOC_LAZY_LOCK\nextern bool isthreaded;\n#else\n#  undef isthreaded /* Undo private_namespace.h definition. */\n#  define isthreaded true\n#endif\n\nbool\tmalloc_mutex_init(malloc_mutex_t *mutex);\nvoid\tmalloc_mutex_prefork(malloc_mutex_t *mutex);\nvoid\tmalloc_mutex_postfork_parent(malloc_mutex_t *mutex);\nvoid\tmalloc_mutex_postfork_child(malloc_mutex_t *mutex);\nbool\tmutex_boot(void);\n\n#endif /* JEMALLOC_H_EXTERNS */\n/******************************************************************************/\n#ifdef JEMALLOC_H_INLINES\n\n#ifndef JEMALLOC_ENABLE_INLINE\nvoid\tmalloc_mutex_lock(malloc_mutex_t *mutex);\nvoid\tmalloc_mutex_unlock(malloc_mutex_t *mutex);\n#endif\n\n#if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_MUTEX_C_))\nJEMALLOC_INLINE void\nmalloc_mutex_lock(malloc_mutex_t *mutex)\n{\n\n\tif (isthreaded) {\n#ifdef _WIN32\n\t\tEnterCriticalSection(&mutex->lock);\n#elif (defined(JEMALLOC_OSSPIN))\n\t\tOSSpinLockLock(&mutex->lock);\n#else\n\t\tpthread_mutex_lock(&mutex->lock);\n#endif\n\t}\n}\n\nJEMALLOC_INLINE void\nmalloc_mutex_unlock(malloc_mutex_t *mutex)\n{\n\n\tif (isthreaded) {\n#ifdef _WIN32\n\t\tLeaveCriticalSection(&mutex->lock);\n#elif (defined(JEMALLOC_OSSPIN))\n\t\tOSSpinLockUnlock(&mutex->lock);\n#else\n\t\tpthread_mutex_unlock(&mutex->lock);\n#endif\n\t}\n}\n#endif\n\n#endif /* JEMALLOC_H_INLINES */\n/******************************************************************************/\n"
  },
  {
    "path": "deps/jemalloc/include/jemalloc/internal/private_namespace.h",
    "content": "#define\ta0calloc JEMALLOC_N(a0calloc)\n#define\ta0free JEMALLOC_N(a0free)\n#define\ta0malloc JEMALLOC_N(a0malloc)\n#define\tarena_alloc_junk_small JEMALLOC_N(arena_alloc_junk_small)\n#define\tarena_bin_index JEMALLOC_N(arena_bin_index)\n#define\tarena_bin_info JEMALLOC_N(arena_bin_info)\n#define\tarena_boot JEMALLOC_N(arena_boot)\n#define\tarena_dalloc JEMALLOC_N(arena_dalloc)\n#define\tarena_dalloc_bin JEMALLOC_N(arena_dalloc_bin)\n#define\tarena_dalloc_bin_locked JEMALLOC_N(arena_dalloc_bin_locked)\n#define\tarena_dalloc_junk_small JEMALLOC_N(arena_dalloc_junk_small)\n#define\tarena_dalloc_large JEMALLOC_N(arena_dalloc_large)\n#define\tarena_dalloc_large_locked JEMALLOC_N(arena_dalloc_large_locked)\n#define\tarena_dalloc_small JEMALLOC_N(arena_dalloc_small)\n#define\tarena_dss_prec_get JEMALLOC_N(arena_dss_prec_get)\n#define\tarena_dss_prec_set JEMALLOC_N(arena_dss_prec_set)\n#define\tarena_malloc JEMALLOC_N(arena_malloc)\n#define\tarena_malloc_large JEMALLOC_N(arena_malloc_large)\n#define\tarena_malloc_small JEMALLOC_N(arena_malloc_small)\n#define\tarena_mapbits_allocated_get JEMALLOC_N(arena_mapbits_allocated_get)\n#define\tarena_mapbits_binind_get JEMALLOC_N(arena_mapbits_binind_get)\n#define\tarena_mapbits_dirty_get JEMALLOC_N(arena_mapbits_dirty_get)\n#define\tarena_mapbits_get JEMALLOC_N(arena_mapbits_get)\n#define\tarena_mapbits_large_binind_set JEMALLOC_N(arena_mapbits_large_binind_set)\n#define\tarena_mapbits_large_get JEMALLOC_N(arena_mapbits_large_get)\n#define\tarena_mapbits_large_set JEMALLOC_N(arena_mapbits_large_set)\n#define\tarena_mapbits_large_size_get JEMALLOC_N(arena_mapbits_large_size_get)\n#define\tarena_mapbits_small_runind_get JEMALLOC_N(arena_mapbits_small_runind_get)\n#define\tarena_mapbits_small_set JEMALLOC_N(arena_mapbits_small_set)\n#define\tarena_mapbits_unallocated_set JEMALLOC_N(arena_mapbits_unallocated_set)\n#define\tarena_mapbits_unallocated_size_get JEMALLOC_N(arena_mapbits_unallocated_size_get)\n#define\tarena_mapbits_unallocated_size_set JEMALLOC_N(arena_mapbits_unallocated_size_set)\n#define\tarena_mapbits_unzeroed_get JEMALLOC_N(arena_mapbits_unzeroed_get)\n#define\tarena_mapbits_unzeroed_set JEMALLOC_N(arena_mapbits_unzeroed_set)\n#define\tarena_mapbitsp_get JEMALLOC_N(arena_mapbitsp_get)\n#define\tarena_mapp_get JEMALLOC_N(arena_mapp_get)\n#define\tarena_maxclass JEMALLOC_N(arena_maxclass)\n#define\tarena_new JEMALLOC_N(arena_new)\n#define\tarena_palloc JEMALLOC_N(arena_palloc)\n#define\tarena_postfork_child JEMALLOC_N(arena_postfork_child)\n#define\tarena_postfork_parent JEMALLOC_N(arena_postfork_parent)\n#define\tarena_prefork JEMALLOC_N(arena_prefork)\n#define\tarena_prof_accum JEMALLOC_N(arena_prof_accum)\n#define\tarena_prof_ctx_get JEMALLOC_N(arena_prof_ctx_get)\n#define\tarena_prof_ctx_set JEMALLOC_N(arena_prof_ctx_set)\n#define\tarena_prof_promoted JEMALLOC_N(arena_prof_promoted)\n#define\tarena_ptr_small_binind_get JEMALLOC_N(arena_ptr_small_binind_get)\n#define\tarena_purge_all JEMALLOC_N(arena_purge_all)\n#define\tarena_ralloc JEMALLOC_N(arena_ralloc)\n#define\tarena_ralloc_no_move JEMALLOC_N(arena_ralloc_no_move)\n#define\tarena_run_regind JEMALLOC_N(arena_run_regind)\n#define\tarena_salloc JEMALLOC_N(arena_salloc)\n#define\tarena_stats_merge JEMALLOC_N(arena_stats_merge)\n#define\tarena_tcache_fill_small JEMALLOC_N(arena_tcache_fill_small)\n#define\tarenas JEMALLOC_N(arenas)\n#define\tarenas_booted JEMALLOC_N(arenas_booted)\n#define\tarenas_cleanup JEMALLOC_N(arenas_cleanup)\n#define\tarenas_extend JEMALLOC_N(arenas_extend)\n#define\tarenas_initialized JEMALLOC_N(arenas_initialized)\n#define\tarenas_lock JEMALLOC_N(arenas_lock)\n#define\tarenas_tls JEMALLOC_N(arenas_tls)\n#define\tarenas_tsd JEMALLOC_N(arenas_tsd)\n#define\tarenas_tsd_boot JEMALLOC_N(arenas_tsd_boot)\n#define\tarenas_tsd_cleanup_wrapper JEMALLOC_N(arenas_tsd_cleanup_wrapper)\n#define\tarenas_tsd_get JEMALLOC_N(arenas_tsd_get)\n#define\tarenas_tsd_set JEMALLOC_N(arenas_tsd_set)\n#define\tatomic_add_u JEMALLOC_N(atomic_add_u)\n#define\tatomic_add_uint32 JEMALLOC_N(atomic_add_uint32)\n#define\tatomic_add_uint64 JEMALLOC_N(atomic_add_uint64)\n#define\tatomic_add_z JEMALLOC_N(atomic_add_z)\n#define\tatomic_sub_u JEMALLOC_N(atomic_sub_u)\n#define\tatomic_sub_uint32 JEMALLOC_N(atomic_sub_uint32)\n#define\tatomic_sub_uint64 JEMALLOC_N(atomic_sub_uint64)\n#define\tatomic_sub_z JEMALLOC_N(atomic_sub_z)\n#define\tbase_alloc JEMALLOC_N(base_alloc)\n#define\tbase_boot JEMALLOC_N(base_boot)\n#define\tbase_calloc JEMALLOC_N(base_calloc)\n#define\tbase_node_alloc JEMALLOC_N(base_node_alloc)\n#define\tbase_node_dealloc JEMALLOC_N(base_node_dealloc)\n#define\tbase_postfork_child JEMALLOC_N(base_postfork_child)\n#define\tbase_postfork_parent JEMALLOC_N(base_postfork_parent)\n#define\tbase_prefork JEMALLOC_N(base_prefork)\n#define\tbitmap_full JEMALLOC_N(bitmap_full)\n#define\tbitmap_get JEMALLOC_N(bitmap_get)\n#define\tbitmap_info_init JEMALLOC_N(bitmap_info_init)\n#define\tbitmap_info_ngroups JEMALLOC_N(bitmap_info_ngroups)\n#define\tbitmap_init JEMALLOC_N(bitmap_init)\n#define\tbitmap_set JEMALLOC_N(bitmap_set)\n#define\tbitmap_sfu JEMALLOC_N(bitmap_sfu)\n#define\tbitmap_size JEMALLOC_N(bitmap_size)\n#define\tbitmap_unset JEMALLOC_N(bitmap_unset)\n#define\tbt_init JEMALLOC_N(bt_init)\n#define\tbuferror JEMALLOC_N(buferror)\n#define\tchoose_arena JEMALLOC_N(choose_arena)\n#define\tchoose_arena_hard JEMALLOC_N(choose_arena_hard)\n#define\tchunk_alloc JEMALLOC_N(chunk_alloc)\n#define\tchunk_alloc_dss JEMALLOC_N(chunk_alloc_dss)\n#define\tchunk_alloc_mmap JEMALLOC_N(chunk_alloc_mmap)\n#define\tchunk_boot JEMALLOC_N(chunk_boot)\n#define\tchunk_dealloc JEMALLOC_N(chunk_dealloc)\n#define\tchunk_dealloc_mmap JEMALLOC_N(chunk_dealloc_mmap)\n#define\tchunk_dss_boot JEMALLOC_N(chunk_dss_boot)\n#define\tchunk_dss_postfork_child JEMALLOC_N(chunk_dss_postfork_child)\n#define\tchunk_dss_postfork_parent JEMALLOC_N(chunk_dss_postfork_parent)\n#define\tchunk_dss_prec_get JEMALLOC_N(chunk_dss_prec_get)\n#define\tchunk_dss_prec_set JEMALLOC_N(chunk_dss_prec_set)\n#define\tchunk_dss_prefork JEMALLOC_N(chunk_dss_prefork)\n#define\tchunk_in_dss JEMALLOC_N(chunk_in_dss)\n#define\tchunk_npages JEMALLOC_N(chunk_npages)\n#define\tchunk_postfork_child JEMALLOC_N(chunk_postfork_child)\n#define\tchunk_postfork_parent JEMALLOC_N(chunk_postfork_parent)\n#define\tchunk_prefork JEMALLOC_N(chunk_prefork)\n#define\tchunk_unmap JEMALLOC_N(chunk_unmap)\n#define\tchunks_mtx JEMALLOC_N(chunks_mtx)\n#define\tchunks_rtree JEMALLOC_N(chunks_rtree)\n#define\tchunksize JEMALLOC_N(chunksize)\n#define\tchunksize_mask JEMALLOC_N(chunksize_mask)\n#define\tckh_bucket_search JEMALLOC_N(ckh_bucket_search)\n#define\tckh_count JEMALLOC_N(ckh_count)\n#define\tckh_delete JEMALLOC_N(ckh_delete)\n#define\tckh_evict_reloc_insert JEMALLOC_N(ckh_evict_reloc_insert)\n#define\tckh_insert JEMALLOC_N(ckh_insert)\n#define\tckh_isearch JEMALLOC_N(ckh_isearch)\n#define\tckh_iter JEMALLOC_N(ckh_iter)\n#define\tckh_new JEMALLOC_N(ckh_new)\n#define\tckh_pointer_hash JEMALLOC_N(ckh_pointer_hash)\n#define\tckh_pointer_keycomp JEMALLOC_N(ckh_pointer_keycomp)\n#define\tckh_rebuild JEMALLOC_N(ckh_rebuild)\n#define\tckh_remove JEMALLOC_N(ckh_remove)\n#define\tckh_search JEMALLOC_N(ckh_search)\n#define\tckh_string_hash JEMALLOC_N(ckh_string_hash)\n#define\tckh_string_keycomp JEMALLOC_N(ckh_string_keycomp)\n#define\tckh_try_bucket_insert JEMALLOC_N(ckh_try_bucket_insert)\n#define\tckh_try_insert JEMALLOC_N(ckh_try_insert)\n#define\tctl_boot JEMALLOC_N(ctl_boot)\n#define\tctl_bymib JEMALLOC_N(ctl_bymib)\n#define\tctl_byname JEMALLOC_N(ctl_byname)\n#define\tctl_nametomib JEMALLOC_N(ctl_nametomib)\n#define\tctl_postfork_child JEMALLOC_N(ctl_postfork_child)\n#define\tctl_postfork_parent JEMALLOC_N(ctl_postfork_parent)\n#define\tctl_prefork JEMALLOC_N(ctl_prefork)\n#define\tdss_prec_names JEMALLOC_N(dss_prec_names)\n#define\textent_tree_ad_first JEMALLOC_N(extent_tree_ad_first)\n#define\textent_tree_ad_insert JEMALLOC_N(extent_tree_ad_insert)\n#define\textent_tree_ad_iter JEMALLOC_N(extent_tree_ad_iter)\n#define\textent_tree_ad_iter_recurse JEMALLOC_N(extent_tree_ad_iter_recurse)\n#define\textent_tree_ad_iter_start JEMALLOC_N(extent_tree_ad_iter_start)\n#define\textent_tree_ad_last JEMALLOC_N(extent_tree_ad_last)\n#define\textent_tree_ad_new JEMALLOC_N(extent_tree_ad_new)\n#define\textent_tree_ad_next JEMALLOC_N(extent_tree_ad_next)\n#define\textent_tree_ad_nsearch JEMALLOC_N(extent_tree_ad_nsearch)\n#define\textent_tree_ad_prev JEMALLOC_N(extent_tree_ad_prev)\n#define\textent_tree_ad_psearch JEMALLOC_N(extent_tree_ad_psearch)\n#define\textent_tree_ad_remove JEMALLOC_N(extent_tree_ad_remove)\n#define\textent_tree_ad_reverse_iter JEMALLOC_N(extent_tree_ad_reverse_iter)\n#define\textent_tree_ad_reverse_iter_recurse JEMALLOC_N(extent_tree_ad_reverse_iter_recurse)\n#define\textent_tree_ad_reverse_iter_start JEMALLOC_N(extent_tree_ad_reverse_iter_start)\n#define\textent_tree_ad_search JEMALLOC_N(extent_tree_ad_search)\n#define\textent_tree_szad_first JEMALLOC_N(extent_tree_szad_first)\n#define\textent_tree_szad_insert JEMALLOC_N(extent_tree_szad_insert)\n#define\textent_tree_szad_iter JEMALLOC_N(extent_tree_szad_iter)\n#define\textent_tree_szad_iter_recurse JEMALLOC_N(extent_tree_szad_iter_recurse)\n#define\textent_tree_szad_iter_start JEMALLOC_N(extent_tree_szad_iter_start)\n#define\textent_tree_szad_last JEMALLOC_N(extent_tree_szad_last)\n#define\textent_tree_szad_new JEMALLOC_N(extent_tree_szad_new)\n#define\textent_tree_szad_next JEMALLOC_N(extent_tree_szad_next)\n#define\textent_tree_szad_nsearch JEMALLOC_N(extent_tree_szad_nsearch)\n#define\textent_tree_szad_prev JEMALLOC_N(extent_tree_szad_prev)\n#define\textent_tree_szad_psearch JEMALLOC_N(extent_tree_szad_psearch)\n#define\textent_tree_szad_remove JEMALLOC_N(extent_tree_szad_remove)\n#define\textent_tree_szad_reverse_iter JEMALLOC_N(extent_tree_szad_reverse_iter)\n#define\textent_tree_szad_reverse_iter_recurse JEMALLOC_N(extent_tree_szad_reverse_iter_recurse)\n#define\textent_tree_szad_reverse_iter_start JEMALLOC_N(extent_tree_szad_reverse_iter_start)\n#define\textent_tree_szad_search JEMALLOC_N(extent_tree_szad_search)\n#define\tget_errno JEMALLOC_N(get_errno)\n#define\thash JEMALLOC_N(hash)\n#define\thuge_allocated JEMALLOC_N(huge_allocated)\n#define\thuge_boot JEMALLOC_N(huge_boot)\n#define\thuge_dalloc JEMALLOC_N(huge_dalloc)\n#define\thuge_malloc JEMALLOC_N(huge_malloc)\n#define\thuge_mtx JEMALLOC_N(huge_mtx)\n#define\thuge_ndalloc JEMALLOC_N(huge_ndalloc)\n#define\thuge_nmalloc JEMALLOC_N(huge_nmalloc)\n#define\thuge_palloc JEMALLOC_N(huge_palloc)\n#define\thuge_postfork_child JEMALLOC_N(huge_postfork_child)\n#define\thuge_postfork_parent JEMALLOC_N(huge_postfork_parent)\n#define\thuge_prefork JEMALLOC_N(huge_prefork)\n#define\thuge_prof_ctx_get JEMALLOC_N(huge_prof_ctx_get)\n#define\thuge_prof_ctx_set JEMALLOC_N(huge_prof_ctx_set)\n#define\thuge_ralloc JEMALLOC_N(huge_ralloc)\n#define\thuge_ralloc_no_move JEMALLOC_N(huge_ralloc_no_move)\n#define\thuge_salloc JEMALLOC_N(huge_salloc)\n#define\tiallocm JEMALLOC_N(iallocm)\n#define\ticalloc JEMALLOC_N(icalloc)\n#define\ticallocx JEMALLOC_N(icallocx)\n#define\tidalloc JEMALLOC_N(idalloc)\n#define\tidallocx JEMALLOC_N(idallocx)\n#define\timalloc JEMALLOC_N(imalloc)\n#define\timallocx JEMALLOC_N(imallocx)\n#define\tipalloc JEMALLOC_N(ipalloc)\n#define\tipallocx JEMALLOC_N(ipallocx)\n#define\tiqalloc JEMALLOC_N(iqalloc)\n#define\tiqallocx JEMALLOC_N(iqallocx)\n#define\tiralloc JEMALLOC_N(iralloc)\n#define\tirallocx JEMALLOC_N(irallocx)\n#define\tisalloc JEMALLOC_N(isalloc)\n#define\tisthreaded JEMALLOC_N(isthreaded)\n#define\tivsalloc JEMALLOC_N(ivsalloc)\n#define\tjemalloc_postfork_child JEMALLOC_N(jemalloc_postfork_child)\n#define\tjemalloc_postfork_parent JEMALLOC_N(jemalloc_postfork_parent)\n#define\tjemalloc_prefork JEMALLOC_N(jemalloc_prefork)\n#define\tmalloc_cprintf JEMALLOC_N(malloc_cprintf)\n#define\tmalloc_mutex_init JEMALLOC_N(malloc_mutex_init)\n#define\tmalloc_mutex_lock JEMALLOC_N(malloc_mutex_lock)\n#define\tmalloc_mutex_postfork_child JEMALLOC_N(malloc_mutex_postfork_child)\n#define\tmalloc_mutex_postfork_parent JEMALLOC_N(malloc_mutex_postfork_parent)\n#define\tmalloc_mutex_prefork JEMALLOC_N(malloc_mutex_prefork)\n#define\tmalloc_mutex_unlock JEMALLOC_N(malloc_mutex_unlock)\n#define\tmalloc_printf JEMALLOC_N(malloc_printf)\n#define\tmalloc_snprintf JEMALLOC_N(malloc_snprintf)\n#define\tmalloc_strtoumax JEMALLOC_N(malloc_strtoumax)\n#define\tmalloc_tsd_boot JEMALLOC_N(malloc_tsd_boot)\n#define\tmalloc_tsd_cleanup_register JEMALLOC_N(malloc_tsd_cleanup_register)\n#define\tmalloc_tsd_dalloc JEMALLOC_N(malloc_tsd_dalloc)\n#define\tmalloc_tsd_malloc JEMALLOC_N(malloc_tsd_malloc)\n#define\tmalloc_tsd_no_cleanup JEMALLOC_N(malloc_tsd_no_cleanup)\n#define\tmalloc_vcprintf JEMALLOC_N(malloc_vcprintf)\n#define\tmalloc_vsnprintf JEMALLOC_N(malloc_vsnprintf)\n#define\tmalloc_write JEMALLOC_N(malloc_write)\n#define\tmap_bias JEMALLOC_N(map_bias)\n#define\tmb_write JEMALLOC_N(mb_write)\n#define\tmutex_boot JEMALLOC_N(mutex_boot)\n#define\tnarenas_auto JEMALLOC_N(narenas_auto)\n#define\tnarenas_total JEMALLOC_N(narenas_total)\n#define\tnarenas_total_get JEMALLOC_N(narenas_total_get)\n#define\tncpus JEMALLOC_N(ncpus)\n#define\tnhbins JEMALLOC_N(nhbins)\n#define\topt_abort JEMALLOC_N(opt_abort)\n#define\topt_junk JEMALLOC_N(opt_junk)\n#define\topt_lg_chunk JEMALLOC_N(opt_lg_chunk)\n#define\topt_lg_dirty_mult JEMALLOC_N(opt_lg_dirty_mult)\n#define\topt_lg_prof_interval JEMALLOC_N(opt_lg_prof_interval)\n#define\topt_lg_prof_sample JEMALLOC_N(opt_lg_prof_sample)\n#define\topt_lg_tcache_max JEMALLOC_N(opt_lg_tcache_max)\n#define\topt_narenas JEMALLOC_N(opt_narenas)\n#define\topt_prof JEMALLOC_N(opt_prof)\n#define\topt_prof_accum JEMALLOC_N(opt_prof_accum)\n#define\topt_prof_active JEMALLOC_N(opt_prof_active)\n#define\topt_prof_final JEMALLOC_N(opt_prof_final)\n#define\topt_prof_gdump JEMALLOC_N(opt_prof_gdump)\n#define\topt_prof_leak JEMALLOC_N(opt_prof_leak)\n#define\topt_prof_prefix JEMALLOC_N(opt_prof_prefix)\n#define\topt_quarantine JEMALLOC_N(opt_quarantine)\n#define\topt_redzone JEMALLOC_N(opt_redzone)\n#define\topt_stats_print JEMALLOC_N(opt_stats_print)\n#define\topt_tcache JEMALLOC_N(opt_tcache)\n#define\topt_utrace JEMALLOC_N(opt_utrace)\n#define\topt_valgrind JEMALLOC_N(opt_valgrind)\n#define\topt_xmalloc JEMALLOC_N(opt_xmalloc)\n#define\topt_zero JEMALLOC_N(opt_zero)\n#define\tp2rz JEMALLOC_N(p2rz)\n#define\tpages_purge JEMALLOC_N(pages_purge)\n#define\tpow2_ceil JEMALLOC_N(pow2_ceil)\n#define\tprof_backtrace JEMALLOC_N(prof_backtrace)\n#define\tprof_boot0 JEMALLOC_N(prof_boot0)\n#define\tprof_boot1 JEMALLOC_N(prof_boot1)\n#define\tprof_boot2 JEMALLOC_N(prof_boot2)\n#define\tprof_ctx_get JEMALLOC_N(prof_ctx_get)\n#define\tprof_ctx_set JEMALLOC_N(prof_ctx_set)\n#define\tprof_free JEMALLOC_N(prof_free)\n#define\tprof_gdump JEMALLOC_N(prof_gdump)\n#define\tprof_idump JEMALLOC_N(prof_idump)\n#define\tprof_interval JEMALLOC_N(prof_interval)\n#define\tprof_lookup JEMALLOC_N(prof_lookup)\n#define\tprof_malloc JEMALLOC_N(prof_malloc)\n#define\tprof_mdump JEMALLOC_N(prof_mdump)\n#define\tprof_postfork_child JEMALLOC_N(prof_postfork_child)\n#define\tprof_postfork_parent JEMALLOC_N(prof_postfork_parent)\n#define\tprof_prefork JEMALLOC_N(prof_prefork)\n#define\tprof_promote JEMALLOC_N(prof_promote)\n#define\tprof_realloc JEMALLOC_N(prof_realloc)\n#define\tprof_sample_accum_update JEMALLOC_N(prof_sample_accum_update)\n#define\tprof_sample_threshold_update JEMALLOC_N(prof_sample_threshold_update)\n#define\tprof_tdata_booted JEMALLOC_N(prof_tdata_booted)\n#define\tprof_tdata_cleanup JEMALLOC_N(prof_tdata_cleanup)\n#define\tprof_tdata_get JEMALLOC_N(prof_tdata_get)\n#define\tprof_tdata_init JEMALLOC_N(prof_tdata_init)\n#define\tprof_tdata_initialized JEMALLOC_N(prof_tdata_initialized)\n#define\tprof_tdata_tls JEMALLOC_N(prof_tdata_tls)\n#define\tprof_tdata_tsd JEMALLOC_N(prof_tdata_tsd)\n#define\tprof_tdata_tsd_boot JEMALLOC_N(prof_tdata_tsd_boot)\n#define\tprof_tdata_tsd_cleanup_wrapper JEMALLOC_N(prof_tdata_tsd_cleanup_wrapper)\n#define\tprof_tdata_tsd_get JEMALLOC_N(prof_tdata_tsd_get)\n#define\tprof_tdata_tsd_set JEMALLOC_N(prof_tdata_tsd_set)\n#define\tquarantine JEMALLOC_N(quarantine)\n#define\tquarantine_boot JEMALLOC_N(quarantine_boot)\n#define\tquarantine_tsd_boot JEMALLOC_N(quarantine_tsd_boot)\n#define\tquarantine_tsd_cleanup_wrapper JEMALLOC_N(quarantine_tsd_cleanup_wrapper)\n#define\tquarantine_tsd_get JEMALLOC_N(quarantine_tsd_get)\n#define\tquarantine_tsd_set JEMALLOC_N(quarantine_tsd_set)\n#define\tregister_zone JEMALLOC_N(register_zone)\n#define\trtree_get JEMALLOC_N(rtree_get)\n#define\trtree_get_locked JEMALLOC_N(rtree_get_locked)\n#define\trtree_new JEMALLOC_N(rtree_new)\n#define\trtree_postfork_child JEMALLOC_N(rtree_postfork_child)\n#define\trtree_postfork_parent JEMALLOC_N(rtree_postfork_parent)\n#define\trtree_prefork JEMALLOC_N(rtree_prefork)\n#define\trtree_set JEMALLOC_N(rtree_set)\n#define\ts2u JEMALLOC_N(s2u)\n#define\tsa2u JEMALLOC_N(sa2u)\n#define\tset_errno JEMALLOC_N(set_errno)\n#define\tstats_cactive JEMALLOC_N(stats_cactive)\n#define\tstats_cactive_add JEMALLOC_N(stats_cactive_add)\n#define\tstats_cactive_get JEMALLOC_N(stats_cactive_get)\n#define\tstats_cactive_sub JEMALLOC_N(stats_cactive_sub)\n#define\tstats_chunks JEMALLOC_N(stats_chunks)\n#define\tstats_print JEMALLOC_N(stats_print)\n#define\ttcache_alloc_easy JEMALLOC_N(tcache_alloc_easy)\n#define\ttcache_alloc_large JEMALLOC_N(tcache_alloc_large)\n#define\ttcache_alloc_small JEMALLOC_N(tcache_alloc_small)\n#define\ttcache_alloc_small_hard JEMALLOC_N(tcache_alloc_small_hard)\n#define\ttcache_arena_associate JEMALLOC_N(tcache_arena_associate)\n#define\ttcache_arena_dissociate JEMALLOC_N(tcache_arena_dissociate)\n#define\ttcache_bin_flush_large JEMALLOC_N(tcache_bin_flush_large)\n#define\ttcache_bin_flush_small JEMALLOC_N(tcache_bin_flush_small)\n#define\ttcache_bin_info JEMALLOC_N(tcache_bin_info)\n#define\ttcache_boot0 JEMALLOC_N(tcache_boot0)\n#define\ttcache_boot1 JEMALLOC_N(tcache_boot1)\n#define\ttcache_booted JEMALLOC_N(tcache_booted)\n#define\ttcache_create JEMALLOC_N(tcache_create)\n#define\ttcache_dalloc_large JEMALLOC_N(tcache_dalloc_large)\n#define\ttcache_dalloc_small JEMALLOC_N(tcache_dalloc_small)\n#define\ttcache_destroy JEMALLOC_N(tcache_destroy)\n#define\ttcache_enabled_booted JEMALLOC_N(tcache_enabled_booted)\n#define\ttcache_enabled_get JEMALLOC_N(tcache_enabled_get)\n#define\ttcache_enabled_initialized JEMALLOC_N(tcache_enabled_initialized)\n#define\ttcache_enabled_set JEMALLOC_N(tcache_enabled_set)\n#define\ttcache_enabled_tls JEMALLOC_N(tcache_enabled_tls)\n#define\ttcache_enabled_tsd JEMALLOC_N(tcache_enabled_tsd)\n#define\ttcache_enabled_tsd_boot JEMALLOC_N(tcache_enabled_tsd_boot)\n#define\ttcache_enabled_tsd_cleanup_wrapper JEMALLOC_N(tcache_enabled_tsd_cleanup_wrapper)\n#define\ttcache_enabled_tsd_get JEMALLOC_N(tcache_enabled_tsd_get)\n#define\ttcache_enabled_tsd_set JEMALLOC_N(tcache_enabled_tsd_set)\n#define\ttcache_event JEMALLOC_N(tcache_event)\n#define\ttcache_event_hard JEMALLOC_N(tcache_event_hard)\n#define\ttcache_flush JEMALLOC_N(tcache_flush)\n#define\ttcache_get JEMALLOC_N(tcache_get)\n#define\ttcache_initialized JEMALLOC_N(tcache_initialized)\n#define\ttcache_maxclass JEMALLOC_N(tcache_maxclass)\n#define\ttcache_salloc JEMALLOC_N(tcache_salloc)\n#define\ttcache_stats_merge JEMALLOC_N(tcache_stats_merge)\n#define\ttcache_thread_cleanup JEMALLOC_N(tcache_thread_cleanup)\n#define\ttcache_tls JEMALLOC_N(tcache_tls)\n#define\ttcache_tsd JEMALLOC_N(tcache_tsd)\n#define\ttcache_tsd_boot JEMALLOC_N(tcache_tsd_boot)\n#define\ttcache_tsd_cleanup_wrapper JEMALLOC_N(tcache_tsd_cleanup_wrapper)\n#define\ttcache_tsd_get JEMALLOC_N(tcache_tsd_get)\n#define\ttcache_tsd_set JEMALLOC_N(tcache_tsd_set)\n#define\tthread_allocated_booted JEMALLOC_N(thread_allocated_booted)\n#define\tthread_allocated_initialized JEMALLOC_N(thread_allocated_initialized)\n#define\tthread_allocated_tls JEMALLOC_N(thread_allocated_tls)\n#define\tthread_allocated_tsd JEMALLOC_N(thread_allocated_tsd)\n#define\tthread_allocated_tsd_boot JEMALLOC_N(thread_allocated_tsd_boot)\n#define\tthread_allocated_tsd_cleanup_wrapper JEMALLOC_N(thread_allocated_tsd_cleanup_wrapper)\n#define\tthread_allocated_tsd_get JEMALLOC_N(thread_allocated_tsd_get)\n#define\tthread_allocated_tsd_set JEMALLOC_N(thread_allocated_tsd_set)\n#define\tu2rz JEMALLOC_N(u2rz)\n"
  },
  {
    "path": "deps/jemalloc/include/jemalloc/internal/prng.h",
    "content": "/******************************************************************************/\n#ifdef JEMALLOC_H_TYPES\n\n/*\n * Simple linear congruential pseudo-random number generator:\n *\n *   prng(y) = (a*x + c) % m\n *\n * where the following constants ensure maximal period:\n *\n *   a == Odd number (relatively prime to 2^n), and (a-1) is a multiple of 4.\n *   c == Odd number (relatively prime to 2^n).\n *   m == 2^32\n *\n * See Knuth's TAOCP 3rd Ed., Vol. 2, pg. 17 for details on these constraints.\n *\n * This choice of m has the disadvantage that the quality of the bits is\n * proportional to bit position.  For example. the lowest bit has a cycle of 2,\n * the next has a cycle of 4, etc.  For this reason, we prefer to use the upper\n * bits.\n *\n * Macro parameters:\n *   uint32_t r          : Result.\n *   unsigned lg_range   : (0..32], number of least significant bits to return.\n *   uint32_t state      : Seed value.\n *   const uint32_t a, c : See above discussion.\n */\n#define prng32(r, lg_range, state, a, c) do {\t\t\t\t\\\n\tassert(lg_range > 0);\t\t\t\t\t\t\\\n\tassert(lg_range <= 32);\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\tr = (state * (a)) + (c);\t\t\t\t\t\\\n\tstate = r;\t\t\t\t\t\t\t\\\n\tr >>= (32 - lg_range);\t\t\t\t\t\t\\\n} while (false)\n\n/* Same as prng32(), but 64 bits of pseudo-randomness, using uint64_t. */\n#define prng64(r, lg_range, state, a, c) do {\t\t\t\t\\\n\tassert(lg_range > 0);\t\t\t\t\t\t\\\n\tassert(lg_range <= 64);\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\tr = (state * (a)) + (c);\t\t\t\t\t\\\n\tstate = r;\t\t\t\t\t\t\t\\\n\tr >>= (64 - lg_range);\t\t\t\t\t\t\\\n} while (false)\n\n#endif /* JEMALLOC_H_TYPES */\n/******************************************************************************/\n#ifdef JEMALLOC_H_STRUCTS\n\n#endif /* JEMALLOC_H_STRUCTS */\n/******************************************************************************/\n#ifdef JEMALLOC_H_EXTERNS\n\n#endif /* JEMALLOC_H_EXTERNS */\n/******************************************************************************/\n#ifdef JEMALLOC_H_INLINES\n\n#endif /* JEMALLOC_H_INLINES */\n/******************************************************************************/\n"
  },
  {
    "path": "deps/jemalloc/include/jemalloc/internal/prof.h",
    "content": "/******************************************************************************/\n#ifdef JEMALLOC_H_TYPES\n\ntypedef struct prof_bt_s prof_bt_t;\ntypedef struct prof_cnt_s prof_cnt_t;\ntypedef struct prof_thr_cnt_s prof_thr_cnt_t;\ntypedef struct prof_ctx_s prof_ctx_t;\ntypedef struct prof_tdata_s prof_tdata_t;\n\n/* Option defaults. */\n#define\tPROF_PREFIX_DEFAULT\t\t\"jeprof\"\n#define\tLG_PROF_SAMPLE_DEFAULT\t\t19\n#define\tLG_PROF_INTERVAL_DEFAULT\t-1\n\n/*\n * Hard limit on stack backtrace depth.  The version of prof_backtrace() that\n * is based on __builtin_return_address() necessarily has a hard-coded number\n * of backtrace frame handlers, and should be kept in sync with this setting.\n */\n#define\tPROF_BT_MAX\t\t\t128\n\n/* Maximum number of backtraces to store in each per thread LRU cache. */\n#define\tPROF_TCMAX\t\t\t1024\n\n/* Initial hash table size. */\n#define\tPROF_CKH_MINITEMS\t\t64\n\n/* Size of memory buffer to use when writing dump files. */\n#define\tPROF_DUMP_BUFSIZE\t\t65536\n\n/* Size of stack-allocated buffer used by prof_printf(). */\n#define\tPROF_PRINTF_BUFSIZE\t\t128\n\n/*\n * Number of mutexes shared among all ctx's.  No space is allocated for these\n * unless profiling is enabled, so it's okay to over-provision.\n */\n#define\tPROF_NCTX_LOCKS\t\t\t1024\n\n/*\n * prof_tdata pointers close to NULL are used to encode state information that\n * is used for cleaning up during thread shutdown.\n */\n#define\tPROF_TDATA_STATE_REINCARNATED\t((prof_tdata_t *)(uintptr_t)1)\n#define\tPROF_TDATA_STATE_PURGATORY\t((prof_tdata_t *)(uintptr_t)2)\n#define\tPROF_TDATA_STATE_MAX\t\tPROF_TDATA_STATE_PURGATORY\n\n#endif /* JEMALLOC_H_TYPES */\n/******************************************************************************/\n#ifdef JEMALLOC_H_STRUCTS\n\nstruct prof_bt_s {\n\t/* Backtrace, stored as len program counters. */\n\tvoid\t\t**vec;\n\tunsigned\tlen;\n};\n\n#ifdef JEMALLOC_PROF_LIBGCC\n/* Data structure passed to libgcc _Unwind_Backtrace() callback functions. */\ntypedef struct {\n\tprof_bt_t\t*bt;\n\tunsigned\tnignore;\n\tunsigned\tmax;\n} prof_unwind_data_t;\n#endif\n\nstruct prof_cnt_s {\n\t/*\n\t * Profiling counters.  An allocation/deallocation pair can operate on\n\t * different prof_thr_cnt_t objects that are linked into the same\n\t * prof_ctx_t cnts_ql, so it is possible for the cur* counters to go\n\t * negative.  In principle it is possible for the *bytes counters to\n\t * overflow/underflow, but a general solution would require something\n\t * like 128-bit counters; this implementation doesn't bother to solve\n\t * that problem.\n\t */\n\tint64_t\t\tcurobjs;\n\tint64_t\t\tcurbytes;\n\tuint64_t\taccumobjs;\n\tuint64_t\taccumbytes;\n};\n\nstruct prof_thr_cnt_s {\n\t/* Linkage into prof_ctx_t's cnts_ql. */\n\tql_elm(prof_thr_cnt_t)\tcnts_link;\n\n\t/* Linkage into thread's LRU. */\n\tql_elm(prof_thr_cnt_t)\tlru_link;\n\n\t/*\n\t * Associated context.  If a thread frees an object that it did not\n\t * allocate, it is possible that the context is not cached in the\n\t * thread's hash table, in which case it must be able to look up the\n\t * context, insert a new prof_thr_cnt_t into the thread's hash table,\n\t * and link it into the prof_ctx_t's cnts_ql.\n\t */\n\tprof_ctx_t\t\t*ctx;\n\n\t/*\n\t * Threads use memory barriers to update the counters.  Since there is\n\t * only ever one writer, the only challenge is for the reader to get a\n\t * consistent read of the counters.\n\t *\n\t * The writer uses this series of operations:\n\t *\n\t * 1) Increment epoch to an odd number.\n\t * 2) Update counters.\n\t * 3) Increment epoch to an even number.\n\t *\n\t * The reader must assure 1) that the epoch is even while it reads the\n\t * counters, and 2) that the epoch doesn't change between the time it\n\t * starts and finishes reading the counters.\n\t */\n\tunsigned\t\tepoch;\n\n\t/* Profiling counters. */\n\tprof_cnt_t\t\tcnts;\n};\n\nstruct prof_ctx_s {\n\t/* Associated backtrace. */\n\tprof_bt_t\t\t*bt;\n\n\t/* Protects nlimbo, cnt_merged, and cnts_ql. */\n\tmalloc_mutex_t\t\t*lock;\n\n\t/*\n\t * Number of threads that currently cause this ctx to be in a state of\n\t * limbo due to one of:\n\t *   - Initializing per thread counters associated with this ctx.\n\t *   - Preparing to destroy this ctx.\n\t * nlimbo must be 1 (single destroyer) in order to safely destroy the\n\t * ctx.\n\t */\n\tunsigned\t\tnlimbo;\n\n\t/* Temporary storage for summation during dump. */\n\tprof_cnt_t\t\tcnt_summed;\n\n\t/* When threads exit, they merge their stats into cnt_merged. */\n\tprof_cnt_t\t\tcnt_merged;\n\n\t/*\n\t * List of profile counters, one for each thread that has allocated in\n\t * this context.\n\t */\n\tql_head(prof_thr_cnt_t)\tcnts_ql;\n};\n\nstruct prof_tdata_s {\n\t/*\n\t * Hash of (prof_bt_t *)-->(prof_thr_cnt_t *).  Each thread keeps a\n\t * cache of backtraces, with associated thread-specific prof_thr_cnt_t\n\t * objects.  Other threads may read the prof_thr_cnt_t contents, but no\n\t * others will ever write them.\n\t *\n\t * Upon thread exit, the thread must merge all the prof_thr_cnt_t\n\t * counter data into the associated prof_ctx_t objects, and unlink/free\n\t * the prof_thr_cnt_t objects.\n\t */\n\tckh_t\t\t\tbt2cnt;\n\n\t/* LRU for contents of bt2cnt. */\n\tql_head(prof_thr_cnt_t)\tlru_ql;\n\n\t/* Backtrace vector, used for calls to prof_backtrace(). */\n\tvoid\t\t\t**vec;\n\n\t/* Sampling state. */\n\tuint64_t\t\tprng_state;\n\tuint64_t\t\tthreshold;\n\tuint64_t\t\taccum;\n\n\t/* State used to avoid dumping while operating on prof internals. */\n\tbool\t\t\tenq;\n\tbool\t\t\tenq_idump;\n\tbool\t\t\tenq_gdump;\n};\n\n#endif /* JEMALLOC_H_STRUCTS */\n/******************************************************************************/\n#ifdef JEMALLOC_H_EXTERNS\n\nextern bool\topt_prof;\n/*\n * Even if opt_prof is true, sampling can be temporarily disabled by setting\n * opt_prof_active to false.  No locking is used when updating opt_prof_active,\n * so there are no guarantees regarding how long it will take for all threads\n * to notice state changes.\n */\nextern bool\topt_prof_active;\nextern size_t\topt_lg_prof_sample;   /* Mean bytes between samples. */\nextern ssize_t\topt_lg_prof_interval; /* lg(prof_interval). */\nextern bool\topt_prof_gdump;       /* High-water memory dumping. */\nextern bool\topt_prof_final;       /* Final profile dumping. */\nextern bool\topt_prof_leak;        /* Dump leak summary at exit. */\nextern bool\topt_prof_accum;       /* Report cumulative bytes. */\nextern char\topt_prof_prefix[PATH_MAX + 1];\n\n/*\n * Profile dump interval, measured in bytes allocated.  Each arena triggers a\n * profile dump when it reaches this threshold.  The effect is that the\n * interval between profile dumps averages prof_interval, though the actual\n * interval between dumps will tend to be sporadic, and the interval will be a\n * maximum of approximately (prof_interval * narenas).\n */\nextern uint64_t\tprof_interval;\n\n/*\n * If true, promote small sampled objects to large objects, since small run\n * headers do not have embedded profile context pointers.\n */\nextern bool\tprof_promote;\n\nvoid\tbt_init(prof_bt_t *bt, void **vec);\nvoid\tprof_backtrace(prof_bt_t *bt, unsigned nignore);\nprof_thr_cnt_t\t*prof_lookup(prof_bt_t *bt);\nvoid\tprof_idump(void);\nbool\tprof_mdump(const char *filename);\nvoid\tprof_gdump(void);\nprof_tdata_t\t*prof_tdata_init(void);\nvoid\tprof_tdata_cleanup(void *arg);\nvoid\tprof_boot0(void);\nvoid\tprof_boot1(void);\nbool\tprof_boot2(void);\nvoid\tprof_prefork(void);\nvoid\tprof_postfork_parent(void);\nvoid\tprof_postfork_child(void);\n\n#endif /* JEMALLOC_H_EXTERNS */\n/******************************************************************************/\n#ifdef JEMALLOC_H_INLINES\n\n#define\tPROF_ALLOC_PREP(nignore, size, ret) do {\t\t\t\\\n\tprof_tdata_t *prof_tdata;\t\t\t\t\t\\\n\tprof_bt_t bt;\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\tassert(size == s2u(size));\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\tprof_tdata = prof_tdata_get();\t\t\t\t\t\\\n\tif ((uintptr_t)prof_tdata <= (uintptr_t)PROF_TDATA_STATE_MAX) {\t\\\n\t\tif (prof_tdata != NULL)\t\t\t\t\t\\\n\t\t\tret = (prof_thr_cnt_t *)(uintptr_t)1U;\t\t\\\n\t\telse\t\t\t\t\t\t\t\\\n\t\t\tret = NULL;\t\t\t\t\t\\\n\t\tbreak;\t\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\tif (opt_prof_active == false) {\t\t\t\t\t\\\n\t\t/* Sampling is currently inactive, so avoid sampling. */\\\n\t\tret = (prof_thr_cnt_t *)(uintptr_t)1U;\t\t\t\\\n\t} else if (opt_lg_prof_sample == 0) {\t\t\t\t\\\n\t\t/* Don't bother with sampling logic, since sampling   */\\\n\t\t/* interval is 1.                                     */\\\n\t\tbt_init(&bt, prof_tdata->vec);\t\t\t\t\\\n\t\tprof_backtrace(&bt, nignore);\t\t\t\t\\\n\t\tret = prof_lookup(&bt);\t\t\t\t\t\\\n\t} else {\t\t\t\t\t\t\t\\\n\t\tif (prof_tdata->threshold == 0) {\t\t\t\\\n\t\t\t/* Initialize.  Seed the prng differently for */\\\n\t\t\t/* each thread.                               */\\\n\t\t\tprof_tdata->prng_state =\t\t\t\\\n\t\t\t    (uint64_t)(uintptr_t)&size;\t\t\t\\\n\t\t\tprof_sample_threshold_update(prof_tdata);\t\\\n\t\t}\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\t\t/* Determine whether to capture a backtrace based on  */\\\n\t\t/* whether size is enough for prof_accum to reach     */\\\n\t\t/* prof_tdata->threshold.  However, delay updating    */\\\n\t\t/* these variables until prof_{m,re}alloc(), because  */\\\n\t\t/* we don't know for sure that the allocation will    */\\\n\t\t/* succeed.                                           */\\\n\t\t/*                                                    */\\\n\t\t/* Use subtraction rather than addition to avoid      */\\\n\t\t/* potential integer overflow.                        */\\\n\t\tif (size >= prof_tdata->threshold -\t\t\t\\\n\t\t    prof_tdata->accum) {\t\t\t\t\\\n\t\t\tbt_init(&bt, prof_tdata->vec);\t\t\t\\\n\t\t\tprof_backtrace(&bt, nignore);\t\t\t\\\n\t\t\tret = prof_lookup(&bt);\t\t\t\t\\\n\t\t} else\t\t\t\t\t\t\t\\\n\t\t\tret = (prof_thr_cnt_t *)(uintptr_t)1U;\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n} while (0)\n\n#ifndef JEMALLOC_ENABLE_INLINE\nmalloc_tsd_protos(JEMALLOC_ATTR(unused), prof_tdata, prof_tdata_t *)\n\nprof_tdata_t\t*prof_tdata_get(void);\nvoid\tprof_sample_threshold_update(prof_tdata_t *prof_tdata);\nprof_ctx_t\t*prof_ctx_get(const void *ptr);\nvoid\tprof_ctx_set(const void *ptr, prof_ctx_t *ctx);\nbool\tprof_sample_accum_update(size_t size);\nvoid\tprof_malloc(const void *ptr, size_t size, prof_thr_cnt_t *cnt);\nvoid\tprof_realloc(const void *ptr, size_t size, prof_thr_cnt_t *cnt,\n    size_t old_size, prof_ctx_t *old_ctx);\nvoid\tprof_free(const void *ptr, size_t size);\n#endif\n\n#if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_PROF_C_))\n/* Thread-specific backtrace cache, used to reduce bt2ctx contention. */\nmalloc_tsd_externs(prof_tdata, prof_tdata_t *)\nmalloc_tsd_funcs(JEMALLOC_INLINE, prof_tdata, prof_tdata_t *, NULL,\n    prof_tdata_cleanup)\n\nJEMALLOC_INLINE prof_tdata_t *\nprof_tdata_get(void)\n{\n\tprof_tdata_t *prof_tdata;\n\n\tcassert(config_prof);\n\n\tprof_tdata = *prof_tdata_tsd_get();\n\tif ((uintptr_t)prof_tdata <= (uintptr_t)PROF_TDATA_STATE_MAX) {\n\t\tif (prof_tdata == NULL)\n\t\t\tprof_tdata = prof_tdata_init();\n\t}\n\n\treturn (prof_tdata);\n}\n\nJEMALLOC_INLINE void\nprof_sample_threshold_update(prof_tdata_t *prof_tdata)\n{\n\tuint64_t r;\n\tdouble u;\n\n\tcassert(config_prof);\n\n\t/*\n\t * Compute sample threshold as a geometrically distributed random\n\t * variable with mean (2^opt_lg_prof_sample).\n\t *\n\t *                         __        __\n\t *                         |  log(u)  |                     1\n\t * prof_tdata->threshold = | -------- |, where p = -------------------\n\t *                         | log(1-p) |             opt_lg_prof_sample\n\t *                                                 2\n\t *\n\t * For more information on the math, see:\n\t *\n\t *   Non-Uniform Random Variate Generation\n\t *   Luc Devroye\n\t *   Springer-Verlag, New York, 1986\n\t *   pp 500\n\t *   (http://cg.scs.carleton.ca/~luc/rnbookindex.html)\n\t */\n\tprng64(r, 53, prof_tdata->prng_state,\n\t    UINT64_C(6364136223846793005), UINT64_C(1442695040888963407));\n\tu = (double)r * (1.0/9007199254740992.0L);\n\tprof_tdata->threshold = (uint64_t)(log(u) /\n\t    log(1.0 - (1.0 / (double)((uint64_t)1U << opt_lg_prof_sample))))\n\t    + (uint64_t)1U;\n}\n\nJEMALLOC_INLINE prof_ctx_t *\nprof_ctx_get(const void *ptr)\n{\n\tprof_ctx_t *ret;\n\tarena_chunk_t *chunk;\n\n\tcassert(config_prof);\n\tassert(ptr != NULL);\n\n\tchunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);\n\tif (chunk != ptr) {\n\t\t/* Region. */\n\t\tret = arena_prof_ctx_get(ptr);\n\t} else\n\t\tret = huge_prof_ctx_get(ptr);\n\n\treturn (ret);\n}\n\nJEMALLOC_INLINE void\nprof_ctx_set(const void *ptr, prof_ctx_t *ctx)\n{\n\tarena_chunk_t *chunk;\n\n\tcassert(config_prof);\n\tassert(ptr != NULL);\n\n\tchunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);\n\tif (chunk != ptr) {\n\t\t/* Region. */\n\t\tarena_prof_ctx_set(ptr, ctx);\n\t} else\n\t\thuge_prof_ctx_set(ptr, ctx);\n}\n\nJEMALLOC_INLINE bool\nprof_sample_accum_update(size_t size)\n{\n\tprof_tdata_t *prof_tdata;\n\n\tcassert(config_prof);\n\t/* Sampling logic is unnecessary if the interval is 1. */\n\tassert(opt_lg_prof_sample != 0);\n\n\tprof_tdata = *prof_tdata_tsd_get();\n\tif ((uintptr_t)prof_tdata <= (uintptr_t)PROF_TDATA_STATE_MAX)\n\t\treturn (true);\n\n\t/* Take care to avoid integer overflow. */\n\tif (size >= prof_tdata->threshold - prof_tdata->accum) {\n\t\tprof_tdata->accum -= (prof_tdata->threshold - size);\n\t\t/* Compute new sample threshold. */\n\t\tprof_sample_threshold_update(prof_tdata);\n\t\twhile (prof_tdata->accum >= prof_tdata->threshold) {\n\t\t\tprof_tdata->accum -= prof_tdata->threshold;\n\t\t\tprof_sample_threshold_update(prof_tdata);\n\t\t}\n\t\treturn (false);\n\t} else {\n\t\tprof_tdata->accum += size;\n\t\treturn (true);\n\t}\n}\n\nJEMALLOC_INLINE void\nprof_malloc(const void *ptr, size_t size, prof_thr_cnt_t *cnt)\n{\n\n\tcassert(config_prof);\n\tassert(ptr != NULL);\n\tassert(size == isalloc(ptr, true));\n\n\tif (opt_lg_prof_sample != 0) {\n\t\tif (prof_sample_accum_update(size)) {\n\t\t\t/*\n\t\t\t * Don't sample.  For malloc()-like allocation, it is\n\t\t\t * always possible to tell in advance how large an\n\t\t\t * object's usable size will be, so there should never\n\t\t\t * be a difference between the size passed to\n\t\t\t * PROF_ALLOC_PREP() and prof_malloc().\n\t\t\t */\n\t\t\tassert((uintptr_t)cnt == (uintptr_t)1U);\n\t\t}\n\t}\n\n\tif ((uintptr_t)cnt > (uintptr_t)1U) {\n\t\tprof_ctx_set(ptr, cnt->ctx);\n\n\t\tcnt->epoch++;\n\t\t/*********/\n\t\tmb_write();\n\t\t/*********/\n\t\tcnt->cnts.curobjs++;\n\t\tcnt->cnts.curbytes += size;\n\t\tif (opt_prof_accum) {\n\t\t\tcnt->cnts.accumobjs++;\n\t\t\tcnt->cnts.accumbytes += size;\n\t\t}\n\t\t/*********/\n\t\tmb_write();\n\t\t/*********/\n\t\tcnt->epoch++;\n\t\t/*********/\n\t\tmb_write();\n\t\t/*********/\n\t} else\n\t\tprof_ctx_set(ptr, (prof_ctx_t *)(uintptr_t)1U);\n}\n\nJEMALLOC_INLINE void\nprof_realloc(const void *ptr, size_t size, prof_thr_cnt_t *cnt,\n    size_t old_size, prof_ctx_t *old_ctx)\n{\n\tprof_thr_cnt_t *told_cnt;\n\n\tcassert(config_prof);\n\tassert(ptr != NULL || (uintptr_t)cnt <= (uintptr_t)1U);\n\n\tif (ptr != NULL) {\n\t\tassert(size == isalloc(ptr, true));\n\t\tif (opt_lg_prof_sample != 0) {\n\t\t\tif (prof_sample_accum_update(size)) {\n\t\t\t\t/*\n\t\t\t\t * Don't sample.  The size passed to\n\t\t\t\t * PROF_ALLOC_PREP() was larger than what\n\t\t\t\t * actually got allocated, so a backtrace was\n\t\t\t\t * captured for this allocation, even though\n\t\t\t\t * its actual size was insufficient to cross\n\t\t\t\t * the sample threshold.\n\t\t\t\t */\n\t\t\t\tcnt = (prof_thr_cnt_t *)(uintptr_t)1U;\n\t\t\t}\n\t\t}\n\t}\n\n\tif ((uintptr_t)old_ctx > (uintptr_t)1U) {\n\t\ttold_cnt = prof_lookup(old_ctx->bt);\n\t\tif (told_cnt == NULL) {\n\t\t\t/*\n\t\t\t * It's too late to propagate OOM for this realloc(),\n\t\t\t * so operate directly on old_cnt->ctx->cnt_merged.\n\t\t\t */\n\t\t\tmalloc_mutex_lock(old_ctx->lock);\n\t\t\told_ctx->cnt_merged.curobjs--;\n\t\t\told_ctx->cnt_merged.curbytes -= old_size;\n\t\t\tmalloc_mutex_unlock(old_ctx->lock);\n\t\t\ttold_cnt = (prof_thr_cnt_t *)(uintptr_t)1U;\n\t\t}\n\t} else\n\t\ttold_cnt = (prof_thr_cnt_t *)(uintptr_t)1U;\n\n\tif ((uintptr_t)told_cnt > (uintptr_t)1U)\n\t\ttold_cnt->epoch++;\n\tif ((uintptr_t)cnt > (uintptr_t)1U) {\n\t\tprof_ctx_set(ptr, cnt->ctx);\n\t\tcnt->epoch++;\n\t} else if (ptr != NULL)\n\t\tprof_ctx_set(ptr, (prof_ctx_t *)(uintptr_t)1U);\n\t/*********/\n\tmb_write();\n\t/*********/\n\tif ((uintptr_t)told_cnt > (uintptr_t)1U) {\n\t\ttold_cnt->cnts.curobjs--;\n\t\ttold_cnt->cnts.curbytes -= old_size;\n\t}\n\tif ((uintptr_t)cnt > (uintptr_t)1U) {\n\t\tcnt->cnts.curobjs++;\n\t\tcnt->cnts.curbytes += size;\n\t\tif (opt_prof_accum) {\n\t\t\tcnt->cnts.accumobjs++;\n\t\t\tcnt->cnts.accumbytes += size;\n\t\t}\n\t}\n\t/*********/\n\tmb_write();\n\t/*********/\n\tif ((uintptr_t)told_cnt > (uintptr_t)1U)\n\t\ttold_cnt->epoch++;\n\tif ((uintptr_t)cnt > (uintptr_t)1U)\n\t\tcnt->epoch++;\n\t/*********/\n\tmb_write(); /* Not strictly necessary. */\n}\n\nJEMALLOC_INLINE void\nprof_free(const void *ptr, size_t size)\n{\n\tprof_ctx_t *ctx = prof_ctx_get(ptr);\n\n\tcassert(config_prof);\n\n\tif ((uintptr_t)ctx > (uintptr_t)1) {\n\t\tprof_thr_cnt_t *tcnt;\n\t\tassert(size == isalloc(ptr, true));\n\t\ttcnt = prof_lookup(ctx->bt);\n\n\t\tif (tcnt != NULL) {\n\t\t\ttcnt->epoch++;\n\t\t\t/*********/\n\t\t\tmb_write();\n\t\t\t/*********/\n\t\t\ttcnt->cnts.curobjs--;\n\t\t\ttcnt->cnts.curbytes -= size;\n\t\t\t/*********/\n\t\t\tmb_write();\n\t\t\t/*********/\n\t\t\ttcnt->epoch++;\n\t\t\t/*********/\n\t\t\tmb_write();\n\t\t\t/*********/\n\t\t} else {\n\t\t\t/*\n\t\t\t * OOM during free() cannot be propagated, so operate\n\t\t\t * directly on cnt->ctx->cnt_merged.\n\t\t\t */\n\t\t\tmalloc_mutex_lock(ctx->lock);\n\t\t\tctx->cnt_merged.curobjs--;\n\t\t\tctx->cnt_merged.curbytes -= size;\n\t\t\tmalloc_mutex_unlock(ctx->lock);\n\t\t}\n\t}\n}\n#endif\n\n#endif /* JEMALLOC_H_INLINES */\n/******************************************************************************/\n"
  },
  {
    "path": "deps/jemalloc/include/jemalloc/internal/ql.h",
    "content": "/*\n * List definitions.\n */\n#define ql_head(a_type)\t\t\t\t\t\t\t\\\nstruct {\t\t\t\t\t\t\t\t\\\n\ta_type *qlh_first;\t\t\t\t\t\t\\\n}\n\n#define ql_head_initializer(a_head) {NULL}\n\n#define ql_elm(a_type)\tqr(a_type)\n\n/* List functions. */\n#define ql_new(a_head) do {\t\t\t\t\t\t\\\n\t(a_head)->qlh_first = NULL;\t\t\t\t\t\\\n} while (0)\n\n#define ql_elm_new(a_elm, a_field) qr_new((a_elm), a_field)\n\n#define ql_first(a_head) ((a_head)->qlh_first)\n\n#define ql_last(a_head, a_field)\t\t\t\t\t\\\n\t((ql_first(a_head) != NULL)\t\t\t\t\t\\\n\t    ? qr_prev(ql_first(a_head), a_field) : NULL)\n\n#define ql_next(a_head, a_elm, a_field)\t\t\t\t\t\\\n\t((ql_last(a_head, a_field) != (a_elm))\t\t\t\t\\\n\t    ? qr_next((a_elm), a_field)\t: NULL)\n\n#define ql_prev(a_head, a_elm, a_field)\t\t\t\t\t\\\n\t((ql_first(a_head) != (a_elm)) ? qr_prev((a_elm), a_field)\t\\\n\t\t\t\t       : NULL)\n\n#define ql_before_insert(a_head, a_qlelm, a_elm, a_field) do {\t\t\\\n\tqr_before_insert((a_qlelm), (a_elm), a_field);\t\t\t\\\n\tif (ql_first(a_head) == (a_qlelm)) {\t\t\t\t\\\n\t\tql_first(a_head) = (a_elm);\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n} while (0)\n\n#define ql_after_insert(a_qlelm, a_elm, a_field)\t\t\t\\\n\tqr_after_insert((a_qlelm), (a_elm), a_field)\n\n#define ql_head_insert(a_head, a_elm, a_field) do {\t\t\t\\\n\tif (ql_first(a_head) != NULL) {\t\t\t\t\t\\\n\t\tqr_before_insert(ql_first(a_head), (a_elm), a_field);\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\tql_first(a_head) = (a_elm);\t\t\t\t\t\\\n} while (0)\n\n#define ql_tail_insert(a_head, a_elm, a_field) do {\t\t\t\\\n\tif (ql_first(a_head) != NULL) {\t\t\t\t\t\\\n\t\tqr_before_insert(ql_first(a_head), (a_elm), a_field);\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\tql_first(a_head) = qr_next((a_elm), a_field);\t\t\t\\\n} while (0)\n\n#define ql_remove(a_head, a_elm, a_field) do {\t\t\t\t\\\n\tif (ql_first(a_head) == (a_elm)) {\t\t\t\t\\\n\t\tql_first(a_head) = qr_next(ql_first(a_head), a_field);\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\tif (ql_first(a_head) != (a_elm)) {\t\t\t\t\\\n\t\tqr_remove((a_elm), a_field);\t\t\t\t\\\n\t} else {\t\t\t\t\t\t\t\\\n\t\tql_first(a_head) = NULL;\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n} while (0)\n\n#define ql_head_remove(a_head, a_type, a_field) do {\t\t\t\\\n\ta_type *t = ql_first(a_head);\t\t\t\t\t\\\n\tql_remove((a_head), t, a_field);\t\t\t\t\\\n} while (0)\n\n#define ql_tail_remove(a_head, a_type, a_field) do {\t\t\t\\\n\ta_type *t = ql_last(a_head, a_field);\t\t\t\t\\\n\tql_remove((a_head), t, a_field);\t\t\t\t\\\n} while (0)\n\n#define ql_foreach(a_var, a_head, a_field)\t\t\t\t\\\n\tqr_foreach((a_var), ql_first(a_head), a_field)\n\n#define ql_reverse_foreach(a_var, a_head, a_field)\t\t\t\\\n\tqr_reverse_foreach((a_var), ql_first(a_head), a_field)\n"
  },
  {
    "path": "deps/jemalloc/include/jemalloc/internal/qr.h",
    "content": "/* Ring definitions. */\n#define qr(a_type)\t\t\t\t\t\t\t\\\nstruct {\t\t\t\t\t\t\t\t\\\n\ta_type\t*qre_next;\t\t\t\t\t\t\\\n\ta_type\t*qre_prev;\t\t\t\t\t\t\\\n}\n\n/* Ring functions. */\n#define qr_new(a_qr, a_field) do {\t\t\t\t\t\\\n\t(a_qr)->a_field.qre_next = (a_qr);\t\t\t\t\\\n\t(a_qr)->a_field.qre_prev = (a_qr);\t\t\t\t\\\n} while (0)\n\n#define qr_next(a_qr, a_field) ((a_qr)->a_field.qre_next)\n\n#define qr_prev(a_qr, a_field) ((a_qr)->a_field.qre_prev)\n\n#define qr_before_insert(a_qrelm, a_qr, a_field) do {\t\t\t\\\n\t(a_qr)->a_field.qre_prev = (a_qrelm)->a_field.qre_prev;\t\t\\\n\t(a_qr)->a_field.qre_next = (a_qrelm);\t\t\t\t\\\n\t(a_qr)->a_field.qre_prev->a_field.qre_next = (a_qr);\t\t\\\n\t(a_qrelm)->a_field.qre_prev = (a_qr);\t\t\t\t\\\n} while (0)\n\n#define qr_after_insert(a_qrelm, a_qr, a_field)\t\t\t\t\\\n    do\t\t\t\t\t\t\t\t\t\\\n    {\t\t\t\t\t\t\t\t\t\\\n\t(a_qr)->a_field.qre_next = (a_qrelm)->a_field.qre_next;\t\t\\\n\t(a_qr)->a_field.qre_prev = (a_qrelm);\t\t\t\t\\\n\t(a_qr)->a_field.qre_next->a_field.qre_prev = (a_qr);\t\t\\\n\t(a_qrelm)->a_field.qre_next = (a_qr);\t\t\t\t\\\n    } while (0)\n\n#define qr_meld(a_qr_a, a_qr_b, a_field) do {\t\t\t\t\\\n\tvoid *t;\t\t\t\t\t\t\t\\\n\t(a_qr_a)->a_field.qre_prev->a_field.qre_next = (a_qr_b);\t\\\n\t(a_qr_b)->a_field.qre_prev->a_field.qre_next = (a_qr_a);\t\\\n\tt = (a_qr_a)->a_field.qre_prev;\t\t\t\t\t\\\n\t(a_qr_a)->a_field.qre_prev = (a_qr_b)->a_field.qre_prev;\t\\\n\t(a_qr_b)->a_field.qre_prev = t;\t\t\t\t\t\\\n} while (0)\n\n/* qr_meld() and qr_split() are functionally equivalent, so there's no need to\n * have two copies of the code. */\n#define qr_split(a_qr_a, a_qr_b, a_field)\t\t\t\t\\\n\tqr_meld((a_qr_a), (a_qr_b), a_field)\n\n#define qr_remove(a_qr, a_field) do {\t\t\t\t\t\\\n\t(a_qr)->a_field.qre_prev->a_field.qre_next\t\t\t\\\n\t    = (a_qr)->a_field.qre_next;\t\t\t\t\t\\\n\t(a_qr)->a_field.qre_next->a_field.qre_prev\t\t\t\\\n\t    = (a_qr)->a_field.qre_prev;\t\t\t\t\t\\\n\t(a_qr)->a_field.qre_next = (a_qr);\t\t\t\t\\\n\t(a_qr)->a_field.qre_prev = (a_qr);\t\t\t\t\\\n} while (0)\n\n#define qr_foreach(var, a_qr, a_field)\t\t\t\t\t\\\n\tfor ((var) = (a_qr);\t\t\t\t\t\t\\\n\t    (var) != NULL;\t\t\t\t\t\t\\\n\t    (var) = (((var)->a_field.qre_next != (a_qr))\t\t\\\n\t    ? (var)->a_field.qre_next : NULL))\n\n#define qr_reverse_foreach(var, a_qr, a_field)\t\t\t\t\\\n\tfor ((var) = ((a_qr) != NULL) ? qr_prev(a_qr, a_field) : NULL;\t\\\n\t    (var) != NULL;\t\t\t\t\t\t\\\n\t    (var) = (((var) != (a_qr))\t\t\t\t\t\\\n\t    ? (var)->a_field.qre_prev : NULL))\n"
  },
  {
    "path": "deps/jemalloc/include/jemalloc/internal/quarantine.h",
    "content": "/******************************************************************************/\n#ifdef JEMALLOC_H_TYPES\n\n/* Default per thread quarantine size if valgrind is enabled. */\n#define\tJEMALLOC_VALGRIND_QUARANTINE_DEFAULT\t(ZU(1) << 24)\n\n#endif /* JEMALLOC_H_TYPES */\n/******************************************************************************/\n#ifdef JEMALLOC_H_STRUCTS\n\n#endif /* JEMALLOC_H_STRUCTS */\n/******************************************************************************/\n#ifdef JEMALLOC_H_EXTERNS\n\nvoid\tquarantine(void *ptr);\nbool\tquarantine_boot(void);\n\n#endif /* JEMALLOC_H_EXTERNS */\n/******************************************************************************/\n#ifdef JEMALLOC_H_INLINES\n\n#endif /* JEMALLOC_H_INLINES */\n/******************************************************************************/\n\n"
  },
  {
    "path": "deps/jemalloc/include/jemalloc/internal/rb.h",
    "content": "/*-\n *******************************************************************************\n *\n * cpp macro implementation of left-leaning 2-3 red-black trees.  Parent\n * pointers are not used, and color bits are stored in the least significant\n * bit of right-child pointers (if RB_COMPACT is defined), thus making node\n * linkage as compact as is possible for red-black trees.\n *\n * Usage:\n *\n *   #include <stdint.h>\n *   #include <stdbool.h>\n *   #define NDEBUG // (Optional, see assert(3).)\n *   #include <assert.h>\n *   #define RB_COMPACT // (Optional, embed color bits in right-child pointers.)\n *   #include <rb.h>\n *   ...\n *\n *******************************************************************************\n */\n\n#ifndef RB_H_\n#define\tRB_H_\n\n#if 0\n__FBSDID(\"$FreeBSD: head/lib/libc/stdlib/rb.h 204493 2010-02-28 22:57:13Z jasone $\");\n#endif\n\n#ifdef RB_COMPACT\n/* Node structure. */\n#define\trb_node(a_type)\t\t\t\t\t\t\t\\\nstruct {\t\t\t\t\t\t\t\t\\\n    a_type *rbn_left;\t\t\t\t\t\t\t\\\n    a_type *rbn_right_red;\t\t\t\t\t\t\\\n}\n#else\n#define\trb_node(a_type)\t\t\t\t\t\t\t\\\nstruct {\t\t\t\t\t\t\t\t\\\n    a_type *rbn_left;\t\t\t\t\t\t\t\\\n    a_type *rbn_right;\t\t\t\t\t\t\t\\\n    bool rbn_red;\t\t\t\t\t\t\t\\\n}\n#endif\n\n/* Root structure. */\n#define\trb_tree(a_type)\t\t\t\t\t\t\t\\\nstruct {\t\t\t\t\t\t\t\t\\\n    a_type *rbt_root;\t\t\t\t\t\t\t\\\n    a_type rbt_nil;\t\t\t\t\t\t\t\\\n}\n\n/* Left accessors. */\n#define\trbtn_left_get(a_type, a_field, a_node)\t\t\t\t\\\n    ((a_node)->a_field.rbn_left)\n#define\trbtn_left_set(a_type, a_field, a_node, a_left) do {\t\t\\\n    (a_node)->a_field.rbn_left = a_left;\t\t\t\t\\\n} while (0)\n\n#ifdef RB_COMPACT\n/* Right accessors. */\n#define\trbtn_right_get(a_type, a_field, a_node)\t\t\t\t\\\n    ((a_type *) (((intptr_t) (a_node)->a_field.rbn_right_red)\t\t\\\n      & ((ssize_t)-2)))\n#define\trbtn_right_set(a_type, a_field, a_node, a_right) do {\t\t\\\n    (a_node)->a_field.rbn_right_red = (a_type *) (((uintptr_t) a_right)\t\\\n      | (((uintptr_t) (a_node)->a_field.rbn_right_red) & ((size_t)1)));\t\\\n} while (0)\n\n/* Color accessors. */\n#define\trbtn_red_get(a_type, a_field, a_node)\t\t\t\t\\\n    ((bool) (((uintptr_t) (a_node)->a_field.rbn_right_red)\t\t\\\n      & ((size_t)1)))\n#define\trbtn_color_set(a_type, a_field, a_node, a_red) do {\t\t\\\n    (a_node)->a_field.rbn_right_red = (a_type *) ((((intptr_t)\t\t\\\n      (a_node)->a_field.rbn_right_red) & ((ssize_t)-2))\t\t\t\\\n      | ((ssize_t)a_red));\t\t\t\t\t\t\\\n} while (0)\n#define\trbtn_red_set(a_type, a_field, a_node) do {\t\t\t\\\n    (a_node)->a_field.rbn_right_red = (a_type *) (((uintptr_t)\t\t\\\n      (a_node)->a_field.rbn_right_red) | ((size_t)1));\t\t\t\\\n} while (0)\n#define\trbtn_black_set(a_type, a_field, a_node) do {\t\t\t\\\n    (a_node)->a_field.rbn_right_red = (a_type *) (((intptr_t)\t\t\\\n      (a_node)->a_field.rbn_right_red) & ((ssize_t)-2));\t\t\\\n} while (0)\n#else\n/* Right accessors. */\n#define\trbtn_right_get(a_type, a_field, a_node)\t\t\t\t\\\n    ((a_node)->a_field.rbn_right)\n#define\trbtn_right_set(a_type, a_field, a_node, a_right) do {\t\t\\\n    (a_node)->a_field.rbn_right = a_right;\t\t\t\t\\\n} while (0)\n\n/* Color accessors. */\n#define\trbtn_red_get(a_type, a_field, a_node)\t\t\t\t\\\n    ((a_node)->a_field.rbn_red)\n#define\trbtn_color_set(a_type, a_field, a_node, a_red) do {\t\t\\\n    (a_node)->a_field.rbn_red = (a_red);\t\t\t\t\\\n} while (0)\n#define\trbtn_red_set(a_type, a_field, a_node) do {\t\t\t\\\n    (a_node)->a_field.rbn_red = true;\t\t\t\t\t\\\n} while (0)\n#define\trbtn_black_set(a_type, a_field, a_node) do {\t\t\t\\\n    (a_node)->a_field.rbn_red = false;\t\t\t\t\t\\\n} while (0)\n#endif\n\n/* Node initializer. */\n#define\trbt_node_new(a_type, a_field, a_rbt, a_node) do {\t\t\\\n    rbtn_left_set(a_type, a_field, (a_node), &(a_rbt)->rbt_nil);\t\\\n    rbtn_right_set(a_type, a_field, (a_node), &(a_rbt)->rbt_nil);\t\\\n    rbtn_red_set(a_type, a_field, (a_node));\t\t\t\t\\\n} while (0)\n\n/* Tree initializer. */\n#define\trb_new(a_type, a_field, a_rbt) do {\t\t\t\t\\\n    (a_rbt)->rbt_root = &(a_rbt)->rbt_nil;\t\t\t\t\\\n    rbt_node_new(a_type, a_field, a_rbt, &(a_rbt)->rbt_nil);\t\t\\\n    rbtn_black_set(a_type, a_field, &(a_rbt)->rbt_nil);\t\t\t\\\n} while (0)\n\n/* Internal utility macros. */\n#define\trbtn_first(a_type, a_field, a_rbt, a_root, r_node) do {\t\t\\\n    (r_node) = (a_root);\t\t\t\t\t\t\\\n    if ((r_node) != &(a_rbt)->rbt_nil) {\t\t\t\t\\\n\tfor (;\t\t\t\t\t\t\t\t\\\n\t  rbtn_left_get(a_type, a_field, (r_node)) != &(a_rbt)->rbt_nil;\\\n\t  (r_node) = rbtn_left_get(a_type, a_field, (r_node))) {\t\\\n\t}\t\t\t\t\t\t\t\t\\\n    }\t\t\t\t\t\t\t\t\t\\\n} while (0)\n\n#define\trbtn_last(a_type, a_field, a_rbt, a_root, r_node) do {\t\t\\\n    (r_node) = (a_root);\t\t\t\t\t\t\\\n    if ((r_node) != &(a_rbt)->rbt_nil) {\t\t\t\t\\\n\tfor (; rbtn_right_get(a_type, a_field, (r_node)) !=\t\t\\\n\t  &(a_rbt)->rbt_nil; (r_node) = rbtn_right_get(a_type, a_field,\t\\\n\t  (r_node))) {\t\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n    }\t\t\t\t\t\t\t\t\t\\\n} while (0)\n\n#define\trbtn_rotate_left(a_type, a_field, a_node, r_node) do {\t\t\\\n    (r_node) = rbtn_right_get(a_type, a_field, (a_node));\t\t\\\n    rbtn_right_set(a_type, a_field, (a_node),\t\t\t\t\\\n      rbtn_left_get(a_type, a_field, (r_node)));\t\t\t\\\n    rbtn_left_set(a_type, a_field, (r_node), (a_node));\t\t\t\\\n} while (0)\n\n#define\trbtn_rotate_right(a_type, a_field, a_node, r_node) do {\t\t\\\n    (r_node) = rbtn_left_get(a_type, a_field, (a_node));\t\t\\\n    rbtn_left_set(a_type, a_field, (a_node),\t\t\t\t\\\n      rbtn_right_get(a_type, a_field, (r_node)));\t\t\t\\\n    rbtn_right_set(a_type, a_field, (r_node), (a_node));\t\t\\\n} while (0)\n\n/*\n * The rb_proto() macro generates function prototypes that correspond to the\n * functions generated by an equivalently parameterized call to rb_gen().\n */\n\n#define\trb_proto(a_attr, a_prefix, a_rbt_type, a_type)\t\t\t\\\na_attr void\t\t\t\t\t\t\t\t\\\na_prefix##new(a_rbt_type *rbtree);\t\t\t\t\t\\\na_attr a_type *\t\t\t\t\t\t\t\t\\\na_prefix##first(a_rbt_type *rbtree);\t\t\t\t\t\\\na_attr a_type *\t\t\t\t\t\t\t\t\\\na_prefix##last(a_rbt_type *rbtree);\t\t\t\t\t\\\na_attr a_type *\t\t\t\t\t\t\t\t\\\na_prefix##next(a_rbt_type *rbtree, a_type *node);\t\t\t\\\na_attr a_type *\t\t\t\t\t\t\t\t\\\na_prefix##prev(a_rbt_type *rbtree, a_type *node);\t\t\t\\\na_attr a_type *\t\t\t\t\t\t\t\t\\\na_prefix##search(a_rbt_type *rbtree, a_type *key);\t\t\t\\\na_attr a_type *\t\t\t\t\t\t\t\t\\\na_prefix##nsearch(a_rbt_type *rbtree, a_type *key);\t\t\t\\\na_attr a_type *\t\t\t\t\t\t\t\t\\\na_prefix##psearch(a_rbt_type *rbtree, a_type *key);\t\t\t\\\na_attr void\t\t\t\t\t\t\t\t\\\na_prefix##insert(a_rbt_type *rbtree, a_type *node);\t\t\t\\\na_attr void\t\t\t\t\t\t\t\t\\\na_prefix##remove(a_rbt_type *rbtree, a_type *node);\t\t\t\\\na_attr a_type *\t\t\t\t\t\t\t\t\\\na_prefix##iter(a_rbt_type *rbtree, a_type *start, a_type *(*cb)(\t\\\n  a_rbt_type *, a_type *, void *), void *arg);\t\t\t\t\\\na_attr a_type *\t\t\t\t\t\t\t\t\\\na_prefix##reverse_iter(a_rbt_type *rbtree, a_type *start,\t\t\\\n  a_type *(*cb)(a_rbt_type *, a_type *, void *), void *arg);\n\n/*\n * The rb_gen() macro generates a type-specific red-black tree implementation,\n * based on the above cpp macros.\n *\n * Arguments:\n *\n *   a_attr    : Function attribute for generated functions (ex: static).\n *   a_prefix  : Prefix for generated functions (ex: ex_).\n *   a_rb_type : Type for red-black tree data structure (ex: ex_t).\n *   a_type    : Type for red-black tree node data structure (ex: ex_node_t).\n *   a_field   : Name of red-black tree node linkage (ex: ex_link).\n *   a_cmp     : Node comparison function name, with the following prototype:\n *                 int (a_cmp *)(a_type *a_node, a_type *a_other);\n *                                       ^^^^^^\n *                                    or a_key\n *               Interpretation of comparision function return values:\n *                 -1 : a_node <  a_other\n *                  0 : a_node == a_other\n *                  1 : a_node >  a_other\n *               In all cases, the a_node or a_key macro argument is the first\n *               argument to the comparison function, which makes it possible\n *               to write comparison functions that treat the first argument\n *               specially.\n *\n * Assuming the following setup:\n *\n *   typedef struct ex_node_s ex_node_t;\n *   struct ex_node_s {\n *       rb_node(ex_node_t) ex_link;\n *   };\n *   typedef rb_tree(ex_node_t) ex_t;\n *   rb_gen(static, ex_, ex_t, ex_node_t, ex_link, ex_cmp)\n *\n * The following API is generated:\n *\n *   static void\n *   ex_new(ex_t *tree);\n *       Description: Initialize a red-black tree structure.\n *       Args:\n *         tree: Pointer to an uninitialized red-black tree object.\n *\n *   static ex_node_t *\n *   ex_first(ex_t *tree);\n *   static ex_node_t *\n *   ex_last(ex_t *tree);\n *       Description: Get the first/last node in tree.\n *       Args:\n *         tree: Pointer to an initialized red-black tree object.\n *       Ret: First/last node in tree, or NULL if tree is empty.\n *\n *   static ex_node_t *\n *   ex_next(ex_t *tree, ex_node_t *node);\n *   static ex_node_t *\n *   ex_prev(ex_t *tree, ex_node_t *node);\n *       Description: Get node's successor/predecessor.\n *       Args:\n *         tree: Pointer to an initialized red-black tree object.\n *         node: A node in tree.\n *       Ret: node's successor/predecessor in tree, or NULL if node is\n *            last/first.\n *\n *   static ex_node_t *\n *   ex_search(ex_t *tree, ex_node_t *key);\n *       Description: Search for node that matches key.\n *       Args:\n *         tree: Pointer to an initialized red-black tree object.\n *         key : Search key.\n *       Ret: Node in tree that matches key, or NULL if no match.\n *\n *   static ex_node_t *\n *   ex_nsearch(ex_t *tree, ex_node_t *key);\n *   static ex_node_t *\n *   ex_psearch(ex_t *tree, ex_node_t *key);\n *       Description: Search for node that matches key.  If no match is found,\n *                    return what would be key's successor/predecessor, were\n *                    key in tree.\n *       Args:\n *         tree: Pointer to an initialized red-black tree object.\n *         key : Search key.\n *       Ret: Node in tree that matches key, or if no match, hypothetical node's\n *            successor/predecessor (NULL if no successor/predecessor).\n *\n *   static void\n *   ex_insert(ex_t *tree, ex_node_t *node);\n *       Description: Insert node into tree.\n *       Args:\n *         tree: Pointer to an initialized red-black tree object.\n *         node: Node to be inserted into tree.\n *\n *   static void\n *   ex_remove(ex_t *tree, ex_node_t *node);\n *       Description: Remove node from tree.\n *       Args:\n *         tree: Pointer to an initialized red-black tree object.\n *         node: Node in tree to be removed.\n *\n *   static ex_node_t *\n *   ex_iter(ex_t *tree, ex_node_t *start, ex_node_t *(*cb)(ex_t *,\n *     ex_node_t *, void *), void *arg);\n *   static ex_node_t *\n *   ex_reverse_iter(ex_t *tree, ex_node_t *start, ex_node *(*cb)(ex_t *,\n *     ex_node_t *, void *), void *arg);\n *       Description: Iterate forward/backward over tree, starting at node.  If\n *                    tree is modified, iteration must be immediately\n *                    terminated by the callback function that causes the\n *                    modification.\n *       Args:\n *         tree : Pointer to an initialized red-black tree object.\n *         start: Node at which to start iteration, or NULL to start at\n *                first/last node.\n *         cb   : Callback function, which is called for each node during\n *                iteration.  Under normal circumstances the callback function\n *                should return NULL, which causes iteration to continue.  If a\n *                callback function returns non-NULL, iteration is immediately\n *                terminated and the non-NULL return value is returned by the\n *                iterator.  This is useful for re-starting iteration after\n *                modifying tree.\n *         arg  : Opaque pointer passed to cb().\n *       Ret: NULL if iteration completed, or the non-NULL callback return value\n *            that caused termination of the iteration.\n */\n#define\trb_gen(a_attr, a_prefix, a_rbt_type, a_type, a_field, a_cmp)\t\\\na_attr void\t\t\t\t\t\t\t\t\\\na_prefix##new(a_rbt_type *rbtree) {\t\t\t\t\t\\\n    rb_new(a_type, a_field, rbtree);\t\t\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\na_attr a_type *\t\t\t\t\t\t\t\t\\\na_prefix##first(a_rbt_type *rbtree) {\t\t\t\t\t\\\n    a_type *ret;\t\t\t\t\t\t\t\\\n    rbtn_first(a_type, a_field, rbtree, rbtree->rbt_root, ret);\t\t\\\n    if (ret == &rbtree->rbt_nil) {\t\t\t\t\t\\\n\tret = NULL;\t\t\t\t\t\t\t\\\n    }\t\t\t\t\t\t\t\t\t\\\n    return (ret);\t\t\t\t\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\na_attr a_type *\t\t\t\t\t\t\t\t\\\na_prefix##last(a_rbt_type *rbtree) {\t\t\t\t\t\\\n    a_type *ret;\t\t\t\t\t\t\t\\\n    rbtn_last(a_type, a_field, rbtree, rbtree->rbt_root, ret);\t\t\\\n    if (ret == &rbtree->rbt_nil) {\t\t\t\t\t\\\n\tret = NULL;\t\t\t\t\t\t\t\\\n    }\t\t\t\t\t\t\t\t\t\\\n    return (ret);\t\t\t\t\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\na_attr a_type *\t\t\t\t\t\t\t\t\\\na_prefix##next(a_rbt_type *rbtree, a_type *node) {\t\t\t\\\n    a_type *ret;\t\t\t\t\t\t\t\\\n    if (rbtn_right_get(a_type, a_field, node) != &rbtree->rbt_nil) {\t\\\n\trbtn_first(a_type, a_field, rbtree, rbtn_right_get(a_type,\t\\\n\t  a_field, node), ret);\t\t\t\t\t\t\\\n    } else {\t\t\t\t\t\t\t\t\\\n\ta_type *tnode = rbtree->rbt_root;\t\t\t\t\\\n\tassert(tnode != &rbtree->rbt_nil);\t\t\t\t\\\n\tret = &rbtree->rbt_nil;\t\t\t\t\t\t\\\n\twhile (true) {\t\t\t\t\t\t\t\\\n\t    int cmp = (a_cmp)(node, tnode);\t\t\t\t\\\n\t    if (cmp < 0) {\t\t\t\t\t\t\\\n\t\tret = tnode;\t\t\t\t\t\t\\\n\t\ttnode = rbtn_left_get(a_type, a_field, tnode);\t\t\\\n\t    } else if (cmp > 0) {\t\t\t\t\t\\\n\t\ttnode = rbtn_right_get(a_type, a_field, tnode);\t\t\\\n\t    } else {\t\t\t\t\t\t\t\\\n\t\tbreak;\t\t\t\t\t\t\t\\\n\t    }\t\t\t\t\t\t\t\t\\\n\t    assert(tnode != &rbtree->rbt_nil);\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n    }\t\t\t\t\t\t\t\t\t\\\n    if (ret == &rbtree->rbt_nil) {\t\t\t\t\t\\\n\tret = (NULL);\t\t\t\t\t\t\t\\\n    }\t\t\t\t\t\t\t\t\t\\\n    return (ret);\t\t\t\t\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\na_attr a_type *\t\t\t\t\t\t\t\t\\\na_prefix##prev(a_rbt_type *rbtree, a_type *node) {\t\t\t\\\n    a_type *ret;\t\t\t\t\t\t\t\\\n    if (rbtn_left_get(a_type, a_field, node) != &rbtree->rbt_nil) {\t\\\n\trbtn_last(a_type, a_field, rbtree, rbtn_left_get(a_type,\t\\\n\t  a_field, node), ret);\t\t\t\t\t\t\\\n    } else {\t\t\t\t\t\t\t\t\\\n\ta_type *tnode = rbtree->rbt_root;\t\t\t\t\\\n\tassert(tnode != &rbtree->rbt_nil);\t\t\t\t\\\n\tret = &rbtree->rbt_nil;\t\t\t\t\t\t\\\n\twhile (true) {\t\t\t\t\t\t\t\\\n\t    int cmp = (a_cmp)(node, tnode);\t\t\t\t\\\n\t    if (cmp < 0) {\t\t\t\t\t\t\\\n\t\ttnode = rbtn_left_get(a_type, a_field, tnode);\t\t\\\n\t    } else if (cmp > 0) {\t\t\t\t\t\\\n\t\tret = tnode;\t\t\t\t\t\t\\\n\t\ttnode = rbtn_right_get(a_type, a_field, tnode);\t\t\\\n\t    } else {\t\t\t\t\t\t\t\\\n\t\tbreak;\t\t\t\t\t\t\t\\\n\t    }\t\t\t\t\t\t\t\t\\\n\t    assert(tnode != &rbtree->rbt_nil);\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n    }\t\t\t\t\t\t\t\t\t\\\n    if (ret == &rbtree->rbt_nil) {\t\t\t\t\t\\\n\tret = (NULL);\t\t\t\t\t\t\t\\\n    }\t\t\t\t\t\t\t\t\t\\\n    return (ret);\t\t\t\t\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\na_attr a_type *\t\t\t\t\t\t\t\t\\\na_prefix##search(a_rbt_type *rbtree, a_type *key) {\t\t\t\\\n    a_type *ret;\t\t\t\t\t\t\t\\\n    int cmp;\t\t\t\t\t\t\t\t\\\n    ret = rbtree->rbt_root;\t\t\t\t\t\t\\\n    while (ret != &rbtree->rbt_nil\t\t\t\t\t\\\n      && (cmp = (a_cmp)(key, ret)) != 0) {\t\t\t\t\\\n\tif (cmp < 0) {\t\t\t\t\t\t\t\\\n\t    ret = rbtn_left_get(a_type, a_field, ret);\t\t\t\\\n\t} else {\t\t\t\t\t\t\t\\\n\t    ret = rbtn_right_get(a_type, a_field, ret);\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n    }\t\t\t\t\t\t\t\t\t\\\n    if (ret == &rbtree->rbt_nil) {\t\t\t\t\t\\\n\tret = (NULL);\t\t\t\t\t\t\t\\\n    }\t\t\t\t\t\t\t\t\t\\\n    return (ret);\t\t\t\t\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\na_attr a_type *\t\t\t\t\t\t\t\t\\\na_prefix##nsearch(a_rbt_type *rbtree, a_type *key) {\t\t\t\\\n    a_type *ret;\t\t\t\t\t\t\t\\\n    a_type *tnode = rbtree->rbt_root;\t\t\t\t\t\\\n    ret = &rbtree->rbt_nil;\t\t\t\t\t\t\\\n    while (tnode != &rbtree->rbt_nil) {\t\t\t\t\t\\\n\tint cmp = (a_cmp)(key, tnode);\t\t\t\t\t\\\n\tif (cmp < 0) {\t\t\t\t\t\t\t\\\n\t    ret = tnode;\t\t\t\t\t\t\\\n\t    tnode = rbtn_left_get(a_type, a_field, tnode);\t\t\\\n\t} else if (cmp > 0) {\t\t\t\t\t\t\\\n\t    tnode = rbtn_right_get(a_type, a_field, tnode);\t\t\\\n\t} else {\t\t\t\t\t\t\t\\\n\t    ret = tnode;\t\t\t\t\t\t\\\n\t    break;\t\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n    }\t\t\t\t\t\t\t\t\t\\\n    if (ret == &rbtree->rbt_nil) {\t\t\t\t\t\\\n\tret = (NULL);\t\t\t\t\t\t\t\\\n    }\t\t\t\t\t\t\t\t\t\\\n    return (ret);\t\t\t\t\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\na_attr a_type *\t\t\t\t\t\t\t\t\\\na_prefix##psearch(a_rbt_type *rbtree, a_type *key) {\t\t\t\\\n    a_type *ret;\t\t\t\t\t\t\t\\\n    a_type *tnode = rbtree->rbt_root;\t\t\t\t\t\\\n    ret = &rbtree->rbt_nil;\t\t\t\t\t\t\\\n    while (tnode != &rbtree->rbt_nil) {\t\t\t\t\t\\\n\tint cmp = (a_cmp)(key, tnode);\t\t\t\t\t\\\n\tif (cmp < 0) {\t\t\t\t\t\t\t\\\n\t    tnode = rbtn_left_get(a_type, a_field, tnode);\t\t\\\n\t} else if (cmp > 0) {\t\t\t\t\t\t\\\n\t    ret = tnode;\t\t\t\t\t\t\\\n\t    tnode = rbtn_right_get(a_type, a_field, tnode);\t\t\\\n\t} else {\t\t\t\t\t\t\t\\\n\t    ret = tnode;\t\t\t\t\t\t\\\n\t    break;\t\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n    }\t\t\t\t\t\t\t\t\t\\\n    if (ret == &rbtree->rbt_nil) {\t\t\t\t\t\\\n\tret = (NULL);\t\t\t\t\t\t\t\\\n    }\t\t\t\t\t\t\t\t\t\\\n    return (ret);\t\t\t\t\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\na_attr void\t\t\t\t\t\t\t\t\\\na_prefix##insert(a_rbt_type *rbtree, a_type *node) {\t\t\t\\\n    struct {\t\t\t\t\t\t\t\t\\\n\ta_type *node;\t\t\t\t\t\t\t\\\n\tint cmp;\t\t\t\t\t\t\t\\\n    } path[sizeof(void *) << 4], *pathp;\t\t\t\t\\\n    rbt_node_new(a_type, a_field, rbtree, node);\t\t\t\\\n    /* Wind. */\t\t\t\t\t\t\t\t\\\n    path->node = rbtree->rbt_root;\t\t\t\t\t\\\n    for (pathp = path; pathp->node != &rbtree->rbt_nil; pathp++) {\t\\\n\tint cmp = pathp->cmp = a_cmp(node, pathp->node);\t\t\\\n\tassert(cmp != 0);\t\t\t\t\t\t\\\n\tif (cmp < 0) {\t\t\t\t\t\t\t\\\n\t    pathp[1].node = rbtn_left_get(a_type, a_field,\t\t\\\n\t      pathp->node);\t\t\t\t\t\t\\\n\t} else {\t\t\t\t\t\t\t\\\n\t    pathp[1].node = rbtn_right_get(a_type, a_field,\t\t\\\n\t      pathp->node);\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n    }\t\t\t\t\t\t\t\t\t\\\n    pathp->node = node;\t\t\t\t\t\t\t\\\n    /* Unwind. */\t\t\t\t\t\t\t\\\n    for (pathp--; (uintptr_t)pathp >= (uintptr_t)path; pathp--) {\t\\\n\ta_type *cnode = pathp->node;\t\t\t\t\t\\\n\tif (pathp->cmp < 0) {\t\t\t\t\t\t\\\n\t    a_type *left = pathp[1].node;\t\t\t\t\\\n\t    rbtn_left_set(a_type, a_field, cnode, left);\t\t\\\n\t    if (rbtn_red_get(a_type, a_field, left)) {\t\t\t\\\n\t\ta_type *leftleft = rbtn_left_get(a_type, a_field, left);\\\n\t\tif (rbtn_red_get(a_type, a_field, leftleft)) {\t\t\\\n\t\t    /* Fix up 4-node. */\t\t\t\t\\\n\t\t    a_type *tnode;\t\t\t\t\t\\\n\t\t    rbtn_black_set(a_type, a_field, leftleft);\t\t\\\n\t\t    rbtn_rotate_right(a_type, a_field, cnode, tnode);\t\\\n\t\t    cnode = tnode;\t\t\t\t\t\\\n\t\t}\t\t\t\t\t\t\t\\\n\t    } else {\t\t\t\t\t\t\t\\\n\t\treturn;\t\t\t\t\t\t\t\\\n\t    }\t\t\t\t\t\t\t\t\\\n\t} else {\t\t\t\t\t\t\t\\\n\t    a_type *right = pathp[1].node;\t\t\t\t\\\n\t    rbtn_right_set(a_type, a_field, cnode, right);\t\t\\\n\t    if (rbtn_red_get(a_type, a_field, right)) {\t\t\t\\\n\t\ta_type *left = rbtn_left_get(a_type, a_field, cnode);\t\\\n\t\tif (rbtn_red_get(a_type, a_field, left)) {\t\t\\\n\t\t    /* Split 4-node. */\t\t\t\t\t\\\n\t\t    rbtn_black_set(a_type, a_field, left);\t\t\\\n\t\t    rbtn_black_set(a_type, a_field, right);\t\t\\\n\t\t    rbtn_red_set(a_type, a_field, cnode);\t\t\\\n\t\t} else {\t\t\t\t\t\t\\\n\t\t    /* Lean left. */\t\t\t\t\t\\\n\t\t    a_type *tnode;\t\t\t\t\t\\\n\t\t    bool tred = rbtn_red_get(a_type, a_field, cnode);\t\\\n\t\t    rbtn_rotate_left(a_type, a_field, cnode, tnode);\t\\\n\t\t    rbtn_color_set(a_type, a_field, tnode, tred);\t\\\n\t\t    rbtn_red_set(a_type, a_field, cnode);\t\t\\\n\t\t    cnode = tnode;\t\t\t\t\t\\\n\t\t}\t\t\t\t\t\t\t\\\n\t    } else {\t\t\t\t\t\t\t\\\n\t\treturn;\t\t\t\t\t\t\t\\\n\t    }\t\t\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\tpathp->node = cnode;\t\t\t\t\t\t\\\n    }\t\t\t\t\t\t\t\t\t\\\n    /* Set root, and make it black. */\t\t\t\t\t\\\n    rbtree->rbt_root = path->node;\t\t\t\t\t\\\n    rbtn_black_set(a_type, a_field, rbtree->rbt_root);\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\na_attr void\t\t\t\t\t\t\t\t\\\na_prefix##remove(a_rbt_type *rbtree, a_type *node) {\t\t\t\\\n    struct {\t\t\t\t\t\t\t\t\\\n\ta_type *node;\t\t\t\t\t\t\t\\\n\tint cmp;\t\t\t\t\t\t\t\\\n    } *pathp, *nodep, path[sizeof(void *) << 4];\t\t\t\\\n    /* Wind. */\t\t\t\t\t\t\t\t\\\n    nodep = NULL; /* Silence compiler warning. */\t\t\t\\\n    path->node = rbtree->rbt_root;\t\t\t\t\t\\\n    for (pathp = path; pathp->node != &rbtree->rbt_nil; pathp++) {\t\\\n\tint cmp = pathp->cmp = a_cmp(node, pathp->node);\t\t\\\n\tif (cmp < 0) {\t\t\t\t\t\t\t\\\n\t    pathp[1].node = rbtn_left_get(a_type, a_field,\t\t\\\n\t      pathp->node);\t\t\t\t\t\t\\\n\t} else {\t\t\t\t\t\t\t\\\n\t    pathp[1].node = rbtn_right_get(a_type, a_field,\t\t\\\n\t      pathp->node);\t\t\t\t\t\t\\\n\t    if (cmp == 0) {\t\t\t\t\t\t\\\n\t        /* Find node's successor, in preparation for swap. */\t\\\n\t\tpathp->cmp = 1;\t\t\t\t\t\t\\\n\t\tnodep = pathp;\t\t\t\t\t\t\\\n\t\tfor (pathp++; pathp->node != &rbtree->rbt_nil;\t\t\\\n\t\t  pathp++) {\t\t\t\t\t\t\\\n\t\t    pathp->cmp = -1;\t\t\t\t\t\\\n\t\t    pathp[1].node = rbtn_left_get(a_type, a_field,\t\\\n\t\t      pathp->node);\t\t\t\t\t\\\n\t\t}\t\t\t\t\t\t\t\\\n\t\tbreak;\t\t\t\t\t\t\t\\\n\t    }\t\t\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n    }\t\t\t\t\t\t\t\t\t\\\n    assert(nodep->node == node);\t\t\t\t\t\\\n    pathp--;\t\t\t\t\t\t\t\t\\\n    if (pathp->node != node) {\t\t\t\t\t\t\\\n\t/* Swap node with its successor. */\t\t\t\t\\\n\tbool tred = rbtn_red_get(a_type, a_field, pathp->node);\t\t\\\n\trbtn_color_set(a_type, a_field, pathp->node,\t\t\t\\\n\t  rbtn_red_get(a_type, a_field, node));\t\t\t\t\\\n\trbtn_left_set(a_type, a_field, pathp->node,\t\t\t\\\n\t  rbtn_left_get(a_type, a_field, node));\t\t\t\\\n\t/* If node's successor is its right child, the following code */\\\n\t/* will do the wrong thing for the right child pointer.       */\\\n\t/* However, it doesn't matter, because the pointer will be    */\\\n\t/* properly set when the successor is pruned.                 */\\\n\trbtn_right_set(a_type, a_field, pathp->node,\t\t\t\\\n\t  rbtn_right_get(a_type, a_field, node));\t\t\t\\\n\trbtn_color_set(a_type, a_field, node, tred);\t\t\t\\\n\t/* The pruned leaf node's child pointers are never accessed   */\\\n\t/* again, so don't bother setting them to nil.                */\\\n\tnodep->node = pathp->node;\t\t\t\t\t\\\n\tpathp->node = node;\t\t\t\t\t\t\\\n\tif (nodep == path) {\t\t\t\t\t\t\\\n\t    rbtree->rbt_root = nodep->node;\t\t\t\t\\\n\t} else {\t\t\t\t\t\t\t\\\n\t    if (nodep[-1].cmp < 0) {\t\t\t\t\t\\\n\t\trbtn_left_set(a_type, a_field, nodep[-1].node,\t\t\\\n\t\t  nodep->node);\t\t\t\t\t\t\\\n\t    } else {\t\t\t\t\t\t\t\\\n\t\trbtn_right_set(a_type, a_field, nodep[-1].node,\t\t\\\n\t\t  nodep->node);\t\t\t\t\t\t\\\n\t    }\t\t\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n    } else {\t\t\t\t\t\t\t\t\\\n\ta_type *left = rbtn_left_get(a_type, a_field, node);\t\t\\\n\tif (left != &rbtree->rbt_nil) {\t\t\t\t\t\\\n\t    /* node has no successor, but it has a left child.        */\\\n\t    /* Splice node out, without losing the left child.        */\\\n\t    assert(rbtn_red_get(a_type, a_field, node) == false);\t\\\n\t    assert(rbtn_red_get(a_type, a_field, left));\t\t\\\n\t    rbtn_black_set(a_type, a_field, left);\t\t\t\\\n\t    if (pathp == path) {\t\t\t\t\t\\\n\t\trbtree->rbt_root = left;\t\t\t\t\\\n\t    } else {\t\t\t\t\t\t\t\\\n\t\tif (pathp[-1].cmp < 0) {\t\t\t\t\\\n\t\t    rbtn_left_set(a_type, a_field, pathp[-1].node,\t\\\n\t\t      left);\t\t\t\t\t\t\\\n\t\t} else {\t\t\t\t\t\t\\\n\t\t    rbtn_right_set(a_type, a_field, pathp[-1].node,\t\\\n\t\t      left);\t\t\t\t\t\t\\\n\t\t}\t\t\t\t\t\t\t\\\n\t    }\t\t\t\t\t\t\t\t\\\n\t    return;\t\t\t\t\t\t\t\\\n\t} else if (pathp == path) {\t\t\t\t\t\\\n\t    /* The tree only contained one node. */\t\t\t\\\n\t    rbtree->rbt_root = &rbtree->rbt_nil;\t\t\t\\\n\t    return;\t\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n    }\t\t\t\t\t\t\t\t\t\\\n    if (rbtn_red_get(a_type, a_field, pathp->node)) {\t\t\t\\\n\t/* Prune red node, which requires no fixup. */\t\t\t\\\n\tassert(pathp[-1].cmp < 0);\t\t\t\t\t\\\n\trbtn_left_set(a_type, a_field, pathp[-1].node,\t\t\t\\\n\t  &rbtree->rbt_nil);\t\t\t\t\t\t\\\n\treturn;\t\t\t\t\t\t\t\t\\\n    }\t\t\t\t\t\t\t\t\t\\\n    /* The node to be pruned is black, so unwind until balance is     */\\\n    /* restored.                                                      */\\\n    pathp->node = &rbtree->rbt_nil;\t\t\t\t\t\\\n    for (pathp--; (uintptr_t)pathp >= (uintptr_t)path; pathp--) {\t\\\n\tassert(pathp->cmp != 0);\t\t\t\t\t\\\n\tif (pathp->cmp < 0) {\t\t\t\t\t\t\\\n\t    rbtn_left_set(a_type, a_field, pathp->node,\t\t\t\\\n\t      pathp[1].node);\t\t\t\t\t\t\\\n\t    assert(rbtn_red_get(a_type, a_field, pathp[1].node)\t\t\\\n\t      == false);\t\t\t\t\t\t\\\n\t    if (rbtn_red_get(a_type, a_field, pathp->node)) {\t\t\\\n\t\ta_type *right = rbtn_right_get(a_type, a_field,\t\t\\\n\t\t  pathp->node);\t\t\t\t\t\t\\\n\t\ta_type *rightleft = rbtn_left_get(a_type, a_field,\t\\\n\t\t  right);\t\t\t\t\t\t\\\n\t\ta_type *tnode;\t\t\t\t\t\t\\\n\t\tif (rbtn_red_get(a_type, a_field, rightleft)) {\t\t\\\n\t\t    /* In the following diagrams, ||, //, and \\\\      */\\\n\t\t    /* indicate the path to the removed node.         */\\\n\t\t    /*                                                */\\\n\t\t    /*      ||                                        */\\\n\t\t    /*    pathp(r)                                    */\\\n\t\t    /*  //        \\                                   */\\\n\t\t    /* (b)        (b)                                 */\\\n\t\t    /*           /                                    */\\\n\t\t    /*          (r)                                   */\\\n\t\t    /*                                                */\\\n\t\t    rbtn_black_set(a_type, a_field, pathp->node);\t\\\n\t\t    rbtn_rotate_right(a_type, a_field, right, tnode);\t\\\n\t\t    rbtn_right_set(a_type, a_field, pathp->node, tnode);\\\n\t\t    rbtn_rotate_left(a_type, a_field, pathp->node,\t\\\n\t\t      tnode);\t\t\t\t\t\t\\\n\t\t} else {\t\t\t\t\t\t\\\n\t\t    /*      ||                                        */\\\n\t\t    /*    pathp(r)                                    */\\\n\t\t    /*  //        \\                                   */\\\n\t\t    /* (b)        (b)                                 */\\\n\t\t    /*           /                                    */\\\n\t\t    /*          (b)                                   */\\\n\t\t    /*                                                */\\\n\t\t    rbtn_rotate_left(a_type, a_field, pathp->node,\t\\\n\t\t      tnode);\t\t\t\t\t\t\\\n\t\t}\t\t\t\t\t\t\t\\\n\t\t/* Balance restored, but rotation modified subtree    */\\\n\t\t/* root.                                              */\\\n\t\tassert((uintptr_t)pathp > (uintptr_t)path);\t\t\\\n\t\tif (pathp[-1].cmp < 0) {\t\t\t\t\\\n\t\t    rbtn_left_set(a_type, a_field, pathp[-1].node,\t\\\n\t\t      tnode);\t\t\t\t\t\t\\\n\t\t} else {\t\t\t\t\t\t\\\n\t\t    rbtn_right_set(a_type, a_field, pathp[-1].node,\t\\\n\t\t      tnode);\t\t\t\t\t\t\\\n\t\t}\t\t\t\t\t\t\t\\\n\t\treturn;\t\t\t\t\t\t\t\\\n\t    } else {\t\t\t\t\t\t\t\\\n\t\ta_type *right = rbtn_right_get(a_type, a_field,\t\t\\\n\t\t  pathp->node);\t\t\t\t\t\t\\\n\t\ta_type *rightleft = rbtn_left_get(a_type, a_field,\t\\\n\t\t  right);\t\t\t\t\t\t\\\n\t\tif (rbtn_red_get(a_type, a_field, rightleft)) {\t\t\\\n\t\t    /*      ||                                        */\\\n\t\t    /*    pathp(b)                                    */\\\n\t\t    /*  //        \\                                   */\\\n\t\t    /* (b)        (b)                                 */\\\n\t\t    /*           /                                    */\\\n\t\t    /*          (r)                                   */\\\n\t\t    a_type *tnode;\t\t\t\t\t\\\n\t\t    rbtn_black_set(a_type, a_field, rightleft);\t\t\\\n\t\t    rbtn_rotate_right(a_type, a_field, right, tnode);\t\\\n\t\t    rbtn_right_set(a_type, a_field, pathp->node, tnode);\\\n\t\t    rbtn_rotate_left(a_type, a_field, pathp->node,\t\\\n\t\t      tnode);\t\t\t\t\t\t\\\n\t\t    /* Balance restored, but rotation modified        */\\\n\t\t    /* subree root, which may actually be the tree    */\\\n\t\t    /* root.                                          */\\\n\t\t    if (pathp == path) {\t\t\t\t\\\n\t\t\t/* Set root. */\t\t\t\t\t\\\n\t\t\trbtree->rbt_root = tnode;\t\t\t\\\n\t\t    } else {\t\t\t\t\t\t\\\n\t\t\tif (pathp[-1].cmp < 0) {\t\t\t\\\n\t\t\t    rbtn_left_set(a_type, a_field,\t\t\\\n\t\t\t      pathp[-1].node, tnode);\t\t\t\\\n\t\t\t} else {\t\t\t\t\t\\\n\t\t\t    rbtn_right_set(a_type, a_field,\t\t\\\n\t\t\t      pathp[-1].node, tnode);\t\t\t\\\n\t\t\t}\t\t\t\t\t\t\\\n\t\t    }\t\t\t\t\t\t\t\\\n\t\t    return;\t\t\t\t\t\t\\\n\t\t} else {\t\t\t\t\t\t\\\n\t\t    /*      ||                                        */\\\n\t\t    /*    pathp(b)                                    */\\\n\t\t    /*  //        \\                                   */\\\n\t\t    /* (b)        (b)                                 */\\\n\t\t    /*           /                                    */\\\n\t\t    /*          (b)                                   */\\\n\t\t    a_type *tnode;\t\t\t\t\t\\\n\t\t    rbtn_red_set(a_type, a_field, pathp->node);\t\t\\\n\t\t    rbtn_rotate_left(a_type, a_field, pathp->node,\t\\\n\t\t      tnode);\t\t\t\t\t\t\\\n\t\t    pathp->node = tnode;\t\t\t\t\\\n\t\t}\t\t\t\t\t\t\t\\\n\t    }\t\t\t\t\t\t\t\t\\\n\t} else {\t\t\t\t\t\t\t\\\n\t    a_type *left;\t\t\t\t\t\t\\\n\t    rbtn_right_set(a_type, a_field, pathp->node,\t\t\\\n\t      pathp[1].node);\t\t\t\t\t\t\\\n\t    left = rbtn_left_get(a_type, a_field, pathp->node);\t\t\\\n\t    if (rbtn_red_get(a_type, a_field, left)) {\t\t\t\\\n\t\ta_type *tnode;\t\t\t\t\t\t\\\n\t\ta_type *leftright = rbtn_right_get(a_type, a_field,\t\\\n\t\t  left);\t\t\t\t\t\t\\\n\t\ta_type *leftrightleft = rbtn_left_get(a_type, a_field,\t\\\n\t\t  leftright);\t\t\t\t\t\t\\\n\t\tif (rbtn_red_get(a_type, a_field, leftrightleft)) {\t\\\n\t\t    /*      ||                                        */\\\n\t\t    /*    pathp(b)                                    */\\\n\t\t    /*   /        \\\\                                  */\\\n\t\t    /* (r)        (b)                                 */\\\n\t\t    /*   \\                                            */\\\n\t\t    /*   (b)                                          */\\\n\t\t    /*   /                                            */\\\n\t\t    /* (r)                                            */\\\n\t\t    a_type *unode;\t\t\t\t\t\\\n\t\t    rbtn_black_set(a_type, a_field, leftrightleft);\t\\\n\t\t    rbtn_rotate_right(a_type, a_field, pathp->node,\t\\\n\t\t      unode);\t\t\t\t\t\t\\\n\t\t    rbtn_rotate_right(a_type, a_field, pathp->node,\t\\\n\t\t      tnode);\t\t\t\t\t\t\\\n\t\t    rbtn_right_set(a_type, a_field, unode, tnode);\t\\\n\t\t    rbtn_rotate_left(a_type, a_field, unode, tnode);\t\\\n\t\t} else {\t\t\t\t\t\t\\\n\t\t    /*      ||                                        */\\\n\t\t    /*    pathp(b)                                    */\\\n\t\t    /*   /        \\\\                                  */\\\n\t\t    /* (r)        (b)                                 */\\\n\t\t    /*   \\                                            */\\\n\t\t    /*   (b)                                          */\\\n\t\t    /*   /                                            */\\\n\t\t    /* (b)                                            */\\\n\t\t    assert(leftright != &rbtree->rbt_nil);\t\t\\\n\t\t    rbtn_red_set(a_type, a_field, leftright);\t\t\\\n\t\t    rbtn_rotate_right(a_type, a_field, pathp->node,\t\\\n\t\t      tnode);\t\t\t\t\t\t\\\n\t\t    rbtn_black_set(a_type, a_field, tnode);\t\t\\\n\t\t}\t\t\t\t\t\t\t\\\n\t\t/* Balance restored, but rotation modified subtree    */\\\n\t\t/* root, which may actually be the tree root.         */\\\n\t\tif (pathp == path) {\t\t\t\t\t\\\n\t\t    /* Set root. */\t\t\t\t\t\\\n\t\t    rbtree->rbt_root = tnode;\t\t\t\t\\\n\t\t} else {\t\t\t\t\t\t\\\n\t\t    if (pathp[-1].cmp < 0) {\t\t\t\t\\\n\t\t\trbtn_left_set(a_type, a_field, pathp[-1].node,\t\\\n\t\t\t  tnode);\t\t\t\t\t\\\n\t\t    } else {\t\t\t\t\t\t\\\n\t\t\trbtn_right_set(a_type, a_field, pathp[-1].node,\t\\\n\t\t\t  tnode);\t\t\t\t\t\\\n\t\t    }\t\t\t\t\t\t\t\\\n\t\t}\t\t\t\t\t\t\t\\\n\t\treturn;\t\t\t\t\t\t\t\\\n\t    } else if (rbtn_red_get(a_type, a_field, pathp->node)) {\t\\\n\t\ta_type *leftleft = rbtn_left_get(a_type, a_field, left);\\\n\t\tif (rbtn_red_get(a_type, a_field, leftleft)) {\t\t\\\n\t\t    /*        ||                                      */\\\n\t\t    /*      pathp(r)                                  */\\\n\t\t    /*     /        \\\\                                */\\\n\t\t    /*   (b)        (b)                               */\\\n\t\t    /*   /                                            */\\\n\t\t    /* (r)                                            */\\\n\t\t    a_type *tnode;\t\t\t\t\t\\\n\t\t    rbtn_black_set(a_type, a_field, pathp->node);\t\\\n\t\t    rbtn_red_set(a_type, a_field, left);\t\t\\\n\t\t    rbtn_black_set(a_type, a_field, leftleft);\t\t\\\n\t\t    rbtn_rotate_right(a_type, a_field, pathp->node,\t\\\n\t\t      tnode);\t\t\t\t\t\t\\\n\t\t    /* Balance restored, but rotation modified        */\\\n\t\t    /* subtree root.                                  */\\\n\t\t    assert((uintptr_t)pathp > (uintptr_t)path);\t\t\\\n\t\t    if (pathp[-1].cmp < 0) {\t\t\t\t\\\n\t\t\trbtn_left_set(a_type, a_field, pathp[-1].node,\t\\\n\t\t\t  tnode);\t\t\t\t\t\\\n\t\t    } else {\t\t\t\t\t\t\\\n\t\t\trbtn_right_set(a_type, a_field, pathp[-1].node,\t\\\n\t\t\t  tnode);\t\t\t\t\t\\\n\t\t    }\t\t\t\t\t\t\t\\\n\t\t    return;\t\t\t\t\t\t\\\n\t\t} else {\t\t\t\t\t\t\\\n\t\t    /*        ||                                      */\\\n\t\t    /*      pathp(r)                                  */\\\n\t\t    /*     /        \\\\                                */\\\n\t\t    /*   (b)        (b)                               */\\\n\t\t    /*   /                                            */\\\n\t\t    /* (b)                                            */\\\n\t\t    rbtn_red_set(a_type, a_field, left);\t\t\\\n\t\t    rbtn_black_set(a_type, a_field, pathp->node);\t\\\n\t\t    /* Balance restored. */\t\t\t\t\\\n\t\t    return;\t\t\t\t\t\t\\\n\t\t}\t\t\t\t\t\t\t\\\n\t    } else {\t\t\t\t\t\t\t\\\n\t\ta_type *leftleft = rbtn_left_get(a_type, a_field, left);\\\n\t\tif (rbtn_red_get(a_type, a_field, leftleft)) {\t\t\\\n\t\t    /*               ||                               */\\\n\t\t    /*             pathp(b)                           */\\\n\t\t    /*            /        \\\\                         */\\\n\t\t    /*          (b)        (b)                        */\\\n\t\t    /*          /                                     */\\\n\t\t    /*        (r)                                     */\\\n\t\t    a_type *tnode;\t\t\t\t\t\\\n\t\t    rbtn_black_set(a_type, a_field, leftleft);\t\t\\\n\t\t    rbtn_rotate_right(a_type, a_field, pathp->node,\t\\\n\t\t      tnode);\t\t\t\t\t\t\\\n\t\t    /* Balance restored, but rotation modified        */\\\n\t\t    /* subtree root, which may actually be the tree   */\\\n\t\t    /* root.                                          */\\\n\t\t    if (pathp == path) {\t\t\t\t\\\n\t\t\t/* Set root. */\t\t\t\t\t\\\n\t\t\trbtree->rbt_root = tnode;\t\t\t\\\n\t\t    } else {\t\t\t\t\t\t\\\n\t\t\tif (pathp[-1].cmp < 0) {\t\t\t\\\n\t\t\t    rbtn_left_set(a_type, a_field,\t\t\\\n\t\t\t      pathp[-1].node, tnode);\t\t\t\\\n\t\t\t} else {\t\t\t\t\t\\\n\t\t\t    rbtn_right_set(a_type, a_field,\t\t\\\n\t\t\t      pathp[-1].node, tnode);\t\t\t\\\n\t\t\t}\t\t\t\t\t\t\\\n\t\t    }\t\t\t\t\t\t\t\\\n\t\t    return;\t\t\t\t\t\t\\\n\t\t} else {\t\t\t\t\t\t\\\n\t\t    /*               ||                               */\\\n\t\t    /*             pathp(b)                           */\\\n\t\t    /*            /        \\\\                         */\\\n\t\t    /*          (b)        (b)                        */\\\n\t\t    /*          /                                     */\\\n\t\t    /*        (b)                                     */\\\n\t\t    rbtn_red_set(a_type, a_field, left);\t\t\\\n\t\t}\t\t\t\t\t\t\t\\\n\t    }\t\t\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n    }\t\t\t\t\t\t\t\t\t\\\n    /* Set root. */\t\t\t\t\t\t\t\\\n    rbtree->rbt_root = path->node;\t\t\t\t\t\\\n    assert(rbtn_red_get(a_type, a_field, rbtree->rbt_root) == false);\t\\\n}\t\t\t\t\t\t\t\t\t\\\na_attr a_type *\t\t\t\t\t\t\t\t\\\na_prefix##iter_recurse(a_rbt_type *rbtree, a_type *node,\t\t\\\n  a_type *(*cb)(a_rbt_type *, a_type *, void *), void *arg) {\t\t\\\n    if (node == &rbtree->rbt_nil) {\t\t\t\t\t\\\n\treturn (&rbtree->rbt_nil);\t\t\t\t\t\\\n    } else {\t\t\t\t\t\t\t\t\\\n\ta_type *ret;\t\t\t\t\t\t\t\\\n\tif ((ret = a_prefix##iter_recurse(rbtree, rbtn_left_get(a_type,\t\\\n\t  a_field, node), cb, arg)) != &rbtree->rbt_nil\t\t\t\\\n\t  || (ret = cb(rbtree, node, arg)) != NULL) {\t\t\t\\\n\t    return (ret);\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\treturn (a_prefix##iter_recurse(rbtree, rbtn_right_get(a_type,\t\\\n\t  a_field, node), cb, arg));\t\t\t\t\t\\\n    }\t\t\t\t\t\t\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\na_attr a_type *\t\t\t\t\t\t\t\t\\\na_prefix##iter_start(a_rbt_type *rbtree, a_type *start, a_type *node,\t\\\n  a_type *(*cb)(a_rbt_type *, a_type *, void *), void *arg) {\t\t\\\n    int cmp = a_cmp(start, node);\t\t\t\t\t\\\n    if (cmp < 0) {\t\t\t\t\t\t\t\\\n\ta_type *ret;\t\t\t\t\t\t\t\\\n\tif ((ret = a_prefix##iter_start(rbtree, start,\t\t\t\\\n\t  rbtn_left_get(a_type, a_field, node), cb, arg)) !=\t\t\\\n\t  &rbtree->rbt_nil || (ret = cb(rbtree, node, arg)) != NULL) {\t\\\n\t    return (ret);\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\treturn (a_prefix##iter_recurse(rbtree, rbtn_right_get(a_type,\t\\\n\t  a_field, node), cb, arg));\t\t\t\t\t\\\n    } else if (cmp > 0) {\t\t\t\t\t\t\\\n\treturn (a_prefix##iter_start(rbtree, start,\t\t\t\\\n\t  rbtn_right_get(a_type, a_field, node), cb, arg));\t\t\\\n    } else {\t\t\t\t\t\t\t\t\\\n\ta_type *ret;\t\t\t\t\t\t\t\\\n\tif ((ret = cb(rbtree, node, arg)) != NULL) {\t\t\t\\\n\t    return (ret);\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\treturn (a_prefix##iter_recurse(rbtree, rbtn_right_get(a_type,\t\\\n\t  a_field, node), cb, arg));\t\t\t\t\t\\\n    }\t\t\t\t\t\t\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\na_attr a_type *\t\t\t\t\t\t\t\t\\\na_prefix##iter(a_rbt_type *rbtree, a_type *start, a_type *(*cb)(\t\\\n  a_rbt_type *, a_type *, void *), void *arg) {\t\t\t\t\\\n    a_type *ret;\t\t\t\t\t\t\t\\\n    if (start != NULL) {\t\t\t\t\t\t\\\n\tret = a_prefix##iter_start(rbtree, start, rbtree->rbt_root,\t\\\n\t  cb, arg);\t\t\t\t\t\t\t\\\n    } else {\t\t\t\t\t\t\t\t\\\n\tret = a_prefix##iter_recurse(rbtree, rbtree->rbt_root, cb, arg);\\\n    }\t\t\t\t\t\t\t\t\t\\\n    if (ret == &rbtree->rbt_nil) {\t\t\t\t\t\\\n\tret = NULL;\t\t\t\t\t\t\t\\\n    }\t\t\t\t\t\t\t\t\t\\\n    return (ret);\t\t\t\t\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\na_attr a_type *\t\t\t\t\t\t\t\t\\\na_prefix##reverse_iter_recurse(a_rbt_type *rbtree, a_type *node,\t\\\n  a_type *(*cb)(a_rbt_type *, a_type *, void *), void *arg) {\t\t\\\n    if (node == &rbtree->rbt_nil) {\t\t\t\t\t\\\n\treturn (&rbtree->rbt_nil);\t\t\t\t\t\\\n    } else {\t\t\t\t\t\t\t\t\\\n\ta_type *ret;\t\t\t\t\t\t\t\\\n\tif ((ret = a_prefix##reverse_iter_recurse(rbtree,\t\t\\\n\t  rbtn_right_get(a_type, a_field, node), cb, arg)) !=\t\t\\\n\t  &rbtree->rbt_nil || (ret = cb(rbtree, node, arg)) != NULL) {\t\\\n\t    return (ret);\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\treturn (a_prefix##reverse_iter_recurse(rbtree,\t\t\t\\\n\t  rbtn_left_get(a_type, a_field, node), cb, arg));\t\t\\\n    }\t\t\t\t\t\t\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\na_attr a_type *\t\t\t\t\t\t\t\t\\\na_prefix##reverse_iter_start(a_rbt_type *rbtree, a_type *start,\t\t\\\n  a_type *node, a_type *(*cb)(a_rbt_type *, a_type *, void *),\t\t\\\n  void *arg) {\t\t\t\t\t\t\t\t\\\n    int cmp = a_cmp(start, node);\t\t\t\t\t\\\n    if (cmp > 0) {\t\t\t\t\t\t\t\\\n\ta_type *ret;\t\t\t\t\t\t\t\\\n\tif ((ret = a_prefix##reverse_iter_start(rbtree, start,\t\t\\\n\t  rbtn_right_get(a_type, a_field, node), cb, arg)) !=\t\t\\\n\t  &rbtree->rbt_nil || (ret = cb(rbtree, node, arg)) != NULL) {\t\\\n\t    return (ret);\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\treturn (a_prefix##reverse_iter_recurse(rbtree,\t\t\t\\\n\t  rbtn_left_get(a_type, a_field, node), cb, arg));\t\t\\\n    } else if (cmp < 0) {\t\t\t\t\t\t\\\n\treturn (a_prefix##reverse_iter_start(rbtree, start,\t\t\\\n\t  rbtn_left_get(a_type, a_field, node), cb, arg));\t\t\\\n    } else {\t\t\t\t\t\t\t\t\\\n\ta_type *ret;\t\t\t\t\t\t\t\\\n\tif ((ret = cb(rbtree, node, arg)) != NULL) {\t\t\t\\\n\t    return (ret);\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\treturn (a_prefix##reverse_iter_recurse(rbtree,\t\t\t\\\n\t  rbtn_left_get(a_type, a_field, node), cb, arg));\t\t\\\n    }\t\t\t\t\t\t\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\na_attr a_type *\t\t\t\t\t\t\t\t\\\na_prefix##reverse_iter(a_rbt_type *rbtree, a_type *start,\t\t\\\n  a_type *(*cb)(a_rbt_type *, a_type *, void *), void *arg) {\t\t\\\n    a_type *ret;\t\t\t\t\t\t\t\\\n    if (start != NULL) {\t\t\t\t\t\t\\\n\tret = a_prefix##reverse_iter_start(rbtree, start,\t\t\\\n\t  rbtree->rbt_root, cb, arg);\t\t\t\t\t\\\n    } else {\t\t\t\t\t\t\t\t\\\n\tret = a_prefix##reverse_iter_recurse(rbtree, rbtree->rbt_root,\t\\\n\t  cb, arg);\t\t\t\t\t\t\t\\\n    }\t\t\t\t\t\t\t\t\t\\\n    if (ret == &rbtree->rbt_nil) {\t\t\t\t\t\\\n\tret = NULL;\t\t\t\t\t\t\t\\\n    }\t\t\t\t\t\t\t\t\t\\\n    return (ret);\t\t\t\t\t\t\t\\\n}\n\n#endif /* RB_H_ */\n"
  },
  {
    "path": "deps/jemalloc/include/jemalloc/internal/rtree.h",
    "content": "/*\n * This radix tree implementation is tailored to the singular purpose of\n * tracking which chunks are currently owned by jemalloc.  This functionality\n * is mandatory for OS X, where jemalloc must be able to respond to object\n * ownership queries.\n *\n *******************************************************************************\n */\n#ifdef JEMALLOC_H_TYPES\n\ntypedef struct rtree_s rtree_t;\n\n/*\n * Size of each radix tree node (must be a power of 2).  This impacts tree\n * depth.\n */\n#if (LG_SIZEOF_PTR == 2)\n#  define RTREE_NODESIZE (1U << 14)\n#else\n#  define RTREE_NODESIZE CACHELINE\n#endif\n\n#endif /* JEMALLOC_H_TYPES */\n/******************************************************************************/\n#ifdef JEMALLOC_H_STRUCTS\n\nstruct rtree_s {\n\tmalloc_mutex_t\tmutex;\n\tvoid\t\t**root;\n\tunsigned\theight;\n\tunsigned\tlevel2bits[1]; /* Dynamically sized. */\n};\n\n#endif /* JEMALLOC_H_STRUCTS */\n/******************************************************************************/\n#ifdef JEMALLOC_H_EXTERNS\n\nrtree_t\t*rtree_new(unsigned bits);\nvoid\trtree_prefork(rtree_t *rtree);\nvoid\trtree_postfork_parent(rtree_t *rtree);\nvoid\trtree_postfork_child(rtree_t *rtree);\n\n#endif /* JEMALLOC_H_EXTERNS */\n/******************************************************************************/\n#ifdef JEMALLOC_H_INLINES\n\n#ifndef JEMALLOC_ENABLE_INLINE\n#ifndef JEMALLOC_DEBUG\nvoid\t*rtree_get_locked(rtree_t *rtree, uintptr_t key);\n#endif\nvoid\t*rtree_get(rtree_t *rtree, uintptr_t key);\nbool\trtree_set(rtree_t *rtree, uintptr_t key, void *val);\n#endif\n\n#if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_RTREE_C_))\n#define\tRTREE_GET_GENERATE(f)\t\t\t\t\t\t\\\n/* The least significant bits of the key are ignored. */\t\t\\\nJEMALLOC_INLINE void *\t\t\t\t\t\t\t\\\nf(rtree_t *rtree, uintptr_t key)\t\t\t\t\t\\\n{\t\t\t\t\t\t\t\t\t\\\n\tvoid *ret;\t\t\t\t\t\t\t\\\n\tuintptr_t subkey;\t\t\t\t\t\t\\\n\tunsigned i, lshift, height, bits;\t\t\t\t\\\n\tvoid **node, **child;\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\tRTREE_LOCK(&rtree->mutex);\t\t\t\t\t\\\n\tfor (i = lshift = 0, height = rtree->height, node = rtree->root;\\\n\t    i < height - 1;\t\t\t\t\t\t\\\n\t    i++, lshift += bits, node = child) {\t\t\t\\\n\t\tbits = rtree->level2bits[i];\t\t\t\t\\\n\t\tsubkey = (key << lshift) >> ((ZU(1) << (LG_SIZEOF_PTR + \\\n\t\t    3)) - bits);\t\t\t\t\t\\\n\t\tchild = (void**)node[subkey];\t\t\t\t\\\n\t\tif (child == NULL) {\t\t\t\t\t\\\n\t\t\tRTREE_UNLOCK(&rtree->mutex);\t\t\t\\\n\t\t\treturn (NULL);\t\t\t\t\t\\\n\t\t}\t\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\t/*\t\t\t\t\t\t\t\t\\\n\t * node is a leaf, so it contains values rather than node\t\\\n\t * pointers.\t\t\t\t\t\t\t\\\n\t */\t\t\t\t\t\t\t\t\\\n\tbits = rtree->level2bits[i];\t\t\t\t\t\\\n\tsubkey = (key << lshift) >> ((ZU(1) << (LG_SIZEOF_PTR+3)) -\t\\\n\t    bits);\t\t\t\t\t\t\t\\\n\tret = node[subkey];\t\t\t\t\t\t\\\n\tRTREE_UNLOCK(&rtree->mutex);\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\tRTREE_GET_VALIDATE\t\t\t\t\t\t\\\n\treturn (ret);\t\t\t\t\t\t\t\\\n}\n\n#ifdef JEMALLOC_DEBUG\n#  define RTREE_LOCK(l)\t\tmalloc_mutex_lock(l)\n#  define RTREE_UNLOCK(l)\tmalloc_mutex_unlock(l)\n#  define RTREE_GET_VALIDATE\nRTREE_GET_GENERATE(rtree_get_locked)\n#  undef RTREE_LOCK\n#  undef RTREE_UNLOCK\n#  undef RTREE_GET_VALIDATE\n#endif\n\n#define\tRTREE_LOCK(l)\n#define\tRTREE_UNLOCK(l)\n#ifdef JEMALLOC_DEBUG\n   /*\n    * Suppose that it were possible for a jemalloc-allocated chunk to be\n    * munmap()ped, followed by a different allocator in another thread re-using\n    * overlapping virtual memory, all without invalidating the cached rtree\n    * value.  The result would be a false positive (the rtree would claim that\n    * jemalloc owns memory that it had actually discarded).  This scenario\n    * seems impossible, but the following assertion is a prudent sanity check.\n    */\n#  define RTREE_GET_VALIDATE\t\t\t\t\t\t\\\n\tassert(rtree_get_locked(rtree, key) == ret);\n#else\n#  define RTREE_GET_VALIDATE\n#endif\nRTREE_GET_GENERATE(rtree_get)\n#undef RTREE_LOCK\n#undef RTREE_UNLOCK\n#undef RTREE_GET_VALIDATE\n\nJEMALLOC_INLINE bool\nrtree_set(rtree_t *rtree, uintptr_t key, void *val)\n{\n\tuintptr_t subkey;\n\tunsigned i, lshift, height, bits;\n\tvoid **node, **child;\n\n\tmalloc_mutex_lock(&rtree->mutex);\n\tfor (i = lshift = 0, height = rtree->height, node = rtree->root;\n\t    i < height - 1;\n\t    i++, lshift += bits, node = child) {\n\t\tbits = rtree->level2bits[i];\n\t\tsubkey = (key << lshift) >> ((ZU(1) << (LG_SIZEOF_PTR+3)) -\n\t\t    bits);\n\t\tchild = (void**)node[subkey];\n\t\tif (child == NULL) {\n\t\t\tchild = (void**)base_alloc(sizeof(void *) <<\n\t\t\t    rtree->level2bits[i+1]);\n\t\t\tif (child == NULL) {\n\t\t\t\tmalloc_mutex_unlock(&rtree->mutex);\n\t\t\t\treturn (true);\n\t\t\t}\n\t\t\tmemset(child, 0, sizeof(void *) <<\n\t\t\t    rtree->level2bits[i+1]);\n\t\t\tnode[subkey] = child;\n\t\t}\n\t}\n\n\t/* node is a leaf, so it contains values rather than node pointers. */\n\tbits = rtree->level2bits[i];\n\tsubkey = (key << lshift) >> ((ZU(1) << (LG_SIZEOF_PTR+3)) - bits);\n\tnode[subkey] = val;\n\tmalloc_mutex_unlock(&rtree->mutex);\n\n\treturn (false);\n}\n#endif\n\n#endif /* JEMALLOC_H_INLINES */\n/******************************************************************************/\n"
  },
  {
    "path": "deps/jemalloc/include/jemalloc/internal/size_classes.sh",
    "content": "#!/bin/sh\n\n# The following limits are chosen such that they cover all supported platforms.\n\n# Range of quanta.\nlg_qmin=3\nlg_qmax=4\n\n# The range of tiny size classes is [2^lg_tmin..2^(lg_q-1)].\nlg_tmin=3\n\n# Range of page sizes.\nlg_pmin=12\nlg_pmax=16\n\npow2() {\n  e=$1\n  pow2_result=1\n  while [ ${e} -gt 0 ] ; do\n    pow2_result=$((${pow2_result} + ${pow2_result}))\n    e=$((${e} - 1))\n  done\n}\n\ncat <<EOF\n/* This file was automatically generated by size_classes.sh. */\n/******************************************************************************/\n#ifdef JEMALLOC_H_TYPES\n\nEOF\n\nlg_q=${lg_qmin}\nwhile [ ${lg_q} -le ${lg_qmax} ] ; do\n  lg_t=${lg_tmin}\n  while [ ${lg_t} -le ${lg_q} ] ; do\n    lg_p=${lg_pmin}\n    while [ ${lg_p} -le ${lg_pmax} ] ; do\n      echo \"#if (LG_TINY_MIN == ${lg_t} && LG_QUANTUM == ${lg_q} && LG_PAGE == ${lg_p})\"\n      echo \"#define\tSIZE_CLASSES_DEFINED\"\n      pow2 ${lg_q}; q=${pow2_result}\n      pow2 ${lg_t}; t=${pow2_result}\n      pow2 ${lg_p}; p=${pow2_result}\n      bin=0\n      psz=0\n      sz=${t}\n      delta=$((${sz} - ${psz}))\n      echo \"/*  SIZE_CLASS(bin,\tdelta,\tsz) */\"\n      echo \"#define\tSIZE_CLASSES\t\t\t\t\t\t\t\\\\\"\n\n      # Tiny size classes.\n      while [ ${sz} -lt ${q} ] ; do\n        echo \"    SIZE_CLASS(${bin},\t${delta},\t${sz})\t\t\t\t\t\\\\\"\n        bin=$((${bin} + 1))\n        psz=${sz}\n        sz=$((${sz} + ${sz}))\n        delta=$((${sz} - ${psz}))\n      done\n      # Quantum-multiple size classes.  For each doubling of sz, as many as 4\n      # size classes exist.  Their spacing is the greater of:\n      # - q\n      # - sz/4, where sz is a power of 2\n      while [ ${sz} -lt ${p} ] ; do\n        if [ ${sz} -ge $((${q} * 4)) ] ; then\n          i=$((${sz} / 4))\n        else\n          i=${q}\n        fi\n        next_2pow=$((${sz} * 2))\n        while [ ${sz} -lt $next_2pow ] ; do\n          echo \"    SIZE_CLASS(${bin},\t${delta},\t${sz})\t\t\t\t\t\\\\\"\n          bin=$((${bin} + 1))\n          psz=${sz}\n          sz=$((${sz} + ${i}))\n          delta=$((${sz} - ${psz}))\n        done\n      done\n      echo\n      echo \"#define\tNBINS\t\t${bin}\"\n      echo \"#define\tSMALL_MAXCLASS\t${psz}\"\n      echo \"#endif\"\n      echo\n      lg_p=$((${lg_p} + 1))\n    done\n    lg_t=$((${lg_t} + 1))\n  done\n  lg_q=$((${lg_q} + 1))\ndone\n\ncat <<EOF\n#ifndef SIZE_CLASSES_DEFINED\n#  error \"No size class definitions match configuration\"\n#endif\n#undef SIZE_CLASSES_DEFINED\n/*\n * The small_size2bin lookup table uses uint8_t to encode each bin index, so we\n * cannot support more than 256 small size classes.  Further constrain NBINS to\n * 255 to support prof_promote, since all small size classes, plus a \"not\n * small\" size class must be stored in 8 bits of arena_chunk_map_t's bits\n * field.\n */\n#if (NBINS > 255)\n#  error \"Too many small size classes\"\n#endif\n\n#endif /* JEMALLOC_H_TYPES */\n/******************************************************************************/\n#ifdef JEMALLOC_H_STRUCTS\n\n\n#endif /* JEMALLOC_H_STRUCTS */\n/******************************************************************************/\n#ifdef JEMALLOC_H_EXTERNS\n\n\n#endif /* JEMALLOC_H_EXTERNS */\n/******************************************************************************/\n#ifdef JEMALLOC_H_INLINES\n\n\n#endif /* JEMALLOC_H_INLINES */\n/******************************************************************************/\nEOF\n"
  },
  {
    "path": "deps/jemalloc/include/jemalloc/internal/stats.h",
    "content": "/******************************************************************************/\n#ifdef JEMALLOC_H_TYPES\n\ntypedef struct tcache_bin_stats_s tcache_bin_stats_t;\ntypedef struct malloc_bin_stats_s malloc_bin_stats_t;\ntypedef struct malloc_large_stats_s malloc_large_stats_t;\ntypedef struct arena_stats_s arena_stats_t;\ntypedef struct chunk_stats_s chunk_stats_t;\n\n#endif /* JEMALLOC_H_TYPES */\n/******************************************************************************/\n#ifdef JEMALLOC_H_STRUCTS\n\nstruct tcache_bin_stats_s {\n\t/*\n\t * Number of allocation requests that corresponded to the size of this\n\t * bin.\n\t */\n\tuint64_t\tnrequests;\n};\n\nstruct malloc_bin_stats_s {\n\t/*\n\t * Current number of bytes allocated, including objects currently\n\t * cached by tcache.\n\t */\n\tsize_t\t\tallocated;\n\n\t/*\n\t * Total number of allocation/deallocation requests served directly by\n\t * the bin.  Note that tcache may allocate an object, then recycle it\n\t * many times, resulting many increments to nrequests, but only one\n\t * each to nmalloc and ndalloc.\n\t */\n\tuint64_t\tnmalloc;\n\tuint64_t\tndalloc;\n\n\t/*\n\t * Number of allocation requests that correspond to the size of this\n\t * bin.  This includes requests served by tcache, though tcache only\n\t * periodically merges into this counter.\n\t */\n\tuint64_t\tnrequests;\n\n\t/* Number of tcache fills from this bin. */\n\tuint64_t\tnfills;\n\n\t/* Number of tcache flushes to this bin. */\n\tuint64_t\tnflushes;\n\n\t/* Total number of runs created for this bin's size class. */\n\tuint64_t\tnruns;\n\n\t/*\n\t * Total number of runs reused by extracting them from the runs tree for\n\t * this bin's size class.\n\t */\n\tuint64_t\treruns;\n\n\t/* Current number of runs in this bin. */\n\tsize_t\t\tcurruns;\n};\n\nstruct malloc_large_stats_s {\n\t/*\n\t * Total number of allocation/deallocation requests served directly by\n\t * the arena.  Note that tcache may allocate an object, then recycle it\n\t * many times, resulting many increments to nrequests, but only one\n\t * each to nmalloc and ndalloc.\n\t */\n\tuint64_t\tnmalloc;\n\tuint64_t\tndalloc;\n\n\t/*\n\t * Number of allocation requests that correspond to this size class.\n\t * This includes requests served by tcache, though tcache only\n\t * periodically merges into this counter.\n\t */\n\tuint64_t\tnrequests;\n\n\t/* Current number of runs of this size class. */\n\tsize_t\t\tcurruns;\n};\n\nstruct arena_stats_s {\n\t/* Number of bytes currently mapped. */\n\tsize_t\t\tmapped;\n\n\t/*\n\t * Total number of purge sweeps, total number of madvise calls made,\n\t * and total pages purged in order to keep dirty unused memory under\n\t * control.\n\t */\n\tuint64_t\tnpurge;\n\tuint64_t\tnmadvise;\n\tuint64_t\tpurged;\n\n\t/* Per-size-category statistics. */\n\tsize_t\t\tallocated_large;\n\tuint64_t\tnmalloc_large;\n\tuint64_t\tndalloc_large;\n\tuint64_t\tnrequests_large;\n\n\t/*\n\t * One element for each possible size class, including sizes that\n\t * overlap with bin size classes.  This is necessary because ipalloc()\n\t * sometimes has to use such large objects in order to assure proper\n\t * alignment.\n\t */\n\tmalloc_large_stats_t\t*lstats;\n};\n\nstruct chunk_stats_s {\n\t/* Number of chunks that were allocated. */\n\tuint64_t\tnchunks;\n\n\t/* High-water mark for number of chunks allocated. */\n\tsize_t\t\thighchunks;\n\n\t/*\n\t * Current number of chunks allocated.  This value isn't maintained for\n\t * any other purpose, so keep track of it in order to be able to set\n\t * highchunks.\n\t */\n\tsize_t\t\tcurchunks;\n};\n\n#endif /* JEMALLOC_H_STRUCTS */\n/******************************************************************************/\n#ifdef JEMALLOC_H_EXTERNS\n\nextern bool\topt_stats_print;\n\nextern size_t\tstats_cactive;\n\nvoid\tstats_print(void (*write)(void *, const char *), void *cbopaque,\n    const char *opts);\n\n#endif /* JEMALLOC_H_EXTERNS */\n/******************************************************************************/\n#ifdef JEMALLOC_H_INLINES\n\n#ifndef JEMALLOC_ENABLE_INLINE\nsize_t\tstats_cactive_get(void);\nvoid\tstats_cactive_add(size_t size);\nvoid\tstats_cactive_sub(size_t size);\n#endif\n\n#if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_STATS_C_))\nJEMALLOC_INLINE size_t\nstats_cactive_get(void)\n{\n\n\treturn (atomic_read_z(&stats_cactive));\n}\n\nJEMALLOC_INLINE void\nstats_cactive_add(size_t size)\n{\n\n\tatomic_add_z(&stats_cactive, size);\n}\n\nJEMALLOC_INLINE void\nstats_cactive_sub(size_t size)\n{\n\n\tatomic_sub_z(&stats_cactive, size);\n}\n#endif\n\n#endif /* JEMALLOC_H_INLINES */\n/******************************************************************************/\n"
  },
  {
    "path": "deps/jemalloc/include/jemalloc/internal/tcache.h",
    "content": "/******************************************************************************/\n#ifdef JEMALLOC_H_TYPES\n\ntypedef struct tcache_bin_info_s tcache_bin_info_t;\ntypedef struct tcache_bin_s tcache_bin_t;\ntypedef struct tcache_s tcache_t;\n\n/*\n * tcache pointers close to NULL are used to encode state information that is\n * used for two purposes: preventing thread caching on a per thread basis and\n * cleaning up during thread shutdown.\n */\n#define\tTCACHE_STATE_DISABLED\t\t((tcache_t *)(uintptr_t)1)\n#define\tTCACHE_STATE_REINCARNATED\t((tcache_t *)(uintptr_t)2)\n#define\tTCACHE_STATE_PURGATORY\t\t((tcache_t *)(uintptr_t)3)\n#define\tTCACHE_STATE_MAX\t\tTCACHE_STATE_PURGATORY\n\n/*\n * Absolute maximum number of cache slots for each small bin in the thread\n * cache.  This is an additional constraint beyond that imposed as: twice the\n * number of regions per run for this size class.\n *\n * This constant must be an even number.\n */\n#define\tTCACHE_NSLOTS_SMALL_MAX\t\t200\n\n/* Number of cache slots for large size classes. */\n#define\tTCACHE_NSLOTS_LARGE\t\t20\n\n/* (1U << opt_lg_tcache_max) is used to compute tcache_maxclass. */\n#define\tLG_TCACHE_MAXCLASS_DEFAULT\t15\n\n/*\n * TCACHE_GC_SWEEP is the approximate number of allocation events between\n * full GC sweeps.  Integer rounding may cause the actual number to be\n * slightly higher, since GC is performed incrementally.\n */\n#define\tTCACHE_GC_SWEEP\t\t\t8192\n\n/* Number of tcache allocation/deallocation events between incremental GCs. */\n#define\tTCACHE_GC_INCR\t\t\t\t\t\t\t\\\n    ((TCACHE_GC_SWEEP / NBINS) + ((TCACHE_GC_SWEEP / NBINS == 0) ? 0 : 1))\n\n#endif /* JEMALLOC_H_TYPES */\n/******************************************************************************/\n#ifdef JEMALLOC_H_STRUCTS\n\ntypedef enum {\n\ttcache_enabled_false   = 0, /* Enable cast to/from bool. */\n\ttcache_enabled_true    = 1,\n\ttcache_enabled_default = 2\n} tcache_enabled_t;\n\n/*\n * Read-only information associated with each element of tcache_t's tbins array\n * is stored separately, mainly to reduce memory usage.\n */\nstruct tcache_bin_info_s {\n\tunsigned\tncached_max;\t/* Upper limit on ncached. */\n};\n\nstruct tcache_bin_s {\n\ttcache_bin_stats_t tstats;\n\tint\t\tlow_water;\t/* Min # cached since last GC. */\n\tunsigned\tlg_fill_div;\t/* Fill (ncached_max >> lg_fill_div). */\n\tunsigned\tncached;\t/* # of cached objects. */\n\tvoid\t\t**avail;\t/* Stack of available objects. */\n};\n\nstruct tcache_s {\n\tql_elm(tcache_t) link;\t\t/* Used for aggregating stats. */\n\tuint64_t\tprof_accumbytes;/* Cleared after arena_prof_accum() */\n\tarena_t\t\t*arena;\t\t/* This thread's arena. */\n\tunsigned\tev_cnt;\t\t/* Event count since incremental GC. */\n\tunsigned\tnext_gc_bin;\t/* Next bin to GC. */\n\ttcache_bin_t\ttbins[1];\t/* Dynamically sized. */\n\t/*\n\t * The pointer stacks associated with tbins follow as a contiguous\n\t * array.  During tcache initialization, the avail pointer in each\n\t * element of tbins is initialized to point to the proper offset within\n\t * this array.\n\t */\n};\n\n#endif /* JEMALLOC_H_STRUCTS */\n/******************************************************************************/\n#ifdef JEMALLOC_H_EXTERNS\n\nextern bool\topt_tcache;\nextern ssize_t\topt_lg_tcache_max;\n\nextern tcache_bin_info_t\t*tcache_bin_info;\n\n/*\n * Number of tcache bins.  There are NBINS small-object bins, plus 0 or more\n * large-object bins.\n */\nextern size_t\t\t\tnhbins;\n\n/* Maximum cached size class. */\nextern size_t\t\t\ttcache_maxclass;\n\nsize_t\ttcache_salloc(const void *ptr);\nvoid\ttcache_event_hard(tcache_t *tcache);\nvoid\t*tcache_alloc_small_hard(tcache_t *tcache, tcache_bin_t *tbin,\n    size_t binind);\nvoid\ttcache_bin_flush_small(tcache_bin_t *tbin, size_t binind, unsigned rem,\n    tcache_t *tcache);\nvoid\ttcache_bin_flush_large(tcache_bin_t *tbin, size_t binind, unsigned rem,\n    tcache_t *tcache);\nvoid\ttcache_arena_associate(tcache_t *tcache, arena_t *arena);\nvoid\ttcache_arena_dissociate(tcache_t *tcache);\ntcache_t *tcache_create(arena_t *arena);\nvoid\ttcache_destroy(tcache_t *tcache);\nvoid\ttcache_thread_cleanup(void *arg);\nvoid\ttcache_stats_merge(tcache_t *tcache, arena_t *arena);\nbool\ttcache_boot0(void);\nbool\ttcache_boot1(void);\n\n#endif /* JEMALLOC_H_EXTERNS */\n/******************************************************************************/\n#ifdef JEMALLOC_H_INLINES\n\n#ifndef JEMALLOC_ENABLE_INLINE\nmalloc_tsd_protos(JEMALLOC_ATTR(unused), tcache, tcache_t *)\nmalloc_tsd_protos(JEMALLOC_ATTR(unused), tcache_enabled, tcache_enabled_t)\n\nvoid\ttcache_event(tcache_t *tcache);\nvoid\ttcache_flush(void);\nbool\ttcache_enabled_get(void);\ntcache_t *tcache_get(bool create);\nvoid\ttcache_enabled_set(bool enabled);\nvoid\t*tcache_alloc_easy(tcache_bin_t *tbin);\nvoid\t*tcache_alloc_small(tcache_t *tcache, size_t size, bool zero);\nvoid\t*tcache_alloc_large(tcache_t *tcache, size_t size, bool zero);\nvoid\ttcache_dalloc_small(tcache_t *tcache, void *ptr, size_t binind);\nvoid\ttcache_dalloc_large(tcache_t *tcache, void *ptr, size_t size);\n#endif\n\n#if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_TCACHE_C_))\n/* Map of thread-specific caches. */\nmalloc_tsd_externs(tcache, tcache_t *)\nmalloc_tsd_funcs(JEMALLOC_INLINE, tcache, tcache_t *, NULL,\n    tcache_thread_cleanup)\n/* Per thread flag that allows thread caches to be disabled. */\nmalloc_tsd_externs(tcache_enabled, tcache_enabled_t)\nmalloc_tsd_funcs(JEMALLOC_INLINE, tcache_enabled, tcache_enabled_t,\n    tcache_enabled_default, malloc_tsd_no_cleanup)\n\nJEMALLOC_INLINE void\ntcache_flush(void)\n{\n\ttcache_t *tcache;\n\n\tcassert(config_tcache);\n\n\ttcache = *tcache_tsd_get();\n\tif ((uintptr_t)tcache <= (uintptr_t)TCACHE_STATE_MAX)\n\t\treturn;\n\ttcache_destroy(tcache);\n\ttcache = NULL;\n\ttcache_tsd_set(&tcache);\n}\n\nJEMALLOC_INLINE bool\ntcache_enabled_get(void)\n{\n\ttcache_enabled_t tcache_enabled;\n\n\tcassert(config_tcache);\n\n\ttcache_enabled = *tcache_enabled_tsd_get();\n\tif (tcache_enabled == tcache_enabled_default) {\n\t\ttcache_enabled = (tcache_enabled_t)opt_tcache;\n\t\ttcache_enabled_tsd_set(&tcache_enabled);\n\t}\n\n\treturn ((bool)tcache_enabled);\n}\n\nJEMALLOC_INLINE void\ntcache_enabled_set(bool enabled)\n{\n\ttcache_enabled_t tcache_enabled;\n\ttcache_t *tcache;\n\n\tcassert(config_tcache);\n\n\ttcache_enabled = (tcache_enabled_t)enabled;\n\ttcache_enabled_tsd_set(&tcache_enabled);\n\ttcache = *tcache_tsd_get();\n\tif (enabled) {\n\t\tif (tcache == TCACHE_STATE_DISABLED) {\n\t\t\ttcache = NULL;\n\t\t\ttcache_tsd_set(&tcache);\n\t\t}\n\t} else /* disabled */ {\n\t\tif (tcache > TCACHE_STATE_MAX) {\n\t\t\ttcache_destroy(tcache);\n\t\t\ttcache = NULL;\n\t\t}\n\t\tif (tcache == NULL) {\n\t\t\ttcache = TCACHE_STATE_DISABLED;\n\t\t\ttcache_tsd_set(&tcache);\n\t\t}\n\t}\n}\n\nJEMALLOC_INLINE tcache_t *\ntcache_get(bool create)\n{\n\ttcache_t *tcache;\n\n\tif (config_tcache == false)\n\t\treturn (NULL);\n\tif (config_lazy_lock && isthreaded == false)\n\t\treturn (NULL);\n\n\ttcache = *tcache_tsd_get();\n\tif ((uintptr_t)tcache <= (uintptr_t)TCACHE_STATE_MAX) {\n\t\tif (tcache == TCACHE_STATE_DISABLED)\n\t\t\treturn (NULL);\n\t\tif (tcache == NULL) {\n\t\t\tif (create == false) {\n\t\t\t\t/*\n\t\t\t\t * Creating a tcache here would cause\n\t\t\t\t * allocation as a side effect of free().\n\t\t\t\t * Ordinarily that would be okay since\n\t\t\t\t * tcache_create() failure is a soft failure\n\t\t\t\t * that doesn't propagate.  However, if TLS\n\t\t\t\t * data are freed via free() as in glibc,\n\t\t\t\t * subtle corruption could result from setting\n\t\t\t\t * a TLS variable after its backing memory is\n\t\t\t\t * freed.\n\t\t\t\t */\n\t\t\t\treturn (NULL);\n\t\t\t}\n\t\t\tif (tcache_enabled_get() == false) {\n\t\t\t\ttcache_enabled_set(false); /* Memoize. */\n\t\t\t\treturn (NULL);\n\t\t\t}\n\t\t\treturn (tcache_create(choose_arena(NULL)));\n\t\t}\n\t\tif (tcache == TCACHE_STATE_PURGATORY) {\n\t\t\t/*\n\t\t\t * Make a note that an allocator function was called\n\t\t\t * after tcache_thread_cleanup() was called.\n\t\t\t */\n\t\t\ttcache = TCACHE_STATE_REINCARNATED;\n\t\t\ttcache_tsd_set(&tcache);\n\t\t\treturn (NULL);\n\t\t}\n\t\tif (tcache == TCACHE_STATE_REINCARNATED)\n\t\t\treturn (NULL);\n\t\tnot_reached();\n\t}\n\n\treturn (tcache);\n}\n\nJEMALLOC_INLINE void\ntcache_event(tcache_t *tcache)\n{\n\n\tif (TCACHE_GC_INCR == 0)\n\t\treturn;\n\n\ttcache->ev_cnt++;\n\tassert(tcache->ev_cnt <= TCACHE_GC_INCR);\n\tif (tcache->ev_cnt == TCACHE_GC_INCR)\n\t\ttcache_event_hard(tcache);\n}\n\nJEMALLOC_INLINE void *\ntcache_alloc_easy(tcache_bin_t *tbin)\n{\n\tvoid *ret;\n\n\tif (tbin->ncached == 0) {\n\t\ttbin->low_water = -1;\n\t\treturn (NULL);\n\t}\n\ttbin->ncached--;\n\tif ((int)tbin->ncached < tbin->low_water)\n\t\ttbin->low_water = tbin->ncached;\n\tret = tbin->avail[tbin->ncached];\n\treturn (ret);\n}\n\nJEMALLOC_INLINE void *\ntcache_alloc_small(tcache_t *tcache, size_t size, bool zero)\n{\n\tvoid *ret;\n\tsize_t binind;\n\ttcache_bin_t *tbin;\n\n\tbinind = SMALL_SIZE2BIN(size);\n\tassert(binind < NBINS);\n\ttbin = &tcache->tbins[binind];\n\tret = tcache_alloc_easy(tbin);\n\tif (ret == NULL) {\n\t\tret = tcache_alloc_small_hard(tcache, tbin, binind);\n\t\tif (ret == NULL)\n\t\t\treturn (NULL);\n\t}\n\tassert(tcache_salloc(ret) == arena_bin_info[binind].reg_size);\n\n\tif (zero == false) {\n\t\tif (config_fill) {\n\t\t\tif (opt_junk) {\n\t\t\t\tarena_alloc_junk_small(ret,\n\t\t\t\t    &arena_bin_info[binind], false);\n\t\t\t} else if (opt_zero)\n\t\t\t\tmemset(ret, 0, size);\n\t\t}\n\t} else {\n\t\tif (config_fill && opt_junk) {\n\t\t\tarena_alloc_junk_small(ret, &arena_bin_info[binind],\n\t\t\t    true);\n\t\t}\n\t\tVALGRIND_MAKE_MEM_UNDEFINED(ret, size);\n\t\tmemset(ret, 0, size);\n\t}\n\n\tif (config_stats)\n\t\ttbin->tstats.nrequests++;\n\tif (config_prof)\n\t\ttcache->prof_accumbytes += arena_bin_info[binind].reg_size;\n\ttcache_event(tcache);\n\treturn (ret);\n}\n\nJEMALLOC_INLINE void *\ntcache_alloc_large(tcache_t *tcache, size_t size, bool zero)\n{\n\tvoid *ret;\n\tsize_t binind;\n\ttcache_bin_t *tbin;\n\n\tsize = PAGE_CEILING(size);\n\tassert(size <= tcache_maxclass);\n\tbinind = NBINS + (size >> LG_PAGE) - 1;\n\tassert(binind < nhbins);\n\ttbin = &tcache->tbins[binind];\n\tret = tcache_alloc_easy(tbin);\n\tif (ret == NULL) {\n\t\t/*\n\t\t * Only allocate one large object at a time, because it's quite\n\t\t * expensive to create one and not use it.\n\t\t */\n\t\tret = arena_malloc_large(tcache->arena, size, zero);\n\t\tif (ret == NULL)\n\t\t\treturn (NULL);\n\t} else {\n\t\tif (config_prof && prof_promote && size == PAGE) {\n\t\t\tarena_chunk_t *chunk =\n\t\t\t    (arena_chunk_t *)CHUNK_ADDR2BASE(ret);\n\t\t\tsize_t pageind = (((uintptr_t)ret - (uintptr_t)chunk) >>\n\t\t\t    LG_PAGE);\n\t\t\tarena_mapbits_large_binind_set(chunk, pageind,\n\t\t\t    BININD_INVALID);\n\t\t}\n\t\tif (zero == false) {\n\t\t\tif (config_fill) {\n\t\t\t\tif (opt_junk)\n\t\t\t\t\tmemset(ret, 0xa5, size);\n\t\t\t\telse if (opt_zero)\n\t\t\t\t\tmemset(ret, 0, size);\n\t\t\t}\n\t\t} else {\n\t\t\tVALGRIND_MAKE_MEM_UNDEFINED(ret, size);\n\t\t\tmemset(ret, 0, size);\n\t\t}\n\n\t\tif (config_stats)\n\t\t\ttbin->tstats.nrequests++;\n\t\tif (config_prof)\n\t\t\ttcache->prof_accumbytes += size;\n\t}\n\n\ttcache_event(tcache);\n\treturn (ret);\n}\n\nJEMALLOC_INLINE void\ntcache_dalloc_small(tcache_t *tcache, void *ptr, size_t binind)\n{\n\ttcache_bin_t *tbin;\n\ttcache_bin_info_t *tbin_info;\n\n\tassert(tcache_salloc(ptr) <= SMALL_MAXCLASS);\n\n\tif (config_fill && opt_junk)\n\t\tarena_dalloc_junk_small(ptr, &arena_bin_info[binind]);\n\n\ttbin = &tcache->tbins[binind];\n\ttbin_info = &tcache_bin_info[binind];\n\tif (tbin->ncached == tbin_info->ncached_max) {\n\t\ttcache_bin_flush_small(tbin, binind, (tbin_info->ncached_max >>\n\t\t    1), tcache);\n\t}\n\tassert(tbin->ncached < tbin_info->ncached_max);\n\ttbin->avail[tbin->ncached] = ptr;\n\ttbin->ncached++;\n\n\ttcache_event(tcache);\n}\n\nJEMALLOC_INLINE void\ntcache_dalloc_large(tcache_t *tcache, void *ptr, size_t size)\n{\n\tsize_t binind;\n\ttcache_bin_t *tbin;\n\ttcache_bin_info_t *tbin_info;\n\n\tassert((size & PAGE_MASK) == 0);\n\tassert(tcache_salloc(ptr) > SMALL_MAXCLASS);\n\tassert(tcache_salloc(ptr) <= tcache_maxclass);\n\n\tbinind = NBINS + (size >> LG_PAGE) - 1;\n\n\tif (config_fill && opt_junk)\n\t\tmemset(ptr, 0x5a, size);\n\n\ttbin = &tcache->tbins[binind];\n\ttbin_info = &tcache_bin_info[binind];\n\tif (tbin->ncached == tbin_info->ncached_max) {\n\t\ttcache_bin_flush_large(tbin, binind, (tbin_info->ncached_max >>\n\t\t    1), tcache);\n\t}\n\tassert(tbin->ncached < tbin_info->ncached_max);\n\ttbin->avail[tbin->ncached] = ptr;\n\ttbin->ncached++;\n\n\ttcache_event(tcache);\n}\n#endif\n\n#endif /* JEMALLOC_H_INLINES */\n/******************************************************************************/\n"
  },
  {
    "path": "deps/jemalloc/include/jemalloc/internal/tsd.h",
    "content": "/******************************************************************************/\n#ifdef JEMALLOC_H_TYPES\n\n/* Maximum number of malloc_tsd users with cleanup functions. */\n#define\tMALLOC_TSD_CLEANUPS_MAX\t8\n\ntypedef bool (*malloc_tsd_cleanup_t)(void);\n\n/*\n * TLS/TSD-agnostic macro-based implementation of thread-specific data.  There\n * are four macros that support (at least) three use cases: file-private,\n * library-private, and library-private inlined.  Following is an example\n * library-private tsd variable:\n *\n * In example.h:\n *   typedef struct {\n *           int x;\n *           int y;\n *   } example_t;\n *   #define EX_INITIALIZER JEMALLOC_CONCAT({0, 0})\n *   malloc_tsd_protos(, example, example_t *)\n *   malloc_tsd_externs(example, example_t *)\n * In example.c:\n *   malloc_tsd_data(, example, example_t *, EX_INITIALIZER)\n *   malloc_tsd_funcs(, example, example_t *, EX_INITIALIZER,\n *       example_tsd_cleanup)\n *\n * The result is a set of generated functions, e.g.:\n *\n *   bool example_tsd_boot(void) {...}\n *   example_t **example_tsd_get() {...}\n *   void example_tsd_set(example_t **val) {...}\n *\n * Note that all of the functions deal in terms of (a_type *) rather than\n * (a_type)  so that it is possible to support non-pointer types (unlike\n * pthreads TSD).  example_tsd_cleanup() is passed an (a_type *) pointer that is\n * cast to (void *).  This means that the cleanup function needs to cast *and*\n * dereference the function argument, e.g.:\n *\n *   void\n *   example_tsd_cleanup(void *arg)\n *   {\n *           example_t *example = *(example_t **)arg;\n *\n *           [...]\n *           if ([want the cleanup function to be called again]) {\n *                   example_tsd_set(&example);\n *           }\n *   }\n *\n * If example_tsd_set() is called within example_tsd_cleanup(), it will be\n * called again.  This is similar to how pthreads TSD destruction works, except\n * that pthreads only calls the cleanup function again if the value was set to\n * non-NULL.\n */\n\n/* malloc_tsd_protos(). */\n#define\tmalloc_tsd_protos(a_attr, a_name, a_type)\t\t\t\\\na_attr bool\t\t\t\t\t\t\t\t\\\na_name##_tsd_boot(void);\t\t\t\t\t\t\\\na_attr a_type *\t\t\t\t\t\t\t\t\\\na_name##_tsd_get(void);\t\t\t\t\t\t\t\\\na_attr void\t\t\t\t\t\t\t\t\\\na_name##_tsd_set(a_type *val);\n\n/* malloc_tsd_externs(). */\n#ifdef JEMALLOC_MALLOC_THREAD_CLEANUP\n#define\tmalloc_tsd_externs(a_name, a_type)\t\t\t\t\\\nextern __thread a_type\ta_name##_tls;\t\t\t\t\t\\\nextern __thread bool\ta_name##_initialized;\t\t\t\t\\\nextern bool\t\ta_name##_booted;\n#elif (defined(JEMALLOC_TLS))\n#define\tmalloc_tsd_externs(a_name, a_type)\t\t\t\t\\\nextern __thread a_type\ta_name##_tls;\t\t\t\t\t\\\nextern pthread_key_t\ta_name##_tsd;\t\t\t\t\t\\\nextern bool\t\ta_name##_booted;\n#elif (defined(_WIN32))\n#define malloc_tsd_externs(a_name, a_type)\t\t\t\t\\\nextern DWORD\t\ta_name##_tsd;\t\t\t\t\t\\\nextern bool\t\ta_name##_booted;\n#else\n#define\tmalloc_tsd_externs(a_name, a_type)\t\t\t\t\\\nextern pthread_key_t\ta_name##_tsd;\t\t\t\t\t\\\nextern bool\t\ta_name##_booted;\n#endif\n\n/* malloc_tsd_data(). */\n#ifdef JEMALLOC_MALLOC_THREAD_CLEANUP\n#define\tmalloc_tsd_data(a_attr, a_name, a_type, a_initializer)\t\t\\\na_attr __thread a_type JEMALLOC_TLS_MODEL\t\t\t\t\\\n    a_name##_tls = a_initializer;\t\t\t\t\t\\\na_attr __thread bool JEMALLOC_TLS_MODEL\t\t\t\t\t\\\n    a_name##_initialized = false;\t\t\t\t\t\\\na_attr bool\t\ta_name##_booted = false;\n#elif (defined(JEMALLOC_TLS))\n#define\tmalloc_tsd_data(a_attr, a_name, a_type, a_initializer)\t\t\\\na_attr __thread a_type JEMALLOC_TLS_MODEL\t\t\t\t\\\n    a_name##_tls = a_initializer;\t\t\t\t\t\\\na_attr pthread_key_t\ta_name##_tsd;\t\t\t\t\t\\\na_attr bool\t\ta_name##_booted = false;\n#elif (defined(_WIN32))\n#define\tmalloc_tsd_data(a_attr, a_name, a_type, a_initializer)\t\t\\\na_attr DWORD\t\ta_name##_tsd;\t\t\t\t\t\\\na_attr bool\t\ta_name##_booted = false;\n#else\n#define\tmalloc_tsd_data(a_attr, a_name, a_type, a_initializer)\t\t\\\na_attr pthread_key_t\ta_name##_tsd;\t\t\t\t\t\\\na_attr bool\t\ta_name##_booted = false;\n#endif\n\n/* malloc_tsd_funcs(). */\n#ifdef JEMALLOC_MALLOC_THREAD_CLEANUP\n#define\tmalloc_tsd_funcs(a_attr, a_name, a_type, a_initializer,\t\t\\\n    a_cleanup)\t\t\t\t\t\t\t\t\\\n/* Initialization/cleanup. */\t\t\t\t\t\t\\\na_attr bool\t\t\t\t\t\t\t\t\\\na_name##_tsd_cleanup_wrapper(void)\t\t\t\t\t\\\n{\t\t\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\tif (a_name##_initialized) {\t\t\t\t\t\\\n\t\ta_name##_initialized = false;\t\t\t\t\\\n\t\ta_cleanup(&a_name##_tls);\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\treturn (a_name##_initialized);\t\t\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\na_attr bool\t\t\t\t\t\t\t\t\\\na_name##_tsd_boot(void)\t\t\t\t\t\t\t\\\n{\t\t\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\tif (a_cleanup != malloc_tsd_no_cleanup) {\t\t\t\\\n\t\tmalloc_tsd_cleanup_register(\t\t\t\t\\\n\t\t    &a_name##_tsd_cleanup_wrapper);\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\ta_name##_booted = true;\t\t\t\t\t\t\\\n\treturn (false);\t\t\t\t\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\n/* Get/set. */\t\t\t\t\t\t\t\t\\\na_attr a_type *\t\t\t\t\t\t\t\t\\\na_name##_tsd_get(void)\t\t\t\t\t\t\t\\\n{\t\t\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\tassert(a_name##_booted);\t\t\t\t\t\\\n\treturn (&a_name##_tls);\t\t\t\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\na_attr void\t\t\t\t\t\t\t\t\\\na_name##_tsd_set(a_type *val)\t\t\t\t\t\t\\\n{\t\t\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\tassert(a_name##_booted);\t\t\t\t\t\\\n\ta_name##_tls = (*val);\t\t\t\t\t\t\\\n\tif (a_cleanup != malloc_tsd_no_cleanup)\t\t\t\t\\\n\t\ta_name##_initialized = true;\t\t\t\t\\\n}\n#elif (defined(JEMALLOC_TLS))\n#define\tmalloc_tsd_funcs(a_attr, a_name, a_type, a_initializer,\t\t\\\n    a_cleanup)\t\t\t\t\t\t\t\t\\\n/* Initialization/cleanup. */\t\t\t\t\t\t\\\na_attr bool\t\t\t\t\t\t\t\t\\\na_name##_tsd_boot(void)\t\t\t\t\t\t\t\\\n{\t\t\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\tif (a_cleanup != malloc_tsd_no_cleanup) {\t\t\t\\\n\t\tif (pthread_key_create(&a_name##_tsd, a_cleanup) != 0)\t\\\n\t\t\treturn (true);\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\ta_name##_booted = true;\t\t\t\t\t\t\\\n\treturn (false);\t\t\t\t\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\n/* Get/set. */\t\t\t\t\t\t\t\t\\\na_attr a_type *\t\t\t\t\t\t\t\t\\\na_name##_tsd_get(void)\t\t\t\t\t\t\t\\\n{\t\t\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\tassert(a_name##_booted);\t\t\t\t\t\\\n\treturn (&a_name##_tls);\t\t\t\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\na_attr void\t\t\t\t\t\t\t\t\\\na_name##_tsd_set(a_type *val)\t\t\t\t\t\t\\\n{\t\t\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\tassert(a_name##_booted);\t\t\t\t\t\\\n\ta_name##_tls = (*val);\t\t\t\t\t\t\\\n\tif (a_cleanup != malloc_tsd_no_cleanup) {\t\t\t\\\n\t\tif (pthread_setspecific(a_name##_tsd,\t\t\t\\\n\t\t    (void *)(&a_name##_tls))) {\t\t\t\t\\\n\t\t\tmalloc_write(\"<jemalloc>: Error\"\t\t\\\n\t\t\t    \" setting TSD for \"#a_name\"\\n\");\t\t\\\n\t\t\tif (opt_abort)\t\t\t\t\t\\\n\t\t\t\tabort();\t\t\t\t\\\n\t\t}\t\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n}\n#elif (defined(_WIN32))\n#define\tmalloc_tsd_funcs(a_attr, a_name, a_type, a_initializer,\t\t\\\n    a_cleanup)\t\t\t\t\t\t\t\t\\\n/* Data structure. */\t\t\t\t\t\t\t\\\ntypedef struct {\t\t\t\t\t\t\t\\\n\tbool\tinitialized;\t\t\t\t\t\t\\\n\ta_type\tval;\t\t\t\t\t\t\t\\\n} a_name##_tsd_wrapper_t;\t\t\t\t\t\t\\\n/* Initialization/cleanup. */\t\t\t\t\t\t\\\na_attr bool\t\t\t\t\t\t\t\t\\\na_name##_tsd_cleanup_wrapper(void)\t\t\t\t\t\\\n{\t\t\t\t\t\t\t\t\t\\\n\ta_name##_tsd_wrapper_t *wrapper;\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\twrapper = (a_name##_tsd_wrapper_t *) TlsGetValue(a_name##_tsd);\t\\\n\tif (wrapper == NULL)\t\t\t\t\t\t\\\n\t\treturn (false);\t\t\t\t\t\t\\\n\tif (a_cleanup != malloc_tsd_no_cleanup &&\t\t\t\\\n\t    wrapper->initialized) {\t\t\t\t\t\\\n\t\ta_type val = wrapper->val;\t\t\t\t\\\n\t\ta_type tsd_static_data = a_initializer;\t\t\t\\\n\t\twrapper->initialized = false;\t\t\t\t\\\n\t\twrapper->val = tsd_static_data;\t\t\t\t\\\n\t\ta_cleanup(&val);\t\t\t\t\t\\\n\t\tif (wrapper->initialized) {\t\t\t\t\\\n\t\t\t/* Trigger another cleanup round. */\t\t\\\n\t\t\treturn (true);\t\t\t\t\t\\\n\t\t}\t\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\tmalloc_tsd_dalloc(wrapper);\t\t\t\t\t\\\n\treturn (false);\t\t\t\t\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\na_attr bool\t\t\t\t\t\t\t\t\\\na_name##_tsd_boot(void)\t\t\t\t\t\t\t\\\n{\t\t\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\ta_name##_tsd = TlsAlloc();\t\t\t\t\t\\\n\tif (a_name##_tsd == TLS_OUT_OF_INDEXES)\t\t\t\t\\\n\t\treturn (true);\t\t\t\t\t\t\\\n\tif (a_cleanup != malloc_tsd_no_cleanup) {\t\t\t\\\n\t\tmalloc_tsd_cleanup_register(\t\t\t\t\\\n\t\t    &a_name##_tsd_cleanup_wrapper);\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\ta_name##_booted = true;\t\t\t\t\t\t\\\n\treturn (false);\t\t\t\t\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\n/* Get/set. */\t\t\t\t\t\t\t\t\\\na_attr a_name##_tsd_wrapper_t *\t\t\t\t\t\t\\\na_name##_tsd_get_wrapper(void)\t\t\t\t\t\t\\\n{\t\t\t\t\t\t\t\t\t\\\n\ta_name##_tsd_wrapper_t *wrapper = (a_name##_tsd_wrapper_t *)\t\\\n\t    TlsGetValue(a_name##_tsd);\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\tif (wrapper == NULL) {\t\t\t\t\t\t\\\n\t\twrapper = (a_name##_tsd_wrapper_t *)\t\t\t\\\n\t\t    malloc_tsd_malloc(sizeof(a_name##_tsd_wrapper_t));\t\\\n\t\tif (wrapper == NULL) {\t\t\t\t\t\\\n\t\t\tmalloc_write(\"<jemalloc>: Error allocating\"\t\\\n\t\t\t    \" TSD for \"#a_name\"\\n\");\t\t\t\\\n\t\t\tabort();\t\t\t\t\t\\\n\t\t} else {\t\t\t\t\t\t\\\n\t\t\tstatic a_type tsd_static_data = a_initializer;\t\\\n\t\t\twrapper->initialized = false;\t\t\t\\\n\t\t\twrapper->val = tsd_static_data;\t\t\t\\\n\t\t}\t\t\t\t\t\t\t\\\n\t\tif (!TlsSetValue(a_name##_tsd, (void *)wrapper)) {\t\\\n\t\t\tmalloc_write(\"<jemalloc>: Error setting\"\t\\\n\t\t\t    \" TSD for \"#a_name\"\\n\");\t\t\t\\\n\t\t\tabort();\t\t\t\t\t\\\n\t\t}\t\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\treturn (wrapper);\t\t\t\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\na_attr a_type *\t\t\t\t\t\t\t\t\\\na_name##_tsd_get(void)\t\t\t\t\t\t\t\\\n{\t\t\t\t\t\t\t\t\t\\\n\ta_name##_tsd_wrapper_t *wrapper;\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\tassert(a_name##_booted);\t\t\t\t\t\\\n\twrapper = a_name##_tsd_get_wrapper();\t\t\t\t\\\n\treturn (&wrapper->val);\t\t\t\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\na_attr void\t\t\t\t\t\t\t\t\\\na_name##_tsd_set(a_type *val)\t\t\t\t\t\t\\\n{\t\t\t\t\t\t\t\t\t\\\n\ta_name##_tsd_wrapper_t *wrapper;\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\tassert(a_name##_booted);\t\t\t\t\t\\\n\twrapper = a_name##_tsd_get_wrapper();\t\t\t\t\\\n\twrapper->val = *(val);\t\t\t\t\t\t\\\n\tif (a_cleanup != malloc_tsd_no_cleanup)\t\t\t\t\\\n\t\twrapper->initialized = true;\t\t\t\t\\\n}\n#else\n#define\tmalloc_tsd_funcs(a_attr, a_name, a_type, a_initializer,\t\t\\\n    a_cleanup)\t\t\t\t\t\t\t\t\\\n/* Data structure. */\t\t\t\t\t\t\t\\\ntypedef struct {\t\t\t\t\t\t\t\\\n\tbool\tinitialized;\t\t\t\t\t\t\\\n\ta_type\tval;\t\t\t\t\t\t\t\\\n} a_name##_tsd_wrapper_t;\t\t\t\t\t\t\\\n/* Initialization/cleanup. */\t\t\t\t\t\t\\\na_attr void\t\t\t\t\t\t\t\t\\\na_name##_tsd_cleanup_wrapper(void *arg)\t\t\t\t\t\\\n{\t\t\t\t\t\t\t\t\t\\\n\ta_name##_tsd_wrapper_t *wrapper = (a_name##_tsd_wrapper_t *)arg;\\\n\t\t\t\t\t\t\t\t\t\\\n\tif (a_cleanup != malloc_tsd_no_cleanup &&\t\t\t\\\n\t    wrapper->initialized) {\t\t\t\t\t\\\n\t\twrapper->initialized = false;\t\t\t\t\\\n\t\ta_cleanup(&wrapper->val);\t\t\t\t\\\n\t\tif (wrapper->initialized) {\t\t\t\t\\\n\t\t\t/* Trigger another cleanup round. */\t\t\\\n\t\t\tif (pthread_setspecific(a_name##_tsd,\t\t\\\n\t\t\t    (void *)wrapper)) {\t\t\t\t\\\n\t\t\t\tmalloc_write(\"<jemalloc>: Error\"\t\\\n\t\t\t\t    \" setting TSD for \"#a_name\"\\n\");\t\\\n\t\t\t\tif (opt_abort)\t\t\t\t\\\n\t\t\t\t\tabort();\t\t\t\\\n\t\t\t}\t\t\t\t\t\t\\\n\t\t\treturn;\t\t\t\t\t\t\\\n\t\t}\t\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\tmalloc_tsd_dalloc(wrapper);\t\t\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\na_attr bool\t\t\t\t\t\t\t\t\\\na_name##_tsd_boot(void)\t\t\t\t\t\t\t\\\n{\t\t\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\tif (pthread_key_create(&a_name##_tsd,\t\t\t\t\\\n\t    a_name##_tsd_cleanup_wrapper) != 0)\t\t\t\t\\\n\t\treturn (true);\t\t\t\t\t\t\\\n\ta_name##_booted = true;\t\t\t\t\t\t\\\n\treturn (false);\t\t\t\t\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\n/* Get/set. */\t\t\t\t\t\t\t\t\\\na_attr a_name##_tsd_wrapper_t *\t\t\t\t\t\t\\\na_name##_tsd_get_wrapper(void)\t\t\t\t\t\t\\\n{\t\t\t\t\t\t\t\t\t\\\n\ta_name##_tsd_wrapper_t *wrapper = (a_name##_tsd_wrapper_t *)\t\\\n\t    pthread_getspecific(a_name##_tsd);\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\tif (wrapper == NULL) {\t\t\t\t\t\t\\\n\t\twrapper = (a_name##_tsd_wrapper_t *)\t\t\t\\\n\t\t    malloc_tsd_malloc(sizeof(a_name##_tsd_wrapper_t));\t\\\n\t\tif (wrapper == NULL) {\t\t\t\t\t\\\n\t\t\tmalloc_write(\"<jemalloc>: Error allocating\"\t\\\n\t\t\t    \" TSD for \"#a_name\"\\n\");\t\t\t\\\n\t\t\tabort();\t\t\t\t\t\\\n\t\t} else {\t\t\t\t\t\t\\\n\t\t\tstatic a_type tsd_static_data = a_initializer;\t\\\n\t\t\twrapper->initialized = false;\t\t\t\\\n\t\t\twrapper->val = tsd_static_data;\t\t\t\\\n\t\t}\t\t\t\t\t\t\t\\\n\t\tif (pthread_setspecific(a_name##_tsd,\t\t\t\\\n\t\t    (void *)wrapper)) {\t\t\t\t\t\\\n\t\t\tmalloc_write(\"<jemalloc>: Error setting\"\t\\\n\t\t\t    \" TSD for \"#a_name\"\\n\");\t\t\t\\\n\t\t\tabort();\t\t\t\t\t\\\n\t\t}\t\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\treturn (wrapper);\t\t\t\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\na_attr a_type *\t\t\t\t\t\t\t\t\\\na_name##_tsd_get(void)\t\t\t\t\t\t\t\\\n{\t\t\t\t\t\t\t\t\t\\\n\ta_name##_tsd_wrapper_t *wrapper;\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\tassert(a_name##_booted);\t\t\t\t\t\\\n\twrapper = a_name##_tsd_get_wrapper();\t\t\t\t\\\n\treturn (&wrapper->val);\t\t\t\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\na_attr void\t\t\t\t\t\t\t\t\\\na_name##_tsd_set(a_type *val)\t\t\t\t\t\t\\\n{\t\t\t\t\t\t\t\t\t\\\n\ta_name##_tsd_wrapper_t *wrapper;\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\tassert(a_name##_booted);\t\t\t\t\t\\\n\twrapper = a_name##_tsd_get_wrapper();\t\t\t\t\\\n\twrapper->val = *(val);\t\t\t\t\t\t\\\n\tif (a_cleanup != malloc_tsd_no_cleanup)\t\t\t\t\\\n\t\twrapper->initialized = true;\t\t\t\t\\\n}\n#endif\n\n#endif /* JEMALLOC_H_TYPES */\n/******************************************************************************/\n#ifdef JEMALLOC_H_STRUCTS\n\n#endif /* JEMALLOC_H_STRUCTS */\n/******************************************************************************/\n#ifdef JEMALLOC_H_EXTERNS\n\nvoid\t*malloc_tsd_malloc(size_t size);\nvoid\tmalloc_tsd_dalloc(void *wrapper);\nvoid\tmalloc_tsd_no_cleanup(void *);\nvoid\tmalloc_tsd_cleanup_register(bool (*f)(void));\nvoid\tmalloc_tsd_boot(void);\n\n#endif /* JEMALLOC_H_EXTERNS */\n/******************************************************************************/\n#ifdef JEMALLOC_H_INLINES\n\n#endif /* JEMALLOC_H_INLINES */\n/******************************************************************************/\n"
  },
  {
    "path": "deps/jemalloc/include/jemalloc/internal/util.h",
    "content": "/******************************************************************************/\n#ifdef JEMALLOC_H_TYPES\n\n/* Size of stack-allocated buffer passed to buferror(). */\n#define\tBUFERROR_BUF\t\t64\n\n/*\n * Size of stack-allocated buffer used by malloc_{,v,vc}printf().  This must be\n * large enough for all possible uses within jemalloc.\n */\n#define\tMALLOC_PRINTF_BUFSIZE\t4096\n\n/*\n * Wrap a cpp argument that contains commas such that it isn't broken up into\n * multiple arguments.\n */\n#define JEMALLOC_CONCAT(...) __VA_ARGS__\n\n/*\n * Silence compiler warnings due to uninitialized values.  This is used\n * wherever the compiler fails to recognize that the variable is never used\n * uninitialized.\n */\n#ifdef JEMALLOC_CC_SILENCE\n#  define JEMALLOC_CC_SILENCE_INIT(v) = v\n#else\n#  define JEMALLOC_CC_SILENCE_INIT(v)\n#endif\n\n/*\n * Define a custom assert() in order to reduce the chances of deadlock during\n * assertion failure.\n */\n#ifndef assert\n#define\tassert(e) do {\t\t\t\t\t\t\t\\\n\tif (config_debug && !(e)) {\t\t\t\t\t\\\n\t\tmalloc_printf(\t\t\t\t\t\t\\\n\t\t    \"<jemalloc>: %s:%d: Failed assertion: \\\"%s\\\"\\n\",\t\\\n\t\t    __FILE__, __LINE__, #e);\t\t\t\t\\\n\t\tabort();\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n} while (0)\n#endif\n\n/* Use to assert a particular configuration, e.g., cassert(config_debug). */\n#define\tcassert(c) do {\t\t\t\t\t\t\t\\\n\tif ((c) == false)\t\t\t\t\t\t\\\n\t\tassert(false);\t\t\t\t\t\t\\\n} while (0)\n\n#ifndef not_reached\n#define\tnot_reached() do {\t\t\t\t\t\t\\\n\tif (config_debug) {\t\t\t\t\t\t\\\n\t\tmalloc_printf(\t\t\t\t\t\t\\\n\t\t    \"<jemalloc>: %s:%d: Unreachable code reached\\n\",\t\\\n\t\t    __FILE__, __LINE__);\t\t\t\t\\\n\t\tabort();\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n} while (0)\n#endif\n\n#ifndef not_implemented\n#define\tnot_implemented() do {\t\t\t\t\t\t\\\n\tif (config_debug) {\t\t\t\t\t\t\\\n\t\tmalloc_printf(\"<jemalloc>: %s:%d: Not implemented\\n\",\t\\\n\t\t    __FILE__, __LINE__);\t\t\t\t\\\n\t\tabort();\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n} while (0)\n#endif\n\n#define\tassert_not_implemented(e) do {\t\t\t\t\t\\\n\tif (config_debug && !(e))\t\t\t\t\t\\\n\t\tnot_implemented();\t\t\t\t\t\\\n} while (0)\n\n#endif /* JEMALLOC_H_TYPES */\n/******************************************************************************/\n#ifdef JEMALLOC_H_STRUCTS\n\n#endif /* JEMALLOC_H_STRUCTS */\n/******************************************************************************/\n#ifdef JEMALLOC_H_EXTERNS\n\nint\tbuferror(char *buf, size_t buflen);\nuintmax_t\tmalloc_strtoumax(const char *nptr, char **endptr, int base);\nvoid\tmalloc_write(const char *s);\n\n/*\n * malloc_vsnprintf() supports a subset of snprintf(3) that avoids floating\n * point math.\n */\nint\tmalloc_vsnprintf(char *str, size_t size, const char *format,\n    va_list ap);\nint\tmalloc_snprintf(char *str, size_t size, const char *format, ...)\n    JEMALLOC_ATTR(format(printf, 3, 4));\nvoid\tmalloc_vcprintf(void (*write_cb)(void *, const char *), void *cbopaque,\n    const char *format, va_list ap);\nvoid malloc_cprintf(void (*write)(void *, const char *), void *cbopaque,\n    const char *format, ...) JEMALLOC_ATTR(format(printf, 3, 4));\nvoid\tmalloc_printf(const char *format, ...)\n    JEMALLOC_ATTR(format(printf, 1, 2));\n\n#endif /* JEMALLOC_H_EXTERNS */\n/******************************************************************************/\n#ifdef JEMALLOC_H_INLINES\n\n#ifndef JEMALLOC_ENABLE_INLINE\nsize_t\tpow2_ceil(size_t x);\nvoid\tmalloc_write(const char *s);\nvoid\tset_errno(int errnum);\nint\tget_errno(void);\n#endif\n\n#if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_UTIL_C_))\n/* Compute the smallest power of 2 that is >= x. */\nJEMALLOC_INLINE size_t\npow2_ceil(size_t x)\n{\n\n\tx--;\n\tx |= x >> 1;\n\tx |= x >> 2;\n\tx |= x >> 4;\n\tx |= x >> 8;\n\tx |= x >> 16;\n#if (LG_SIZEOF_PTR == 3)\n\tx |= x >> 32;\n#endif\n\tx++;\n\treturn (x);\n}\n\n/* Sets error code */\nJEMALLOC_INLINE void\nset_errno(int errnum)\n{\n\n#ifdef _WIN32\n\tSetLastError(errnum);\n#else\n\terrno = errnum;\n#endif\n}\n\n/* Get last error code */\nJEMALLOC_INLINE int\nget_errno(void)\n{\n\n#ifdef _WIN32\n\treturn (GetLastError());\n#else\n\treturn (errno);\n#endif\n}\n#endif\n\n#endif /* JEMALLOC_H_INLINES */\n/******************************************************************************/\n"
  },
  {
    "path": "deps/jemalloc/include/jemalloc/jemalloc.h.in",
    "content": "#ifndef JEMALLOC_H_\n#define\tJEMALLOC_H_\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <limits.h>\n#include <strings.h>\n\n#define\tJEMALLOC_VERSION \"@jemalloc_version@\"\n#define\tJEMALLOC_VERSION_MAJOR @jemalloc_version_major@\n#define\tJEMALLOC_VERSION_MINOR @jemalloc_version_minor@\n#define\tJEMALLOC_VERSION_BUGFIX @jemalloc_version_bugfix@\n#define\tJEMALLOC_VERSION_NREV @jemalloc_version_nrev@\n#define\tJEMALLOC_VERSION_GID \"@jemalloc_version_gid@\"\n\n#include \"jemalloc_defs@install_suffix@.h\"\n\n#ifdef JEMALLOC_EXPERIMENTAL\n#define\tALLOCM_LG_ALIGN(la)\t(la)\n#if LG_SIZEOF_PTR == 2\n#define\tALLOCM_ALIGN(a)\t(ffs(a)-1)\n#else\n#define\tALLOCM_ALIGN(a)\t((a < (size_t)INT_MAX) ? ffs(a)-1 : ffs(a>>32)+31)\n#endif\n#define\tALLOCM_ZERO\t((int)0x40)\n#define\tALLOCM_NO_MOVE\t((int)0x80)\n/* Bias arena index bits so that 0 encodes \"ALLOCM_ARENA() unspecified\". */\n#define\tALLOCM_ARENA(a)\t((int)(((a)+1) << 8))\n\n#define\tALLOCM_SUCCESS\t\t0\n#define\tALLOCM_ERR_OOM\t\t1\n#define\tALLOCM_ERR_NOT_MOVED\t2\n#endif\n\n/*\n * The je_ prefix on the following public symbol declarations is an artifact of\n * namespace management, and should be omitted in application code unless\n * JEMALLOC_NO_DEMANGLE is defined (see below).\n */\nextern JEMALLOC_EXPORT const char\t*je_malloc_conf;\nextern JEMALLOC_EXPORT void\t\t(*je_malloc_message)(void *cbopaque,\n    const char *s);\n\nJEMALLOC_EXPORT void\t*je_malloc(size_t size) JEMALLOC_ATTR(malloc);\nJEMALLOC_EXPORT void\t*je_calloc(size_t num, size_t size)\n    JEMALLOC_ATTR(malloc);\nJEMALLOC_EXPORT int\tje_posix_memalign(void **memptr, size_t alignment,\n    size_t size) JEMALLOC_ATTR(nonnull(1));\nJEMALLOC_EXPORT void\t*je_aligned_alloc(size_t alignment, size_t size)\n    JEMALLOC_ATTR(malloc);\nJEMALLOC_EXPORT void\t*je_realloc(void *ptr, size_t size);\nJEMALLOC_EXPORT void\tje_free(void *ptr);\n\n#ifdef JEMALLOC_OVERRIDE_MEMALIGN\nJEMALLOC_EXPORT void *\tje_memalign(size_t alignment, size_t size)\n    JEMALLOC_ATTR(malloc);\n#endif\n\n#ifdef JEMALLOC_OVERRIDE_VALLOC\nJEMALLOC_EXPORT void *\tje_valloc(size_t size) JEMALLOC_ATTR(malloc);\n#endif\n\nJEMALLOC_EXPORT size_t\tje_malloc_usable_size(\n    JEMALLOC_USABLE_SIZE_CONST void *ptr);\nJEMALLOC_EXPORT void\tje_malloc_stats_print(void (*write_cb)(void *,\n    const char *), void *je_cbopaque, const char *opts);\nJEMALLOC_EXPORT int\tje_mallctl(const char *name, void *oldp,\n    size_t *oldlenp, void *newp, size_t newlen);\nJEMALLOC_EXPORT int\tje_mallctlnametomib(const char *name, size_t *mibp,\n    size_t *miblenp);\nJEMALLOC_EXPORT int\tje_mallctlbymib(const size_t *mib, size_t miblen,\n    void *oldp, size_t *oldlenp, void *newp, size_t newlen);\n\n#ifdef JEMALLOC_EXPERIMENTAL\nJEMALLOC_EXPORT int\tje_allocm(void **ptr, size_t *rsize, size_t size,\n    int flags) JEMALLOC_ATTR(nonnull(1));\nJEMALLOC_EXPORT int\tje_rallocm(void **ptr, size_t *rsize, size_t size,\n    size_t extra, int flags) JEMALLOC_ATTR(nonnull(1));\nJEMALLOC_EXPORT int\tje_sallocm(const void *ptr, size_t *rsize, int flags)\n    JEMALLOC_ATTR(nonnull(1));\nJEMALLOC_EXPORT int\tje_dallocm(void *ptr, int flags)\n    JEMALLOC_ATTR(nonnull(1));\nJEMALLOC_EXPORT int\tje_nallocm(size_t *rsize, size_t size, int flags);\n#endif\n\n/*\n * By default application code must explicitly refer to mangled symbol names,\n * so that it is possible to use jemalloc in conjunction with another allocator\n * in the same application.  Define JEMALLOC_MANGLE in order to cause automatic\n * name mangling that matches the API prefixing that happened as a result of\n * --with-mangling and/or --with-jemalloc-prefix configuration settings.\n */\n#ifdef JEMALLOC_MANGLE\n#ifndef JEMALLOC_NO_DEMANGLE\n#define\tJEMALLOC_NO_DEMANGLE\n#endif\n#define\tmalloc_conf je_malloc_conf\n#define\tmalloc_message je_malloc_message\n#define\tmalloc je_malloc\n#define\tcalloc je_calloc\n#define\tposix_memalign je_posix_memalign\n#define\taligned_alloc je_aligned_alloc\n#define\trealloc je_realloc\n#define\tfree je_free\n#define\tmalloc_usable_size je_malloc_usable_size\n#define\tmalloc_stats_print je_malloc_stats_print\n#define\tmallctl je_mallctl\n#define\tmallctlnametomib je_mallctlnametomib\n#define\tmallctlbymib je_mallctlbymib\n#define\tmemalign je_memalign\n#define\tvalloc je_valloc\n#ifdef JEMALLOC_EXPERIMENTAL\n#define\tallocm je_allocm\n#define\trallocm je_rallocm\n#define\tsallocm je_sallocm\n#define\tdallocm je_dallocm\n#define\tnallocm je_nallocm\n#endif\n#endif\n\n/*\n * The je_* macros can be used as stable alternative names for the public\n * jemalloc API if JEMALLOC_NO_DEMANGLE is defined.  This is primarily meant\n * for use in jemalloc itself, but it can be used by application code to\n * provide isolation from the name mangling specified via --with-mangling\n * and/or --with-jemalloc-prefix.\n */\n#ifndef JEMALLOC_NO_DEMANGLE\n#undef je_malloc_conf\n#undef je_malloc_message\n#undef je_malloc\n#undef je_calloc\n#undef je_posix_memalign\n#undef je_aligned_alloc\n#undef je_realloc\n#undef je_free\n#undef je_malloc_usable_size\n#undef je_malloc_stats_print\n#undef je_mallctl\n#undef je_mallctlnametomib\n#undef je_mallctlbymib\n#undef je_memalign\n#undef je_valloc\n#ifdef JEMALLOC_EXPERIMENTAL\n#undef je_allocm\n#undef je_rallocm\n#undef je_sallocm\n#undef je_dallocm\n#undef je_nallocm\n#endif\n#endif\n\n#ifdef __cplusplus\n};\n#endif\n#endif /* JEMALLOC_H_ */\n"
  },
  {
    "path": "deps/jemalloc/include/jemalloc/jemalloc_defs.h.in",
    "content": "/*\n * If JEMALLOC_PREFIX is defined via --with-jemalloc-prefix, it will cause all\n * public APIs to be prefixed.  This makes it possible, with some care, to use\n * multiple allocators simultaneously.\n */\n#undef JEMALLOC_PREFIX\n#undef JEMALLOC_CPREFIX\n\n/*\n * Name mangling for public symbols is controlled by --with-mangling and\n * --with-jemalloc-prefix.  With default settings the je_ prefix is stripped by\n * these macro definitions.\n */\n#undef je_malloc_conf\n#undef je_malloc_message\n#undef je_malloc\n#undef je_calloc\n#undef je_posix_memalign\n#undef je_aligned_alloc\n#undef je_realloc\n#undef je_free\n#undef je_malloc_usable_size\n#undef je_malloc_stats_print\n#undef je_mallctl\n#undef je_mallctlnametomib\n#undef je_mallctlbymib\n#undef je_memalign\n#undef je_valloc\n#undef je_allocm\n#undef je_rallocm\n#undef je_sallocm\n#undef je_dallocm\n#undef je_nallocm\n\n/*\n * JEMALLOC_PRIVATE_NAMESPACE is used as a prefix for all library-private APIs.\n * For shared libraries, symbol visibility mechanisms prevent these symbols\n * from being exported, but for static libraries, naming collisions are a real\n * possibility.\n */\n#undef JEMALLOC_PRIVATE_NAMESPACE\n#undef JEMALLOC_N\n\n/*\n * Hyper-threaded CPUs may need a special instruction inside spin loops in\n * order to yield to another virtual CPU.\n */\n#undef CPU_SPINWAIT\n\n/* Defined if the equivalent of FreeBSD's atomic(9) functions are available. */\n#undef JEMALLOC_ATOMIC9\n\n/*\n * Defined if OSAtomic*() functions are available, as provided by Darwin, and\n * documented in the atomic(3) manual page.\n */\n#undef JEMALLOC_OSATOMIC\n\n/*\n * Defined if __sync_add_and_fetch(uint32_t *, uint32_t) and\n * __sync_sub_and_fetch(uint32_t *, uint32_t) are available, despite\n * __GCC_HAVE_SYNC_COMPARE_AND_SWAP_4 not being defined (which means the\n * functions are defined in libgcc instead of being inlines)\n */\n#undef JE_FORCE_SYNC_COMPARE_AND_SWAP_4\n\n/*\n * Defined if __sync_add_and_fetch(uint64_t *, uint64_t) and\n * __sync_sub_and_fetch(uint64_t *, uint64_t) are available, despite\n * __GCC_HAVE_SYNC_COMPARE_AND_SWAP_8 not being defined (which means the\n * functions are defined in libgcc instead of being inlines)\n */\n#undef JE_FORCE_SYNC_COMPARE_AND_SWAP_8\n\n/*\n * Defined if OSSpin*() functions are available, as provided by Darwin, and\n * documented in the spinlock(3) manual page.\n */\n#undef JEMALLOC_OSSPIN\n\n/*\n * Defined if _malloc_thread_cleanup() exists.  At least in the case of\n * FreeBSD, pthread_key_create() allocates, which if used during malloc\n * bootstrapping will cause recursion into the pthreads library.  Therefore, if\n * _malloc_thread_cleanup() exists, use it as the basis for thread cleanup in\n * malloc_tsd.\n */\n#undef JEMALLOC_MALLOC_THREAD_CLEANUP\n\n/*\n * Defined if threaded initialization is known to be safe on this platform.\n * Among other things, it must be possible to initialize a mutex without\n * triggering allocation in order for threaded allocation to be safe.\n */\n#undef JEMALLOC_THREADED_INIT\n\n/*\n * Defined if the pthreads implementation defines\n * _pthread_mutex_init_calloc_cb(), in which case the function is used in order\n * to avoid recursive allocation during mutex initialization.\n */\n#undef JEMALLOC_MUTEX_INIT_CB\n\n/* Defined if __attribute__((...)) syntax is supported. */\n#undef JEMALLOC_HAVE_ATTR\n#ifdef JEMALLOC_HAVE_ATTR\n#  define JEMALLOC_ATTR(s) __attribute__((s))\n#  define JEMALLOC_EXPORT JEMALLOC_ATTR(visibility(\"default\"))\n#  define JEMALLOC_ALIGNED(s) JEMALLOC_ATTR(aligned(s))\n#  define JEMALLOC_SECTION(s) JEMALLOC_ATTR(section(s))\n#  define JEMALLOC_NOINLINE JEMALLOC_ATTR(noinline)\n#elif _MSC_VER\n#  define JEMALLOC_ATTR(s)\n#  ifdef DLLEXPORT\n#    define JEMALLOC_EXPORT __declspec(dllexport)\n#  else\n#    define JEMALLOC_EXPORT __declspec(dllimport)\n#  endif\n#  define JEMALLOC_ALIGNED(s) __declspec(align(s))\n#  define JEMALLOC_SECTION(s) __declspec(allocate(s))\n#  define JEMALLOC_NOINLINE __declspec(noinline)\n#else\n#  define JEMALLOC_ATTR(s)\n#  define JEMALLOC_EXPORT\n#  define JEMALLOC_ALIGNED(s)\n#  define JEMALLOC_SECTION(s)\n#  define JEMALLOC_NOINLINE\n#endif\n\n/* Defined if sbrk() is supported. */\n#undef JEMALLOC_HAVE_SBRK\n\n/* Non-empty if the tls_model attribute is supported. */\n#undef JEMALLOC_TLS_MODEL\n\n/* JEMALLOC_CC_SILENCE enables code that silences unuseful compiler warnings. */\n#undef JEMALLOC_CC_SILENCE\n\n/*\n * JEMALLOC_DEBUG enables assertions and other sanity checks, and disables\n * inline functions.\n */\n#undef JEMALLOC_DEBUG\n\n/* JEMALLOC_STATS enables statistics calculation. */\n#undef JEMALLOC_STATS\n\n/* JEMALLOC_PROF enables allocation profiling. */\n#undef JEMALLOC_PROF\n\n/* Use libunwind for profile backtracing if defined. */\n#undef JEMALLOC_PROF_LIBUNWIND\n\n/* Use libgcc for profile backtracing if defined. */\n#undef JEMALLOC_PROF_LIBGCC\n\n/* Use gcc intrinsics for profile backtracing if defined. */\n#undef JEMALLOC_PROF_GCC\n\n/*\n * JEMALLOC_TCACHE enables a thread-specific caching layer for small objects.\n * This makes it possible to allocate/deallocate objects without any locking\n * when the cache is in the steady state.\n */\n#undef JEMALLOC_TCACHE\n\n/*\n * JEMALLOC_DSS enables use of sbrk(2) to allocate chunks from the data storage\n * segment (DSS).\n */\n#undef JEMALLOC_DSS\n\n/* Support memory filling (junk/zero/quarantine/redzone). */\n#undef JEMALLOC_FILL\n\n/* Support the experimental API. */\n#undef JEMALLOC_EXPERIMENTAL\n\n/* Support utrace(2)-based tracing. */\n#undef JEMALLOC_UTRACE\n\n/* Support Valgrind. */\n#undef JEMALLOC_VALGRIND\n\n/* Support optional abort() on OOM. */\n#undef JEMALLOC_XMALLOC\n\n/* Support lazy locking (avoid locking unless a second thread is launched). */\n#undef JEMALLOC_LAZY_LOCK\n\n/* One page is 2^STATIC_PAGE_SHIFT bytes. */\n#undef STATIC_PAGE_SHIFT\n\n/*\n * If defined, use munmap() to unmap freed chunks, rather than storing them for\n * later reuse.  This is disabled by default on Linux because common sequences\n * of mmap()/munmap() calls will cause virtual memory map holes.\n */\n#undef JEMALLOC_MUNMAP\n\n/*\n * If defined, use mremap(...MREMAP_FIXED...) for huge realloc().  This is\n * disabled by default because it is Linux-specific and it will cause virtual\n * memory map holes, much like munmap(2) does.\n */\n#undef JEMALLOC_MREMAP\n\n/* TLS is used to map arenas and magazine caches to threads. */\n#undef JEMALLOC_TLS\n\n/*\n * JEMALLOC_IVSALLOC enables ivsalloc(), which verifies that pointers reside\n * within jemalloc-owned chunks before dereferencing them.\n */\n#undef JEMALLOC_IVSALLOC\n\n/*\n * Define overrides for non-standard allocator-related functions if they\n * are present on the system.\n */\n#undef JEMALLOC_OVERRIDE_MEMALIGN\n#undef JEMALLOC_OVERRIDE_VALLOC\n\n/*\n * At least Linux omits the \"const\" in:\n *\n *   size_t malloc_usable_size(const void *ptr);\n *\n * Match the operating system's prototype.\n */\n#undef JEMALLOC_USABLE_SIZE_CONST\n\n/*\n * Darwin (OS X) uses zones to work around Mach-O symbol override shortcomings.\n */\n#undef JEMALLOC_ZONE\n#undef JEMALLOC_ZONE_VERSION\n\n/*\n * Methods for purging unused pages differ between operating systems.\n *\n *   madvise(..., MADV_DONTNEED) : On Linux, this immediately discards pages,\n *                                 such that new pages will be demand-zeroed if\n *                                 the address region is later touched.\n *   madvise(..., MADV_FREE) : On FreeBSD and Darwin, this marks pages as being\n *                             unused, such that they will be discarded rather\n *                             than swapped out.\n */\n#undef JEMALLOC_PURGE_MADVISE_DONTNEED\n#undef JEMALLOC_PURGE_MADVISE_FREE\n\n/* sizeof(void *) == 2^LG_SIZEOF_PTR. */\n#undef LG_SIZEOF_PTR\n\n/* sizeof(int) == 2^LG_SIZEOF_INT. */\n#undef LG_SIZEOF_INT\n\n/* sizeof(long) == 2^LG_SIZEOF_LONG. */\n#undef LG_SIZEOF_LONG\n\n/* sizeof(intmax_t) == 2^LG_SIZEOF_INTMAX_T. */\n#undef LG_SIZEOF_INTMAX_T\n"
  },
  {
    "path": "deps/jemalloc/include/msvc_compat/inttypes.h",
    "content": "// ISO C9x  compliant inttypes.h for Microsoft Visual Studio\n// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 \n// \n//  Copyright (c) 2006 Alexander Chemeris\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//   1. Redistributions of source code must retain the above copyright notice,\n//      this list of conditions and the following disclaimer.\n// \n//   2. 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// \n//   3. The name of the author may be used to endorse or promote products\n//      derived from this software without specific prior written permission.\n// \n// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED\n// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\n// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO\n// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;\n// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, \n// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR\n// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\n// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n// \n///////////////////////////////////////////////////////////////////////////////\n\n#ifndef _MSC_VER // [\n#error \"Use this header only with Microsoft Visual C++ compilers!\"\n#endif // _MSC_VER ]\n\n#ifndef _MSC_INTTYPES_H_ // [\n#define _MSC_INTTYPES_H_\n\n#if _MSC_VER > 1000\n#pragma once\n#endif\n\n#include \"stdint.h\"\n\n// 7.8 Format conversion of integer types\n\ntypedef struct {\n   intmax_t quot;\n   intmax_t rem;\n} imaxdiv_t;\n\n// 7.8.1 Macros for format specifiers\n\n#if !defined(__cplusplus) || defined(__STDC_FORMAT_MACROS) // [   See footnote 185 at page 198\n\n#ifdef _WIN64\n#  define __PRI64_PREFIX        \"l\"\n#  define __PRIPTR_PREFIX       \"l\"\n#else\n#  define __PRI64_PREFIX        \"ll\"\n#  define __PRIPTR_PREFIX\n#endif\n\n// The fprintf macros for signed integers are:\n#define PRId8       \"d\"\n#define PRIi8       \"i\"\n#define PRIdLEAST8  \"d\"\n#define PRIiLEAST8  \"i\"\n#define PRIdFAST8   \"d\"\n#define PRIiFAST8   \"i\"\n\n#define PRId16       \"hd\"\n#define PRIi16       \"hi\"\n#define PRIdLEAST16  \"hd\"\n#define PRIiLEAST16  \"hi\"\n#define PRIdFAST16   \"hd\"\n#define PRIiFAST16   \"hi\"\n\n#define PRId32       \"d\"\n#define PRIi32       \"i\"\n#define PRIdLEAST32  \"d\"\n#define PRIiLEAST32  \"i\"\n#define PRIdFAST32   \"d\"\n#define PRIiFAST32   \"i\"\n\n#define PRId64       __PRI64_PREFIX \"d\"\n#define PRIi64       __PRI64_PREFIX \"i\"\n#define PRIdLEAST64  __PRI64_PREFIX \"d\"\n#define PRIiLEAST64  __PRI64_PREFIX \"i\"\n#define PRIdFAST64   __PRI64_PREFIX \"d\"\n#define PRIiFAST64   __PRI64_PREFIX \"i\"\n\n#define PRIdMAX     __PRI64_PREFIX \"d\"\n#define PRIiMAX     __PRI64_PREFIX \"i\"\n\n#define PRIdPTR     __PRIPTR_PREFIX \"d\"\n#define PRIiPTR     __PRIPTR_PREFIX \"i\"\n\n// The fprintf macros for unsigned integers are:\n#define PRIo8       \"o\"\n#define PRIu8       \"u\"\n#define PRIx8       \"x\"\n#define PRIX8       \"X\"\n#define PRIoLEAST8  \"o\"\n#define PRIuLEAST8  \"u\"\n#define PRIxLEAST8  \"x\"\n#define PRIXLEAST8  \"X\"\n#define PRIoFAST8   \"o\"\n#define PRIuFAST8   \"u\"\n#define PRIxFAST8   \"x\"\n#define PRIXFAST8   \"X\"\n\n#define PRIo16       \"ho\"\n#define PRIu16       \"hu\"\n#define PRIx16       \"hx\"\n#define PRIX16       \"hX\"\n#define PRIoLEAST16  \"ho\"\n#define PRIuLEAST16  \"hu\"\n#define PRIxLEAST16  \"hx\"\n#define PRIXLEAST16  \"hX\"\n#define PRIoFAST16   \"ho\"\n#define PRIuFAST16   \"hu\"\n#define PRIxFAST16   \"hx\"\n#define PRIXFAST16   \"hX\"\n\n#define PRIo32       \"o\"\n#define PRIu32       \"u\"\n#define PRIx32       \"x\"\n#define PRIX32       \"X\"\n#define PRIoLEAST32  \"o\"\n#define PRIuLEAST32  \"u\"\n#define PRIxLEAST32  \"x\"\n#define PRIXLEAST32  \"X\"\n#define PRIoFAST32   \"o\"\n#define PRIuFAST32   \"u\"\n#define PRIxFAST32   \"x\"\n#define PRIXFAST32   \"X\"\n\n#define PRIo64       __PRI64_PREFIX \"o\"\n#define PRIu64       __PRI64_PREFIX \"u\"\n#define PRIx64       __PRI64_PREFIX \"x\"\n#define PRIX64       __PRI64_PREFIX \"X\"\n#define PRIoLEAST64  __PRI64_PREFIX \"o\"\n#define PRIuLEAST64  __PRI64_PREFIX \"u\"\n#define PRIxLEAST64  __PRI64_PREFIX \"x\"\n#define PRIXLEAST64  __PRI64_PREFIX \"X\"\n#define PRIoFAST64   __PRI64_PREFIX \"o\"\n#define PRIuFAST64   __PRI64_PREFIX \"u\"\n#define PRIxFAST64   __PRI64_PREFIX \"x\"\n#define PRIXFAST64   __PRI64_PREFIX \"X\"\n\n#define PRIoMAX     __PRI64_PREFIX \"o\"\n#define PRIuMAX     __PRI64_PREFIX \"u\"\n#define PRIxMAX     __PRI64_PREFIX \"x\"\n#define PRIXMAX     __PRI64_PREFIX \"X\"\n\n#define PRIoPTR     __PRIPTR_PREFIX \"o\"\n#define PRIuPTR     __PRIPTR_PREFIX \"u\"\n#define PRIxPTR     __PRIPTR_PREFIX \"x\"\n#define PRIXPTR     __PRIPTR_PREFIX \"X\"\n\n// The fscanf macros for signed integers are:\n#define SCNd8       \"d\"\n#define SCNi8       \"i\"\n#define SCNdLEAST8  \"d\"\n#define SCNiLEAST8  \"i\"\n#define SCNdFAST8   \"d\"\n#define SCNiFAST8   \"i\"\n\n#define SCNd16       \"hd\"\n#define SCNi16       \"hi\"\n#define SCNdLEAST16  \"hd\"\n#define SCNiLEAST16  \"hi\"\n#define SCNdFAST16   \"hd\"\n#define SCNiFAST16   \"hi\"\n\n#define SCNd32       \"ld\"\n#define SCNi32       \"li\"\n#define SCNdLEAST32  \"ld\"\n#define SCNiLEAST32  \"li\"\n#define SCNdFAST32   \"ld\"\n#define SCNiFAST32   \"li\"\n\n#define SCNd64       \"I64d\"\n#define SCNi64       \"I64i\"\n#define SCNdLEAST64  \"I64d\"\n#define SCNiLEAST64  \"I64i\"\n#define SCNdFAST64   \"I64d\"\n#define SCNiFAST64   \"I64i\"\n\n#define SCNdMAX     \"I64d\"\n#define SCNiMAX     \"I64i\"\n\n#ifdef _WIN64 // [\n#  define SCNdPTR     \"I64d\"\n#  define SCNiPTR     \"I64i\"\n#else  // _WIN64 ][\n#  define SCNdPTR     \"ld\"\n#  define SCNiPTR     \"li\"\n#endif  // _WIN64 ]\n\n// The fscanf macros for unsigned integers are:\n#define SCNo8       \"o\"\n#define SCNu8       \"u\"\n#define SCNx8       \"x\"\n#define SCNX8       \"X\"\n#define SCNoLEAST8  \"o\"\n#define SCNuLEAST8  \"u\"\n#define SCNxLEAST8  \"x\"\n#define SCNXLEAST8  \"X\"\n#define SCNoFAST8   \"o\"\n#define SCNuFAST8   \"u\"\n#define SCNxFAST8   \"x\"\n#define SCNXFAST8   \"X\"\n\n#define SCNo16       \"ho\"\n#define SCNu16       \"hu\"\n#define SCNx16       \"hx\"\n#define SCNX16       \"hX\"\n#define SCNoLEAST16  \"ho\"\n#define SCNuLEAST16  \"hu\"\n#define SCNxLEAST16  \"hx\"\n#define SCNXLEAST16  \"hX\"\n#define SCNoFAST16   \"ho\"\n#define SCNuFAST16   \"hu\"\n#define SCNxFAST16   \"hx\"\n#define SCNXFAST16   \"hX\"\n\n#define SCNo32       \"lo\"\n#define SCNu32       \"lu\"\n#define SCNx32       \"lx\"\n#define SCNX32       \"lX\"\n#define SCNoLEAST32  \"lo\"\n#define SCNuLEAST32  \"lu\"\n#define SCNxLEAST32  \"lx\"\n#define SCNXLEAST32  \"lX\"\n#define SCNoFAST32   \"lo\"\n#define SCNuFAST32   \"lu\"\n#define SCNxFAST32   \"lx\"\n#define SCNXFAST32   \"lX\"\n\n#define SCNo64       \"I64o\"\n#define SCNu64       \"I64u\"\n#define SCNx64       \"I64x\"\n#define SCNX64       \"I64X\"\n#define SCNoLEAST64  \"I64o\"\n#define SCNuLEAST64  \"I64u\"\n#define SCNxLEAST64  \"I64x\"\n#define SCNXLEAST64  \"I64X\"\n#define SCNoFAST64   \"I64o\"\n#define SCNuFAST64   \"I64u\"\n#define SCNxFAST64   \"I64x\"\n#define SCNXFAST64   \"I64X\"\n\n#define SCNoMAX     \"I64o\"\n#define SCNuMAX     \"I64u\"\n#define SCNxMAX     \"I64x\"\n#define SCNXMAX     \"I64X\"\n\n#ifdef _WIN64 // [\n#  define SCNoPTR     \"I64o\"\n#  define SCNuPTR     \"I64u\"\n#  define SCNxPTR     \"I64x\"\n#  define SCNXPTR     \"I64X\"\n#else  // _WIN64 ][\n#  define SCNoPTR     \"lo\"\n#  define SCNuPTR     \"lu\"\n#  define SCNxPTR     \"lx\"\n#  define SCNXPTR     \"lX\"\n#endif  // _WIN64 ]\n\n#endif // __STDC_FORMAT_MACROS ]\n\n// 7.8.2 Functions for greatest-width integer types\n\n// 7.8.2.1 The imaxabs function\n#define imaxabs _abs64\n\n// 7.8.2.2 The imaxdiv function\n\n// This is modified version of div() function from Microsoft's div.c found\n// in %MSVC.NET%\\crt\\src\\div.c\n#ifdef STATIC_IMAXDIV // [\nstatic\n#else // STATIC_IMAXDIV ][\n_inline\n#endif // STATIC_IMAXDIV ]\nimaxdiv_t __cdecl imaxdiv(intmax_t numer, intmax_t denom)\n{\n   imaxdiv_t result;\n\n   result.quot = numer / denom;\n   result.rem = numer % denom;\n\n   if (numer < 0 && result.rem > 0) {\n      // did division wrong; must fix up\n      ++result.quot;\n      result.rem -= denom;\n   }\n\n   return result;\n}\n\n// 7.8.2.3 The strtoimax and strtoumax functions\n#define strtoimax _strtoi64\n#define strtoumax _strtoui64\n\n// 7.8.2.4 The wcstoimax and wcstoumax functions\n#define wcstoimax _wcstoi64\n#define wcstoumax _wcstoui64\n\n\n#endif // _MSC_INTTYPES_H_ ]\n"
  },
  {
    "path": "deps/jemalloc/include/msvc_compat/stdbool.h",
    "content": "#ifndef stdbool_h\n#define stdbool_h\n\n#include <wtypes.h>\n\n/* MSVC doesn't define _Bool or bool in C, but does have BOOL */\n/* Note this doesn't pass autoconf's test because (bool) 0.5 != true */\ntypedef BOOL _Bool;\n\n#define bool _Bool\n#define true 1\n#define false 0\n\n#define __bool_true_false_are_defined 1\n\n#endif /* stdbool_h */\n"
  },
  {
    "path": "deps/jemalloc/include/msvc_compat/stdint.h",
    "content": "// ISO C9x  compliant stdint.h for Microsoft Visual Studio\n// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 \n// \n//  Copyright (c) 2006-2008 Alexander Chemeris\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//   1. Redistributions of source code must retain the above copyright notice,\n//      this list of conditions and the following disclaimer.\n// \n//   2. 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// \n//   3. The name of the author may be used to endorse or promote products\n//      derived from this software without specific prior written permission.\n// \n// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED\n// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\n// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO\n// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;\n// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, \n// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR\n// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\n// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n// \n///////////////////////////////////////////////////////////////////////////////\n\n#ifndef _MSC_VER // [\n#error \"Use this header only with Microsoft Visual C++ compilers!\"\n#endif // _MSC_VER ]\n\n#ifndef _MSC_STDINT_H_ // [\n#define _MSC_STDINT_H_\n\n#if _MSC_VER > 1000\n#pragma once\n#endif\n\n#include <limits.h>\n\n// For Visual Studio 6 in C++ mode and for many Visual Studio versions when\n// compiling for ARM we should wrap <wchar.h> include with 'extern \"C++\" {}'\n// or compiler give many errors like this:\n//   error C2733: second C linkage of overloaded function 'wmemchr' not allowed\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n#  include <wchar.h>\n#ifdef __cplusplus\n}\n#endif\n\n// Define _W64 macros to mark types changing their size, like intptr_t.\n#ifndef _W64\n#  if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300\n#     define _W64 __w64\n#  else\n#     define _W64\n#  endif\n#endif\n\n\n// 7.18.1 Integer types\n\n// 7.18.1.1 Exact-width integer types\n\n// Visual Studio 6 and Embedded Visual C++ 4 doesn't\n// realize that, e.g. char has the same size as __int8\n// so we give up on __intX for them.\n#if (_MSC_VER < 1300)\n   typedef signed char       int8_t;\n   typedef signed short      int16_t;\n   typedef signed int        int32_t;\n   typedef unsigned char     uint8_t;\n   typedef unsigned short    uint16_t;\n   typedef unsigned int      uint32_t;\n#else\n   typedef signed __int8     int8_t;\n   typedef signed __int16    int16_t;\n   typedef signed __int32    int32_t;\n   typedef unsigned __int8   uint8_t;\n   typedef unsigned __int16  uint16_t;\n   typedef unsigned __int32  uint32_t;\n#endif\ntypedef signed __int64       int64_t;\ntypedef unsigned __int64     uint64_t;\n\n\n// 7.18.1.2 Minimum-width integer types\ntypedef int8_t    int_least8_t;\ntypedef int16_t   int_least16_t;\ntypedef int32_t   int_least32_t;\ntypedef int64_t   int_least64_t;\ntypedef uint8_t   uint_least8_t;\ntypedef uint16_t  uint_least16_t;\ntypedef uint32_t  uint_least32_t;\ntypedef uint64_t  uint_least64_t;\n\n// 7.18.1.3 Fastest minimum-width integer types\ntypedef int8_t    int_fast8_t;\ntypedef int16_t   int_fast16_t;\ntypedef int32_t   int_fast32_t;\ntypedef int64_t   int_fast64_t;\ntypedef uint8_t   uint_fast8_t;\ntypedef uint16_t  uint_fast16_t;\ntypedef uint32_t  uint_fast32_t;\ntypedef uint64_t  uint_fast64_t;\n\n// 7.18.1.4 Integer types capable of holding object pointers\n#ifdef _WIN64 // [\n   typedef signed __int64    intptr_t;\n   typedef unsigned __int64  uintptr_t;\n#else // _WIN64 ][\n   typedef _W64 signed int   intptr_t;\n   typedef _W64 unsigned int uintptr_t;\n#endif // _WIN64 ]\n\n// 7.18.1.5 Greatest-width integer types\ntypedef int64_t   intmax_t;\ntypedef uint64_t  uintmax_t;\n\n\n// 7.18.2 Limits of specified-width integer types\n\n#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [   See footnote 220 at page 257 and footnote 221 at page 259\n\n// 7.18.2.1 Limits of exact-width integer types\n#define INT8_MIN     ((int8_t)_I8_MIN)\n#define INT8_MAX     _I8_MAX\n#define INT16_MIN    ((int16_t)_I16_MIN)\n#define INT16_MAX    _I16_MAX\n#define INT32_MIN    ((int32_t)_I32_MIN)\n#define INT32_MAX    _I32_MAX\n#define INT64_MIN    ((int64_t)_I64_MIN)\n#define INT64_MAX    _I64_MAX\n#define UINT8_MAX    _UI8_MAX\n#define UINT16_MAX   _UI16_MAX\n#define UINT32_MAX   _UI32_MAX\n#define UINT64_MAX   _UI64_MAX\n\n// 7.18.2.2 Limits of minimum-width integer types\n#define INT_LEAST8_MIN    INT8_MIN\n#define INT_LEAST8_MAX    INT8_MAX\n#define INT_LEAST16_MIN   INT16_MIN\n#define INT_LEAST16_MAX   INT16_MAX\n#define INT_LEAST32_MIN   INT32_MIN\n#define INT_LEAST32_MAX   INT32_MAX\n#define INT_LEAST64_MIN   INT64_MIN\n#define INT_LEAST64_MAX   INT64_MAX\n#define UINT_LEAST8_MAX   UINT8_MAX\n#define UINT_LEAST16_MAX  UINT16_MAX\n#define UINT_LEAST32_MAX  UINT32_MAX\n#define UINT_LEAST64_MAX  UINT64_MAX\n\n// 7.18.2.3 Limits of fastest minimum-width integer types\n#define INT_FAST8_MIN    INT8_MIN\n#define INT_FAST8_MAX    INT8_MAX\n#define INT_FAST16_MIN   INT16_MIN\n#define INT_FAST16_MAX   INT16_MAX\n#define INT_FAST32_MIN   INT32_MIN\n#define INT_FAST32_MAX   INT32_MAX\n#define INT_FAST64_MIN   INT64_MIN\n#define INT_FAST64_MAX   INT64_MAX\n#define UINT_FAST8_MAX   UINT8_MAX\n#define UINT_FAST16_MAX  UINT16_MAX\n#define UINT_FAST32_MAX  UINT32_MAX\n#define UINT_FAST64_MAX  UINT64_MAX\n\n// 7.18.2.4 Limits of integer types capable of holding object pointers\n#ifdef _WIN64 // [\n#  define INTPTR_MIN   INT64_MIN\n#  define INTPTR_MAX   INT64_MAX\n#  define UINTPTR_MAX  UINT64_MAX\n#else // _WIN64 ][\n#  define INTPTR_MIN   INT32_MIN\n#  define INTPTR_MAX   INT32_MAX\n#  define UINTPTR_MAX  UINT32_MAX\n#endif // _WIN64 ]\n\n// 7.18.2.5 Limits of greatest-width integer types\n#define INTMAX_MIN   INT64_MIN\n#define INTMAX_MAX   INT64_MAX\n#define UINTMAX_MAX  UINT64_MAX\n\n// 7.18.3 Limits of other integer types\n\n#ifdef _WIN64 // [\n#  define PTRDIFF_MIN  _I64_MIN\n#  define PTRDIFF_MAX  _I64_MAX\n#else  // _WIN64 ][\n#  define PTRDIFF_MIN  _I32_MIN\n#  define PTRDIFF_MAX  _I32_MAX\n#endif  // _WIN64 ]\n\n#define SIG_ATOMIC_MIN  INT_MIN\n#define SIG_ATOMIC_MAX  INT_MAX\n\n#ifndef SIZE_MAX // [\n#  ifdef _WIN64 // [\n#     define SIZE_MAX  _UI64_MAX\n#  else // _WIN64 ][\n#     define SIZE_MAX  _UI32_MAX\n#  endif // _WIN64 ]\n#endif // SIZE_MAX ]\n\n// WCHAR_MIN and WCHAR_MAX are also defined in <wchar.h>\n#ifndef WCHAR_MIN // [\n#  define WCHAR_MIN  0\n#endif  // WCHAR_MIN ]\n#ifndef WCHAR_MAX // [\n#  define WCHAR_MAX  _UI16_MAX\n#endif  // WCHAR_MAX ]\n\n#define WINT_MIN  0\n#define WINT_MAX  _UI16_MAX\n\n#endif // __STDC_LIMIT_MACROS ]\n\n\n// 7.18.4 Limits of other integer types\n\n#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [   See footnote 224 at page 260\n\n// 7.18.4.1 Macros for minimum-width integer constants\n\n#define INT8_C(val)  val##i8\n#define INT16_C(val) val##i16\n#define INT32_C(val) val##i32\n#define INT64_C(val) val##i64\n\n#define UINT8_C(val)  val##ui8\n#define UINT16_C(val) val##ui16\n#define UINT32_C(val) val##ui32\n#define UINT64_C(val) val##ui64\n\n// 7.18.4.2 Macros for greatest-width integer constants\n#define INTMAX_C   INT64_C\n#define UINTMAX_C  UINT64_C\n\n#endif // __STDC_CONSTANT_MACROS ]\n\n\n#endif // _MSC_STDINT_H_ ]\n"
  },
  {
    "path": "deps/jemalloc/include/msvc_compat/strings.h",
    "content": "#ifndef strings_h\n#define strings_h\n\n/* MSVC doesn't define ffs/ffsl. This dummy strings.h header is provided\n * for both */\n#include <intrin.h>\n#pragma intrinsic(_BitScanForward)\nstatic __forceinline int ffsl(long x)\n{\n\tunsigned long i;\n\n\tif (_BitScanForward(&i, x))\n\t\treturn (i + 1);\n\treturn (0);\n}\n\nstatic __forceinline int ffs(int x)\n{\n\n\treturn (ffsl(x));\n}\n\n#endif\n"
  },
  {
    "path": "deps/jemalloc/install-sh",
    "content": "#! /bin/sh\n#\n# install - install a program, script, or datafile\n# This comes from X11R5 (mit/util/scripts/install.sh).\n#\n# Copyright 1991 by the Massachusetts Institute of Technology\n#\n# Permission to use, copy, modify, distribute, and sell this software and its\n# documentation for any purpose is hereby granted without fee, provided that\n# the above copyright notice appear in all copies and that both that\n# copyright notice and this permission notice appear in supporting\n# documentation, and that the name of M.I.T. not be used in advertising or\n# publicity pertaining to distribution of the software without specific,\n# written prior permission.  M.I.T. makes no representations about the\n# suitability of this software for any purpose.  It is provided \"as is\"\n# without express or implied warranty.\n#\n# Calling this script install-sh is preferred over install.sh, to prevent\n# `make' implicit rules from creating a file called install from it\n# when there is no Makefile.\n#\n# This script is compatible with the BSD install script, but was written\n# from scratch.  It can only install one file at a time, a restriction\n# shared with many OS's install programs.\n\n\n# set DOITPROG to echo to test this script\n\n# Don't use :- since 4.3BSD and earlier shells don't like it.\ndoit=\"${DOITPROG-}\"\n\n\n# put in absolute paths if you don't have them in your path; or use env. vars.\n\nmvprog=\"${MVPROG-mv}\"\ncpprog=\"${CPPROG-cp}\"\nchmodprog=\"${CHMODPROG-chmod}\"\nchownprog=\"${CHOWNPROG-chown}\"\nchgrpprog=\"${CHGRPPROG-chgrp}\"\nstripprog=\"${STRIPPROG-strip}\"\nrmprog=\"${RMPROG-rm}\"\nmkdirprog=\"${MKDIRPROG-mkdir}\"\n\ntransformbasename=\"\"\ntransform_arg=\"\"\ninstcmd=\"$mvprog\"\nchmodcmd=\"$chmodprog 0755\"\nchowncmd=\"\"\nchgrpcmd=\"\"\nstripcmd=\"\"\nrmcmd=\"$rmprog -f\"\nmvcmd=\"$mvprog\"\nsrc=\"\"\ndst=\"\"\ndir_arg=\"\"\n\nwhile [ x\"$1\" != x ]; do\n    case $1 in\n\t-c) instcmd=\"$cpprog\"\n\t    shift\n\t    continue;;\n\n\t-d) dir_arg=true\n\t    shift\n\t    continue;;\n\n\t-m) chmodcmd=\"$chmodprog $2\"\n\t    shift\n\t    shift\n\t    continue;;\n\n\t-o) chowncmd=\"$chownprog $2\"\n\t    shift\n\t    shift\n\t    continue;;\n\n\t-g) chgrpcmd=\"$chgrpprog $2\"\n\t    shift\n\t    shift\n\t    continue;;\n\n\t-s) stripcmd=\"$stripprog\"\n\t    shift\n\t    continue;;\n\n\t-t=*) transformarg=`echo $1 | sed 's/-t=//'`\n\t    shift\n\t    continue;;\n\n\t-b=*) transformbasename=`echo $1 | sed 's/-b=//'`\n\t    shift\n\t    continue;;\n\n\t*)  if [ x\"$src\" = x ]\n\t    then\n\t\tsrc=$1\n\t    else\n\t\t# this colon is to work around a 386BSD /bin/sh bug\n\t\t:\n\t\tdst=$1\n\t    fi\n\t    shift\n\t    continue;;\n    esac\ndone\n\nif [ x\"$src\" = x ]\nthen\n\techo \"install:\tno input file specified\"\n\texit 1\nelse\n\ttrue\nfi\n\nif [ x\"$dir_arg\" != x ]; then\n\tdst=$src\n\tsrc=\"\"\n\t\n\tif [ -d $dst ]; then\n\t\tinstcmd=:\n\telse\n\t\tinstcmd=mkdir\n\tfi\nelse\n\n# Waiting for this to be detected by the \"$instcmd $src $dsttmp\" command\n# might cause directories to be created, which would be especially bad \n# if $src (and thus $dsttmp) contains '*'.\n\n\tif [ -f $src -o -d $src ]\n\tthen\n\t\ttrue\n\telse\n\t\techo \"install:  $src does not exist\"\n\t\texit 1\n\tfi\n\t\n\tif [ x\"$dst\" = x ]\n\tthen\n\t\techo \"install:\tno destination specified\"\n\t\texit 1\n\telse\n\t\ttrue\n\tfi\n\n# If destination is a directory, append the input filename; if your system\n# does not like double slashes in filenames, you may need to add some logic\n\n\tif [ -d $dst ]\n\tthen\n\t\tdst=\"$dst\"/`basename $src`\n\telse\n\t\ttrue\n\tfi\nfi\n\n## this sed command emulates the dirname command\ndstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`\n\n# Make sure that the destination directory exists.\n#  this part is taken from Noah Friedman's mkinstalldirs script\n\n# Skip lots of stat calls in the usual case.\nif [ ! -d \"$dstdir\" ]; then\ndefaultIFS='\t\n'\nIFS=\"${IFS-${defaultIFS}}\"\n\noIFS=\"${IFS}\"\n# Some sh's can't handle IFS=/ for some reason.\nIFS='%'\nset - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`\nIFS=\"${oIFS}\"\n\npathcomp=''\n\nwhile [ $# -ne 0 ] ; do\n\tpathcomp=\"${pathcomp}${1}\"\n\tshift\n\n\tif [ ! -d \"${pathcomp}\" ] ;\n        then\n\t\t$mkdirprog \"${pathcomp}\"\n\telse\n\t\ttrue\n\tfi\n\n\tpathcomp=\"${pathcomp}/\"\ndone\nfi\n\nif [ x\"$dir_arg\" != x ]\nthen\n\t$doit $instcmd $dst &&\n\n\tif [ x\"$chowncmd\" != x ]; then $doit $chowncmd $dst; else true ; fi &&\n\tif [ x\"$chgrpcmd\" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&\n\tif [ x\"$stripcmd\" != x ]; then $doit $stripcmd $dst; else true ; fi &&\n\tif [ x\"$chmodcmd\" != x ]; then $doit $chmodcmd $dst; else true ; fi\nelse\n\n# If we're going to rename the final executable, determine the name now.\n\n\tif [ x\"$transformarg\" = x ] \n\tthen\n\t\tdstfile=`basename $dst`\n\telse\n\t\tdstfile=`basename $dst $transformbasename | \n\t\t\tsed $transformarg`$transformbasename\n\tfi\n\n# don't allow the sed command to completely eliminate the filename\n\n\tif [ x\"$dstfile\" = x ] \n\tthen\n\t\tdstfile=`basename $dst`\n\telse\n\t\ttrue\n\tfi\n\n# Make a temp file name in the proper directory.\n\n\tdsttmp=$dstdir/#inst.$$#\n\n# Move or copy the file name to the temp name\n\n\t$doit $instcmd $src $dsttmp &&\n\n\ttrap \"rm -f ${dsttmp}\" 0 &&\n\n# and set any options; do chmod last to preserve setuid bits\n\n# If any of these fail, we abort the whole thing.  If we want to\n# ignore errors from any of these, just make sure not to ignore\n# errors from the above \"$doit $instcmd $src $dsttmp\" command.\n\n\tif [ x\"$chowncmd\" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&\n\tif [ x\"$chgrpcmd\" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&\n\tif [ x\"$stripcmd\" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&\n\tif [ x\"$chmodcmd\" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&\n\n# Now rename the file to the real destination.\n\n\t$doit $rmcmd -f $dstdir/$dstfile &&\n\t$doit $mvcmd $dsttmp $dstdir/$dstfile \n\nfi &&\n\n\nexit 0\n"
  },
  {
    "path": "deps/jemalloc/src/arena.c",
    "content": "#define\tJEMALLOC_ARENA_C_\n#include \"jemalloc/internal/jemalloc_internal.h\"\n\n/******************************************************************************/\n/* Data. */\n\nssize_t\t\topt_lg_dirty_mult = LG_DIRTY_MULT_DEFAULT;\narena_bin_info_t\tarena_bin_info[NBINS];\n\nJEMALLOC_ALIGNED(CACHELINE)\nconst uint8_t\tsmall_size2bin[] = {\n#define\tS2B_8(i)\ti,\n#define\tS2B_16(i)\tS2B_8(i) S2B_8(i)\n#define\tS2B_32(i)\tS2B_16(i) S2B_16(i)\n#define\tS2B_64(i)\tS2B_32(i) S2B_32(i)\n#define\tS2B_128(i)\tS2B_64(i) S2B_64(i)\n#define\tS2B_256(i)\tS2B_128(i) S2B_128(i)\n#define\tS2B_512(i)\tS2B_256(i) S2B_256(i)\n#define\tS2B_1024(i)\tS2B_512(i) S2B_512(i)\n#define\tS2B_2048(i)\tS2B_1024(i) S2B_1024(i)\n#define\tS2B_4096(i)\tS2B_2048(i) S2B_2048(i)\n#define\tS2B_8192(i)\tS2B_4096(i) S2B_4096(i)\n#define\tSIZE_CLASS(bin, delta, size)\t\t\t\t\t\\\n\tS2B_##delta(bin)\n\tSIZE_CLASSES\n#undef S2B_8\n#undef S2B_16\n#undef S2B_32\n#undef S2B_64\n#undef S2B_128\n#undef S2B_256\n#undef S2B_512\n#undef S2B_1024\n#undef S2B_2048\n#undef S2B_4096\n#undef S2B_8192\n#undef SIZE_CLASS\n};\n\n/******************************************************************************/\n/* Function prototypes for non-inline static functions. */\n\nstatic void\tarena_avail_insert(arena_t *arena, arena_chunk_t *chunk,\n    size_t pageind, size_t npages, bool maybe_adjac_pred,\n    bool maybe_adjac_succ);\nstatic void\tarena_avail_remove(arena_t *arena, arena_chunk_t *chunk,\n    size_t pageind, size_t npages, bool maybe_adjac_pred,\n    bool maybe_adjac_succ);\nstatic void\tarena_run_split(arena_t *arena, arena_run_t *run, size_t size,\n    bool large, size_t binind, bool zero);\nstatic arena_chunk_t *arena_chunk_alloc(arena_t *arena);\nstatic void\tarena_chunk_dealloc(arena_t *arena, arena_chunk_t *chunk);\nstatic arena_run_t\t*arena_run_alloc_helper(arena_t *arena, size_t size,\n    bool large, size_t binind, bool zero);\nstatic arena_run_t *arena_run_alloc(arena_t *arena, size_t size, bool large,\n    size_t binind, bool zero);\nstatic arena_chunk_t\t*chunks_dirty_iter_cb(arena_chunk_tree_t *tree,\n    arena_chunk_t *chunk, void *arg);\nstatic void\tarena_purge(arena_t *arena, bool all);\nstatic void\tarena_run_dalloc(arena_t *arena, arena_run_t *run, bool dirty,\n    bool cleaned);\nstatic void\tarena_run_trim_head(arena_t *arena, arena_chunk_t *chunk,\n    arena_run_t *run, size_t oldsize, size_t newsize);\nstatic void\tarena_run_trim_tail(arena_t *arena, arena_chunk_t *chunk,\n    arena_run_t *run, size_t oldsize, size_t newsize, bool dirty);\nstatic arena_run_t\t*arena_bin_runs_first(arena_bin_t *bin);\nstatic void\tarena_bin_runs_insert(arena_bin_t *bin, arena_run_t *run);\nstatic void\tarena_bin_runs_remove(arena_bin_t *bin, arena_run_t *run);\nstatic arena_run_t *arena_bin_nonfull_run_tryget(arena_bin_t *bin);\nstatic arena_run_t *arena_bin_nonfull_run_get(arena_t *arena, arena_bin_t *bin);\nstatic void\t*arena_bin_malloc_hard(arena_t *arena, arena_bin_t *bin);\nstatic void\tarena_dissociate_bin_run(arena_chunk_t *chunk, arena_run_t *run,\n    arena_bin_t *bin);\nstatic void\tarena_dalloc_bin_run(arena_t *arena, arena_chunk_t *chunk,\n    arena_run_t *run, arena_bin_t *bin);\nstatic void\tarena_bin_lower_run(arena_t *arena, arena_chunk_t *chunk,\n    arena_run_t *run, arena_bin_t *bin);\nstatic void\tarena_ralloc_large_shrink(arena_t *arena, arena_chunk_t *chunk,\n    void *ptr, size_t oldsize, size_t size);\nstatic bool\tarena_ralloc_large_grow(arena_t *arena, arena_chunk_t *chunk,\n    void *ptr, size_t oldsize, size_t size, size_t extra, bool zero);\nstatic bool\tarena_ralloc_large(void *ptr, size_t oldsize, size_t size,\n    size_t extra, bool zero);\nstatic size_t\tbin_info_run_size_calc(arena_bin_info_t *bin_info,\n    size_t min_run_size);\nstatic void\tbin_info_init(void);\n\n/******************************************************************************/\n\nstatic inline int\narena_run_comp(arena_chunk_map_t *a, arena_chunk_map_t *b)\n{\n\tuintptr_t a_mapelm = (uintptr_t)a;\n\tuintptr_t b_mapelm = (uintptr_t)b;\n\n\tassert(a != NULL);\n\tassert(b != NULL);\n\n\treturn ((a_mapelm > b_mapelm) - (a_mapelm < b_mapelm));\n}\n\n/* Generate red-black tree functions. */\nrb_gen(static UNUSED, arena_run_tree_, arena_run_tree_t, arena_chunk_map_t,\n    u.rb_link, arena_run_comp)\n\nstatic inline int\narena_avail_comp(arena_chunk_map_t *a, arena_chunk_map_t *b)\n{\n\tint ret;\n\tsize_t a_size = a->bits & ~PAGE_MASK;\n\tsize_t b_size = b->bits & ~PAGE_MASK;\n\n\tret = (a_size > b_size) - (a_size < b_size);\n\tif (ret == 0) {\n\t\tuintptr_t a_mapelm, b_mapelm;\n\n\t\tif ((a->bits & CHUNK_MAP_KEY) != CHUNK_MAP_KEY)\n\t\t\ta_mapelm = (uintptr_t)a;\n\t\telse {\n\t\t\t/*\n\t\t\t * Treat keys as though they are lower than anything\n\t\t\t * else.\n\t\t\t */\n\t\t\ta_mapelm = 0;\n\t\t}\n\t\tb_mapelm = (uintptr_t)b;\n\n\t\tret = (a_mapelm > b_mapelm) - (a_mapelm < b_mapelm);\n\t}\n\n\treturn (ret);\n}\n\n/* Generate red-black tree functions. */\nrb_gen(static UNUSED, arena_avail_tree_, arena_avail_tree_t, arena_chunk_map_t,\n    u.rb_link, arena_avail_comp)\n\nstatic inline int\narena_chunk_dirty_comp(arena_chunk_t *a, arena_chunk_t *b)\n{\n\n\tassert(a != NULL);\n\tassert(b != NULL);\n\n\t/*\n\t * Short-circuit for self comparison.  The following comparison code\n\t * would come to the same result, but at the cost of executing the slow\n\t * path.\n\t */\n\tif (a == b)\n\t\treturn (0);\n\n\t/*\n\t * Order such that chunks with higher fragmentation are \"less than\"\n\t * those with lower fragmentation -- purging order is from \"least\" to\n\t * \"greatest\".  Fragmentation is measured as:\n\t *\n\t *     mean current avail run size\n\t *   --------------------------------\n\t *   mean defragmented avail run size\n\t *\n\t *            navail\n\t *         -----------\n\t *         nruns_avail           nruns_avail-nruns_adjac\n\t * = ========================= = -----------------------\n\t *            navail                  nruns_avail\n\t *    -----------------------\n\t *    nruns_avail-nruns_adjac\n\t *\n\t * The following code multiplies away the denominator prior to\n\t * comparison, in order to avoid division.\n\t *\n\t */\n\t{\n\t\tsize_t a_val = (a->nruns_avail - a->nruns_adjac) *\n\t\t    b->nruns_avail;\n\t\tsize_t b_val = (b->nruns_avail - b->nruns_adjac) *\n\t\t    a->nruns_avail;\n\n\t\tif (a_val < b_val)\n\t\t\treturn (1);\n\t\tif (a_val > b_val)\n\t\t\treturn (-1);\n\t}\n\t/*\n\t * Break ties by chunk address.  For fragmented chunks, report lower\n\t * addresses as \"lower\", so that fragmentation reduction happens first\n\t * at lower addresses.  However, use the opposite ordering for\n\t * unfragmented chunks, in order to increase the chances of\n\t * re-allocating dirty runs.\n\t */\n\t{\n\t\tuintptr_t a_chunk = (uintptr_t)a;\n\t\tuintptr_t b_chunk = (uintptr_t)b;\n\t\tint ret = ((a_chunk > b_chunk) - (a_chunk < b_chunk));\n\t\tif (a->nruns_adjac == 0) {\n\t\t\tassert(b->nruns_adjac == 0);\n\t\t\tret = -ret;\n\t\t}\n\t\treturn (ret);\n\t}\n}\n\n/* Generate red-black tree functions. */\nrb_gen(static UNUSED, arena_chunk_dirty_, arena_chunk_tree_t, arena_chunk_t,\n    dirty_link, arena_chunk_dirty_comp)\n\nstatic inline bool\narena_avail_adjac_pred(arena_chunk_t *chunk, size_t pageind)\n{\n\tbool ret;\n\n\tif (pageind-1 < map_bias)\n\t\tret = false;\n\telse {\n\t\tret = (arena_mapbits_allocated_get(chunk, pageind-1) == 0);\n\t\tassert(ret == false || arena_mapbits_dirty_get(chunk,\n\t\t    pageind-1) != arena_mapbits_dirty_get(chunk, pageind));\n\t}\n\treturn (ret);\n}\n\nstatic inline bool\narena_avail_adjac_succ(arena_chunk_t *chunk, size_t pageind, size_t npages)\n{\n\tbool ret;\n\n\tif (pageind+npages == chunk_npages)\n\t\tret = false;\n\telse {\n\t\tassert(pageind+npages < chunk_npages);\n\t\tret = (arena_mapbits_allocated_get(chunk, pageind+npages) == 0);\n\t\tassert(ret == false || arena_mapbits_dirty_get(chunk, pageind)\n\t\t    != arena_mapbits_dirty_get(chunk, pageind+npages));\n\t}\n\treturn (ret);\n}\n\nstatic inline bool\narena_avail_adjac(arena_chunk_t *chunk, size_t pageind, size_t npages)\n{\n\n\treturn (arena_avail_adjac_pred(chunk, pageind) ||\n\t    arena_avail_adjac_succ(chunk, pageind, npages));\n}\n\nstatic void\narena_avail_insert(arena_t *arena, arena_chunk_t *chunk, size_t pageind,\n    size_t npages, bool maybe_adjac_pred, bool maybe_adjac_succ)\n{\n\n\tassert(npages == (arena_mapbits_unallocated_size_get(chunk, pageind) >>\n\t    LG_PAGE));\n\n\t/*\n\t * chunks_dirty is keyed by nruns_{avail,adjac}, so the chunk must be\n\t * removed and reinserted even if the run to be inserted is clean.\n\t */\n\tif (chunk->ndirty != 0)\n\t\tarena_chunk_dirty_remove(&arena->chunks_dirty, chunk);\n\n\tif (maybe_adjac_pred && arena_avail_adjac_pred(chunk, pageind))\n\t\tchunk->nruns_adjac++;\n\tif (maybe_adjac_succ && arena_avail_adjac_succ(chunk, pageind, npages))\n\t\tchunk->nruns_adjac++;\n\tchunk->nruns_avail++;\n\tassert(chunk->nruns_avail > chunk->nruns_adjac);\n\n\tif (arena_mapbits_dirty_get(chunk, pageind) != 0) {\n\t\tarena->ndirty += npages;\n\t\tchunk->ndirty += npages;\n\t}\n\tif (chunk->ndirty != 0)\n\t\tarena_chunk_dirty_insert(&arena->chunks_dirty, chunk);\n\n\tarena_avail_tree_insert(&arena->runs_avail, arena_mapp_get(chunk,\n\t    pageind));\n}\n\nstatic void\narena_avail_remove(arena_t *arena, arena_chunk_t *chunk, size_t pageind,\n    size_t npages, bool maybe_adjac_pred, bool maybe_adjac_succ)\n{\n\n\tassert(npages == (arena_mapbits_unallocated_size_get(chunk, pageind) >>\n\t    LG_PAGE));\n\n\t/*\n\t * chunks_dirty is keyed by nruns_{avail,adjac}, so the chunk must be\n\t * removed and reinserted even if the run to be removed is clean.\n\t */\n\tif (chunk->ndirty != 0)\n\t\tarena_chunk_dirty_remove(&arena->chunks_dirty, chunk);\n\n\tif (maybe_adjac_pred && arena_avail_adjac_pred(chunk, pageind))\n\t\tchunk->nruns_adjac--;\n\tif (maybe_adjac_succ && arena_avail_adjac_succ(chunk, pageind, npages))\n\t\tchunk->nruns_adjac--;\n\tchunk->nruns_avail--;\n\tassert(chunk->nruns_avail > chunk->nruns_adjac || (chunk->nruns_avail\n\t    == 0 && chunk->nruns_adjac == 0));\n\n\tif (arena_mapbits_dirty_get(chunk, pageind) != 0) {\n\t\tarena->ndirty -= npages;\n\t\tchunk->ndirty -= npages;\n\t}\n\tif (chunk->ndirty != 0)\n\t\tarena_chunk_dirty_insert(&arena->chunks_dirty, chunk);\n\n\tarena_avail_tree_remove(&arena->runs_avail, arena_mapp_get(chunk,\n\t    pageind));\n}\n\nstatic inline void *\narena_run_reg_alloc(arena_run_t *run, arena_bin_info_t *bin_info)\n{\n\tvoid *ret;\n\tunsigned regind;\n\tbitmap_t *bitmap = (bitmap_t *)((uintptr_t)run +\n\t    (uintptr_t)bin_info->bitmap_offset);\n\n\tassert(run->nfree > 0);\n\tassert(bitmap_full(bitmap, &bin_info->bitmap_info) == false);\n\n\tregind = bitmap_sfu(bitmap, &bin_info->bitmap_info);\n\tret = (void *)((uintptr_t)run + (uintptr_t)bin_info->reg0_offset +\n\t    (uintptr_t)(bin_info->reg_interval * regind));\n\trun->nfree--;\n\tif (regind == run->nextind)\n\t\trun->nextind++;\n\tassert(regind < run->nextind);\n\treturn (ret);\n}\n\nstatic inline void\narena_run_reg_dalloc(arena_run_t *run, void *ptr)\n{\n\tarena_chunk_t *chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(run);\n\tsize_t pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> LG_PAGE;\n\tsize_t mapbits = arena_mapbits_get(chunk, pageind);\n\tsize_t binind = arena_ptr_small_binind_get(ptr, mapbits);\n\tarena_bin_info_t *bin_info = &arena_bin_info[binind];\n\tunsigned regind = arena_run_regind(run, bin_info, ptr);\n\tbitmap_t *bitmap = (bitmap_t *)((uintptr_t)run +\n\t    (uintptr_t)bin_info->bitmap_offset);\n\n\tassert(run->nfree < bin_info->nregs);\n\t/* Freeing an interior pointer can cause assertion failure. */\n\tassert(((uintptr_t)ptr - ((uintptr_t)run +\n\t    (uintptr_t)bin_info->reg0_offset)) %\n\t    (uintptr_t)bin_info->reg_interval == 0);\n\tassert((uintptr_t)ptr >= (uintptr_t)run +\n\t    (uintptr_t)bin_info->reg0_offset);\n\t/* Freeing an unallocated pointer can cause assertion failure. */\n\tassert(bitmap_get(bitmap, &bin_info->bitmap_info, regind));\n\n\tbitmap_unset(bitmap, &bin_info->bitmap_info, regind);\n\trun->nfree++;\n}\n\nstatic inline void\narena_chunk_validate_zeroed(arena_chunk_t *chunk, size_t run_ind)\n{\n\tsize_t i;\n\tUNUSED size_t *p = (size_t *)((uintptr_t)chunk + (run_ind << LG_PAGE));\n\n\tfor (i = 0; i < PAGE / sizeof(size_t); i++)\n\t\tassert(p[i] == 0);\n}\n\nstatic void\narena_run_split(arena_t *arena, arena_run_t *run, size_t size, bool large,\n    size_t binind, bool zero)\n{\n\tarena_chunk_t *chunk;\n\tsize_t run_ind, total_pages, need_pages, rem_pages, i;\n\tsize_t flag_dirty;\n\n\tassert((large && binind == BININD_INVALID) || (large == false && binind\n\t    != BININD_INVALID));\n\n\tchunk = (arena_chunk_t *)CHUNK_ADDR2BASE(run);\n\trun_ind = (unsigned)(((uintptr_t)run - (uintptr_t)chunk) >> LG_PAGE);\n\tflag_dirty = arena_mapbits_dirty_get(chunk, run_ind);\n\ttotal_pages = arena_mapbits_unallocated_size_get(chunk, run_ind) >>\n\t    LG_PAGE;\n\tassert(arena_mapbits_dirty_get(chunk, run_ind+total_pages-1) ==\n\t    flag_dirty);\n\tneed_pages = (size >> LG_PAGE);\n\tassert(need_pages > 0);\n\tassert(need_pages <= total_pages);\n\trem_pages = total_pages - need_pages;\n\n\tarena_avail_remove(arena, chunk, run_ind, total_pages, true, true);\n\tif (config_stats) {\n\t\t/*\n\t\t * Update stats_cactive if nactive is crossing a chunk\n\t\t * multiple.\n\t\t */\n\t\tsize_t cactive_diff = CHUNK_CEILING((arena->nactive +\n\t\t    need_pages) << LG_PAGE) - CHUNK_CEILING(arena->nactive <<\n\t\t    LG_PAGE);\n\t\tif (cactive_diff != 0)\n\t\t\tstats_cactive_add(cactive_diff);\n\t}\n\tarena->nactive += need_pages;\n\n\t/* Keep track of trailing unused pages for later use. */\n\tif (rem_pages > 0) {\n\t\tif (flag_dirty != 0) {\n\t\t\tarena_mapbits_unallocated_set(chunk, run_ind+need_pages,\n\t\t\t    (rem_pages << LG_PAGE), CHUNK_MAP_DIRTY);\n\t\t\tarena_mapbits_unallocated_set(chunk,\n\t\t\t    run_ind+total_pages-1, (rem_pages << LG_PAGE),\n\t\t\t    CHUNK_MAP_DIRTY);\n\t\t} else {\n\t\t\tarena_mapbits_unallocated_set(chunk, run_ind+need_pages,\n\t\t\t    (rem_pages << LG_PAGE),\n\t\t\t    arena_mapbits_unzeroed_get(chunk,\n\t\t\t    run_ind+need_pages));\n\t\t\tarena_mapbits_unallocated_set(chunk,\n\t\t\t    run_ind+total_pages-1, (rem_pages << LG_PAGE),\n\t\t\t    arena_mapbits_unzeroed_get(chunk,\n\t\t\t    run_ind+total_pages-1));\n\t\t}\n\t\tarena_avail_insert(arena, chunk, run_ind+need_pages, rem_pages,\n\t\t    false, true);\n\t}\n\n\t/*\n\t * Update the page map separately for large vs. small runs, since it is\n\t * possible to avoid iteration for large mallocs.\n\t */\n\tif (large) {\n\t\tif (zero) {\n\t\t\tif (flag_dirty == 0) {\n\t\t\t\t/*\n\t\t\t\t * The run is clean, so some pages may be\n\t\t\t\t * zeroed (i.e. never before touched).\n\t\t\t\t */\n\t\t\t\tfor (i = 0; i < need_pages; i++) {\n\t\t\t\t\tif (arena_mapbits_unzeroed_get(chunk,\n\t\t\t\t\t    run_ind+i) != 0) {\n\t\t\t\t\t\tVALGRIND_MAKE_MEM_UNDEFINED(\n\t\t\t\t\t\t    (void *)((uintptr_t)\n\t\t\t\t\t\t    chunk + ((run_ind+i) <<\n\t\t\t\t\t\t    LG_PAGE)), PAGE);\n\t\t\t\t\t\tmemset((void *)((uintptr_t)\n\t\t\t\t\t\t    chunk + ((run_ind+i) <<\n\t\t\t\t\t\t    LG_PAGE)), 0, PAGE);\n\t\t\t\t\t} else if (config_debug) {\n\t\t\t\t\t\tVALGRIND_MAKE_MEM_DEFINED(\n\t\t\t\t\t\t    (void *)((uintptr_t)\n\t\t\t\t\t\t    chunk + ((run_ind+i) <<\n\t\t\t\t\t\t    LG_PAGE)), PAGE);\n\t\t\t\t\t\tarena_chunk_validate_zeroed(\n\t\t\t\t\t\t    chunk, run_ind+i);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t/*\n\t\t\t\t * The run is dirty, so all pages must be\n\t\t\t\t * zeroed.\n\t\t\t\t */\n\t\t\t\tVALGRIND_MAKE_MEM_UNDEFINED((void\n\t\t\t\t    *)((uintptr_t)chunk + (run_ind <<\n\t\t\t\t    LG_PAGE)), (need_pages << LG_PAGE));\n\t\t\t\tmemset((void *)((uintptr_t)chunk + (run_ind <<\n\t\t\t\t    LG_PAGE)), 0, (need_pages << LG_PAGE));\n\t\t\t}\n\t\t}\n\n\t\t/*\n\t\t * Set the last element first, in case the run only contains one\n\t\t * page (i.e. both statements set the same element).\n\t\t */\n\t\tarena_mapbits_large_set(chunk, run_ind+need_pages-1, 0,\n\t\t    flag_dirty);\n\t\tarena_mapbits_large_set(chunk, run_ind, size, flag_dirty);\n\t} else {\n\t\tassert(zero == false);\n\t\t/*\n\t\t * Propagate the dirty and unzeroed flags to the allocated\n\t\t * small run, so that arena_dalloc_bin_run() has the ability to\n\t\t * conditionally trim clean pages.\n\t\t */\n\t\tarena_mapbits_small_set(chunk, run_ind, 0, binind, flag_dirty);\n\t\t/*\n\t\t * The first page will always be dirtied during small run\n\t\t * initialization, so a validation failure here would not\n\t\t * actually cause an observable failure.\n\t\t */\n\t\tif (config_debug && flag_dirty == 0 &&\n\t\t    arena_mapbits_unzeroed_get(chunk, run_ind) == 0)\n\t\t\tarena_chunk_validate_zeroed(chunk, run_ind);\n\t\tfor (i = 1; i < need_pages - 1; i++) {\n\t\t\tarena_mapbits_small_set(chunk, run_ind+i, i, binind, 0);\n\t\t\tif (config_debug && flag_dirty == 0 &&\n\t\t\t    arena_mapbits_unzeroed_get(chunk, run_ind+i) == 0)\n\t\t\t\tarena_chunk_validate_zeroed(chunk, run_ind+i);\n\t\t}\n\t\tarena_mapbits_small_set(chunk, run_ind+need_pages-1,\n\t\t    need_pages-1, binind, flag_dirty);\n\t\tif (config_debug && flag_dirty == 0 &&\n\t\t    arena_mapbits_unzeroed_get(chunk, run_ind+need_pages-1) ==\n\t\t    0) {\n\t\t\tarena_chunk_validate_zeroed(chunk,\n\t\t\t    run_ind+need_pages-1);\n\t\t}\n\t}\n}\n\nstatic arena_chunk_t *\narena_chunk_alloc(arena_t *arena)\n{\n\tarena_chunk_t *chunk;\n\tsize_t i;\n\n\tif (arena->spare != NULL) {\n\t\tchunk = arena->spare;\n\t\tarena->spare = NULL;\n\n\t\tassert(arena_mapbits_allocated_get(chunk, map_bias) == 0);\n\t\tassert(arena_mapbits_allocated_get(chunk, chunk_npages-1) == 0);\n\t\tassert(arena_mapbits_unallocated_size_get(chunk, map_bias) ==\n\t\t    arena_maxclass);\n\t\tassert(arena_mapbits_unallocated_size_get(chunk,\n\t\t    chunk_npages-1) == arena_maxclass);\n\t\tassert(arena_mapbits_dirty_get(chunk, map_bias) ==\n\t\t    arena_mapbits_dirty_get(chunk, chunk_npages-1));\n\t} else {\n\t\tbool zero;\n\t\tsize_t unzeroed;\n\n\t\tzero = false;\n\t\tmalloc_mutex_unlock(&arena->lock);\n\t\tchunk = (arena_chunk_t *)chunk_alloc(chunksize, chunksize,\n\t\t    false, &zero, arena->dss_prec);\n\t\tmalloc_mutex_lock(&arena->lock);\n\t\tif (chunk == NULL)\n\t\t\treturn (NULL);\n\t\tif (config_stats)\n\t\t\tarena->stats.mapped += chunksize;\n\n\t\tchunk->arena = arena;\n\n\t\t/*\n\t\t * Claim that no pages are in use, since the header is merely\n\t\t * overhead.\n\t\t */\n\t\tchunk->ndirty = 0;\n\n\t\tchunk->nruns_avail = 0;\n\t\tchunk->nruns_adjac = 0;\n\n\t\t/*\n\t\t * Initialize the map to contain one maximal free untouched run.\n\t\t * Mark the pages as zeroed iff chunk_alloc() returned a zeroed\n\t\t * chunk.\n\t\t */\n\t\tunzeroed = zero ? 0 : CHUNK_MAP_UNZEROED;\n\t\tarena_mapbits_unallocated_set(chunk, map_bias, arena_maxclass,\n\t\t    unzeroed);\n\t\t/*\n\t\t * There is no need to initialize the internal page map entries\n\t\t * unless the chunk is not zeroed.\n\t\t */\n\t\tif (zero == false) {\n\t\t\tfor (i = map_bias+1; i < chunk_npages-1; i++)\n\t\t\t\tarena_mapbits_unzeroed_set(chunk, i, unzeroed);\n\t\t} else if (config_debug) {\n\t\t\tfor (i = map_bias+1; i < chunk_npages-1; i++) {\n\t\t\t\tassert(arena_mapbits_unzeroed_get(chunk, i) ==\n\t\t\t\t    unzeroed);\n\t\t\t}\n\t\t}\n\t\tarena_mapbits_unallocated_set(chunk, chunk_npages-1,\n\t\t    arena_maxclass, unzeroed);\n\t}\n\n\t/* Insert the run into the runs_avail tree. */\n\tarena_avail_insert(arena, chunk, map_bias, chunk_npages-map_bias,\n\t    false, false);\n\n\treturn (chunk);\n}\n\nstatic void\narena_chunk_dealloc(arena_t *arena, arena_chunk_t *chunk)\n{\n\tassert(arena_mapbits_allocated_get(chunk, map_bias) == 0);\n\tassert(arena_mapbits_allocated_get(chunk, chunk_npages-1) == 0);\n\tassert(arena_mapbits_unallocated_size_get(chunk, map_bias) ==\n\t    arena_maxclass);\n\tassert(arena_mapbits_unallocated_size_get(chunk, chunk_npages-1) ==\n\t    arena_maxclass);\n\tassert(arena_mapbits_dirty_get(chunk, map_bias) ==\n\t    arena_mapbits_dirty_get(chunk, chunk_npages-1));\n\n\t/*\n\t * Remove run from the runs_avail tree, so that the arena does not use\n\t * it.\n\t */\n\tarena_avail_remove(arena, chunk, map_bias, chunk_npages-map_bias,\n\t    false, false);\n\n\tif (arena->spare != NULL) {\n\t\tarena_chunk_t *spare = arena->spare;\n\n\t\tarena->spare = chunk;\n\t\tmalloc_mutex_unlock(&arena->lock);\n\t\tchunk_dealloc((void *)spare, chunksize, true);\n\t\tmalloc_mutex_lock(&arena->lock);\n\t\tif (config_stats)\n\t\t\tarena->stats.mapped -= chunksize;\n\t} else\n\t\tarena->spare = chunk;\n}\n\nstatic arena_run_t *\narena_run_alloc_helper(arena_t *arena, size_t size, bool large, size_t binind,\n    bool zero)\n{\n\tarena_run_t *run;\n\tarena_chunk_map_t *mapelm, key;\n\n\tkey.bits = size | CHUNK_MAP_KEY;\n\tmapelm = arena_avail_tree_nsearch(&arena->runs_avail, &key);\n\tif (mapelm != NULL) {\n\t\tarena_chunk_t *run_chunk = CHUNK_ADDR2BASE(mapelm);\n\t\tsize_t pageind = (((uintptr_t)mapelm -\n\t\t    (uintptr_t)run_chunk->map) / sizeof(arena_chunk_map_t))\n\t\t    + map_bias;\n\n\t\trun = (arena_run_t *)((uintptr_t)run_chunk + (pageind <<\n\t\t    LG_PAGE));\n\t\tarena_run_split(arena, run, size, large, binind, zero);\n\t\treturn (run);\n\t}\n\n\treturn (NULL);\n}\n\nstatic arena_run_t *\narena_run_alloc(arena_t *arena, size_t size, bool large, size_t binind,\n    bool zero)\n{\n\tarena_chunk_t *chunk;\n\tarena_run_t *run;\n\n\tassert(size <= arena_maxclass);\n\tassert((size & PAGE_MASK) == 0);\n\tassert((large && binind == BININD_INVALID) || (large == false && binind\n\t    != BININD_INVALID));\n\n\t/* Search the arena's chunks for the lowest best fit. */\n\trun = arena_run_alloc_helper(arena, size, large, binind, zero);\n\tif (run != NULL)\n\t\treturn (run);\n\n\t/*\n\t * No usable runs.  Create a new chunk from which to allocate the run.\n\t */\n\tchunk = arena_chunk_alloc(arena);\n\tif (chunk != NULL) {\n\t\trun = (arena_run_t *)((uintptr_t)chunk + (map_bias << LG_PAGE));\n\t\tarena_run_split(arena, run, size, large, binind, zero);\n\t\treturn (run);\n\t}\n\n\t/*\n\t * arena_chunk_alloc() failed, but another thread may have made\n\t * sufficient memory available while this one dropped arena->lock in\n\t * arena_chunk_alloc(), so search one more time.\n\t */\n\treturn (arena_run_alloc_helper(arena, size, large, binind, zero));\n}\n\nstatic inline void\narena_maybe_purge(arena_t *arena)\n{\n\tsize_t npurgeable, threshold;\n\n\t/* Don't purge if the option is disabled. */\n\tif (opt_lg_dirty_mult < 0)\n\t\treturn;\n\t/* Don't purge if all dirty pages are already being purged. */\n\tif (arena->ndirty <= arena->npurgatory)\n\t\treturn;\n\tnpurgeable = arena->ndirty - arena->npurgatory;\n\tthreshold = (arena->nactive >> opt_lg_dirty_mult);\n\t/*\n\t * Don't purge unless the number of purgeable pages exceeds the\n\t * threshold.\n\t */\n\tif (npurgeable <= threshold)\n\t\treturn;\n\n\tarena_purge(arena, false);\n}\n\nstatic inline size_t\narena_chunk_purge(arena_t *arena, arena_chunk_t *chunk, bool all)\n{\n\tsize_t npurged;\n\tql_head(arena_chunk_map_t) mapelms;\n\tarena_chunk_map_t *mapelm;\n\tsize_t pageind, npages;\n\tsize_t nmadvise;\n\n\tql_new(&mapelms);\n\n\t/*\n\t * If chunk is the spare, temporarily re-allocate it, 1) so that its\n\t * run is reinserted into runs_avail, and 2) so that it cannot be\n\t * completely discarded by another thread while arena->lock is dropped\n\t * by this thread.  Note that the arena_run_dalloc() call will\n\t * implicitly deallocate the chunk, so no explicit action is required\n\t * in this function to deallocate the chunk.\n\t *\n\t * Note that once a chunk contains dirty pages, it cannot again contain\n\t * a single run unless 1) it is a dirty run, or 2) this function purges\n\t * dirty pages and causes the transition to a single clean run.  Thus\n\t * (chunk == arena->spare) is possible, but it is not possible for\n\t * this function to be called on the spare unless it contains a dirty\n\t * run.\n\t */\n\tif (chunk == arena->spare) {\n\t\tassert(arena_mapbits_dirty_get(chunk, map_bias) != 0);\n\t\tassert(arena_mapbits_dirty_get(chunk, chunk_npages-1) != 0);\n\n\t\tarena_chunk_alloc(arena);\n\t}\n\n\tif (config_stats)\n\t\tarena->stats.purged += chunk->ndirty;\n\n\t/*\n\t * Operate on all dirty runs if there is no clean/dirty run\n\t * fragmentation.\n\t */\n\tif (chunk->nruns_adjac == 0)\n\t\tall = true;\n\n\t/*\n\t * Temporarily allocate free dirty runs within chunk.  If all is false,\n\t * only operate on dirty runs that are fragments; otherwise operate on\n\t * all dirty runs.\n\t */\n\tfor (pageind = map_bias; pageind < chunk_npages; pageind += npages) {\n\t\tmapelm = arena_mapp_get(chunk, pageind);\n\t\tif (arena_mapbits_allocated_get(chunk, pageind) == 0) {\n\t\t\tsize_t run_size =\n\t\t\t    arena_mapbits_unallocated_size_get(chunk, pageind);\n\n\t\t\tnpages = run_size >> LG_PAGE;\n\t\t\tassert(pageind + npages <= chunk_npages);\n\t\t\tassert(arena_mapbits_dirty_get(chunk, pageind) ==\n\t\t\t    arena_mapbits_dirty_get(chunk, pageind+npages-1));\n\n\t\t\tif (arena_mapbits_dirty_get(chunk, pageind) != 0 &&\n\t\t\t    (all || arena_avail_adjac(chunk, pageind,\n\t\t\t    npages))) {\n\t\t\t\tarena_run_t *run = (arena_run_t *)((uintptr_t)\n\t\t\t\t    chunk + (uintptr_t)(pageind << LG_PAGE));\n\n\t\t\t\tarena_run_split(arena, run, run_size, true,\n\t\t\t\t    BININD_INVALID, false);\n\t\t\t\t/* Append to list for later processing. */\n\t\t\t\tql_elm_new(mapelm, u.ql_link);\n\t\t\t\tql_tail_insert(&mapelms, mapelm, u.ql_link);\n\t\t\t}\n\t\t} else {\n\t\t\t/* Skip run. */\n\t\t\tif (arena_mapbits_large_get(chunk, pageind) != 0) {\n\t\t\t\tnpages = arena_mapbits_large_size_get(chunk,\n\t\t\t\t    pageind) >> LG_PAGE;\n\t\t\t} else {\n\t\t\t\tsize_t binind;\n\t\t\t\tarena_bin_info_t *bin_info;\n\t\t\t\tarena_run_t *run = (arena_run_t *)((uintptr_t)\n\t\t\t\t    chunk + (uintptr_t)(pageind << LG_PAGE));\n\n\t\t\t\tassert(arena_mapbits_small_runind_get(chunk,\n\t\t\t\t    pageind) == 0);\n\t\t\t\tbinind = arena_bin_index(arena, run->bin);\n\t\t\t\tbin_info = &arena_bin_info[binind];\n\t\t\t\tnpages = bin_info->run_size >> LG_PAGE;\n\t\t\t}\n\t\t}\n\t}\n\tassert(pageind == chunk_npages);\n\tassert(chunk->ndirty == 0 || all == false);\n\tassert(chunk->nruns_adjac == 0);\n\n\tmalloc_mutex_unlock(&arena->lock);\n\tif (config_stats)\n\t\tnmadvise = 0;\n\tnpurged = 0;\n\tql_foreach(mapelm, &mapelms, u.ql_link) {\n\t\tbool unzeroed;\n\t\tsize_t flag_unzeroed, i;\n\n\t\tpageind = (((uintptr_t)mapelm - (uintptr_t)chunk->map) /\n\t\t    sizeof(arena_chunk_map_t)) + map_bias;\n\t\tnpages = arena_mapbits_large_size_get(chunk, pageind) >>\n\t\t    LG_PAGE;\n\t\tassert(pageind + npages <= chunk_npages);\n\t\tunzeroed = pages_purge((void *)((uintptr_t)chunk + (pageind <<\n\t\t    LG_PAGE)), (npages << LG_PAGE));\n\t\tflag_unzeroed = unzeroed ? CHUNK_MAP_UNZEROED : 0;\n\t\t/*\n\t\t * Set the unzeroed flag for all pages, now that pages_purge()\n\t\t * has returned whether the pages were zeroed as a side effect\n\t\t * of purging.  This chunk map modification is safe even though\n\t\t * the arena mutex isn't currently owned by this thread,\n\t\t * because the run is marked as allocated, thus protecting it\n\t\t * from being modified by any other thread.  As long as these\n\t\t * writes don't perturb the first and last elements'\n\t\t * CHUNK_MAP_ALLOCATED bits, behavior is well defined.\n\t\t */\n\t\tfor (i = 0; i < npages; i++) {\n\t\t\tarena_mapbits_unzeroed_set(chunk, pageind+i,\n\t\t\t    flag_unzeroed);\n\t\t}\n\t\tnpurged += npages;\n\t\tif (config_stats)\n\t\t\tnmadvise++;\n\t}\n\tmalloc_mutex_lock(&arena->lock);\n\tif (config_stats)\n\t\tarena->stats.nmadvise += nmadvise;\n\n\t/* Deallocate runs. */\n\tfor (mapelm = ql_first(&mapelms); mapelm != NULL;\n\t    mapelm = ql_first(&mapelms)) {\n\t\tarena_run_t *run;\n\n\t\tpageind = (((uintptr_t)mapelm - (uintptr_t)chunk->map) /\n\t\t    sizeof(arena_chunk_map_t)) + map_bias;\n\t\trun = (arena_run_t *)((uintptr_t)chunk + (uintptr_t)(pageind <<\n\t\t    LG_PAGE));\n\t\tql_remove(&mapelms, mapelm, u.ql_link);\n\t\tarena_run_dalloc(arena, run, false, true);\n\t}\n\n\treturn (npurged);\n}\n\nstatic arena_chunk_t *\nchunks_dirty_iter_cb(arena_chunk_tree_t *tree, arena_chunk_t *chunk, void *arg)\n{\n       size_t *ndirty = (size_t *)arg;\n\n       assert(chunk->ndirty != 0);\n       *ndirty += chunk->ndirty;\n       return (NULL);\n}\n\nstatic void\narena_purge(arena_t *arena, bool all)\n{\n\tarena_chunk_t *chunk;\n\tsize_t npurgatory;\n\tif (config_debug) {\n\t\tsize_t ndirty = 0;\n\n\t\tarena_chunk_dirty_iter(&arena->chunks_dirty, NULL,\n\t\t    chunks_dirty_iter_cb, (void *)&ndirty);\n\t\tassert(ndirty == arena->ndirty);\n\t}\n\tassert(arena->ndirty > arena->npurgatory || all);\n\tassert((arena->nactive >> opt_lg_dirty_mult) < (arena->ndirty -\n\t    arena->npurgatory) || all);\n\n\tif (config_stats)\n\t\tarena->stats.npurge++;\n\n\t/*\n\t * Compute the minimum number of pages that this thread should try to\n\t * purge, and add the result to arena->npurgatory.  This will keep\n\t * multiple threads from racing to reduce ndirty below the threshold.\n\t */\n\t{\n\t\tsize_t npurgeable = arena->ndirty - arena->npurgatory;\n\n\t\tif (all == false) {\n\t\t\tsize_t threshold = (arena->nactive >>\n\t\t\t    opt_lg_dirty_mult);\n\n\t\t\tnpurgatory = npurgeable - threshold;\n\t\t} else\n\t\t\tnpurgatory = npurgeable;\n\t}\n\tarena->npurgatory += npurgatory;\n\n\twhile (npurgatory > 0) {\n\t\tsize_t npurgeable, npurged, nunpurged;\n\n\t\t/* Get next chunk with dirty pages. */\n\t\tchunk = arena_chunk_dirty_first(&arena->chunks_dirty);\n\t\tif (chunk == NULL) {\n\t\t\t/*\n\t\t\t * This thread was unable to purge as many pages as\n\t\t\t * originally intended, due to races with other threads\n\t\t\t * that either did some of the purging work, or re-used\n\t\t\t * dirty pages.\n\t\t\t */\n\t\t\tarena->npurgatory -= npurgatory;\n\t\t\treturn;\n\t\t}\n\t\tnpurgeable = chunk->ndirty;\n\t\tassert(npurgeable != 0);\n\n\t\tif (npurgeable > npurgatory && chunk->nruns_adjac == 0) {\n\t\t\t/*\n\t\t\t * This thread will purge all the dirty pages in chunk,\n\t\t\t * so set npurgatory to reflect this thread's intent to\n\t\t\t * purge the pages.  This tends to reduce the chances\n\t\t\t * of the following scenario:\n\t\t\t *\n\t\t\t * 1) This thread sets arena->npurgatory such that\n\t\t\t *    (arena->ndirty - arena->npurgatory) is at the\n\t\t\t *    threshold.\n\t\t\t * 2) This thread drops arena->lock.\n\t\t\t * 3) Another thread causes one or more pages to be\n\t\t\t *    dirtied, and immediately determines that it must\n\t\t\t *    purge dirty pages.\n\t\t\t *\n\t\t\t * If this scenario *does* play out, that's okay,\n\t\t\t * because all of the purging work being done really\n\t\t\t * needs to happen.\n\t\t\t */\n\t\t\tarena->npurgatory += npurgeable - npurgatory;\n\t\t\tnpurgatory = npurgeable;\n\t\t}\n\n\t\t/*\n\t\t * Keep track of how many pages are purgeable, versus how many\n\t\t * actually get purged, and adjust counters accordingly.\n\t\t */\n\t\tarena->npurgatory -= npurgeable;\n\t\tnpurgatory -= npurgeable;\n\t\tnpurged = arena_chunk_purge(arena, chunk, all);\n\t\tnunpurged = npurgeable - npurged;\n\t\tarena->npurgatory += nunpurged;\n\t\tnpurgatory += nunpurged;\n\t}\n}\n\nvoid\narena_purge_all(arena_t *arena)\n{\n\n\tmalloc_mutex_lock(&arena->lock);\n\tarena_purge(arena, true);\n\tmalloc_mutex_unlock(&arena->lock);\n}\n\nstatic void\narena_run_dalloc(arena_t *arena, arena_run_t *run, bool dirty, bool cleaned)\n{\n\tarena_chunk_t *chunk;\n\tsize_t size, run_ind, run_pages, flag_dirty;\n\n\tchunk = (arena_chunk_t *)CHUNK_ADDR2BASE(run);\n\trun_ind = (size_t)(((uintptr_t)run - (uintptr_t)chunk) >> LG_PAGE);\n\tassert(run_ind >= map_bias);\n\tassert(run_ind < chunk_npages);\n\tif (arena_mapbits_large_get(chunk, run_ind) != 0) {\n\t\tsize = arena_mapbits_large_size_get(chunk, run_ind);\n\t\tassert(size == PAGE ||\n\t\t    arena_mapbits_large_size_get(chunk,\n\t\t    run_ind+(size>>LG_PAGE)-1) == 0);\n\t} else {\n\t\tsize_t binind = arena_bin_index(arena, run->bin);\n\t\tarena_bin_info_t *bin_info = &arena_bin_info[binind];\n\t\tsize = bin_info->run_size;\n\t}\n\trun_pages = (size >> LG_PAGE);\n\tif (config_stats) {\n\t\t/*\n\t\t * Update stats_cactive if nactive is crossing a chunk\n\t\t * multiple.\n\t\t */\n\t\tsize_t cactive_diff = CHUNK_CEILING(arena->nactive << LG_PAGE) -\n\t\t    CHUNK_CEILING((arena->nactive - run_pages) << LG_PAGE);\n\t\tif (cactive_diff != 0)\n\t\t\tstats_cactive_sub(cactive_diff);\n\t}\n\tarena->nactive -= run_pages;\n\n\t/*\n\t * The run is dirty if the caller claims to have dirtied it, as well as\n\t * if it was already dirty before being allocated and the caller\n\t * doesn't claim to have cleaned it.\n\t */\n\tassert(arena_mapbits_dirty_get(chunk, run_ind) ==\n\t    arena_mapbits_dirty_get(chunk, run_ind+run_pages-1));\n\tif (cleaned == false && arena_mapbits_dirty_get(chunk, run_ind) != 0)\n\t\tdirty = true;\n\tflag_dirty = dirty ? CHUNK_MAP_DIRTY : 0;\n\n\t/* Mark pages as unallocated in the chunk map. */\n\tif (dirty) {\n\t\tarena_mapbits_unallocated_set(chunk, run_ind, size,\n\t\t    CHUNK_MAP_DIRTY);\n\t\tarena_mapbits_unallocated_set(chunk, run_ind+run_pages-1, size,\n\t\t    CHUNK_MAP_DIRTY);\n\t} else {\n\t\tarena_mapbits_unallocated_set(chunk, run_ind, size,\n\t\t    arena_mapbits_unzeroed_get(chunk, run_ind));\n\t\tarena_mapbits_unallocated_set(chunk, run_ind+run_pages-1, size,\n\t\t    arena_mapbits_unzeroed_get(chunk, run_ind+run_pages-1));\n\t}\n\n\t/* Try to coalesce forward. */\n\tif (run_ind + run_pages < chunk_npages &&\n\t    arena_mapbits_allocated_get(chunk, run_ind+run_pages) == 0 &&\n\t    arena_mapbits_dirty_get(chunk, run_ind+run_pages) == flag_dirty) {\n\t\tsize_t nrun_size = arena_mapbits_unallocated_size_get(chunk,\n\t\t    run_ind+run_pages);\n\t\tsize_t nrun_pages = nrun_size >> LG_PAGE;\n\n\t\t/*\n\t\t * Remove successor from runs_avail; the coalesced run is\n\t\t * inserted later.\n\t\t */\n\t\tassert(arena_mapbits_unallocated_size_get(chunk,\n\t\t    run_ind+run_pages+nrun_pages-1) == nrun_size);\n\t\tassert(arena_mapbits_dirty_get(chunk,\n\t\t    run_ind+run_pages+nrun_pages-1) == flag_dirty);\n\t\tarena_avail_remove(arena, chunk, run_ind+run_pages, nrun_pages,\n\t\t    false, true);\n\n\t\tsize += nrun_size;\n\t\trun_pages += nrun_pages;\n\n\t\tarena_mapbits_unallocated_size_set(chunk, run_ind, size);\n\t\tarena_mapbits_unallocated_size_set(chunk, run_ind+run_pages-1,\n\t\t    size);\n\t}\n\n\t/* Try to coalesce backward. */\n\tif (run_ind > map_bias && arena_mapbits_allocated_get(chunk, run_ind-1)\n\t    == 0 && arena_mapbits_dirty_get(chunk, run_ind-1) == flag_dirty) {\n\t\tsize_t prun_size = arena_mapbits_unallocated_size_get(chunk,\n\t\t    run_ind-1);\n\t\tsize_t prun_pages = prun_size >> LG_PAGE;\n\n\t\trun_ind -= prun_pages;\n\n\t\t/*\n\t\t * Remove predecessor from runs_avail; the coalesced run is\n\t\t * inserted later.\n\t\t */\n\t\tassert(arena_mapbits_unallocated_size_get(chunk, run_ind) ==\n\t\t    prun_size);\n\t\tassert(arena_mapbits_dirty_get(chunk, run_ind) == flag_dirty);\n\t\tarena_avail_remove(arena, chunk, run_ind, prun_pages, true,\n\t\t    false);\n\n\t\tsize += prun_size;\n\t\trun_pages += prun_pages;\n\n\t\tarena_mapbits_unallocated_size_set(chunk, run_ind, size);\n\t\tarena_mapbits_unallocated_size_set(chunk, run_ind+run_pages-1,\n\t\t    size);\n\t}\n\n\t/* Insert into runs_avail, now that coalescing is complete. */\n\tassert(arena_mapbits_unallocated_size_get(chunk, run_ind) ==\n\t    arena_mapbits_unallocated_size_get(chunk, run_ind+run_pages-1));\n\tassert(arena_mapbits_dirty_get(chunk, run_ind) ==\n\t    arena_mapbits_dirty_get(chunk, run_ind+run_pages-1));\n\tarena_avail_insert(arena, chunk, run_ind, run_pages, true, true);\n\n\t/* Deallocate chunk if it is now completely unused. */\n\tif (size == arena_maxclass) {\n\t\tassert(run_ind == map_bias);\n\t\tassert(run_pages == (arena_maxclass >> LG_PAGE));\n\t\tarena_chunk_dealloc(arena, chunk);\n\t}\n\n\t/*\n\t * It is okay to do dirty page processing here even if the chunk was\n\t * deallocated above, since in that case it is the spare.  Waiting\n\t * until after possible chunk deallocation to do dirty processing\n\t * allows for an old spare to be fully deallocated, thus decreasing the\n\t * chances of spuriously crossing the dirty page purging threshold.\n\t */\n\tif (dirty)\n\t\tarena_maybe_purge(arena);\n}\n\nstatic void\narena_run_trim_head(arena_t *arena, arena_chunk_t *chunk, arena_run_t *run,\n    size_t oldsize, size_t newsize)\n{\n\tsize_t pageind = ((uintptr_t)run - (uintptr_t)chunk) >> LG_PAGE;\n\tsize_t head_npages = (oldsize - newsize) >> LG_PAGE;\n\tsize_t flag_dirty = arena_mapbits_dirty_get(chunk, pageind);\n\n\tassert(oldsize > newsize);\n\n\t/*\n\t * Update the chunk map so that arena_run_dalloc() can treat the\n\t * leading run as separately allocated.  Set the last element of each\n\t * run first, in case of single-page runs.\n\t */\n\tassert(arena_mapbits_large_size_get(chunk, pageind) == oldsize);\n\tarena_mapbits_large_set(chunk, pageind+head_npages-1, 0, flag_dirty);\n\tarena_mapbits_large_set(chunk, pageind, oldsize-newsize, flag_dirty);\n\n\tif (config_debug) {\n\t\tUNUSED size_t tail_npages = newsize >> LG_PAGE;\n\t\tassert(arena_mapbits_large_size_get(chunk,\n\t\t    pageind+head_npages+tail_npages-1) == 0);\n\t\tassert(arena_mapbits_dirty_get(chunk,\n\t\t    pageind+head_npages+tail_npages-1) == flag_dirty);\n\t}\n\tarena_mapbits_large_set(chunk, pageind+head_npages, newsize,\n\t    flag_dirty);\n\n\tarena_run_dalloc(arena, run, false, false);\n}\n\nstatic void\narena_run_trim_tail(arena_t *arena, arena_chunk_t *chunk, arena_run_t *run,\n    size_t oldsize, size_t newsize, bool dirty)\n{\n\tsize_t pageind = ((uintptr_t)run - (uintptr_t)chunk) >> LG_PAGE;\n\tsize_t head_npages = newsize >> LG_PAGE;\n\tsize_t flag_dirty = arena_mapbits_dirty_get(chunk, pageind);\n\n\tassert(oldsize > newsize);\n\n\t/*\n\t * Update the chunk map so that arena_run_dalloc() can treat the\n\t * trailing run as separately allocated.  Set the last element of each\n\t * run first, in case of single-page runs.\n\t */\n\tassert(arena_mapbits_large_size_get(chunk, pageind) == oldsize);\n\tarena_mapbits_large_set(chunk, pageind+head_npages-1, 0, flag_dirty);\n\tarena_mapbits_large_set(chunk, pageind, newsize, flag_dirty);\n\n\tif (config_debug) {\n\t\tUNUSED size_t tail_npages = (oldsize - newsize) >> LG_PAGE;\n\t\tassert(arena_mapbits_large_size_get(chunk,\n\t\t    pageind+head_npages+tail_npages-1) == 0);\n\t\tassert(arena_mapbits_dirty_get(chunk,\n\t\t    pageind+head_npages+tail_npages-1) == flag_dirty);\n\t}\n\tarena_mapbits_large_set(chunk, pageind+head_npages, oldsize-newsize,\n\t    flag_dirty);\n\n\tarena_run_dalloc(arena, (arena_run_t *)((uintptr_t)run + newsize),\n\t    dirty, false);\n}\n\nstatic arena_run_t *\narena_bin_runs_first(arena_bin_t *bin)\n{\n\tarena_chunk_map_t *mapelm = arena_run_tree_first(&bin->runs);\n\tif (mapelm != NULL) {\n\t\tarena_chunk_t *chunk;\n\t\tsize_t pageind;\n\t\tarena_run_t *run;\n\n\t\tchunk = (arena_chunk_t *)CHUNK_ADDR2BASE(mapelm);\n\t\tpageind = ((((uintptr_t)mapelm - (uintptr_t)chunk->map) /\n\t\t    sizeof(arena_chunk_map_t))) + map_bias;\n\t\trun = (arena_run_t *)((uintptr_t)chunk + (uintptr_t)((pageind -\n\t\t    arena_mapbits_small_runind_get(chunk, pageind)) <<\n\t\t    LG_PAGE));\n\t\treturn (run);\n\t}\n\n\treturn (NULL);\n}\n\nstatic void\narena_bin_runs_insert(arena_bin_t *bin, arena_run_t *run)\n{\n\tarena_chunk_t *chunk = CHUNK_ADDR2BASE(run);\n\tsize_t pageind = ((uintptr_t)run - (uintptr_t)chunk) >> LG_PAGE;\n\tarena_chunk_map_t *mapelm = arena_mapp_get(chunk, pageind);\n\n\tassert(arena_run_tree_search(&bin->runs, mapelm) == NULL);\n\n\tarena_run_tree_insert(&bin->runs, mapelm);\n}\n\nstatic void\narena_bin_runs_remove(arena_bin_t *bin, arena_run_t *run)\n{\n\tarena_chunk_t *chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(run);\n\tsize_t pageind = ((uintptr_t)run - (uintptr_t)chunk) >> LG_PAGE;\n\tarena_chunk_map_t *mapelm = arena_mapp_get(chunk, pageind);\n\n\tassert(arena_run_tree_search(&bin->runs, mapelm) != NULL);\n\n\tarena_run_tree_remove(&bin->runs, mapelm);\n}\n\nstatic arena_run_t *\narena_bin_nonfull_run_tryget(arena_bin_t *bin)\n{\n\tarena_run_t *run = arena_bin_runs_first(bin);\n\tif (run != NULL) {\n\t\tarena_bin_runs_remove(bin, run);\n\t\tif (config_stats)\n\t\t\tbin->stats.reruns++;\n\t}\n\treturn (run);\n}\n\nstatic arena_run_t *\narena_bin_nonfull_run_get(arena_t *arena, arena_bin_t *bin)\n{\n\tarena_run_t *run;\n\tsize_t binind;\n\tarena_bin_info_t *bin_info;\n\n\t/* Look for a usable run. */\n\trun = arena_bin_nonfull_run_tryget(bin);\n\tif (run != NULL)\n\t\treturn (run);\n\t/* No existing runs have any space available. */\n\n\tbinind = arena_bin_index(arena, bin);\n\tbin_info = &arena_bin_info[binind];\n\n\t/* Allocate a new run. */\n\tmalloc_mutex_unlock(&bin->lock);\n\t/******************************/\n\tmalloc_mutex_lock(&arena->lock);\n\trun = arena_run_alloc(arena, bin_info->run_size, false, binind, false);\n\tif (run != NULL) {\n\t\tbitmap_t *bitmap = (bitmap_t *)((uintptr_t)run +\n\t\t    (uintptr_t)bin_info->bitmap_offset);\n\n\t\t/* Initialize run internals. */\n\t\tVALGRIND_MAKE_MEM_UNDEFINED(run, bin_info->reg0_offset -\n\t\t    bin_info->redzone_size);\n\t\trun->bin = bin;\n\t\trun->nextind = 0;\n\t\trun->nfree = bin_info->nregs;\n\t\tbitmap_init(bitmap, &bin_info->bitmap_info);\n\t}\n\tmalloc_mutex_unlock(&arena->lock);\n\t/********************************/\n\tmalloc_mutex_lock(&bin->lock);\n\tif (run != NULL) {\n\t\tif (config_stats) {\n\t\t\tbin->stats.nruns++;\n\t\t\tbin->stats.curruns++;\n\t\t}\n\t\treturn (run);\n\t}\n\n\t/*\n\t * arena_run_alloc() failed, but another thread may have made\n\t * sufficient memory available while this one dropped bin->lock above,\n\t * so search one more time.\n\t */\n\trun = arena_bin_nonfull_run_tryget(bin);\n\tif (run != NULL)\n\t\treturn (run);\n\n\treturn (NULL);\n}\n\n/* Re-fill bin->runcur, then call arena_run_reg_alloc(). */\nstatic void *\narena_bin_malloc_hard(arena_t *arena, arena_bin_t *bin)\n{\n\tvoid *ret;\n\tsize_t binind;\n\tarena_bin_info_t *bin_info;\n\tarena_run_t *run;\n\n\tbinind = arena_bin_index(arena, bin);\n\tbin_info = &arena_bin_info[binind];\n\tbin->runcur = NULL;\n\trun = arena_bin_nonfull_run_get(arena, bin);\n\tif (bin->runcur != NULL && bin->runcur->nfree > 0) {\n\t\t/*\n\t\t * Another thread updated runcur while this one ran without the\n\t\t * bin lock in arena_bin_nonfull_run_get().\n\t\t */\n\t\tassert(bin->runcur->nfree > 0);\n\t\tret = arena_run_reg_alloc(bin->runcur, bin_info);\n\t\tif (run != NULL) {\n\t\t\tarena_chunk_t *chunk;\n\n\t\t\t/*\n\t\t\t * arena_run_alloc() may have allocated run, or it may\n\t\t\t * have pulled run from the bin's run tree.  Therefore\n\t\t\t * it is unsafe to make any assumptions about how run\n\t\t\t * has previously been used, and arena_bin_lower_run()\n\t\t\t * must be called, as if a region were just deallocated\n\t\t\t * from the run.\n\t\t\t */\n\t\t\tchunk = (arena_chunk_t *)CHUNK_ADDR2BASE(run);\n\t\t\tif (run->nfree == bin_info->nregs)\n\t\t\t\tarena_dalloc_bin_run(arena, chunk, run, bin);\n\t\t\telse\n\t\t\t\tarena_bin_lower_run(arena, chunk, run, bin);\n\t\t}\n\t\treturn (ret);\n\t}\n\n\tif (run == NULL)\n\t\treturn (NULL);\n\n\tbin->runcur = run;\n\n\tassert(bin->runcur->nfree > 0);\n\n\treturn (arena_run_reg_alloc(bin->runcur, bin_info));\n}\n\nvoid\narena_prof_accum(arena_t *arena, uint64_t accumbytes)\n{\n\n\tcassert(config_prof);\n\n\tif (config_prof && prof_interval != 0) {\n\t\tarena->prof_accumbytes += accumbytes;\n\t\tif (arena->prof_accumbytes >= prof_interval) {\n\t\t\tprof_idump();\n\t\t\tarena->prof_accumbytes -= prof_interval;\n\t\t}\n\t}\n}\n\nvoid\narena_tcache_fill_small(arena_t *arena, tcache_bin_t *tbin, size_t binind,\n    uint64_t prof_accumbytes)\n{\n\tunsigned i, nfill;\n\tarena_bin_t *bin;\n\tarena_run_t *run;\n\tvoid *ptr;\n\n\tassert(tbin->ncached == 0);\n\n\tif (config_prof) {\n\t\tmalloc_mutex_lock(&arena->lock);\n\t\tarena_prof_accum(arena, prof_accumbytes);\n\t\tmalloc_mutex_unlock(&arena->lock);\n\t}\n\tbin = &arena->bins[binind];\n\tmalloc_mutex_lock(&bin->lock);\n\tfor (i = 0, nfill = (tcache_bin_info[binind].ncached_max >>\n\t    tbin->lg_fill_div); i < nfill; i++) {\n\t\tif ((run = bin->runcur) != NULL && run->nfree > 0)\n\t\t\tptr = arena_run_reg_alloc(run, &arena_bin_info[binind]);\n\t\telse\n\t\t\tptr = arena_bin_malloc_hard(arena, bin);\n\t\tif (ptr == NULL)\n\t\t\tbreak;\n\t\tif (config_fill && opt_junk) {\n\t\t\tarena_alloc_junk_small(ptr, &arena_bin_info[binind],\n\t\t\t    true);\n\t\t}\n\t\t/* Insert such that low regions get used first. */\n\t\ttbin->avail[nfill - 1 - i] = ptr;\n\t}\n\tif (config_stats) {\n\t\tbin->stats.allocated += i * arena_bin_info[binind].reg_size;\n\t\tbin->stats.nmalloc += i;\n\t\tbin->stats.nrequests += tbin->tstats.nrequests;\n\t\tbin->stats.nfills++;\n\t\ttbin->tstats.nrequests = 0;\n\t}\n\tmalloc_mutex_unlock(&bin->lock);\n\ttbin->ncached = i;\n}\n\nvoid\narena_alloc_junk_small(void *ptr, arena_bin_info_t *bin_info, bool zero)\n{\n\n\tif (zero) {\n\t\tsize_t redzone_size = bin_info->redzone_size;\n\t\tmemset((void *)((uintptr_t)ptr - redzone_size), 0xa5,\n\t\t    redzone_size);\n\t\tmemset((void *)((uintptr_t)ptr + bin_info->reg_size), 0xa5,\n\t\t    redzone_size);\n\t} else {\n\t\tmemset((void *)((uintptr_t)ptr - bin_info->redzone_size), 0xa5,\n\t\t    bin_info->reg_interval);\n\t}\n}\n\nvoid\narena_dalloc_junk_small(void *ptr, arena_bin_info_t *bin_info)\n{\n\tsize_t size = bin_info->reg_size;\n\tsize_t redzone_size = bin_info->redzone_size;\n\tsize_t i;\n\tbool error = false;\n\n\tfor (i = 1; i <= redzone_size; i++) {\n\t\tunsigned byte;\n\t\tif ((byte = *(uint8_t *)((uintptr_t)ptr - i)) != 0xa5) {\n\t\t\terror = true;\n\t\t\tmalloc_printf(\"<jemalloc>: Corrupt redzone \"\n\t\t\t    \"%zu byte%s before %p (size %zu), byte=%#x\\n\", i,\n\t\t\t    (i == 1) ? \"\" : \"s\", ptr, size, byte);\n\t\t}\n\t}\n\tfor (i = 0; i < redzone_size; i++) {\n\t\tunsigned byte;\n\t\tif ((byte = *(uint8_t *)((uintptr_t)ptr + size + i)) != 0xa5) {\n\t\t\terror = true;\n\t\t\tmalloc_printf(\"<jemalloc>: Corrupt redzone \"\n\t\t\t    \"%zu byte%s after end of %p (size %zu), byte=%#x\\n\",\n\t\t\t    i, (i == 1) ? \"\" : \"s\", ptr, size, byte);\n\t\t}\n\t}\n\tif (opt_abort && error)\n\t\tabort();\n\n\tmemset((void *)((uintptr_t)ptr - redzone_size), 0x5a,\n\t    bin_info->reg_interval);\n}\n\nvoid *\narena_malloc_small(arena_t *arena, size_t size, bool zero)\n{\n\tvoid *ret;\n\tarena_bin_t *bin;\n\tarena_run_t *run;\n\tsize_t binind;\n\n\tbinind = SMALL_SIZE2BIN(size);\n\tassert(binind < NBINS);\n\tbin = &arena->bins[binind];\n\tsize = arena_bin_info[binind].reg_size;\n\n\tmalloc_mutex_lock(&bin->lock);\n\tif ((run = bin->runcur) != NULL && run->nfree > 0)\n\t\tret = arena_run_reg_alloc(run, &arena_bin_info[binind]);\n\telse\n\t\tret = arena_bin_malloc_hard(arena, bin);\n\n\tif (ret == NULL) {\n\t\tmalloc_mutex_unlock(&bin->lock);\n\t\treturn (NULL);\n\t}\n\n\tif (config_stats) {\n\t\tbin->stats.allocated += size;\n\t\tbin->stats.nmalloc++;\n\t\tbin->stats.nrequests++;\n\t}\n\tmalloc_mutex_unlock(&bin->lock);\n\tif (config_prof && isthreaded == false) {\n\t\tmalloc_mutex_lock(&arena->lock);\n\t\tarena_prof_accum(arena, size);\n\t\tmalloc_mutex_unlock(&arena->lock);\n\t}\n\n\tif (zero == false) {\n\t\tif (config_fill) {\n\t\t\tif (opt_junk) {\n\t\t\t\tarena_alloc_junk_small(ret,\n\t\t\t\t    &arena_bin_info[binind], false);\n\t\t\t} else if (opt_zero)\n\t\t\t\tmemset(ret, 0, size);\n\t\t}\n\t} else {\n\t\tif (config_fill && opt_junk) {\n\t\t\tarena_alloc_junk_small(ret, &arena_bin_info[binind],\n\t\t\t    true);\n\t\t}\n\t\tVALGRIND_MAKE_MEM_UNDEFINED(ret, size);\n\t\tmemset(ret, 0, size);\n\t}\n\n\treturn (ret);\n}\n\nvoid *\narena_malloc_large(arena_t *arena, size_t size, bool zero)\n{\n\tvoid *ret;\n\n\t/* Large allocation. */\n\tsize = PAGE_CEILING(size);\n\tmalloc_mutex_lock(&arena->lock);\n\tret = (void *)arena_run_alloc(arena, size, true, BININD_INVALID, zero);\n\tif (ret == NULL) {\n\t\tmalloc_mutex_unlock(&arena->lock);\n\t\treturn (NULL);\n\t}\n\tif (config_stats) {\n\t\tarena->stats.nmalloc_large++;\n\t\tarena->stats.nrequests_large++;\n\t\tarena->stats.allocated_large += size;\n\t\tarena->stats.lstats[(size >> LG_PAGE) - 1].nmalloc++;\n\t\tarena->stats.lstats[(size >> LG_PAGE) - 1].nrequests++;\n\t\tarena->stats.lstats[(size >> LG_PAGE) - 1].curruns++;\n\t}\n\tif (config_prof)\n\t\tarena_prof_accum(arena, size);\n\tmalloc_mutex_unlock(&arena->lock);\n\n\tif (zero == false) {\n\t\tif (config_fill) {\n\t\t\tif (opt_junk)\n\t\t\t\tmemset(ret, 0xa5, size);\n\t\t\telse if (opt_zero)\n\t\t\t\tmemset(ret, 0, size);\n\t\t}\n\t}\n\n\treturn (ret);\n}\n\n/* Only handles large allocations that require more than page alignment. */\nvoid *\narena_palloc(arena_t *arena, size_t size, size_t alignment, bool zero)\n{\n\tvoid *ret;\n\tsize_t alloc_size, leadsize, trailsize;\n\tarena_run_t *run;\n\tarena_chunk_t *chunk;\n\n\tassert((size & PAGE_MASK) == 0);\n\n\talignment = PAGE_CEILING(alignment);\n\talloc_size = size + alignment - PAGE;\n\n\tmalloc_mutex_lock(&arena->lock);\n\trun = arena_run_alloc(arena, alloc_size, true, BININD_INVALID, zero);\n\tif (run == NULL) {\n\t\tmalloc_mutex_unlock(&arena->lock);\n\t\treturn (NULL);\n\t}\n\tchunk = (arena_chunk_t *)CHUNK_ADDR2BASE(run);\n\n\tleadsize = ALIGNMENT_CEILING((uintptr_t)run, alignment) -\n\t    (uintptr_t)run;\n\tassert(alloc_size >= leadsize + size);\n\ttrailsize = alloc_size - leadsize - size;\n\tret = (void *)((uintptr_t)run + leadsize);\n\tif (leadsize != 0) {\n\t\tarena_run_trim_head(arena, chunk, run, alloc_size, alloc_size -\n\t\t    leadsize);\n\t}\n\tif (trailsize != 0) {\n\t\tarena_run_trim_tail(arena, chunk, ret, size + trailsize, size,\n\t\t    false);\n\t}\n\n\tif (config_stats) {\n\t\tarena->stats.nmalloc_large++;\n\t\tarena->stats.nrequests_large++;\n\t\tarena->stats.allocated_large += size;\n\t\tarena->stats.lstats[(size >> LG_PAGE) - 1].nmalloc++;\n\t\tarena->stats.lstats[(size >> LG_PAGE) - 1].nrequests++;\n\t\tarena->stats.lstats[(size >> LG_PAGE) - 1].curruns++;\n\t}\n\tmalloc_mutex_unlock(&arena->lock);\n\n\tif (config_fill && zero == false) {\n\t\tif (opt_junk)\n\t\t\tmemset(ret, 0xa5, size);\n\t\telse if (opt_zero)\n\t\t\tmemset(ret, 0, size);\n\t}\n\treturn (ret);\n}\n\nvoid\narena_prof_promoted(const void *ptr, size_t size)\n{\n\tarena_chunk_t *chunk;\n\tsize_t pageind, binind;\n\n\tcassert(config_prof);\n\tassert(ptr != NULL);\n\tassert(CHUNK_ADDR2BASE(ptr) != ptr);\n\tassert(isalloc(ptr, false) == PAGE);\n\tassert(isalloc(ptr, true) == PAGE);\n\tassert(size <= SMALL_MAXCLASS);\n\n\tchunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);\n\tpageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> LG_PAGE;\n\tbinind = SMALL_SIZE2BIN(size);\n\tassert(binind < NBINS);\n\tarena_mapbits_large_binind_set(chunk, pageind, binind);\n\n\tassert(isalloc(ptr, false) == PAGE);\n\tassert(isalloc(ptr, true) == size);\n}\n\nstatic void\narena_dissociate_bin_run(arena_chunk_t *chunk, arena_run_t *run,\n    arena_bin_t *bin)\n{\n\n\t/* Dissociate run from bin. */\n\tif (run == bin->runcur)\n\t\tbin->runcur = NULL;\n\telse {\n\t\tsize_t binind = arena_bin_index(chunk->arena, bin);\n\t\tarena_bin_info_t *bin_info = &arena_bin_info[binind];\n\n\t\tif (bin_info->nregs != 1) {\n\t\t\t/*\n\t\t\t * This block's conditional is necessary because if the\n\t\t\t * run only contains one region, then it never gets\n\t\t\t * inserted into the non-full runs tree.\n\t\t\t */\n\t\t\tarena_bin_runs_remove(bin, run);\n\t\t}\n\t}\n}\n\nstatic void\narena_dalloc_bin_run(arena_t *arena, arena_chunk_t *chunk, arena_run_t *run,\n    arena_bin_t *bin)\n{\n\tsize_t binind;\n\tarena_bin_info_t *bin_info;\n\tsize_t npages, run_ind, past;\n\n\tassert(run != bin->runcur);\n\tassert(arena_run_tree_search(&bin->runs,\n\t    arena_mapp_get(chunk, ((uintptr_t)run-(uintptr_t)chunk)>>LG_PAGE))\n\t    == NULL);\n\n\tbinind = arena_bin_index(chunk->arena, run->bin);\n\tbin_info = &arena_bin_info[binind];\n\n\tmalloc_mutex_unlock(&bin->lock);\n\t/******************************/\n\tnpages = bin_info->run_size >> LG_PAGE;\n\trun_ind = (size_t)(((uintptr_t)run - (uintptr_t)chunk) >> LG_PAGE);\n\tpast = (size_t)(PAGE_CEILING((uintptr_t)run +\n\t    (uintptr_t)bin_info->reg0_offset + (uintptr_t)(run->nextind *\n\t    bin_info->reg_interval - bin_info->redzone_size) -\n\t    (uintptr_t)chunk) >> LG_PAGE);\n\tmalloc_mutex_lock(&arena->lock);\n\n\t/*\n\t * If the run was originally clean, and some pages were never touched,\n\t * trim the clean pages before deallocating the dirty portion of the\n\t * run.\n\t */\n\tassert(arena_mapbits_dirty_get(chunk, run_ind) ==\n\t    arena_mapbits_dirty_get(chunk, run_ind+npages-1));\n\tif (arena_mapbits_dirty_get(chunk, run_ind) == 0 && past - run_ind <\n\t    npages) {\n\t\t/* Trim clean pages.  Convert to large run beforehand. */\n\t\tassert(npages > 0);\n\t\tarena_mapbits_large_set(chunk, run_ind, bin_info->run_size, 0);\n\t\tarena_mapbits_large_set(chunk, run_ind+npages-1, 0, 0);\n\t\tarena_run_trim_tail(arena, chunk, run, (npages << LG_PAGE),\n\t\t    ((past - run_ind) << LG_PAGE), false);\n\t\t/* npages = past - run_ind; */\n\t}\n\tarena_run_dalloc(arena, run, true, false);\n\tmalloc_mutex_unlock(&arena->lock);\n\t/****************************/\n\tmalloc_mutex_lock(&bin->lock);\n\tif (config_stats)\n\t\tbin->stats.curruns--;\n}\n\nstatic void\narena_bin_lower_run(arena_t *arena, arena_chunk_t *chunk, arena_run_t *run,\n    arena_bin_t *bin)\n{\n\n\t/*\n\t * Make sure that if bin->runcur is non-NULL, it refers to the lowest\n\t * non-full run.  It is okay to NULL runcur out rather than proactively\n\t * keeping it pointing at the lowest non-full run.\n\t */\n\tif ((uintptr_t)run < (uintptr_t)bin->runcur) {\n\t\t/* Switch runcur. */\n\t\tif (bin->runcur->nfree > 0)\n\t\t\tarena_bin_runs_insert(bin, bin->runcur);\n\t\tbin->runcur = run;\n\t\tif (config_stats)\n\t\t\tbin->stats.reruns++;\n\t} else\n\t\tarena_bin_runs_insert(bin, run);\n}\n\nvoid\narena_dalloc_bin_locked(arena_t *arena, arena_chunk_t *chunk, void *ptr,\n    arena_chunk_map_t *mapelm)\n{\n\tsize_t pageind;\n\tarena_run_t *run;\n\tarena_bin_t *bin;\n\tarena_bin_info_t *bin_info;\n\tsize_t size, binind;\n\n\tpageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> LG_PAGE;\n\trun = (arena_run_t *)((uintptr_t)chunk + (uintptr_t)((pageind -\n\t    arena_mapbits_small_runind_get(chunk, pageind)) << LG_PAGE));\n\tbin = run->bin;\n\tbinind = arena_ptr_small_binind_get(ptr, mapelm->bits);\n\tbin_info = &arena_bin_info[binind];\n\tif (config_fill || config_stats)\n\t\tsize = bin_info->reg_size;\n\n\tif (config_fill && opt_junk)\n\t\tarena_dalloc_junk_small(ptr, bin_info);\n\n\tarena_run_reg_dalloc(run, ptr);\n\tif (run->nfree == bin_info->nregs) {\n\t\tarena_dissociate_bin_run(chunk, run, bin);\n\t\tarena_dalloc_bin_run(arena, chunk, run, bin);\n\t} else if (run->nfree == 1 && run != bin->runcur)\n\t\tarena_bin_lower_run(arena, chunk, run, bin);\n\n\tif (config_stats) {\n\t\tbin->stats.allocated -= size;\n\t\tbin->stats.ndalloc++;\n\t}\n}\n\nvoid\narena_dalloc_bin(arena_t *arena, arena_chunk_t *chunk, void *ptr,\n    size_t pageind, arena_chunk_map_t *mapelm)\n{\n\tarena_run_t *run;\n\tarena_bin_t *bin;\n\n\trun = (arena_run_t *)((uintptr_t)chunk + (uintptr_t)((pageind -\n\t    arena_mapbits_small_runind_get(chunk, pageind)) << LG_PAGE));\n\tbin = run->bin;\n\tmalloc_mutex_lock(&bin->lock);\n\tarena_dalloc_bin_locked(arena, chunk, ptr, mapelm);\n\tmalloc_mutex_unlock(&bin->lock);\n}\n\nvoid\narena_dalloc_small(arena_t *arena, arena_chunk_t *chunk, void *ptr,\n    size_t pageind)\n{\n\tarena_chunk_map_t *mapelm;\n\n\tif (config_debug) {\n\t\t/* arena_ptr_small_binind_get() does extra sanity checking. */\n\t\tassert(arena_ptr_small_binind_get(ptr, arena_mapbits_get(chunk,\n\t\t    pageind)) != BININD_INVALID);\n\t}\n\tmapelm = arena_mapp_get(chunk, pageind);\n\tarena_dalloc_bin(arena, chunk, ptr, pageind, mapelm);\n}\n\nvoid\narena_dalloc_large_locked(arena_t *arena, arena_chunk_t *chunk, void *ptr)\n{\n\n\tif (config_fill || config_stats) {\n\t\tsize_t pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> LG_PAGE;\n\t\tsize_t size = arena_mapbits_large_size_get(chunk, pageind);\n\n\t\tif (config_fill && config_stats && opt_junk)\n\t\t\tmemset(ptr, 0x5a, size);\n\t\tif (config_stats) {\n\t\t\tarena->stats.ndalloc_large++;\n\t\t\tarena->stats.allocated_large -= size;\n\t\t\tarena->stats.lstats[(size >> LG_PAGE) - 1].ndalloc++;\n\t\t\tarena->stats.lstats[(size >> LG_PAGE) - 1].curruns--;\n\t\t}\n\t}\n\n\tarena_run_dalloc(arena, (arena_run_t *)ptr, true, false);\n}\n\nvoid\narena_dalloc_large(arena_t *arena, arena_chunk_t *chunk, void *ptr)\n{\n\n\tmalloc_mutex_lock(&arena->lock);\n\tarena_dalloc_large_locked(arena, chunk, ptr);\n\tmalloc_mutex_unlock(&arena->lock);\n}\n\nstatic void\narena_ralloc_large_shrink(arena_t *arena, arena_chunk_t *chunk, void *ptr,\n    size_t oldsize, size_t size)\n{\n\n\tassert(size < oldsize);\n\n\t/*\n\t * Shrink the run, and make trailing pages available for other\n\t * allocations.\n\t */\n\tmalloc_mutex_lock(&arena->lock);\n\tarena_run_trim_tail(arena, chunk, (arena_run_t *)ptr, oldsize, size,\n\t    true);\n\tif (config_stats) {\n\t\tarena->stats.ndalloc_large++;\n\t\tarena->stats.allocated_large -= oldsize;\n\t\tarena->stats.lstats[(oldsize >> LG_PAGE) - 1].ndalloc++;\n\t\tarena->stats.lstats[(oldsize >> LG_PAGE) - 1].curruns--;\n\n\t\tarena->stats.nmalloc_large++;\n\t\tarena->stats.nrequests_large++;\n\t\tarena->stats.allocated_large += size;\n\t\tarena->stats.lstats[(size >> LG_PAGE) - 1].nmalloc++;\n\t\tarena->stats.lstats[(size >> LG_PAGE) - 1].nrequests++;\n\t\tarena->stats.lstats[(size >> LG_PAGE) - 1].curruns++;\n\t}\n\tmalloc_mutex_unlock(&arena->lock);\n}\n\nstatic bool\narena_ralloc_large_grow(arena_t *arena, arena_chunk_t *chunk, void *ptr,\n    size_t oldsize, size_t size, size_t extra, bool zero)\n{\n\tsize_t pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> LG_PAGE;\n\tsize_t npages = oldsize >> LG_PAGE;\n\tsize_t followsize;\n\n\tassert(oldsize == arena_mapbits_large_size_get(chunk, pageind));\n\n\t/* Try to extend the run. */\n\tassert(size + extra > oldsize);\n\tmalloc_mutex_lock(&arena->lock);\n\tif (pageind + npages < chunk_npages &&\n\t    arena_mapbits_allocated_get(chunk, pageind+npages) == 0 &&\n\t    (followsize = arena_mapbits_unallocated_size_get(chunk,\n\t    pageind+npages)) >= size - oldsize) {\n\t\t/*\n\t\t * The next run is available and sufficiently large.  Split the\n\t\t * following run, then merge the first part with the existing\n\t\t * allocation.\n\t\t */\n\t\tsize_t flag_dirty;\n\t\tsize_t splitsize = (oldsize + followsize <= size + extra)\n\t\t    ? followsize : size + extra - oldsize;\n\t\tarena_run_split(arena, (arena_run_t *)((uintptr_t)chunk +\n\t\t    ((pageind+npages) << LG_PAGE)), splitsize, true,\n\t\t    BININD_INVALID, zero);\n\n\t\tsize = oldsize + splitsize;\n\t\tnpages = size >> LG_PAGE;\n\n\t\t/*\n\t\t * Mark the extended run as dirty if either portion of the run\n\t\t * was dirty before allocation.  This is rather pedantic,\n\t\t * because there's not actually any sequence of events that\n\t\t * could cause the resulting run to be passed to\n\t\t * arena_run_dalloc() with the dirty argument set to false\n\t\t * (which is when dirty flag consistency would really matter).\n\t\t */\n\t\tflag_dirty = arena_mapbits_dirty_get(chunk, pageind) |\n\t\t    arena_mapbits_dirty_get(chunk, pageind+npages-1);\n\t\tarena_mapbits_large_set(chunk, pageind, size, flag_dirty);\n\t\tarena_mapbits_large_set(chunk, pageind+npages-1, 0, flag_dirty);\n\n\t\tif (config_stats) {\n\t\t\tarena->stats.ndalloc_large++;\n\t\t\tarena->stats.allocated_large -= oldsize;\n\t\t\tarena->stats.lstats[(oldsize >> LG_PAGE) - 1].ndalloc++;\n\t\t\tarena->stats.lstats[(oldsize >> LG_PAGE) - 1].curruns--;\n\n\t\t\tarena->stats.nmalloc_large++;\n\t\t\tarena->stats.nrequests_large++;\n\t\t\tarena->stats.allocated_large += size;\n\t\t\tarena->stats.lstats[(size >> LG_PAGE) - 1].nmalloc++;\n\t\t\tarena->stats.lstats[(size >> LG_PAGE) - 1].nrequests++;\n\t\t\tarena->stats.lstats[(size >> LG_PAGE) - 1].curruns++;\n\t\t}\n\t\tmalloc_mutex_unlock(&arena->lock);\n\t\treturn (false);\n\t}\n\tmalloc_mutex_unlock(&arena->lock);\n\n\treturn (true);\n}\n\n/*\n * Try to resize a large allocation, in order to avoid copying.  This will\n * always fail if growing an object, and the following run is already in use.\n */\nstatic bool\narena_ralloc_large(void *ptr, size_t oldsize, size_t size, size_t extra,\n    bool zero)\n{\n\tsize_t psize;\n\n\tpsize = PAGE_CEILING(size + extra);\n\tif (psize == oldsize) {\n\t\t/* Same size class. */\n\t\tif (config_fill && opt_junk && size < oldsize) {\n\t\t\tmemset((void *)((uintptr_t)ptr + size), 0x5a, oldsize -\n\t\t\t    size);\n\t\t}\n\t\treturn (false);\n\t} else {\n\t\tarena_chunk_t *chunk;\n\t\tarena_t *arena;\n\n\t\tchunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);\n\t\tarena = chunk->arena;\n\n\t\tif (psize < oldsize) {\n\t\t\t/* Fill before shrinking in order avoid a race. */\n\t\t\tif (config_fill && opt_junk) {\n\t\t\t\tmemset((void *)((uintptr_t)ptr + size), 0x5a,\n\t\t\t\t    oldsize - size);\n\t\t\t}\n\t\t\tarena_ralloc_large_shrink(arena, chunk, ptr, oldsize,\n\t\t\t    psize);\n\t\t\treturn (false);\n\t\t} else {\n\t\t\tbool ret = arena_ralloc_large_grow(arena, chunk, ptr,\n\t\t\t    oldsize, PAGE_CEILING(size),\n\t\t\t    psize - PAGE_CEILING(size), zero);\n\t\t\tif (config_fill && ret == false && zero == false &&\n\t\t\t    opt_zero) {\n\t\t\t\tmemset((void *)((uintptr_t)ptr + oldsize), 0,\n\t\t\t\t    size - oldsize);\n\t\t\t}\n\t\t\treturn (ret);\n\t\t}\n\t}\n}\n\nvoid *\narena_ralloc_no_move(void *ptr, size_t oldsize, size_t size, size_t extra,\n    bool zero)\n{\n\n\t/*\n\t * Avoid moving the allocation if the size class can be left the same.\n\t */\n\tif (oldsize <= arena_maxclass) {\n\t\tif (oldsize <= SMALL_MAXCLASS) {\n\t\t\tassert(arena_bin_info[SMALL_SIZE2BIN(oldsize)].reg_size\n\t\t\t    == oldsize);\n\t\t\tif ((size + extra <= SMALL_MAXCLASS &&\n\t\t\t    SMALL_SIZE2BIN(size + extra) ==\n\t\t\t    SMALL_SIZE2BIN(oldsize)) || (size <= oldsize &&\n\t\t\t    size + extra >= oldsize)) {\n\t\t\t\tif (config_fill && opt_junk && size < oldsize) {\n\t\t\t\t\tmemset((void *)((uintptr_t)ptr + size),\n\t\t\t\t\t    0x5a, oldsize - size);\n\t\t\t\t}\n\t\t\t\treturn (ptr);\n\t\t\t}\n\t\t} else {\n\t\t\tassert(size <= arena_maxclass);\n\t\t\tif (size + extra > SMALL_MAXCLASS) {\n\t\t\t\tif (arena_ralloc_large(ptr, oldsize, size,\n\t\t\t\t    extra, zero) == false)\n\t\t\t\t\treturn (ptr);\n\t\t\t}\n\t\t}\n\t}\n\n\t/* Reallocation would require a move. */\n\treturn (NULL);\n}\n\nvoid *\narena_ralloc(arena_t *arena, void *ptr, size_t oldsize, size_t size,\n    size_t extra, size_t alignment, bool zero, bool try_tcache_alloc,\n    bool try_tcache_dalloc)\n{\n\tvoid *ret;\n\tsize_t copysize;\n\n\t/* Try to avoid moving the allocation. */\n\tret = arena_ralloc_no_move(ptr, oldsize, size, extra, zero);\n\tif (ret != NULL)\n\t\treturn (ret);\n\n\t/*\n\t * size and oldsize are different enough that we need to move the\n\t * object.  In that case, fall back to allocating new space and\n\t * copying.\n\t */\n\tif (alignment != 0) {\n\t\tsize_t usize = sa2u(size + extra, alignment);\n\t\tif (usize == 0)\n\t\t\treturn (NULL);\n\t\tret = ipallocx(usize, alignment, zero, try_tcache_alloc, arena);\n\t} else\n\t\tret = arena_malloc(arena, size + extra, zero, try_tcache_alloc);\n\n\tif (ret == NULL) {\n\t\tif (extra == 0)\n\t\t\treturn (NULL);\n\t\t/* Try again, this time without extra. */\n\t\tif (alignment != 0) {\n\t\t\tsize_t usize = sa2u(size, alignment);\n\t\t\tif (usize == 0)\n\t\t\t\treturn (NULL);\n\t\t\tret = ipallocx(usize, alignment, zero, try_tcache_alloc,\n\t\t\t    arena);\n\t\t} else\n\t\t\tret = arena_malloc(arena, size, zero, try_tcache_alloc);\n\n\t\tif (ret == NULL)\n\t\t\treturn (NULL);\n\t}\n\n\t/* Junk/zero-filling were already done by ipalloc()/arena_malloc(). */\n\n\t/*\n\t * Copy at most size bytes (not size+extra), since the caller has no\n\t * expectation that the extra bytes will be reliably preserved.\n\t */\n\tcopysize = (size < oldsize) ? size : oldsize;\n\tVALGRIND_MAKE_MEM_UNDEFINED(ret, copysize);\n\tmemcpy(ret, ptr, copysize);\n\tiqallocx(ptr, try_tcache_dalloc);\n\treturn (ret);\n}\n\ndss_prec_t\narena_dss_prec_get(arena_t *arena)\n{\n\tdss_prec_t ret;\n\n\tmalloc_mutex_lock(&arena->lock);\n\tret = arena->dss_prec;\n\tmalloc_mutex_unlock(&arena->lock);\n\treturn (ret);\n}\n\nvoid\narena_dss_prec_set(arena_t *arena, dss_prec_t dss_prec)\n{\n\n\tmalloc_mutex_lock(&arena->lock);\n\tarena->dss_prec = dss_prec;\n\tmalloc_mutex_unlock(&arena->lock);\n}\n\nvoid\narena_stats_merge(arena_t *arena, const char **dss, size_t *nactive,\n    size_t *ndirty, arena_stats_t *astats, malloc_bin_stats_t *bstats,\n    malloc_large_stats_t *lstats)\n{\n\tunsigned i;\n\n\tmalloc_mutex_lock(&arena->lock);\n\t*dss = dss_prec_names[arena->dss_prec];\n\t*nactive += arena->nactive;\n\t*ndirty += arena->ndirty;\n\n\tastats->mapped += arena->stats.mapped;\n\tastats->npurge += arena->stats.npurge;\n\tastats->nmadvise += arena->stats.nmadvise;\n\tastats->purged += arena->stats.purged;\n\tastats->allocated_large += arena->stats.allocated_large;\n\tastats->nmalloc_large += arena->stats.nmalloc_large;\n\tastats->ndalloc_large += arena->stats.ndalloc_large;\n\tastats->nrequests_large += arena->stats.nrequests_large;\n\n\tfor (i = 0; i < nlclasses; i++) {\n\t\tlstats[i].nmalloc += arena->stats.lstats[i].nmalloc;\n\t\tlstats[i].ndalloc += arena->stats.lstats[i].ndalloc;\n\t\tlstats[i].nrequests += arena->stats.lstats[i].nrequests;\n\t\tlstats[i].curruns += arena->stats.lstats[i].curruns;\n\t}\n\tmalloc_mutex_unlock(&arena->lock);\n\n\tfor (i = 0; i < NBINS; i++) {\n\t\tarena_bin_t *bin = &arena->bins[i];\n\n\t\tmalloc_mutex_lock(&bin->lock);\n\t\tbstats[i].allocated += bin->stats.allocated;\n\t\tbstats[i].nmalloc += bin->stats.nmalloc;\n\t\tbstats[i].ndalloc += bin->stats.ndalloc;\n\t\tbstats[i].nrequests += bin->stats.nrequests;\n\t\tif (config_tcache) {\n\t\t\tbstats[i].nfills += bin->stats.nfills;\n\t\t\tbstats[i].nflushes += bin->stats.nflushes;\n\t\t}\n\t\tbstats[i].nruns += bin->stats.nruns;\n\t\tbstats[i].reruns += bin->stats.reruns;\n\t\tbstats[i].curruns += bin->stats.curruns;\n\t\tmalloc_mutex_unlock(&bin->lock);\n\t}\n}\n\nbool\narena_new(arena_t *arena, unsigned ind)\n{\n\tunsigned i;\n\tarena_bin_t *bin;\n\n\tarena->ind = ind;\n\tarena->nthreads = 0;\n\n\tif (malloc_mutex_init(&arena->lock))\n\t\treturn (true);\n\n\tif (config_stats) {\n\t\tmemset(&arena->stats, 0, sizeof(arena_stats_t));\n\t\tarena->stats.lstats =\n\t\t    (malloc_large_stats_t *)base_alloc(nlclasses *\n\t\t    sizeof(malloc_large_stats_t));\n\t\tif (arena->stats.lstats == NULL)\n\t\t\treturn (true);\n\t\tmemset(arena->stats.lstats, 0, nlclasses *\n\t\t    sizeof(malloc_large_stats_t));\n\t\tif (config_tcache)\n\t\t\tql_new(&arena->tcache_ql);\n\t}\n\n\tif (config_prof)\n\t\tarena->prof_accumbytes = 0;\n\n\tarena->dss_prec = chunk_dss_prec_get();\n\n\t/* Initialize chunks. */\n\tarena_chunk_dirty_new(&arena->chunks_dirty);\n\tarena->spare = NULL;\n\n\tarena->nactive = 0;\n\tarena->ndirty = 0;\n\tarena->npurgatory = 0;\n\n\tarena_avail_tree_new(&arena->runs_avail);\n\n\t/* Initialize bins. */\n\tfor (i = 0; i < NBINS; i++) {\n\t\tbin = &arena->bins[i];\n\t\tif (malloc_mutex_init(&bin->lock))\n\t\t\treturn (true);\n\t\tbin->runcur = NULL;\n\t\tarena_run_tree_new(&bin->runs);\n\t\tif (config_stats)\n\t\t\tmemset(&bin->stats, 0, sizeof(malloc_bin_stats_t));\n\t}\n\n\treturn (false);\n}\n\n/*\n * Calculate bin_info->run_size such that it meets the following constraints:\n *\n *   *) bin_info->run_size >= min_run_size\n *   *) bin_info->run_size <= arena_maxclass\n *   *) run header overhead <= RUN_MAX_OVRHD (or header overhead relaxed).\n *   *) bin_info->nregs <= RUN_MAXREGS\n *\n * bin_info->nregs, bin_info->bitmap_offset, and bin_info->reg0_offset are also\n * calculated here, since these settings are all interdependent.\n */\nstatic size_t\nbin_info_run_size_calc(arena_bin_info_t *bin_info, size_t min_run_size)\n{\n\tsize_t pad_size;\n\tsize_t try_run_size, good_run_size;\n\tuint32_t try_nregs, good_nregs;\n\tuint32_t try_hdr_size, good_hdr_size;\n\tuint32_t try_bitmap_offset, good_bitmap_offset;\n\tuint32_t try_ctx0_offset, good_ctx0_offset;\n\tuint32_t try_redzone0_offset, good_redzone0_offset;\n\n\tassert(min_run_size >= PAGE);\n\tassert(min_run_size <= arena_maxclass);\n\n\t/*\n\t * Determine redzone size based on minimum alignment and minimum\n\t * redzone size.  Add padding to the end of the run if it is needed to\n\t * align the regions.  The padding allows each redzone to be half the\n\t * minimum alignment; without the padding, each redzone would have to\n\t * be twice as large in order to maintain alignment.\n\t */\n\tif (config_fill && opt_redzone) {\n\t\tsize_t align_min = ZU(1) << (ffs(bin_info->reg_size) - 1);\n\t\tif (align_min <= REDZONE_MINSIZE) {\n\t\t\tbin_info->redzone_size = REDZONE_MINSIZE;\n\t\t\tpad_size = 0;\n\t\t} else {\n\t\t\tbin_info->redzone_size = align_min >> 1;\n\t\t\tpad_size = bin_info->redzone_size;\n\t\t}\n\t} else {\n\t\tbin_info->redzone_size = 0;\n\t\tpad_size = 0;\n\t}\n\tbin_info->reg_interval = bin_info->reg_size +\n\t    (bin_info->redzone_size << 1);\n\n\t/*\n\t * Calculate known-valid settings before entering the run_size\n\t * expansion loop, so that the first part of the loop always copies\n\t * valid settings.\n\t *\n\t * The do..while loop iteratively reduces the number of regions until\n\t * the run header and the regions no longer overlap.  A closed formula\n\t * would be quite messy, since there is an interdependency between the\n\t * header's mask length and the number of regions.\n\t */\n\ttry_run_size = min_run_size;\n\ttry_nregs = ((try_run_size - sizeof(arena_run_t)) /\n\t    bin_info->reg_interval)\n\t    + 1; /* Counter-act try_nregs-- in loop. */\n\tif (try_nregs > RUN_MAXREGS) {\n\t\ttry_nregs = RUN_MAXREGS\n\t\t    + 1; /* Counter-act try_nregs-- in loop. */\n\t}\n\tdo {\n\t\ttry_nregs--;\n\t\ttry_hdr_size = sizeof(arena_run_t);\n\t\t/* Pad to a long boundary. */\n\t\ttry_hdr_size = LONG_CEILING(try_hdr_size);\n\t\ttry_bitmap_offset = try_hdr_size;\n\t\t/* Add space for bitmap. */\n\t\ttry_hdr_size += bitmap_size(try_nregs);\n\t\tif (config_prof && opt_prof && prof_promote == false) {\n\t\t\t/* Pad to a quantum boundary. */\n\t\t\ttry_hdr_size = QUANTUM_CEILING(try_hdr_size);\n\t\t\ttry_ctx0_offset = try_hdr_size;\n\t\t\t/* Add space for one (prof_ctx_t *) per region. */\n\t\t\ttry_hdr_size += try_nregs * sizeof(prof_ctx_t *);\n\t\t} else\n\t\t\ttry_ctx0_offset = 0;\n\t\ttry_redzone0_offset = try_run_size - (try_nregs *\n\t\t    bin_info->reg_interval) - pad_size;\n\t} while (try_hdr_size > try_redzone0_offset);\n\n\t/* run_size expansion loop. */\n\tdo {\n\t\t/*\n\t\t * Copy valid settings before trying more aggressive settings.\n\t\t */\n\t\tgood_run_size = try_run_size;\n\t\tgood_nregs = try_nregs;\n\t\tgood_hdr_size = try_hdr_size;\n\t\tgood_bitmap_offset = try_bitmap_offset;\n\t\tgood_ctx0_offset = try_ctx0_offset;\n\t\tgood_redzone0_offset = try_redzone0_offset;\n\n\t\t/* Try more aggressive settings. */\n\t\ttry_run_size += PAGE;\n\t\ttry_nregs = ((try_run_size - sizeof(arena_run_t) - pad_size) /\n\t\t    bin_info->reg_interval)\n\t\t    + 1; /* Counter-act try_nregs-- in loop. */\n\t\tif (try_nregs > RUN_MAXREGS) {\n\t\t\ttry_nregs = RUN_MAXREGS\n\t\t\t    + 1; /* Counter-act try_nregs-- in loop. */\n\t\t}\n\t\tdo {\n\t\t\ttry_nregs--;\n\t\t\ttry_hdr_size = sizeof(arena_run_t);\n\t\t\t/* Pad to a long boundary. */\n\t\t\ttry_hdr_size = LONG_CEILING(try_hdr_size);\n\t\t\ttry_bitmap_offset = try_hdr_size;\n\t\t\t/* Add space for bitmap. */\n\t\t\ttry_hdr_size += bitmap_size(try_nregs);\n\t\t\tif (config_prof && opt_prof && prof_promote == false) {\n\t\t\t\t/* Pad to a quantum boundary. */\n\t\t\t\ttry_hdr_size = QUANTUM_CEILING(try_hdr_size);\n\t\t\t\ttry_ctx0_offset = try_hdr_size;\n\t\t\t\t/*\n\t\t\t\t * Add space for one (prof_ctx_t *) per region.\n\t\t\t\t */\n\t\t\t\ttry_hdr_size += try_nregs *\n\t\t\t\t    sizeof(prof_ctx_t *);\n\t\t\t}\n\t\t\ttry_redzone0_offset = try_run_size - (try_nregs *\n\t\t\t    bin_info->reg_interval) - pad_size;\n\t\t} while (try_hdr_size > try_redzone0_offset);\n\t} while (try_run_size <= arena_maxclass\n\t    && try_run_size <= arena_maxclass\n\t    && RUN_MAX_OVRHD * (bin_info->reg_interval << 3) >\n\t    RUN_MAX_OVRHD_RELAX\n\t    && (try_redzone0_offset << RUN_BFP) > RUN_MAX_OVRHD * try_run_size\n\t    && try_nregs < RUN_MAXREGS);\n\n\tassert(good_hdr_size <= good_redzone0_offset);\n\n\t/* Copy final settings. */\n\tbin_info->run_size = good_run_size;\n\tbin_info->nregs = good_nregs;\n\tbin_info->bitmap_offset = good_bitmap_offset;\n\tbin_info->ctx0_offset = good_ctx0_offset;\n\tbin_info->reg0_offset = good_redzone0_offset + bin_info->redzone_size;\n\n\tassert(bin_info->reg0_offset - bin_info->redzone_size + (bin_info->nregs\n\t    * bin_info->reg_interval) + pad_size == bin_info->run_size);\n\n\treturn (good_run_size);\n}\n\nstatic void\nbin_info_init(void)\n{\n\tarena_bin_info_t *bin_info;\n\tsize_t prev_run_size = PAGE;\n\n#define\tSIZE_CLASS(bin, delta, size)\t\t\t\t\t\\\n\tbin_info = &arena_bin_info[bin];\t\t\t\t\\\n\tbin_info->reg_size = size;\t\t\t\t\t\\\n\tprev_run_size = bin_info_run_size_calc(bin_info, prev_run_size);\\\n\tbitmap_info_init(&bin_info->bitmap_info, bin_info->nregs);\n\tSIZE_CLASSES\n#undef SIZE_CLASS\n}\n\nvoid\narena_boot(void)\n{\n\tsize_t header_size;\n\tunsigned i;\n\n\t/*\n\t * Compute the header size such that it is large enough to contain the\n\t * page map.  The page map is biased to omit entries for the header\n\t * itself, so some iteration is necessary to compute the map bias.\n\t *\n\t * 1) Compute safe header_size and map_bias values that include enough\n\t *    space for an unbiased page map.\n\t * 2) Refine map_bias based on (1) to omit the header pages in the page\n\t *    map.  The resulting map_bias may be one too small.\n\t * 3) Refine map_bias based on (2).  The result will be >= the result\n\t *    from (2), and will always be correct.\n\t */\n\tmap_bias = 0;\n\tfor (i = 0; i < 3; i++) {\n\t\theader_size = offsetof(arena_chunk_t, map) +\n\t\t    (sizeof(arena_chunk_map_t) * (chunk_npages-map_bias));\n\t\tmap_bias = (header_size >> LG_PAGE) + ((header_size & PAGE_MASK)\n\t\t    != 0);\n\t}\n\tassert(map_bias > 0);\n\n\tarena_maxclass = chunksize - (map_bias << LG_PAGE);\n\n\tbin_info_init();\n}\n\nvoid\narena_prefork(arena_t *arena)\n{\n\tunsigned i;\n\n\tmalloc_mutex_prefork(&arena->lock);\n\tfor (i = 0; i < NBINS; i++)\n\t\tmalloc_mutex_prefork(&arena->bins[i].lock);\n}\n\nvoid\narena_postfork_parent(arena_t *arena)\n{\n\tunsigned i;\n\n\tfor (i = 0; i < NBINS; i++)\n\t\tmalloc_mutex_postfork_parent(&arena->bins[i].lock);\n\tmalloc_mutex_postfork_parent(&arena->lock);\n}\n\nvoid\narena_postfork_child(arena_t *arena)\n{\n\tunsigned i;\n\n\tfor (i = 0; i < NBINS; i++)\n\t\tmalloc_mutex_postfork_child(&arena->bins[i].lock);\n\tmalloc_mutex_postfork_child(&arena->lock);\n}\n"
  },
  {
    "path": "deps/jemalloc/src/atomic.c",
    "content": "#define\tJEMALLOC_ATOMIC_C_\n#include \"jemalloc/internal/jemalloc_internal.h\"\n"
  },
  {
    "path": "deps/jemalloc/src/base.c",
    "content": "#define\tJEMALLOC_BASE_C_\n#include \"jemalloc/internal/jemalloc_internal.h\"\n\n/******************************************************************************/\n/* Data. */\n\nstatic malloc_mutex_t\tbase_mtx;\n\n/*\n * Current pages that are being used for internal memory allocations.  These\n * pages are carved up in cacheline-size quanta, so that there is no chance of\n * false cache line sharing.\n */\nstatic void\t\t*base_pages;\nstatic void\t\t*base_next_addr;\nstatic void\t\t*base_past_addr; /* Addr immediately past base_pages. */\nstatic extent_node_t\t*base_nodes;\n\n/******************************************************************************/\n/* Function prototypes for non-inline static functions. */\n\nstatic bool\tbase_pages_alloc(size_t minsize);\n\n/******************************************************************************/\n\nstatic bool\nbase_pages_alloc(size_t minsize)\n{\n\tsize_t csize;\n\tbool zero;\n\n\tassert(minsize != 0);\n\tcsize = CHUNK_CEILING(minsize);\n\tzero = false;\n\tbase_pages = chunk_alloc(csize, chunksize, true, &zero,\n\t    chunk_dss_prec_get());\n\tif (base_pages == NULL)\n\t\treturn (true);\n\tbase_next_addr = base_pages;\n\tbase_past_addr = (void *)((uintptr_t)base_pages + csize);\n\n\treturn (false);\n}\n\nvoid *\nbase_alloc(size_t size)\n{\n\tvoid *ret;\n\tsize_t csize;\n\n\t/* Round size up to nearest multiple of the cacheline size. */\n\tcsize = CACHELINE_CEILING(size);\n\n\tmalloc_mutex_lock(&base_mtx);\n\t/* Make sure there's enough space for the allocation. */\n\tif ((uintptr_t)base_next_addr + csize > (uintptr_t)base_past_addr) {\n\t\tif (base_pages_alloc(csize)) {\n\t\t\tmalloc_mutex_unlock(&base_mtx);\n\t\t\treturn (NULL);\n\t\t}\n\t}\n\t/* Allocate. */\n\tret = base_next_addr;\n\tbase_next_addr = (void *)((uintptr_t)base_next_addr + csize);\n\tmalloc_mutex_unlock(&base_mtx);\n\n\treturn (ret);\n}\n\nvoid *\nbase_calloc(size_t number, size_t size)\n{\n\tvoid *ret = base_alloc(number * size);\n\n\tif (ret != NULL)\n\t\tmemset(ret, 0, number * size);\n\n\treturn (ret);\n}\n\nextent_node_t *\nbase_node_alloc(void)\n{\n\textent_node_t *ret;\n\n\tmalloc_mutex_lock(&base_mtx);\n\tif (base_nodes != NULL) {\n\t\tret = base_nodes;\n\t\tbase_nodes = *(extent_node_t **)ret;\n\t\tmalloc_mutex_unlock(&base_mtx);\n\t} else {\n\t\tmalloc_mutex_unlock(&base_mtx);\n\t\tret = (extent_node_t *)base_alloc(sizeof(extent_node_t));\n\t}\n\n\treturn (ret);\n}\n\nvoid\nbase_node_dealloc(extent_node_t *node)\n{\n\n\tmalloc_mutex_lock(&base_mtx);\n\t*(extent_node_t **)node = base_nodes;\n\tbase_nodes = node;\n\tmalloc_mutex_unlock(&base_mtx);\n}\n\nbool\nbase_boot(void)\n{\n\n\tbase_nodes = NULL;\n\tif (malloc_mutex_init(&base_mtx))\n\t\treturn (true);\n\n\treturn (false);\n}\n\nvoid\nbase_prefork(void)\n{\n\n\tmalloc_mutex_prefork(&base_mtx);\n}\n\nvoid\nbase_postfork_parent(void)\n{\n\n\tmalloc_mutex_postfork_parent(&base_mtx);\n}\n\nvoid\nbase_postfork_child(void)\n{\n\n\tmalloc_mutex_postfork_child(&base_mtx);\n}\n"
  },
  {
    "path": "deps/jemalloc/src/bitmap.c",
    "content": "#define JEMALLOC_BITMAP_C_\n#include \"jemalloc/internal/jemalloc_internal.h\"\n\n/******************************************************************************/\n/* Function prototypes for non-inline static functions. */\n\nstatic size_t\tbits2groups(size_t nbits);\n\n/******************************************************************************/\n\nstatic size_t\nbits2groups(size_t nbits)\n{\n\n\treturn ((nbits >> LG_BITMAP_GROUP_NBITS) +\n\t    !!(nbits & BITMAP_GROUP_NBITS_MASK));\n}\n\nvoid\nbitmap_info_init(bitmap_info_t *binfo, size_t nbits)\n{\n\tunsigned i;\n\tsize_t group_count;\n\n\tassert(nbits > 0);\n\tassert(nbits <= (ZU(1) << LG_BITMAP_MAXBITS));\n\n\t/*\n\t * Compute the number of groups necessary to store nbits bits, and\n\t * progressively work upward through the levels until reaching a level\n\t * that requires only one group.\n\t */\n\tbinfo->levels[0].group_offset = 0;\n\tgroup_count = bits2groups(nbits);\n\tfor (i = 1; group_count > 1; i++) {\n\t\tassert(i < BITMAP_MAX_LEVELS);\n\t\tbinfo->levels[i].group_offset = binfo->levels[i-1].group_offset\n\t\t    + group_count;\n\t\tgroup_count = bits2groups(group_count);\n\t}\n\tbinfo->levels[i].group_offset = binfo->levels[i-1].group_offset\n\t    + group_count;\n\tbinfo->nlevels = i;\n\tbinfo->nbits = nbits;\n}\n\nsize_t\nbitmap_info_ngroups(const bitmap_info_t *binfo)\n{\n\n\treturn (binfo->levels[binfo->nlevels].group_offset << LG_SIZEOF_BITMAP);\n}\n\nsize_t\nbitmap_size(size_t nbits)\n{\n\tbitmap_info_t binfo;\n\n\tbitmap_info_init(&binfo, nbits);\n\treturn (bitmap_info_ngroups(&binfo));\n}\n\nvoid\nbitmap_init(bitmap_t *bitmap, const bitmap_info_t *binfo)\n{\n\tsize_t extra;\n\tunsigned i;\n\n\t/*\n\t * Bits are actually inverted with regard to the external bitmap\n\t * interface, so the bitmap starts out with all 1 bits, except for\n\t * trailing unused bits (if any).  Note that each group uses bit 0 to\n\t * correspond to the first logical bit in the group, so extra bits\n\t * are the most significant bits of the last group.\n\t */\n\tmemset(bitmap, 0xffU, binfo->levels[binfo->nlevels].group_offset <<\n\t    LG_SIZEOF_BITMAP);\n\textra = (BITMAP_GROUP_NBITS - (binfo->nbits & BITMAP_GROUP_NBITS_MASK))\n\t    & BITMAP_GROUP_NBITS_MASK;\n\tif (extra != 0)\n\t\tbitmap[binfo->levels[1].group_offset - 1] >>= extra;\n\tfor (i = 1; i < binfo->nlevels; i++) {\n\t\tsize_t group_count = binfo->levels[i].group_offset -\n\t\t    binfo->levels[i-1].group_offset;\n\t\textra = (BITMAP_GROUP_NBITS - (group_count &\n\t\t    BITMAP_GROUP_NBITS_MASK)) & BITMAP_GROUP_NBITS_MASK;\n\t\tif (extra != 0)\n\t\t\tbitmap[binfo->levels[i+1].group_offset - 1] >>= extra;\n\t}\n}\n"
  },
  {
    "path": "deps/jemalloc/src/chunk.c",
    "content": "#define\tJEMALLOC_CHUNK_C_\n#include \"jemalloc/internal/jemalloc_internal.h\"\n\n/******************************************************************************/\n/* Data. */\n\nconst char\t*opt_dss = DSS_DEFAULT;\nsize_t\t\topt_lg_chunk = LG_CHUNK_DEFAULT;\n\nmalloc_mutex_t\tchunks_mtx;\nchunk_stats_t\tstats_chunks;\n\n/*\n * Trees of chunks that were previously allocated (trees differ only in node\n * ordering).  These are used when allocating chunks, in an attempt to re-use\n * address space.  Depending on function, different tree orderings are needed,\n * which is why there are two trees with the same contents.\n */\nstatic extent_tree_t\tchunks_szad_mmap;\nstatic extent_tree_t\tchunks_ad_mmap;\nstatic extent_tree_t\tchunks_szad_dss;\nstatic extent_tree_t\tchunks_ad_dss;\n\nrtree_t\t\t*chunks_rtree;\n\n/* Various chunk-related settings. */\nsize_t\t\tchunksize;\nsize_t\t\tchunksize_mask; /* (chunksize - 1). */\nsize_t\t\tchunk_npages;\nsize_t\t\tmap_bias;\nsize_t\t\tarena_maxclass; /* Max size class for arenas. */\n\n/******************************************************************************/\n/* Function prototypes for non-inline static functions. */\n\nstatic void\t*chunk_recycle(extent_tree_t *chunks_szad,\n    extent_tree_t *chunks_ad, size_t size, size_t alignment, bool base,\n    bool *zero);\nstatic void\tchunk_record(extent_tree_t *chunks_szad,\n    extent_tree_t *chunks_ad, void *chunk, size_t size);\n\n/******************************************************************************/\n\nstatic void *\nchunk_recycle(extent_tree_t *chunks_szad, extent_tree_t *chunks_ad, size_t size,\n    size_t alignment, bool base, bool *zero)\n{\n\tvoid *ret;\n\textent_node_t *node;\n\textent_node_t key;\n\tsize_t alloc_size, leadsize, trailsize;\n\tbool zeroed;\n\n\tif (base) {\n\t\t/*\n\t\t * This function may need to call base_node_{,de}alloc(), but\n\t\t * the current chunk allocation request is on behalf of the\n\t\t * base allocator.  Avoid deadlock (and if that weren't an\n\t\t * issue, potential for infinite recursion) by returning NULL.\n\t\t */\n\t\treturn (NULL);\n\t}\n\n\talloc_size = size + alignment - chunksize;\n\t/* Beware size_t wrap-around. */\n\tif (alloc_size < size)\n\t\treturn (NULL);\n\tkey.addr = NULL;\n\tkey.size = alloc_size;\n\tmalloc_mutex_lock(&chunks_mtx);\n\tnode = extent_tree_szad_nsearch(chunks_szad, &key);\n\tif (node == NULL) {\n\t\tmalloc_mutex_unlock(&chunks_mtx);\n\t\treturn (NULL);\n\t}\n\tleadsize = ALIGNMENT_CEILING((uintptr_t)node->addr, alignment) -\n\t    (uintptr_t)node->addr;\n\tassert(node->size >= leadsize + size);\n\ttrailsize = node->size - leadsize - size;\n\tret = (void *)((uintptr_t)node->addr + leadsize);\n\t/* Remove node from the tree. */\n\textent_tree_szad_remove(chunks_szad, node);\n\textent_tree_ad_remove(chunks_ad, node);\n\tif (leadsize != 0) {\n\t\t/* Insert the leading space as a smaller chunk. */\n\t\tnode->size = leadsize;\n\t\textent_tree_szad_insert(chunks_szad, node);\n\t\textent_tree_ad_insert(chunks_ad, node);\n\t\tnode = NULL;\n\t}\n\tif (trailsize != 0) {\n\t\t/* Insert the trailing space as a smaller chunk. */\n\t\tif (node == NULL) {\n\t\t\t/*\n\t\t\t * An additional node is required, but\n\t\t\t * base_node_alloc() can cause a new base chunk to be\n\t\t\t * allocated.  Drop chunks_mtx in order to avoid\n\t\t\t * deadlock, and if node allocation fails, deallocate\n\t\t\t * the result before returning an error.\n\t\t\t */\n\t\t\tmalloc_mutex_unlock(&chunks_mtx);\n\t\t\tnode = base_node_alloc();\n\t\t\tif (node == NULL) {\n\t\t\t\tchunk_dealloc(ret, size, true);\n\t\t\t\treturn (NULL);\n\t\t\t}\n\t\t\tmalloc_mutex_lock(&chunks_mtx);\n\t\t}\n\t\tnode->addr = (void *)((uintptr_t)(ret) + size);\n\t\tnode->size = trailsize;\n\t\textent_tree_szad_insert(chunks_szad, node);\n\t\textent_tree_ad_insert(chunks_ad, node);\n\t\tnode = NULL;\n\t}\n\tmalloc_mutex_unlock(&chunks_mtx);\n\n\tzeroed = false;\n\tif (node != NULL) {\n\t\tif (node->zeroed) {\n\t\t\tzeroed = true;\n\t\t\t*zero = true;\n\t\t}\n\t\tbase_node_dealloc(node);\n\t}\n\tif (zeroed == false && *zero) {\n\t\tVALGRIND_MAKE_MEM_UNDEFINED(ret, size);\n\t\tmemset(ret, 0, size);\n\t}\n\treturn (ret);\n}\n\n/*\n * If the caller specifies (*zero == false), it is still possible to receive\n * zeroed memory, in which case *zero is toggled to true.  arena_chunk_alloc()\n * takes advantage of this to avoid demanding zeroed chunks, but taking\n * advantage of them if they are returned.\n */\nvoid *\nchunk_alloc(size_t size, size_t alignment, bool base, bool *zero,\n    dss_prec_t dss_prec)\n{\n\tvoid *ret;\n\n\tassert(size != 0);\n\tassert((size & chunksize_mask) == 0);\n\tassert(alignment != 0);\n\tassert((alignment & chunksize_mask) == 0);\n\n\t/* \"primary\" dss. */\n\tif (config_dss && dss_prec == dss_prec_primary) {\n\t\tif ((ret = chunk_recycle(&chunks_szad_dss, &chunks_ad_dss, size,\n\t\t    alignment, base, zero)) != NULL)\n\t\t\tgoto label_return;\n\t\tif ((ret = chunk_alloc_dss(size, alignment, zero)) != NULL)\n\t\t\tgoto label_return;\n\t}\n\t/* mmap. */\n\tif ((ret = chunk_recycle(&chunks_szad_mmap, &chunks_ad_mmap, size,\n\t    alignment, base, zero)) != NULL)\n\t\tgoto label_return;\n\tif ((ret = chunk_alloc_mmap(size, alignment, zero)) != NULL)\n\t\tgoto label_return;\n\t/* \"secondary\" dss. */\n\tif (config_dss && dss_prec == dss_prec_secondary) {\n\t\tif ((ret = chunk_recycle(&chunks_szad_dss, &chunks_ad_dss, size,\n\t\t    alignment, base, zero)) != NULL)\n\t\t\tgoto label_return;\n\t\tif ((ret = chunk_alloc_dss(size, alignment, zero)) != NULL)\n\t\t\tgoto label_return;\n\t}\n\n\t/* All strategies for allocation failed. */\n\tret = NULL;\nlabel_return:\n\tif (config_ivsalloc && base == false && ret != NULL) {\n\t\tif (rtree_set(chunks_rtree, (uintptr_t)ret, ret)) {\n\t\t\tchunk_dealloc(ret, size, true);\n\t\t\treturn (NULL);\n\t\t}\n\t}\n\tif ((config_stats || config_prof) && ret != NULL) {\n\t\tbool gdump;\n\t\tmalloc_mutex_lock(&chunks_mtx);\n\t\tif (config_stats)\n\t\t\tstats_chunks.nchunks += (size / chunksize);\n\t\tstats_chunks.curchunks += (size / chunksize);\n\t\tif (stats_chunks.curchunks > stats_chunks.highchunks) {\n\t\t\tstats_chunks.highchunks = stats_chunks.curchunks;\n\t\t\tif (config_prof)\n\t\t\t\tgdump = true;\n\t\t} else if (config_prof)\n\t\t\tgdump = false;\n\t\tmalloc_mutex_unlock(&chunks_mtx);\n\t\tif (config_prof && opt_prof && opt_prof_gdump && gdump)\n\t\t\tprof_gdump();\n\t}\n\tif (config_debug && *zero && ret != NULL) {\n\t\tsize_t i;\n\t\tsize_t *p = (size_t *)(uintptr_t)ret;\n\n\t\tVALGRIND_MAKE_MEM_DEFINED(ret, size);\n\t\tfor (i = 0; i < size / sizeof(size_t); i++)\n\t\t\tassert(p[i] == 0);\n\t}\n\tassert(CHUNK_ADDR2BASE(ret) == ret);\n\treturn (ret);\n}\n\nstatic void\nchunk_record(extent_tree_t *chunks_szad, extent_tree_t *chunks_ad, void *chunk,\n    size_t size)\n{\n\tbool unzeroed;\n\textent_node_t *xnode, *node, *prev, key;\n\n\tunzeroed = pages_purge(chunk, size);\n\n\t/*\n\t * Allocate a node before acquiring chunks_mtx even though it might not\n\t * be needed, because base_node_alloc() may cause a new base chunk to\n\t * be allocated, which could cause deadlock if chunks_mtx were already\n\t * held.\n\t */\n\txnode = base_node_alloc();\n\n\tmalloc_mutex_lock(&chunks_mtx);\n\tkey.addr = (void *)((uintptr_t)chunk + size);\n\tnode = extent_tree_ad_nsearch(chunks_ad, &key);\n\t/* Try to coalesce forward. */\n\tif (node != NULL && node->addr == key.addr) {\n\t\t/*\n\t\t * Coalesce chunk with the following address range.  This does\n\t\t * not change the position within chunks_ad, so only\n\t\t * remove/insert from/into chunks_szad.\n\t\t */\n\t\textent_tree_szad_remove(chunks_szad, node);\n\t\tnode->addr = chunk;\n\t\tnode->size += size;\n\t\tnode->zeroed = (node->zeroed && (unzeroed == false));\n\t\textent_tree_szad_insert(chunks_szad, node);\n\t\tif (xnode != NULL)\n\t\t\tbase_node_dealloc(xnode);\n\t} else {\n\t\t/* Coalescing forward failed, so insert a new node. */\n\t\tif (xnode == NULL) {\n\t\t\t/*\n\t\t\t * base_node_alloc() failed, which is an exceedingly\n\t\t\t * unlikely failure.  Leak chunk; its pages have\n\t\t\t * already been purged, so this is only a virtual\n\t\t\t * memory leak.\n\t\t\t */\n\t\t\tmalloc_mutex_unlock(&chunks_mtx);\n\t\t\treturn;\n\t\t}\n\t\tnode = xnode;\n\t\tnode->addr = chunk;\n\t\tnode->size = size;\n\t\tnode->zeroed = (unzeroed == false);\n\t\textent_tree_ad_insert(chunks_ad, node);\n\t\textent_tree_szad_insert(chunks_szad, node);\n\t}\n\n\t/* Try to coalesce backward. */\n\tprev = extent_tree_ad_prev(chunks_ad, node);\n\tif (prev != NULL && (void *)((uintptr_t)prev->addr + prev->size) ==\n\t    chunk) {\n\t\t/*\n\t\t * Coalesce chunk with the previous address range.  This does\n\t\t * not change the position within chunks_ad, so only\n\t\t * remove/insert node from/into chunks_szad.\n\t\t */\n\t\textent_tree_szad_remove(chunks_szad, prev);\n\t\textent_tree_ad_remove(chunks_ad, prev);\n\n\t\textent_tree_szad_remove(chunks_szad, node);\n\t\tnode->addr = prev->addr;\n\t\tnode->size += prev->size;\n\t\tnode->zeroed = (node->zeroed && prev->zeroed);\n\t\textent_tree_szad_insert(chunks_szad, node);\n\n\t\tbase_node_dealloc(prev);\n\t}\n\tmalloc_mutex_unlock(&chunks_mtx);\n}\n\nvoid\nchunk_unmap(void *chunk, size_t size)\n{\n\tassert(chunk != NULL);\n\tassert(CHUNK_ADDR2BASE(chunk) == chunk);\n\tassert(size != 0);\n\tassert((size & chunksize_mask) == 0);\n\n\tif (config_dss && chunk_in_dss(chunk))\n\t\tchunk_record(&chunks_szad_dss, &chunks_ad_dss, chunk, size);\n\telse if (chunk_dealloc_mmap(chunk, size))\n\t\tchunk_record(&chunks_szad_mmap, &chunks_ad_mmap, chunk, size);\n}\n\nvoid\nchunk_dealloc(void *chunk, size_t size, bool unmap)\n{\n\n\tassert(chunk != NULL);\n\tassert(CHUNK_ADDR2BASE(chunk) == chunk);\n\tassert(size != 0);\n\tassert((size & chunksize_mask) == 0);\n\n\tif (config_ivsalloc)\n\t\trtree_set(chunks_rtree, (uintptr_t)chunk, NULL);\n\tif (config_stats || config_prof) {\n\t\tmalloc_mutex_lock(&chunks_mtx);\n\t\tassert(stats_chunks.curchunks >= (size / chunksize));\n\t\tstats_chunks.curchunks -= (size / chunksize);\n\t\tmalloc_mutex_unlock(&chunks_mtx);\n\t}\n\n\tif (unmap)\n\t\tchunk_unmap(chunk, size);\n}\n\nbool\nchunk_boot(void)\n{\n\n\t/* Set variables according to the value of opt_lg_chunk. */\n\tchunksize = (ZU(1) << opt_lg_chunk);\n\tassert(chunksize >= PAGE);\n\tchunksize_mask = chunksize - 1;\n\tchunk_npages = (chunksize >> LG_PAGE);\n\n\tif (config_stats || config_prof) {\n\t\tif (malloc_mutex_init(&chunks_mtx))\n\t\t\treturn (true);\n\t\tmemset(&stats_chunks, 0, sizeof(chunk_stats_t));\n\t}\n\tif (config_dss && chunk_dss_boot())\n\t\treturn (true);\n\textent_tree_szad_new(&chunks_szad_mmap);\n\textent_tree_ad_new(&chunks_ad_mmap);\n\textent_tree_szad_new(&chunks_szad_dss);\n\textent_tree_ad_new(&chunks_ad_dss);\n\tif (config_ivsalloc) {\n\t\tchunks_rtree = rtree_new((ZU(1) << (LG_SIZEOF_PTR+3)) -\n\t\t    opt_lg_chunk);\n\t\tif (chunks_rtree == NULL)\n\t\t\treturn (true);\n\t}\n\n\treturn (false);\n}\n\nvoid\nchunk_prefork(void)\n{\n\n\tmalloc_mutex_lock(&chunks_mtx);\n\tif (config_ivsalloc)\n\t\trtree_prefork(chunks_rtree);\n\tchunk_dss_prefork();\n}\n\nvoid\nchunk_postfork_parent(void)\n{\n\n\tchunk_dss_postfork_parent();\n\tif (config_ivsalloc)\n\t\trtree_postfork_parent(chunks_rtree);\n\tmalloc_mutex_postfork_parent(&chunks_mtx);\n}\n\nvoid\nchunk_postfork_child(void)\n{\n\n\tchunk_dss_postfork_child();\n\tif (config_ivsalloc)\n\t\trtree_postfork_child(chunks_rtree);\n\tmalloc_mutex_postfork_child(&chunks_mtx);\n}\n"
  },
  {
    "path": "deps/jemalloc/src/chunk_dss.c",
    "content": "#define\tJEMALLOC_CHUNK_DSS_C_\n#include \"jemalloc/internal/jemalloc_internal.h\"\n/******************************************************************************/\n/* Data. */\n\nconst char\t*dss_prec_names[] = {\n\t\"disabled\",\n\t\"primary\",\n\t\"secondary\",\n\t\"N/A\"\n};\n\n/* Current dss precedence default, used when creating new arenas. */\nstatic dss_prec_t\tdss_prec_default = DSS_PREC_DEFAULT;\n\n/*\n * Protects sbrk() calls.  This avoids malloc races among threads, though it\n * does not protect against races with threads that call sbrk() directly.\n */\nstatic malloc_mutex_t\tdss_mtx;\n\n/* Base address of the DSS. */\nstatic void\t\t*dss_base;\n/* Current end of the DSS, or ((void *)-1) if the DSS is exhausted. */\nstatic void\t\t*dss_prev;\n/* Current upper limit on DSS addresses. */\nstatic void\t\t*dss_max;\n\n/******************************************************************************/\n\n#ifndef JEMALLOC_HAVE_SBRK\nstatic void *\nsbrk(intptr_t increment)\n{\n\n\tnot_implemented();\n\n\treturn (NULL);\n}\n#endif\n\ndss_prec_t\nchunk_dss_prec_get(void)\n{\n\tdss_prec_t ret;\n\n\tif (config_dss == false)\n\t\treturn (dss_prec_disabled);\n\tmalloc_mutex_lock(&dss_mtx);\n\tret = dss_prec_default;\n\tmalloc_mutex_unlock(&dss_mtx);\n\treturn (ret);\n}\n\nbool\nchunk_dss_prec_set(dss_prec_t dss_prec)\n{\n\n\tif (config_dss == false)\n\t\treturn (true);\n\tmalloc_mutex_lock(&dss_mtx);\n\tdss_prec_default = dss_prec;\n\tmalloc_mutex_unlock(&dss_mtx);\n\treturn (false);\n}\n\nvoid *\nchunk_alloc_dss(size_t size, size_t alignment, bool *zero)\n{\n\tvoid *ret;\n\n\tcassert(config_dss);\n\tassert(size > 0 && (size & chunksize_mask) == 0);\n\tassert(alignment > 0 && (alignment & chunksize_mask) == 0);\n\n\t/*\n\t * sbrk() uses a signed increment argument, so take care not to\n\t * interpret a huge allocation request as a negative increment.\n\t */\n\tif ((intptr_t)size < 0)\n\t\treturn (NULL);\n\n\tmalloc_mutex_lock(&dss_mtx);\n\tif (dss_prev != (void *)-1) {\n\t\tsize_t gap_size, cpad_size;\n\t\tvoid *cpad, *dss_next;\n\t\tintptr_t incr;\n\n\t\t/*\n\t\t * The loop is necessary to recover from races with other\n\t\t * threads that are using the DSS for something other than\n\t\t * malloc.\n\t\t */\n\t\tdo {\n\t\t\t/* Get the current end of the DSS. */\n\t\t\tdss_max = sbrk(0);\n\t\t\t/*\n\t\t\t * Calculate how much padding is necessary to\n\t\t\t * chunk-align the end of the DSS.\n\t\t\t */\n\t\t\tgap_size = (chunksize - CHUNK_ADDR2OFFSET(dss_max)) &\n\t\t\t    chunksize_mask;\n\t\t\t/*\n\t\t\t * Compute how much chunk-aligned pad space (if any) is\n\t\t\t * necessary to satisfy alignment.  This space can be\n\t\t\t * recycled for later use.\n\t\t\t */\n\t\t\tcpad = (void *)((uintptr_t)dss_max + gap_size);\n\t\t\tret = (void *)ALIGNMENT_CEILING((uintptr_t)dss_max,\n\t\t\t    alignment);\n\t\t\tcpad_size = (uintptr_t)ret - (uintptr_t)cpad;\n\t\t\tdss_next = (void *)((uintptr_t)ret + size);\n\t\t\tif ((uintptr_t)ret < (uintptr_t)dss_max ||\n\t\t\t    (uintptr_t)dss_next < (uintptr_t)dss_max) {\n\t\t\t\t/* Wrap-around. */\n\t\t\t\tmalloc_mutex_unlock(&dss_mtx);\n\t\t\t\treturn (NULL);\n\t\t\t}\n\t\t\tincr = gap_size + cpad_size + size;\n\t\t\tdss_prev = sbrk(incr);\n\t\t\tif (dss_prev == dss_max) {\n\t\t\t\t/* Success. */\n\t\t\t\tdss_max = dss_next;\n\t\t\t\tmalloc_mutex_unlock(&dss_mtx);\n\t\t\t\tif (cpad_size != 0)\n\t\t\t\t\tchunk_unmap(cpad, cpad_size);\n\t\t\t\tif (*zero) {\n\t\t\t\t\tVALGRIND_MAKE_MEM_UNDEFINED(ret, size);\n\t\t\t\t\tmemset(ret, 0, size);\n\t\t\t\t}\n\t\t\t\treturn (ret);\n\t\t\t}\n\t\t} while (dss_prev != (void *)-1);\n\t}\n\tmalloc_mutex_unlock(&dss_mtx);\n\n\treturn (NULL);\n}\n\nbool\nchunk_in_dss(void *chunk)\n{\n\tbool ret;\n\n\tcassert(config_dss);\n\n\tmalloc_mutex_lock(&dss_mtx);\n\tif ((uintptr_t)chunk >= (uintptr_t)dss_base\n\t    && (uintptr_t)chunk < (uintptr_t)dss_max)\n\t\tret = true;\n\telse\n\t\tret = false;\n\tmalloc_mutex_unlock(&dss_mtx);\n\n\treturn (ret);\n}\n\nbool\nchunk_dss_boot(void)\n{\n\n\tcassert(config_dss);\n\n\tif (malloc_mutex_init(&dss_mtx))\n\t\treturn (true);\n\tdss_base = sbrk(0);\n\tdss_prev = dss_base;\n\tdss_max = dss_base;\n\n\treturn (false);\n}\n\nvoid\nchunk_dss_prefork(void)\n{\n\n\tif (config_dss)\n\t\tmalloc_mutex_prefork(&dss_mtx);\n}\n\nvoid\nchunk_dss_postfork_parent(void)\n{\n\n\tif (config_dss)\n\t\tmalloc_mutex_postfork_parent(&dss_mtx);\n}\n\nvoid\nchunk_dss_postfork_child(void)\n{\n\n\tif (config_dss)\n\t\tmalloc_mutex_postfork_child(&dss_mtx);\n}\n\n/******************************************************************************/\n"
  },
  {
    "path": "deps/jemalloc/src/chunk_mmap.c",
    "content": "#define\tJEMALLOC_CHUNK_MMAP_C_\n#include \"jemalloc/internal/jemalloc_internal.h\"\n\n/******************************************************************************/\n/* Function prototypes for non-inline static functions. */\n\nstatic void\t*pages_map(void *addr, size_t size);\nstatic void\tpages_unmap(void *addr, size_t size);\nstatic void\t*chunk_alloc_mmap_slow(size_t size, size_t alignment,\n    bool *zero);\n\n/******************************************************************************/\n\nstatic void *\npages_map(void *addr, size_t size)\n{\n\tvoid *ret;\n\n\tassert(size != 0);\n\n#ifdef _WIN32\n\t/*\n\t * If VirtualAlloc can't allocate at the given address when one is\n\t * given, it fails and returns NULL.\n\t */\n\tret = VirtualAlloc(addr, size, MEM_COMMIT | MEM_RESERVE,\n\t    PAGE_READWRITE);\n#else\n\t/*\n\t * We don't use MAP_FIXED here, because it can cause the *replacement*\n\t * of existing mappings, and we only want to create new mappings.\n\t */\n\tret = mmap(addr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON,\n\t    -1, 0);\n\tassert(ret != NULL);\n\n\tif (ret == MAP_FAILED)\n\t\tret = NULL;\n\telse if (addr != NULL && ret != addr) {\n\t\t/*\n\t\t * We succeeded in mapping memory, but not in the right place.\n\t\t */\n\t\tif (munmap(ret, size) == -1) {\n\t\t\tchar buf[BUFERROR_BUF];\n\n\t\t\tbuferror(buf, sizeof(buf));\n\t\t\tmalloc_printf(\"<jemalloc: Error in munmap(): %s\\n\",\n\t\t\t    buf);\n\t\t\tif (opt_abort)\n\t\t\t\tabort();\n\t\t}\n\t\tret = NULL;\n\t}\n#endif\n\tassert(ret == NULL || (addr == NULL && ret != addr)\n\t    || (addr != NULL && ret == addr));\n\treturn (ret);\n}\n\nstatic void\npages_unmap(void *addr, size_t size)\n{\n\n#ifdef _WIN32\n\tif (VirtualFree(addr, 0, MEM_RELEASE) == 0)\n#else\n\tif (munmap(addr, size) == -1)\n#endif\n\t{\n\t\tchar buf[BUFERROR_BUF];\n\n\t\tbuferror(buf, sizeof(buf));\n\t\tmalloc_printf(\"<jemalloc>: Error in \"\n#ifdef _WIN32\n\t\t              \"VirtualFree\"\n#else\n\t\t              \"munmap\"\n#endif\n\t\t              \"(): %s\\n\", buf);\n\t\tif (opt_abort)\n\t\t\tabort();\n\t}\n}\n\nstatic void *\npages_trim(void *addr, size_t alloc_size, size_t leadsize, size_t size)\n{\n\tvoid *ret = (void *)((uintptr_t)addr + leadsize);\n\n\tassert(alloc_size >= leadsize + size);\n#ifdef _WIN32\n\t{\n\t\tvoid *new_addr;\n\n\t\tpages_unmap(addr, alloc_size);\n\t\tnew_addr = pages_map(ret, size);\n\t\tif (new_addr == ret)\n\t\t\treturn (ret);\n\t\tif (new_addr)\n\t\t\tpages_unmap(new_addr, size);\n\t\treturn (NULL);\n\t}\n#else\n\t{\n\t\tsize_t trailsize = alloc_size - leadsize - size;\n\n\t\tif (leadsize != 0)\n\t\t\tpages_unmap(addr, leadsize);\n\t\tif (trailsize != 0)\n\t\t\tpages_unmap((void *)((uintptr_t)ret + size), trailsize);\n\t\treturn (ret);\n\t}\n#endif\n}\n\nbool\npages_purge(void *addr, size_t length)\n{\n\tbool unzeroed;\n\n#ifdef _WIN32\n\tVirtualAlloc(addr, length, MEM_RESET, PAGE_READWRITE);\n\tunzeroed = true;\n#else\n#  ifdef JEMALLOC_PURGE_MADVISE_DONTNEED\n#    define JEMALLOC_MADV_PURGE MADV_DONTNEED\n#    define JEMALLOC_MADV_ZEROS true\n#  elif defined(JEMALLOC_PURGE_MADVISE_FREE)\n#    define JEMALLOC_MADV_PURGE MADV_FREE\n#    define JEMALLOC_MADV_ZEROS false\n#  else\n#    error \"No method defined for purging unused dirty pages.\"\n#  endif\n\tint err = madvise(addr, length, JEMALLOC_MADV_PURGE);\n\tunzeroed = (JEMALLOC_MADV_ZEROS == false || err != 0);\n#  undef JEMALLOC_MADV_PURGE\n#  undef JEMALLOC_MADV_ZEROS\n#endif\n\treturn (unzeroed);\n}\n\nstatic void *\nchunk_alloc_mmap_slow(size_t size, size_t alignment, bool *zero)\n{\n\tvoid *ret, *pages;\n\tsize_t alloc_size, leadsize;\n\n\talloc_size = size + alignment - PAGE;\n\t/* Beware size_t wrap-around. */\n\tif (alloc_size < size)\n\t\treturn (NULL);\n\tdo {\n\t\tpages = pages_map(NULL, alloc_size);\n\t\tif (pages == NULL)\n\t\t\treturn (NULL);\n\t\tleadsize = ALIGNMENT_CEILING((uintptr_t)pages, alignment) -\n\t\t    (uintptr_t)pages;\n\t\tret = pages_trim(pages, alloc_size, leadsize, size);\n\t} while (ret == NULL);\n\n\tassert(ret != NULL);\n\t*zero = true;\n\treturn (ret);\n}\n\nvoid *\nchunk_alloc_mmap(size_t size, size_t alignment, bool *zero)\n{\n\tvoid *ret;\n\tsize_t offset;\n\n\t/*\n\t * Ideally, there would be a way to specify alignment to mmap() (like\n\t * NetBSD has), but in the absence of such a feature, we have to work\n\t * hard to efficiently create aligned mappings.  The reliable, but\n\t * slow method is to create a mapping that is over-sized, then trim the\n\t * excess.  However, that always results in one or two calls to\n\t * pages_unmap().\n\t *\n\t * Optimistically try mapping precisely the right amount before falling\n\t * back to the slow method, with the expectation that the optimistic\n\t * approach works most of the time.\n\t */\n\n\tassert(alignment != 0);\n\tassert((alignment & chunksize_mask) == 0);\n\n\tret = pages_map(NULL, size);\n\tif (ret == NULL)\n\t\treturn (NULL);\n\toffset = ALIGNMENT_ADDR2OFFSET(ret, alignment);\n\tif (offset != 0) {\n\t\tpages_unmap(ret, size);\n\t\treturn (chunk_alloc_mmap_slow(size, alignment, zero));\n\t}\n\n\tassert(ret != NULL);\n\t*zero = true;\n\treturn (ret);\n}\n\nbool\nchunk_dealloc_mmap(void *chunk, size_t size)\n{\n\n\tif (config_munmap)\n\t\tpages_unmap(chunk, size);\n\n\treturn (config_munmap == false);\n}\n"
  },
  {
    "path": "deps/jemalloc/src/ckh.c",
    "content": "/*\n *******************************************************************************\n * Implementation of (2^1+,2) cuckoo hashing, where 2^1+ indicates that each\n * hash bucket contains 2^n cells, for n >= 1, and 2 indicates that two hash\n * functions are employed.  The original cuckoo hashing algorithm was described\n * in:\n *\n *   Pagh, R., F.F. Rodler (2004) Cuckoo Hashing.  Journal of Algorithms\n *     51(2):122-144.\n *\n * Generalization of cuckoo hashing was discussed in:\n *\n *   Erlingsson, U., M. Manasse, F. McSherry (2006) A cool and practical\n *     alternative to traditional hash tables.  In Proceedings of the 7th\n *     Workshop on Distributed Data and Structures (WDAS'06), Santa Clara, CA,\n *     January 2006.\n *\n * This implementation uses precisely two hash functions because that is the\n * fewest that can work, and supporting multiple hashes is an implementation\n * burden.  Here is a reproduction of Figure 1 from Erlingsson et al. (2006)\n * that shows approximate expected maximum load factors for various\n * configurations:\n *\n *           |         #cells/bucket         |\n *   #hashes |   1   |   2   |   4   |   8   |\n *   --------+-------+-------+-------+-------+\n *         1 | 0.006 | 0.006 | 0.03  | 0.12  |\n *         2 | 0.49  | 0.86  |>0.93< |>0.96< |\n *         3 | 0.91  | 0.97  | 0.98  | 0.999 |\n *         4 | 0.97  | 0.99  | 0.999 |       |\n *\n * The number of cells per bucket is chosen such that a bucket fits in one cache\n * line.  So, on 32- and 64-bit systems, we use (8,2) and (4,2) cuckoo hashing,\n * respectively.\n *\n ******************************************************************************/\n#define\tJEMALLOC_CKH_C_\n#include \"jemalloc/internal/jemalloc_internal.h\"\n\n/******************************************************************************/\n/* Function prototypes for non-inline static functions. */\n\nstatic bool\tckh_grow(ckh_t *ckh);\nstatic void\tckh_shrink(ckh_t *ckh);\n\n/******************************************************************************/\n\n/*\n * Search bucket for key and return the cell number if found; SIZE_T_MAX\n * otherwise.\n */\nJEMALLOC_INLINE size_t\nckh_bucket_search(ckh_t *ckh, size_t bucket, const void *key)\n{\n\tckhc_t *cell;\n\tunsigned i;\n\n\tfor (i = 0; i < (ZU(1) << LG_CKH_BUCKET_CELLS); i++) {\n\t\tcell = &ckh->tab[(bucket << LG_CKH_BUCKET_CELLS) + i];\n\t\tif (cell->key != NULL && ckh->keycomp(key, cell->key))\n\t\t\treturn ((bucket << LG_CKH_BUCKET_CELLS) + i);\n\t}\n\n\treturn (SIZE_T_MAX);\n}\n\n/*\n * Search table for key and return cell number if found; SIZE_T_MAX otherwise.\n */\nJEMALLOC_INLINE size_t\nckh_isearch(ckh_t *ckh, const void *key)\n{\n\tsize_t hash1, hash2, bucket, cell;\n\n\tassert(ckh != NULL);\n\n\tckh->hash(key, ckh->lg_curbuckets, &hash1, &hash2);\n\n\t/* Search primary bucket. */\n\tbucket = hash1 & ((ZU(1) << ckh->lg_curbuckets) - 1);\n\tcell = ckh_bucket_search(ckh, bucket, key);\n\tif (cell != SIZE_T_MAX)\n\t\treturn (cell);\n\n\t/* Search secondary bucket. */\n\tbucket = hash2 & ((ZU(1) << ckh->lg_curbuckets) - 1);\n\tcell = ckh_bucket_search(ckh, bucket, key);\n\treturn (cell);\n}\n\nJEMALLOC_INLINE bool\nckh_try_bucket_insert(ckh_t *ckh, size_t bucket, const void *key,\n    const void *data)\n{\n\tckhc_t *cell;\n\tunsigned offset, i;\n\n\t/*\n\t * Cycle through the cells in the bucket, starting at a random position.\n\t * The randomness avoids worst-case search overhead as buckets fill up.\n\t */\n\tprng32(offset, LG_CKH_BUCKET_CELLS, ckh->prng_state, CKH_A, CKH_C);\n\tfor (i = 0; i < (ZU(1) << LG_CKH_BUCKET_CELLS); i++) {\n\t\tcell = &ckh->tab[(bucket << LG_CKH_BUCKET_CELLS) +\n\t\t    ((i + offset) & ((ZU(1) << LG_CKH_BUCKET_CELLS) - 1))];\n\t\tif (cell->key == NULL) {\n\t\t\tcell->key = key;\n\t\t\tcell->data = data;\n\t\t\tckh->count++;\n\t\t\treturn (false);\n\t\t}\n\t}\n\n\treturn (true);\n}\n\n/*\n * No space is available in bucket.  Randomly evict an item, then try to find an\n * alternate location for that item.  Iteratively repeat this\n * eviction/relocation procedure until either success or detection of an\n * eviction/relocation bucket cycle.\n */\nJEMALLOC_INLINE bool\nckh_evict_reloc_insert(ckh_t *ckh, size_t argbucket, void const **argkey,\n    void const **argdata)\n{\n\tconst void *key, *data, *tkey, *tdata;\n\tckhc_t *cell;\n\tsize_t hash1, hash2, bucket, tbucket;\n\tunsigned i;\n\n\tbucket = argbucket;\n\tkey = *argkey;\n\tdata = *argdata;\n\twhile (true) {\n\t\t/*\n\t\t * Choose a random item within the bucket to evict.  This is\n\t\t * critical to correct function, because without (eventually)\n\t\t * evicting all items within a bucket during iteration, it\n\t\t * would be possible to get stuck in an infinite loop if there\n\t\t * were an item for which both hashes indicated the same\n\t\t * bucket.\n\t\t */\n\t\tprng32(i, LG_CKH_BUCKET_CELLS, ckh->prng_state, CKH_A, CKH_C);\n\t\tcell = &ckh->tab[(bucket << LG_CKH_BUCKET_CELLS) + i];\n\t\tassert(cell->key != NULL);\n\n\t\t/* Swap cell->{key,data} and {key,data} (evict). */\n\t\ttkey = cell->key; tdata = cell->data;\n\t\tcell->key = key; cell->data = data;\n\t\tkey = tkey; data = tdata;\n\n#ifdef CKH_COUNT\n\t\tckh->nrelocs++;\n#endif\n\n\t\t/* Find the alternate bucket for the evicted item. */\n\t\tckh->hash(key, ckh->lg_curbuckets, &hash1, &hash2);\n\t\ttbucket = hash2 & ((ZU(1) << ckh->lg_curbuckets) - 1);\n\t\tif (tbucket == bucket) {\n\t\t\ttbucket = hash1 & ((ZU(1) << ckh->lg_curbuckets) - 1);\n\t\t\t/*\n\t\t\t * It may be that (tbucket == bucket) still, if the\n\t\t\t * item's hashes both indicate this bucket.  However,\n\t\t\t * we are guaranteed to eventually escape this bucket\n\t\t\t * during iteration, assuming pseudo-random item\n\t\t\t * selection (true randomness would make infinite\n\t\t\t * looping a remote possibility).  The reason we can\n\t\t\t * never get trapped forever is that there are two\n\t\t\t * cases:\n\t\t\t *\n\t\t\t * 1) This bucket == argbucket, so we will quickly\n\t\t\t *    detect an eviction cycle and terminate.\n\t\t\t * 2) An item was evicted to this bucket from another,\n\t\t\t *    which means that at least one item in this bucket\n\t\t\t *    has hashes that indicate distinct buckets.\n\t\t\t */\n\t\t}\n\t\t/* Check for a cycle. */\n\t\tif (tbucket == argbucket) {\n\t\t\t*argkey = key;\n\t\t\t*argdata = data;\n\t\t\treturn (true);\n\t\t}\n\n\t\tbucket = tbucket;\n\t\tif (ckh_try_bucket_insert(ckh, bucket, key, data) == false)\n\t\t\treturn (false);\n\t}\n}\n\nJEMALLOC_INLINE bool\nckh_try_insert(ckh_t *ckh, void const**argkey, void const**argdata)\n{\n\tsize_t hash1, hash2, bucket;\n\tconst void *key = *argkey;\n\tconst void *data = *argdata;\n\n\tckh->hash(key, ckh->lg_curbuckets, &hash1, &hash2);\n\n\t/* Try to insert in primary bucket. */\n\tbucket = hash1 & ((ZU(1) << ckh->lg_curbuckets) - 1);\n\tif (ckh_try_bucket_insert(ckh, bucket, key, data) == false)\n\t\treturn (false);\n\n\t/* Try to insert in secondary bucket. */\n\tbucket = hash2 & ((ZU(1) << ckh->lg_curbuckets) - 1);\n\tif (ckh_try_bucket_insert(ckh, bucket, key, data) == false)\n\t\treturn (false);\n\n\t/*\n\t * Try to find a place for this item via iterative eviction/relocation.\n\t */\n\treturn (ckh_evict_reloc_insert(ckh, bucket, argkey, argdata));\n}\n\n/*\n * Try to rebuild the hash table from scratch by inserting all items from the\n * old table into the new.\n */\nJEMALLOC_INLINE bool\nckh_rebuild(ckh_t *ckh, ckhc_t *aTab)\n{\n\tsize_t count, i, nins;\n\tconst void *key, *data;\n\n\tcount = ckh->count;\n\tckh->count = 0;\n\tfor (i = nins = 0; nins < count; i++) {\n\t\tif (aTab[i].key != NULL) {\n\t\t\tkey = aTab[i].key;\n\t\t\tdata = aTab[i].data;\n\t\t\tif (ckh_try_insert(ckh, &key, &data)) {\n\t\t\t\tckh->count = count;\n\t\t\t\treturn (true);\n\t\t\t}\n\t\t\tnins++;\n\t\t}\n\t}\n\n\treturn (false);\n}\n\nstatic bool\nckh_grow(ckh_t *ckh)\n{\n\tbool ret;\n\tckhc_t *tab, *ttab;\n\tsize_t lg_curcells;\n\tunsigned lg_prevbuckets;\n\n#ifdef CKH_COUNT\n\tckh->ngrows++;\n#endif\n\n\t/*\n\t * It is possible (though unlikely, given well behaved hashes) that the\n\t * table will have to be doubled more than once in order to create a\n\t * usable table.\n\t */\n\tlg_prevbuckets = ckh->lg_curbuckets;\n\tlg_curcells = ckh->lg_curbuckets + LG_CKH_BUCKET_CELLS;\n\twhile (true) {\n\t\tsize_t usize;\n\n\t\tlg_curcells++;\n\t\tusize = sa2u(sizeof(ckhc_t) << lg_curcells, CACHELINE);\n\t\tif (usize == 0) {\n\t\t\tret = true;\n\t\t\tgoto label_return;\n\t\t}\n\t\ttab = (ckhc_t *)ipalloc(usize, CACHELINE, true);\n\t\tif (tab == NULL) {\n\t\t\tret = true;\n\t\t\tgoto label_return;\n\t\t}\n\t\t/* Swap in new table. */\n\t\tttab = ckh->tab;\n\t\tckh->tab = tab;\n\t\ttab = ttab;\n\t\tckh->lg_curbuckets = lg_curcells - LG_CKH_BUCKET_CELLS;\n\n\t\tif (ckh_rebuild(ckh, tab) == false) {\n\t\t\tidalloc(tab);\n\t\t\tbreak;\n\t\t}\n\n\t\t/* Rebuilding failed, so back out partially rebuilt table. */\n\t\tidalloc(ckh->tab);\n\t\tckh->tab = tab;\n\t\tckh->lg_curbuckets = lg_prevbuckets;\n\t}\n\n\tret = false;\nlabel_return:\n\treturn (ret);\n}\n\nstatic void\nckh_shrink(ckh_t *ckh)\n{\n\tckhc_t *tab, *ttab;\n\tsize_t lg_curcells, usize;\n\tunsigned lg_prevbuckets;\n\n\t/*\n\t * It is possible (though unlikely, given well behaved hashes) that the\n\t * table rebuild will fail.\n\t */\n\tlg_prevbuckets = ckh->lg_curbuckets;\n\tlg_curcells = ckh->lg_curbuckets + LG_CKH_BUCKET_CELLS - 1;\n\tusize = sa2u(sizeof(ckhc_t) << lg_curcells, CACHELINE);\n\tif (usize == 0)\n\t\treturn;\n\ttab = (ckhc_t *)ipalloc(usize, CACHELINE, true);\n\tif (tab == NULL) {\n\t\t/*\n\t\t * An OOM error isn't worth propagating, since it doesn't\n\t\t * prevent this or future operations from proceeding.\n\t\t */\n\t\treturn;\n\t}\n\t/* Swap in new table. */\n\tttab = ckh->tab;\n\tckh->tab = tab;\n\ttab = ttab;\n\tckh->lg_curbuckets = lg_curcells - LG_CKH_BUCKET_CELLS;\n\n\tif (ckh_rebuild(ckh, tab) == false) {\n\t\tidalloc(tab);\n#ifdef CKH_COUNT\n\t\tckh->nshrinks++;\n#endif\n\t\treturn;\n\t}\n\n\t/* Rebuilding failed, so back out partially rebuilt table. */\n\tidalloc(ckh->tab);\n\tckh->tab = tab;\n\tckh->lg_curbuckets = lg_prevbuckets;\n#ifdef CKH_COUNT\n\tckh->nshrinkfails++;\n#endif\n}\n\nbool\nckh_new(ckh_t *ckh, size_t minitems, ckh_hash_t *hash, ckh_keycomp_t *keycomp)\n{\n\tbool ret;\n\tsize_t mincells, usize;\n\tunsigned lg_mincells;\n\n\tassert(minitems > 0);\n\tassert(hash != NULL);\n\tassert(keycomp != NULL);\n\n#ifdef CKH_COUNT\n\tckh->ngrows = 0;\n\tckh->nshrinks = 0;\n\tckh->nshrinkfails = 0;\n\tckh->ninserts = 0;\n\tckh->nrelocs = 0;\n#endif\n\tckh->prng_state = 42; /* Value doesn't really matter. */\n\tckh->count = 0;\n\n\t/*\n\t * Find the minimum power of 2 that is large enough to fit aBaseCount\n\t * entries.  We are using (2+,2) cuckoo hashing, which has an expected\n\t * maximum load factor of at least ~0.86, so 0.75 is a conservative load\n\t * factor that will typically allow 2^aLgMinItems to fit without ever\n\t * growing the table.\n\t */\n\tassert(LG_CKH_BUCKET_CELLS > 0);\n\tmincells = ((minitems + (3 - (minitems % 3))) / 3) << 2;\n\tfor (lg_mincells = LG_CKH_BUCKET_CELLS;\n\t    (ZU(1) << lg_mincells) < mincells;\n\t    lg_mincells++)\n\t\t; /* Do nothing. */\n\tckh->lg_minbuckets = lg_mincells - LG_CKH_BUCKET_CELLS;\n\tckh->lg_curbuckets = lg_mincells - LG_CKH_BUCKET_CELLS;\n\tckh->hash = hash;\n\tckh->keycomp = keycomp;\n\n\tusize = sa2u(sizeof(ckhc_t) << lg_mincells, CACHELINE);\n\tif (usize == 0) {\n\t\tret = true;\n\t\tgoto label_return;\n\t}\n\tckh->tab = (ckhc_t *)ipalloc(usize, CACHELINE, true);\n\tif (ckh->tab == NULL) {\n\t\tret = true;\n\t\tgoto label_return;\n\t}\n\n\tret = false;\nlabel_return:\n\treturn (ret);\n}\n\nvoid\nckh_delete(ckh_t *ckh)\n{\n\n\tassert(ckh != NULL);\n\n#ifdef CKH_VERBOSE\n\tmalloc_printf(\n\t    \"%s(%p): ngrows: %\"PRIu64\", nshrinks: %\"PRIu64\",\"\n\t    \" nshrinkfails: %\"PRIu64\", ninserts: %\"PRIu64\",\"\n\t    \" nrelocs: %\"PRIu64\"\\n\", __func__, ckh,\n\t    (unsigned long long)ckh->ngrows,\n\t    (unsigned long long)ckh->nshrinks,\n\t    (unsigned long long)ckh->nshrinkfails,\n\t    (unsigned long long)ckh->ninserts,\n\t    (unsigned long long)ckh->nrelocs);\n#endif\n\n\tidalloc(ckh->tab);\n#ifdef JEMALLOC_DEBUG\n\tmemset(ckh, 0x5a, sizeof(ckh_t));\n#endif\n}\n\nsize_t\nckh_count(ckh_t *ckh)\n{\n\n\tassert(ckh != NULL);\n\n\treturn (ckh->count);\n}\n\nbool\nckh_iter(ckh_t *ckh, size_t *tabind, void **key, void **data)\n{\n\tsize_t i, ncells;\n\n\tfor (i = *tabind, ncells = (ZU(1) << (ckh->lg_curbuckets +\n\t    LG_CKH_BUCKET_CELLS)); i < ncells; i++) {\n\t\tif (ckh->tab[i].key != NULL) {\n\t\t\tif (key != NULL)\n\t\t\t\t*key = (void *)ckh->tab[i].key;\n\t\t\tif (data != NULL)\n\t\t\t\t*data = (void *)ckh->tab[i].data;\n\t\t\t*tabind = i + 1;\n\t\t\treturn (false);\n\t\t}\n\t}\n\n\treturn (true);\n}\n\nbool\nckh_insert(ckh_t *ckh, const void *key, const void *data)\n{\n\tbool ret;\n\n\tassert(ckh != NULL);\n\tassert(ckh_search(ckh, key, NULL, NULL));\n\n#ifdef CKH_COUNT\n\tckh->ninserts++;\n#endif\n\n\twhile (ckh_try_insert(ckh, &key, &data)) {\n\t\tif (ckh_grow(ckh)) {\n\t\t\tret = true;\n\t\t\tgoto label_return;\n\t\t}\n\t}\n\n\tret = false;\nlabel_return:\n\treturn (ret);\n}\n\nbool\nckh_remove(ckh_t *ckh, const void *searchkey, void **key, void **data)\n{\n\tsize_t cell;\n\n\tassert(ckh != NULL);\n\n\tcell = ckh_isearch(ckh, searchkey);\n\tif (cell != SIZE_T_MAX) {\n\t\tif (key != NULL)\n\t\t\t*key = (void *)ckh->tab[cell].key;\n\t\tif (data != NULL)\n\t\t\t*data = (void *)ckh->tab[cell].data;\n\t\tckh->tab[cell].key = NULL;\n\t\tckh->tab[cell].data = NULL; /* Not necessary. */\n\n\t\tckh->count--;\n\t\t/* Try to halve the table if it is less than 1/4 full. */\n\t\tif (ckh->count < (ZU(1) << (ckh->lg_curbuckets\n\t\t    + LG_CKH_BUCKET_CELLS - 2)) && ckh->lg_curbuckets\n\t\t    > ckh->lg_minbuckets) {\n\t\t\t/* Ignore error due to OOM. */\n\t\t\tckh_shrink(ckh);\n\t\t}\n\n\t\treturn (false);\n\t}\n\n\treturn (true);\n}\n\nbool\nckh_search(ckh_t *ckh, const void *searchkey, void **key, void **data)\n{\n\tsize_t cell;\n\n\tassert(ckh != NULL);\n\n\tcell = ckh_isearch(ckh, searchkey);\n\tif (cell != SIZE_T_MAX) {\n\t\tif (key != NULL)\n\t\t\t*key = (void *)ckh->tab[cell].key;\n\t\tif (data != NULL)\n\t\t\t*data = (void *)ckh->tab[cell].data;\n\t\treturn (false);\n\t}\n\n\treturn (true);\n}\n\nvoid\nckh_string_hash(const void *key, unsigned minbits, size_t *hash1, size_t *hash2)\n{\n\tsize_t ret1, ret2;\n\tuint64_t h;\n\n\tassert(minbits <= 32 || (SIZEOF_PTR == 8 && minbits <= 64));\n\tassert(hash1 != NULL);\n\tassert(hash2 != NULL);\n\n\th = hash(key, strlen((const char *)key), UINT64_C(0x94122f335b332aea));\n\tif (minbits <= 32) {\n\t\t/*\n\t\t * Avoid doing multiple hashes, since a single hash provides\n\t\t * enough bits.\n\t\t */\n\t\tret1 = h & ZU(0xffffffffU);\n\t\tret2 = h >> 32;\n\t} else {\n\t\tret1 = h;\n\t\tret2 = hash(key, strlen((const char *)key),\n\t\t    UINT64_C(0x8432a476666bbc13));\n\t}\n\n\t*hash1 = ret1;\n\t*hash2 = ret2;\n}\n\nbool\nckh_string_keycomp(const void *k1, const void *k2)\n{\n\n    assert(k1 != NULL);\n    assert(k2 != NULL);\n\n    return (strcmp((char *)k1, (char *)k2) ? false : true);\n}\n\nvoid\nckh_pointer_hash(const void *key, unsigned minbits, size_t *hash1,\n    size_t *hash2)\n{\n\tsize_t ret1, ret2;\n\tuint64_t h;\n\tunion {\n\t\tconst void\t*v;\n\t\tuint64_t\ti;\n\t} u;\n\n\tassert(minbits <= 32 || (SIZEOF_PTR == 8 && minbits <= 64));\n\tassert(hash1 != NULL);\n\tassert(hash2 != NULL);\n\n\tassert(sizeof(u.v) == sizeof(u.i));\n#if (LG_SIZEOF_PTR != LG_SIZEOF_INT)\n\tu.i = 0;\n#endif\n\tu.v = key;\n\th = hash(&u.i, sizeof(u.i), UINT64_C(0xd983396e68886082));\n\tif (minbits <= 32) {\n\t\t/*\n\t\t * Avoid doing multiple hashes, since a single hash provides\n\t\t * enough bits.\n\t\t */\n\t\tret1 = h & ZU(0xffffffffU);\n\t\tret2 = h >> 32;\n\t} else {\n\t\tassert(SIZEOF_PTR == 8);\n\t\tret1 = h;\n\t\tret2 = hash(&u.i, sizeof(u.i), UINT64_C(0x5e2be9aff8709a5d));\n\t}\n\n\t*hash1 = ret1;\n\t*hash2 = ret2;\n}\n\nbool\nckh_pointer_keycomp(const void *k1, const void *k2)\n{\n\n\treturn ((k1 == k2) ? true : false);\n}\n"
  },
  {
    "path": "deps/jemalloc/src/ctl.c",
    "content": "#define\tJEMALLOC_CTL_C_\n#include \"jemalloc/internal/jemalloc_internal.h\"\n\n/******************************************************************************/\n/* Data. */\n\n/*\n * ctl_mtx protects the following:\n * - ctl_stats.*\n * - opt_prof_active\n */\nstatic malloc_mutex_t\tctl_mtx;\nstatic bool\t\tctl_initialized;\nstatic uint64_t\t\tctl_epoch;\nstatic ctl_stats_t\tctl_stats;\n\n/******************************************************************************/\n/* Helpers for named and indexed nodes. */\n\nstatic inline const ctl_named_node_t *\nctl_named_node(const ctl_node_t *node)\n{\n\n\treturn ((node->named) ? (const ctl_named_node_t *)node : NULL);\n}\n\nstatic inline const ctl_named_node_t *\nctl_named_children(const ctl_named_node_t *node, int index)\n{\n\tconst ctl_named_node_t *children = ctl_named_node(node->children);\n\n\treturn (children ? &children[index] : NULL);\n}\n\nstatic inline const ctl_indexed_node_t *\nctl_indexed_node(const ctl_node_t *node)\n{\n\n\treturn ((node->named == false) ? (const ctl_indexed_node_t *)node :\n\t    NULL);\n}\n\n/******************************************************************************/\n/* Function prototypes for non-inline static functions. */\n\n#define\tCTL_PROTO(n)\t\t\t\t\t\t\t\\\nstatic int\tn##_ctl(const size_t *mib, size_t miblen, void *oldp,\t\\\n    size_t *oldlenp, void *newp, size_t newlen);\n\n#define\tINDEX_PROTO(n)\t\t\t\t\t\t\t\\\nstatic const ctl_named_node_t\t*n##_index(const size_t *mib,\t\t\\\n    size_t miblen, size_t i);\n\nstatic bool\tctl_arena_init(ctl_arena_stats_t *astats);\nstatic void\tctl_arena_clear(ctl_arena_stats_t *astats);\nstatic void\tctl_arena_stats_amerge(ctl_arena_stats_t *cstats,\n    arena_t *arena);\nstatic void\tctl_arena_stats_smerge(ctl_arena_stats_t *sstats,\n    ctl_arena_stats_t *astats);\nstatic void\tctl_arena_refresh(arena_t *arena, unsigned i);\nstatic bool\tctl_grow(void);\nstatic void\tctl_refresh(void);\nstatic bool\tctl_init(void);\nstatic int\tctl_lookup(const char *name, ctl_node_t const **nodesp,\n    size_t *mibp, size_t *depthp);\n\nCTL_PROTO(version)\nCTL_PROTO(epoch)\nCTL_PROTO(thread_tcache_enabled)\nCTL_PROTO(thread_tcache_flush)\nCTL_PROTO(thread_arena)\nCTL_PROTO(thread_allocated)\nCTL_PROTO(thread_allocatedp)\nCTL_PROTO(thread_deallocated)\nCTL_PROTO(thread_deallocatedp)\nCTL_PROTO(config_debug)\nCTL_PROTO(config_dss)\nCTL_PROTO(config_fill)\nCTL_PROTO(config_lazy_lock)\nCTL_PROTO(config_mremap)\nCTL_PROTO(config_munmap)\nCTL_PROTO(config_prof)\nCTL_PROTO(config_prof_libgcc)\nCTL_PROTO(config_prof_libunwind)\nCTL_PROTO(config_stats)\nCTL_PROTO(config_tcache)\nCTL_PROTO(config_tls)\nCTL_PROTO(config_utrace)\nCTL_PROTO(config_valgrind)\nCTL_PROTO(config_xmalloc)\nCTL_PROTO(opt_abort)\nCTL_PROTO(opt_dss)\nCTL_PROTO(opt_lg_chunk)\nCTL_PROTO(opt_narenas)\nCTL_PROTO(opt_lg_dirty_mult)\nCTL_PROTO(opt_stats_print)\nCTL_PROTO(opt_junk)\nCTL_PROTO(opt_zero)\nCTL_PROTO(opt_quarantine)\nCTL_PROTO(opt_redzone)\nCTL_PROTO(opt_utrace)\nCTL_PROTO(opt_valgrind)\nCTL_PROTO(opt_xmalloc)\nCTL_PROTO(opt_tcache)\nCTL_PROTO(opt_lg_tcache_max)\nCTL_PROTO(opt_prof)\nCTL_PROTO(opt_prof_prefix)\nCTL_PROTO(opt_prof_active)\nCTL_PROTO(opt_lg_prof_sample)\nCTL_PROTO(opt_lg_prof_interval)\nCTL_PROTO(opt_prof_gdump)\nCTL_PROTO(opt_prof_final)\nCTL_PROTO(opt_prof_leak)\nCTL_PROTO(opt_prof_accum)\nCTL_PROTO(arena_i_purge)\nstatic void\tarena_purge(unsigned arena_ind);\nCTL_PROTO(arena_i_dss)\nINDEX_PROTO(arena_i)\nCTL_PROTO(arenas_bin_i_size)\nCTL_PROTO(arenas_bin_i_nregs)\nCTL_PROTO(arenas_bin_i_run_size)\nINDEX_PROTO(arenas_bin_i)\nCTL_PROTO(arenas_lrun_i_size)\nINDEX_PROTO(arenas_lrun_i)\nCTL_PROTO(arenas_narenas)\nCTL_PROTO(arenas_initialized)\nCTL_PROTO(arenas_quantum)\nCTL_PROTO(arenas_page)\nCTL_PROTO(arenas_tcache_max)\nCTL_PROTO(arenas_nbins)\nCTL_PROTO(arenas_nhbins)\nCTL_PROTO(arenas_nlruns)\nCTL_PROTO(arenas_purge)\nCTL_PROTO(arenas_extend)\nCTL_PROTO(prof_active)\nCTL_PROTO(prof_dump)\nCTL_PROTO(prof_interval)\nCTL_PROTO(stats_chunks_current)\nCTL_PROTO(stats_chunks_total)\nCTL_PROTO(stats_chunks_high)\nCTL_PROTO(stats_huge_allocated)\nCTL_PROTO(stats_huge_nmalloc)\nCTL_PROTO(stats_huge_ndalloc)\nCTL_PROTO(stats_arenas_i_small_allocated)\nCTL_PROTO(stats_arenas_i_small_nmalloc)\nCTL_PROTO(stats_arenas_i_small_ndalloc)\nCTL_PROTO(stats_arenas_i_small_nrequests)\nCTL_PROTO(stats_arenas_i_large_allocated)\nCTL_PROTO(stats_arenas_i_large_nmalloc)\nCTL_PROTO(stats_arenas_i_large_ndalloc)\nCTL_PROTO(stats_arenas_i_large_nrequests)\nCTL_PROTO(stats_arenas_i_bins_j_allocated)\nCTL_PROTO(stats_arenas_i_bins_j_nmalloc)\nCTL_PROTO(stats_arenas_i_bins_j_ndalloc)\nCTL_PROTO(stats_arenas_i_bins_j_nrequests)\nCTL_PROTO(stats_arenas_i_bins_j_nfills)\nCTL_PROTO(stats_arenas_i_bins_j_nflushes)\nCTL_PROTO(stats_arenas_i_bins_j_nruns)\nCTL_PROTO(stats_arenas_i_bins_j_nreruns)\nCTL_PROTO(stats_arenas_i_bins_j_curruns)\nINDEX_PROTO(stats_arenas_i_bins_j)\nCTL_PROTO(stats_arenas_i_lruns_j_nmalloc)\nCTL_PROTO(stats_arenas_i_lruns_j_ndalloc)\nCTL_PROTO(stats_arenas_i_lruns_j_nrequests)\nCTL_PROTO(stats_arenas_i_lruns_j_curruns)\nINDEX_PROTO(stats_arenas_i_lruns_j)\nCTL_PROTO(stats_arenas_i_nthreads)\nCTL_PROTO(stats_arenas_i_dss)\nCTL_PROTO(stats_arenas_i_pactive)\nCTL_PROTO(stats_arenas_i_pdirty)\nCTL_PROTO(stats_arenas_i_mapped)\nCTL_PROTO(stats_arenas_i_npurge)\nCTL_PROTO(stats_arenas_i_nmadvise)\nCTL_PROTO(stats_arenas_i_purged)\nINDEX_PROTO(stats_arenas_i)\nCTL_PROTO(stats_cactive)\nCTL_PROTO(stats_allocated)\nCTL_PROTO(stats_active)\nCTL_PROTO(stats_mapped)\n\n/******************************************************************************/\n/* mallctl tree. */\n\n/* Maximum tree depth. */\n#define\tCTL_MAX_DEPTH\t6\n\n#define\tNAME(n)\t{true},\tn\n#define\tCHILD(t, c)\t\t\t\t\t\t\t\\\n\tsizeof(c##_node) / sizeof(ctl_##t##_node_t),\t\t\t\\\n\t(ctl_node_t *)c##_node,\t\t\t\t\t\t\\\n\tNULL\n#define\tCTL(c)\t0, NULL, c##_ctl\n\n/*\n * Only handles internal indexed nodes, since there are currently no external\n * ones.\n */\n#define\tINDEX(i)\t{false},\ti##_index\n\nstatic const ctl_named_node_t\ttcache_node[] = {\n\t{NAME(\"enabled\"),\tCTL(thread_tcache_enabled)},\n\t{NAME(\"flush\"),\t\tCTL(thread_tcache_flush)}\n};\n\nstatic const ctl_named_node_t\tthread_node[] = {\n\t{NAME(\"arena\"),\t\tCTL(thread_arena)},\n\t{NAME(\"allocated\"),\tCTL(thread_allocated)},\n\t{NAME(\"allocatedp\"),\tCTL(thread_allocatedp)},\n\t{NAME(\"deallocated\"),\tCTL(thread_deallocated)},\n\t{NAME(\"deallocatedp\"),\tCTL(thread_deallocatedp)},\n\t{NAME(\"tcache\"),\tCHILD(named, tcache)}\n};\n\nstatic const ctl_named_node_t\tconfig_node[] = {\n\t{NAME(\"debug\"),\t\t\tCTL(config_debug)},\n\t{NAME(\"dss\"),\t\t\tCTL(config_dss)},\n\t{NAME(\"fill\"),\t\t\tCTL(config_fill)},\n\t{NAME(\"lazy_lock\"),\t\tCTL(config_lazy_lock)},\n\t{NAME(\"mremap\"),\t\tCTL(config_mremap)},\n\t{NAME(\"munmap\"),\t\tCTL(config_munmap)},\n\t{NAME(\"prof\"),\t\t\tCTL(config_prof)},\n\t{NAME(\"prof_libgcc\"),\t\tCTL(config_prof_libgcc)},\n\t{NAME(\"prof_libunwind\"),\tCTL(config_prof_libunwind)},\n\t{NAME(\"stats\"),\t\t\tCTL(config_stats)},\n\t{NAME(\"tcache\"),\t\tCTL(config_tcache)},\n\t{NAME(\"tls\"),\t\t\tCTL(config_tls)},\n\t{NAME(\"utrace\"),\t\tCTL(config_utrace)},\n\t{NAME(\"valgrind\"),\t\tCTL(config_valgrind)},\n\t{NAME(\"xmalloc\"),\t\tCTL(config_xmalloc)}\n};\n\nstatic const ctl_named_node_t opt_node[] = {\n\t{NAME(\"abort\"),\t\t\tCTL(opt_abort)},\n\t{NAME(\"dss\"),\t\t\tCTL(opt_dss)},\n\t{NAME(\"lg_chunk\"),\t\tCTL(opt_lg_chunk)},\n\t{NAME(\"narenas\"),\t\tCTL(opt_narenas)},\n\t{NAME(\"lg_dirty_mult\"),\t\tCTL(opt_lg_dirty_mult)},\n\t{NAME(\"stats_print\"),\t\tCTL(opt_stats_print)},\n\t{NAME(\"junk\"),\t\t\tCTL(opt_junk)},\n\t{NAME(\"zero\"),\t\t\tCTL(opt_zero)},\n\t{NAME(\"quarantine\"),\t\tCTL(opt_quarantine)},\n\t{NAME(\"redzone\"),\t\tCTL(opt_redzone)},\n\t{NAME(\"utrace\"),\t\tCTL(opt_utrace)},\n\t{NAME(\"valgrind\"),\t\tCTL(opt_valgrind)},\n\t{NAME(\"xmalloc\"),\t\tCTL(opt_xmalloc)},\n\t{NAME(\"tcache\"),\t\tCTL(opt_tcache)},\n\t{NAME(\"lg_tcache_max\"),\t\tCTL(opt_lg_tcache_max)},\n\t{NAME(\"prof\"),\t\t\tCTL(opt_prof)},\n\t{NAME(\"prof_prefix\"),\t\tCTL(opt_prof_prefix)},\n\t{NAME(\"prof_active\"),\t\tCTL(opt_prof_active)},\n\t{NAME(\"lg_prof_sample\"),\tCTL(opt_lg_prof_sample)},\n\t{NAME(\"lg_prof_interval\"),\tCTL(opt_lg_prof_interval)},\n\t{NAME(\"prof_gdump\"),\t\tCTL(opt_prof_gdump)},\n\t{NAME(\"prof_final\"),\t\tCTL(opt_prof_final)},\n\t{NAME(\"prof_leak\"),\t\tCTL(opt_prof_leak)},\n\t{NAME(\"prof_accum\"),\t\tCTL(opt_prof_accum)}\n};\n\nstatic const ctl_named_node_t arena_i_node[] = {\n\t{NAME(\"purge\"),\t\t\tCTL(arena_i_purge)},\n\t{NAME(\"dss\"),\t\t\tCTL(arena_i_dss)}\n};\nstatic const ctl_named_node_t super_arena_i_node[] = {\n\t{NAME(\"\"),\t\t\tCHILD(named, arena_i)}\n};\n\nstatic const ctl_indexed_node_t arena_node[] = {\n\t{INDEX(arena_i)}\n};\n\nstatic const ctl_named_node_t arenas_bin_i_node[] = {\n\t{NAME(\"size\"),\t\t\tCTL(arenas_bin_i_size)},\n\t{NAME(\"nregs\"),\t\t\tCTL(arenas_bin_i_nregs)},\n\t{NAME(\"run_size\"),\t\tCTL(arenas_bin_i_run_size)}\n};\nstatic const ctl_named_node_t super_arenas_bin_i_node[] = {\n\t{NAME(\"\"),\t\t\tCHILD(named, arenas_bin_i)}\n};\n\nstatic const ctl_indexed_node_t arenas_bin_node[] = {\n\t{INDEX(arenas_bin_i)}\n};\n\nstatic const ctl_named_node_t arenas_lrun_i_node[] = {\n\t{NAME(\"size\"),\t\t\tCTL(arenas_lrun_i_size)}\n};\nstatic const ctl_named_node_t super_arenas_lrun_i_node[] = {\n\t{NAME(\"\"),\t\t\tCHILD(named, arenas_lrun_i)}\n};\n\nstatic const ctl_indexed_node_t arenas_lrun_node[] = {\n\t{INDEX(arenas_lrun_i)}\n};\n\nstatic const ctl_named_node_t arenas_node[] = {\n\t{NAME(\"narenas\"),\t\tCTL(arenas_narenas)},\n\t{NAME(\"initialized\"),\t\tCTL(arenas_initialized)},\n\t{NAME(\"quantum\"),\t\tCTL(arenas_quantum)},\n\t{NAME(\"page\"),\t\t\tCTL(arenas_page)},\n\t{NAME(\"tcache_max\"),\t\tCTL(arenas_tcache_max)},\n\t{NAME(\"nbins\"),\t\t\tCTL(arenas_nbins)},\n\t{NAME(\"nhbins\"),\t\tCTL(arenas_nhbins)},\n\t{NAME(\"bin\"),\t\t\tCHILD(indexed, arenas_bin)},\n\t{NAME(\"nlruns\"),\t\tCTL(arenas_nlruns)},\n\t{NAME(\"lrun\"),\t\t\tCHILD(indexed, arenas_lrun)},\n\t{NAME(\"purge\"),\t\t\tCTL(arenas_purge)},\n\t{NAME(\"extend\"),\t\tCTL(arenas_extend)}\n};\n\nstatic const ctl_named_node_t\tprof_node[] = {\n\t{NAME(\"active\"),\tCTL(prof_active)},\n\t{NAME(\"dump\"),\t\tCTL(prof_dump)},\n\t{NAME(\"interval\"),\tCTL(prof_interval)}\n};\n\nstatic const ctl_named_node_t stats_chunks_node[] = {\n\t{NAME(\"current\"),\t\tCTL(stats_chunks_current)},\n\t{NAME(\"total\"),\t\t\tCTL(stats_chunks_total)},\n\t{NAME(\"high\"),\t\t\tCTL(stats_chunks_high)}\n};\n\nstatic const ctl_named_node_t stats_huge_node[] = {\n\t{NAME(\"allocated\"),\t\tCTL(stats_huge_allocated)},\n\t{NAME(\"nmalloc\"),\t\tCTL(stats_huge_nmalloc)},\n\t{NAME(\"ndalloc\"),\t\tCTL(stats_huge_ndalloc)}\n};\n\nstatic const ctl_named_node_t stats_arenas_i_small_node[] = {\n\t{NAME(\"allocated\"),\t\tCTL(stats_arenas_i_small_allocated)},\n\t{NAME(\"nmalloc\"),\t\tCTL(stats_arenas_i_small_nmalloc)},\n\t{NAME(\"ndalloc\"),\t\tCTL(stats_arenas_i_small_ndalloc)},\n\t{NAME(\"nrequests\"),\t\tCTL(stats_arenas_i_small_nrequests)}\n};\n\nstatic const ctl_named_node_t stats_arenas_i_large_node[] = {\n\t{NAME(\"allocated\"),\t\tCTL(stats_arenas_i_large_allocated)},\n\t{NAME(\"nmalloc\"),\t\tCTL(stats_arenas_i_large_nmalloc)},\n\t{NAME(\"ndalloc\"),\t\tCTL(stats_arenas_i_large_ndalloc)},\n\t{NAME(\"nrequests\"),\t\tCTL(stats_arenas_i_large_nrequests)}\n};\n\nstatic const ctl_named_node_t stats_arenas_i_bins_j_node[] = {\n\t{NAME(\"allocated\"),\t\tCTL(stats_arenas_i_bins_j_allocated)},\n\t{NAME(\"nmalloc\"),\t\tCTL(stats_arenas_i_bins_j_nmalloc)},\n\t{NAME(\"ndalloc\"),\t\tCTL(stats_arenas_i_bins_j_ndalloc)},\n\t{NAME(\"nrequests\"),\t\tCTL(stats_arenas_i_bins_j_nrequests)},\n\t{NAME(\"nfills\"),\t\tCTL(stats_arenas_i_bins_j_nfills)},\n\t{NAME(\"nflushes\"),\t\tCTL(stats_arenas_i_bins_j_nflushes)},\n\t{NAME(\"nruns\"),\t\t\tCTL(stats_arenas_i_bins_j_nruns)},\n\t{NAME(\"nreruns\"),\t\tCTL(stats_arenas_i_bins_j_nreruns)},\n\t{NAME(\"curruns\"),\t\tCTL(stats_arenas_i_bins_j_curruns)}\n};\nstatic const ctl_named_node_t super_stats_arenas_i_bins_j_node[] = {\n\t{NAME(\"\"),\t\t\tCHILD(named, stats_arenas_i_bins_j)}\n};\n\nstatic const ctl_indexed_node_t stats_arenas_i_bins_node[] = {\n\t{INDEX(stats_arenas_i_bins_j)}\n};\n\nstatic const ctl_named_node_t stats_arenas_i_lruns_j_node[] = {\n\t{NAME(\"nmalloc\"),\t\tCTL(stats_arenas_i_lruns_j_nmalloc)},\n\t{NAME(\"ndalloc\"),\t\tCTL(stats_arenas_i_lruns_j_ndalloc)},\n\t{NAME(\"nrequests\"),\t\tCTL(stats_arenas_i_lruns_j_nrequests)},\n\t{NAME(\"curruns\"),\t\tCTL(stats_arenas_i_lruns_j_curruns)}\n};\nstatic const ctl_named_node_t super_stats_arenas_i_lruns_j_node[] = {\n\t{NAME(\"\"),\t\t\tCHILD(named, stats_arenas_i_lruns_j)}\n};\n\nstatic const ctl_indexed_node_t stats_arenas_i_lruns_node[] = {\n\t{INDEX(stats_arenas_i_lruns_j)}\n};\n\nstatic const ctl_named_node_t stats_arenas_i_node[] = {\n\t{NAME(\"nthreads\"),\t\tCTL(stats_arenas_i_nthreads)},\n\t{NAME(\"dss\"),\t\t\tCTL(stats_arenas_i_dss)},\n\t{NAME(\"pactive\"),\t\tCTL(stats_arenas_i_pactive)},\n\t{NAME(\"pdirty\"),\t\tCTL(stats_arenas_i_pdirty)},\n\t{NAME(\"mapped\"),\t\tCTL(stats_arenas_i_mapped)},\n\t{NAME(\"npurge\"),\t\tCTL(stats_arenas_i_npurge)},\n\t{NAME(\"nmadvise\"),\t\tCTL(stats_arenas_i_nmadvise)},\n\t{NAME(\"purged\"),\t\tCTL(stats_arenas_i_purged)},\n\t{NAME(\"small\"),\t\t\tCHILD(named, stats_arenas_i_small)},\n\t{NAME(\"large\"),\t\t\tCHILD(named, stats_arenas_i_large)},\n\t{NAME(\"bins\"),\t\t\tCHILD(indexed, stats_arenas_i_bins)},\n\t{NAME(\"lruns\"),\t\t\tCHILD(indexed, stats_arenas_i_lruns)}\n};\nstatic const ctl_named_node_t super_stats_arenas_i_node[] = {\n\t{NAME(\"\"),\t\t\tCHILD(named, stats_arenas_i)}\n};\n\nstatic const ctl_indexed_node_t stats_arenas_node[] = {\n\t{INDEX(stats_arenas_i)}\n};\n\nstatic const ctl_named_node_t stats_node[] = {\n\t{NAME(\"cactive\"),\t\tCTL(stats_cactive)},\n\t{NAME(\"allocated\"),\t\tCTL(stats_allocated)},\n\t{NAME(\"active\"),\t\tCTL(stats_active)},\n\t{NAME(\"mapped\"),\t\tCTL(stats_mapped)},\n\t{NAME(\"chunks\"),\t\tCHILD(named, stats_chunks)},\n\t{NAME(\"huge\"),\t\t\tCHILD(named, stats_huge)},\n\t{NAME(\"arenas\"),\t\tCHILD(indexed, stats_arenas)}\n};\n\nstatic const ctl_named_node_t\troot_node[] = {\n\t{NAME(\"version\"),\tCTL(version)},\n\t{NAME(\"epoch\"),\t\tCTL(epoch)},\n\t{NAME(\"thread\"),\tCHILD(named, thread)},\n\t{NAME(\"config\"),\tCHILD(named, config)},\n\t{NAME(\"opt\"),\t\tCHILD(named, opt)},\n\t{NAME(\"arena\"),\t\tCHILD(indexed, arena)},\n\t{NAME(\"arenas\"),\tCHILD(named, arenas)},\n\t{NAME(\"prof\"),\t\tCHILD(named, prof)},\n\t{NAME(\"stats\"),\t\tCHILD(named, stats)}\n};\nstatic const ctl_named_node_t super_root_node[] = {\n\t{NAME(\"\"),\t\tCHILD(named, root)}\n};\n\n#undef NAME\n#undef CHILD\n#undef CTL\n#undef INDEX\n\n/******************************************************************************/\n\nstatic bool\nctl_arena_init(ctl_arena_stats_t *astats)\n{\n\n\tif (astats->lstats == NULL) {\n\t\tastats->lstats = (malloc_large_stats_t *)base_alloc(nlclasses *\n\t\t    sizeof(malloc_large_stats_t));\n\t\tif (astats->lstats == NULL)\n\t\t\treturn (true);\n\t}\n\n\treturn (false);\n}\n\nstatic void\nctl_arena_clear(ctl_arena_stats_t *astats)\n{\n\n\tastats->dss = dss_prec_names[dss_prec_limit];\n\tastats->pactive = 0;\n\tastats->pdirty = 0;\n\tif (config_stats) {\n\t\tmemset(&astats->astats, 0, sizeof(arena_stats_t));\n\t\tastats->allocated_small = 0;\n\t\tastats->nmalloc_small = 0;\n\t\tastats->ndalloc_small = 0;\n\t\tastats->nrequests_small = 0;\n\t\tmemset(astats->bstats, 0, NBINS * sizeof(malloc_bin_stats_t));\n\t\tmemset(astats->lstats, 0, nlclasses *\n\t\t    sizeof(malloc_large_stats_t));\n\t}\n}\n\nstatic void\nctl_arena_stats_amerge(ctl_arena_stats_t *cstats, arena_t *arena)\n{\n\tunsigned i;\n\n\tarena_stats_merge(arena, &cstats->dss, &cstats->pactive,\n\t    &cstats->pdirty, &cstats->astats, cstats->bstats, cstats->lstats);\n\n\tfor (i = 0; i < NBINS; i++) {\n\t\tcstats->allocated_small += cstats->bstats[i].allocated;\n\t\tcstats->nmalloc_small += cstats->bstats[i].nmalloc;\n\t\tcstats->ndalloc_small += cstats->bstats[i].ndalloc;\n\t\tcstats->nrequests_small += cstats->bstats[i].nrequests;\n\t}\n}\n\nstatic void\nctl_arena_stats_smerge(ctl_arena_stats_t *sstats, ctl_arena_stats_t *astats)\n{\n\tunsigned i;\n\n\tsstats->pactive += astats->pactive;\n\tsstats->pdirty += astats->pdirty;\n\n\tsstats->astats.mapped += astats->astats.mapped;\n\tsstats->astats.npurge += astats->astats.npurge;\n\tsstats->astats.nmadvise += astats->astats.nmadvise;\n\tsstats->astats.purged += astats->astats.purged;\n\n\tsstats->allocated_small += astats->allocated_small;\n\tsstats->nmalloc_small += astats->nmalloc_small;\n\tsstats->ndalloc_small += astats->ndalloc_small;\n\tsstats->nrequests_small += astats->nrequests_small;\n\n\tsstats->astats.allocated_large += astats->astats.allocated_large;\n\tsstats->astats.nmalloc_large += astats->astats.nmalloc_large;\n\tsstats->astats.ndalloc_large += astats->astats.ndalloc_large;\n\tsstats->astats.nrequests_large += astats->astats.nrequests_large;\n\n\tfor (i = 0; i < nlclasses; i++) {\n\t\tsstats->lstats[i].nmalloc += astats->lstats[i].nmalloc;\n\t\tsstats->lstats[i].ndalloc += astats->lstats[i].ndalloc;\n\t\tsstats->lstats[i].nrequests += astats->lstats[i].nrequests;\n\t\tsstats->lstats[i].curruns += astats->lstats[i].curruns;\n\t}\n\n\tfor (i = 0; i < NBINS; i++) {\n\t\tsstats->bstats[i].allocated += astats->bstats[i].allocated;\n\t\tsstats->bstats[i].nmalloc += astats->bstats[i].nmalloc;\n\t\tsstats->bstats[i].ndalloc += astats->bstats[i].ndalloc;\n\t\tsstats->bstats[i].nrequests += astats->bstats[i].nrequests;\n\t\tif (config_tcache) {\n\t\t\tsstats->bstats[i].nfills += astats->bstats[i].nfills;\n\t\t\tsstats->bstats[i].nflushes +=\n\t\t\t    astats->bstats[i].nflushes;\n\t\t}\n\t\tsstats->bstats[i].nruns += astats->bstats[i].nruns;\n\t\tsstats->bstats[i].reruns += astats->bstats[i].reruns;\n\t\tsstats->bstats[i].curruns += astats->bstats[i].curruns;\n\t}\n}\n\nstatic void\nctl_arena_refresh(arena_t *arena, unsigned i)\n{\n\tctl_arena_stats_t *astats = &ctl_stats.arenas[i];\n\tctl_arena_stats_t *sstats = &ctl_stats.arenas[ctl_stats.narenas];\n\n\tctl_arena_clear(astats);\n\n\tsstats->nthreads += astats->nthreads;\n\tif (config_stats) {\n\t\tctl_arena_stats_amerge(astats, arena);\n\t\t/* Merge into sum stats as well. */\n\t\tctl_arena_stats_smerge(sstats, astats);\n\t} else {\n\t\tastats->pactive += arena->nactive;\n\t\tastats->pdirty += arena->ndirty;\n\t\t/* Merge into sum stats as well. */\n\t\tsstats->pactive += arena->nactive;\n\t\tsstats->pdirty += arena->ndirty;\n\t}\n}\n\nstatic bool\nctl_grow(void)\n{\n\tsize_t astats_size;\n\tctl_arena_stats_t *astats;\n\tarena_t **tarenas;\n\n\t/* Extend arena stats and arenas arrays. */\n\tastats_size = (ctl_stats.narenas + 2) * sizeof(ctl_arena_stats_t);\n\tif (ctl_stats.narenas == narenas_auto) {\n\t\t/* ctl_stats.arenas and arenas came from base_alloc(). */\n\t\tastats = (ctl_arena_stats_t *)imalloc(astats_size);\n\t\tif (astats == NULL)\n\t\t\treturn (true);\n\t\tmemcpy(astats, ctl_stats.arenas, (ctl_stats.narenas + 1) *\n\t\t    sizeof(ctl_arena_stats_t));\n\n\t\ttarenas = (arena_t **)imalloc((ctl_stats.narenas + 1) *\n\t\t    sizeof(arena_t *));\n\t\tif (tarenas == NULL) {\n\t\t\tidalloc(astats);\n\t\t\treturn (true);\n\t\t}\n\t\tmemcpy(tarenas, arenas, ctl_stats.narenas * sizeof(arena_t *));\n\t} else {\n\t\tastats = (ctl_arena_stats_t *)iralloc(ctl_stats.arenas,\n\t\t    astats_size, 0, 0, false, false);\n\t\tif (astats == NULL)\n\t\t\treturn (true);\n\n\t\ttarenas = (arena_t **)iralloc(arenas, (ctl_stats.narenas + 1) *\n\t\t    sizeof(arena_t *), 0, 0, false, false);\n\t\tif (tarenas == NULL)\n\t\t\treturn (true);\n\t}\n\t/* Initialize the new astats and arenas elements. */\n\tmemset(&astats[ctl_stats.narenas + 1], 0, sizeof(ctl_arena_stats_t));\n\tif (ctl_arena_init(&astats[ctl_stats.narenas + 1]))\n\t\treturn (true);\n\ttarenas[ctl_stats.narenas] = NULL;\n\t/* Swap merged stats to their new location. */\n\t{\n\t\tctl_arena_stats_t tstats;\n\t\tmemcpy(&tstats, &astats[ctl_stats.narenas],\n\t\t    sizeof(ctl_arena_stats_t));\n\t\tmemcpy(&astats[ctl_stats.narenas],\n\t\t    &astats[ctl_stats.narenas + 1], sizeof(ctl_arena_stats_t));\n\t\tmemcpy(&astats[ctl_stats.narenas + 1], &tstats,\n\t\t    sizeof(ctl_arena_stats_t));\n\t}\n\tctl_stats.arenas = astats;\n\tctl_stats.narenas++;\n\tmalloc_mutex_lock(&arenas_lock);\n\tarenas = tarenas;\n\tnarenas_total++;\n\tarenas_extend(narenas_total - 1);\n\tmalloc_mutex_unlock(&arenas_lock);\n\n\treturn (false);\n}\n\nstatic void\nctl_refresh(void)\n{\n\tunsigned i;\n\tVARIABLE_ARRAY(arena_t *, tarenas, ctl_stats.narenas);\n\n\tif (config_stats) {\n\t\tmalloc_mutex_lock(&chunks_mtx);\n\t\tctl_stats.chunks.current = stats_chunks.curchunks;\n\t\tctl_stats.chunks.total = stats_chunks.nchunks;\n\t\tctl_stats.chunks.high = stats_chunks.highchunks;\n\t\tmalloc_mutex_unlock(&chunks_mtx);\n\n\t\tmalloc_mutex_lock(&huge_mtx);\n\t\tctl_stats.huge.allocated = huge_allocated;\n\t\tctl_stats.huge.nmalloc = huge_nmalloc;\n\t\tctl_stats.huge.ndalloc = huge_ndalloc;\n\t\tmalloc_mutex_unlock(&huge_mtx);\n\t}\n\n\t/*\n\t * Clear sum stats, since they will be merged into by\n\t * ctl_arena_refresh().\n\t */\n\tctl_stats.arenas[ctl_stats.narenas].nthreads = 0;\n\tctl_arena_clear(&ctl_stats.arenas[ctl_stats.narenas]);\n\n\tmalloc_mutex_lock(&arenas_lock);\n\tmemcpy(tarenas, arenas, sizeof(arena_t *) * ctl_stats.narenas);\n\tfor (i = 0; i < ctl_stats.narenas; i++) {\n\t\tif (arenas[i] != NULL)\n\t\t\tctl_stats.arenas[i].nthreads = arenas[i]->nthreads;\n\t\telse\n\t\t\tctl_stats.arenas[i].nthreads = 0;\n\t}\n\tmalloc_mutex_unlock(&arenas_lock);\n\tfor (i = 0; i < ctl_stats.narenas; i++) {\n\t\tbool initialized = (tarenas[i] != NULL);\n\n\t\tctl_stats.arenas[i].initialized = initialized;\n\t\tif (initialized)\n\t\t\tctl_arena_refresh(tarenas[i], i);\n\t}\n\n\tif (config_stats) {\n\t\tctl_stats.allocated =\n\t\t    ctl_stats.arenas[ctl_stats.narenas].allocated_small\n\t\t    + ctl_stats.arenas[ctl_stats.narenas].astats.allocated_large\n\t\t    + ctl_stats.huge.allocated;\n\t\tctl_stats.active =\n\t\t    (ctl_stats.arenas[ctl_stats.narenas].pactive << LG_PAGE)\n\t\t    + ctl_stats.huge.allocated;\n\t\tctl_stats.mapped = (ctl_stats.chunks.current << opt_lg_chunk);\n\t}\n\n\tctl_epoch++;\n}\n\nstatic bool\nctl_init(void)\n{\n\tbool ret;\n\n\tmalloc_mutex_lock(&ctl_mtx);\n\tif (ctl_initialized == false) {\n\t\t/*\n\t\t * Allocate space for one extra arena stats element, which\n\t\t * contains summed stats across all arenas.\n\t\t */\n\t\tassert(narenas_auto == narenas_total_get());\n\t\tctl_stats.narenas = narenas_auto;\n\t\tctl_stats.arenas = (ctl_arena_stats_t *)base_alloc(\n\t\t    (ctl_stats.narenas + 1) * sizeof(ctl_arena_stats_t));\n\t\tif (ctl_stats.arenas == NULL) {\n\t\t\tret = true;\n\t\t\tgoto label_return;\n\t\t}\n\t\tmemset(ctl_stats.arenas, 0, (ctl_stats.narenas + 1) *\n\t\t    sizeof(ctl_arena_stats_t));\n\n\t\t/*\n\t\t * Initialize all stats structures, regardless of whether they\n\t\t * ever get used.  Lazy initialization would allow errors to\n\t\t * cause inconsistent state to be viewable by the application.\n\t\t */\n\t\tif (config_stats) {\n\t\t\tunsigned i;\n\t\t\tfor (i = 0; i <= ctl_stats.narenas; i++) {\n\t\t\t\tif (ctl_arena_init(&ctl_stats.arenas[i])) {\n\t\t\t\t\tret = true;\n\t\t\t\t\tgoto label_return;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tctl_stats.arenas[ctl_stats.narenas].initialized = true;\n\n\t\tctl_epoch = 0;\n\t\tctl_refresh();\n\t\tctl_initialized = true;\n\t}\n\n\tret = false;\nlabel_return:\n\tmalloc_mutex_unlock(&ctl_mtx);\n\treturn (ret);\n}\n\nstatic int\nctl_lookup(const char *name, ctl_node_t const **nodesp, size_t *mibp,\n    size_t *depthp)\n{\n\tint ret;\n\tconst char *elm, *tdot, *dot;\n\tsize_t elen, i, j;\n\tconst ctl_named_node_t *node;\n\n\telm = name;\n\t/* Equivalent to strchrnul(). */\n\tdot = ((tdot = strchr(elm, '.')) != NULL) ? tdot : strchr(elm, '\\0');\n\telen = (size_t)((uintptr_t)dot - (uintptr_t)elm);\n\tif (elen == 0) {\n\t\tret = ENOENT;\n\t\tgoto label_return;\n\t}\n\tnode = super_root_node;\n\tfor (i = 0; i < *depthp; i++) {\n\t\tassert(node);\n\t\tassert(node->nchildren > 0);\n\t\tif (ctl_named_node(node->children) != NULL) {\n\t\t\tconst ctl_named_node_t *pnode = node;\n\n\t\t\t/* Children are named. */\n\t\t\tfor (j = 0; j < node->nchildren; j++) {\n\t\t\t\tconst ctl_named_node_t *child =\n\t\t\t\t    ctl_named_children(node, j);\n\t\t\t\tif (strlen(child->name) == elen &&\n\t\t\t\t    strncmp(elm, child->name, elen) == 0) {\n\t\t\t\t\tnode = child;\n\t\t\t\t\tif (nodesp != NULL)\n\t\t\t\t\t\tnodesp[i] =\n\t\t\t\t\t\t    (const ctl_node_t *)node;\n\t\t\t\t\tmibp[i] = j;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (node == pnode) {\n\t\t\t\tret = ENOENT;\n\t\t\t\tgoto label_return;\n\t\t\t}\n\t\t} else {\n\t\t\tuintmax_t index;\n\t\t\tconst ctl_indexed_node_t *inode;\n\n\t\t\t/* Children are indexed. */\n\t\t\tindex = malloc_strtoumax(elm, NULL, 10);\n\t\t\tif (index == UINTMAX_MAX || index > SIZE_T_MAX) {\n\t\t\t\tret = ENOENT;\n\t\t\t\tgoto label_return;\n\t\t\t}\n\n\t\t\tinode = ctl_indexed_node(node->children);\n\t\t\tnode = inode->index(mibp, *depthp, (size_t)index);\n\t\t\tif (node == NULL) {\n\t\t\t\tret = ENOENT;\n\t\t\t\tgoto label_return;\n\t\t\t}\n\n\t\t\tif (nodesp != NULL)\n\t\t\t\tnodesp[i] = (const ctl_node_t *)node;\n\t\t\tmibp[i] = (size_t)index;\n\t\t}\n\n\t\tif (node->ctl != NULL) {\n\t\t\t/* Terminal node. */\n\t\t\tif (*dot != '\\0') {\n\t\t\t\t/*\n\t\t\t\t * The name contains more elements than are\n\t\t\t\t * in this path through the tree.\n\t\t\t\t */\n\t\t\t\tret = ENOENT;\n\t\t\t\tgoto label_return;\n\t\t\t}\n\t\t\t/* Complete lookup successful. */\n\t\t\t*depthp = i + 1;\n\t\t\tbreak;\n\t\t}\n\n\t\t/* Update elm. */\n\t\tif (*dot == '\\0') {\n\t\t\t/* No more elements. */\n\t\t\tret = ENOENT;\n\t\t\tgoto label_return;\n\t\t}\n\t\telm = &dot[1];\n\t\tdot = ((tdot = strchr(elm, '.')) != NULL) ? tdot :\n\t\t    strchr(elm, '\\0');\n\t\telen = (size_t)((uintptr_t)dot - (uintptr_t)elm);\n\t}\n\n\tret = 0;\nlabel_return:\n\treturn (ret);\n}\n\nint\nctl_byname(const char *name, void *oldp, size_t *oldlenp, void *newp,\n    size_t newlen)\n{\n\tint ret;\n\tsize_t depth;\n\tctl_node_t const *nodes[CTL_MAX_DEPTH];\n\tsize_t mib[CTL_MAX_DEPTH];\n\tconst ctl_named_node_t *node;\n\n\tif (ctl_initialized == false && ctl_init()) {\n\t\tret = EAGAIN;\n\t\tgoto label_return;\n\t}\n\n\tdepth = CTL_MAX_DEPTH;\n\tret = ctl_lookup(name, nodes, mib, &depth);\n\tif (ret != 0)\n\t\tgoto label_return;\n\n\tnode = ctl_named_node(nodes[depth-1]);\n\tif (node != NULL && node->ctl)\n\t\tret = node->ctl(mib, depth, oldp, oldlenp, newp, newlen);\n\telse {\n\t\t/* The name refers to a partial path through the ctl tree. */\n\t\tret = ENOENT;\n\t}\n\nlabel_return:\n\treturn(ret);\n}\n\nint\nctl_nametomib(const char *name, size_t *mibp, size_t *miblenp)\n{\n\tint ret;\n\n\tif (ctl_initialized == false && ctl_init()) {\n\t\tret = EAGAIN;\n\t\tgoto label_return;\n\t}\n\n\tret = ctl_lookup(name, NULL, mibp, miblenp);\nlabel_return:\n\treturn(ret);\n}\n\nint\nctl_bymib(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,\n    void *newp, size_t newlen)\n{\n\tint ret;\n\tconst ctl_named_node_t *node;\n\tsize_t i;\n\n\tif (ctl_initialized == false && ctl_init()) {\n\t\tret = EAGAIN;\n\t\tgoto label_return;\n\t}\n\n\t/* Iterate down the tree. */\n\tnode = super_root_node;\n\tfor (i = 0; i < miblen; i++) {\n\t\tassert(node);\n\t\tassert(node->nchildren > 0);\n\t\tif (ctl_named_node(node->children) != NULL) {\n\t\t\t/* Children are named. */\n\t\t\tif (node->nchildren <= mib[i]) {\n\t\t\t\tret = ENOENT;\n\t\t\t\tgoto label_return;\n\t\t\t}\n\t\t\tnode = ctl_named_children(node, mib[i]);\n\t\t} else {\n\t\t\tconst ctl_indexed_node_t *inode;\n\n\t\t\t/* Indexed element. */\n\t\t\tinode = ctl_indexed_node(node->children);\n\t\t\tnode = inode->index(mib, miblen, mib[i]);\n\t\t\tif (node == NULL) {\n\t\t\t\tret = ENOENT;\n\t\t\t\tgoto label_return;\n\t\t\t}\n\t\t}\n\t}\n\n\t/* Call the ctl function. */\n\tif (node && node->ctl)\n\t\tret = node->ctl(mib, miblen, oldp, oldlenp, newp, newlen);\n\telse {\n\t\t/* Partial MIB. */\n\t\tret = ENOENT;\n\t}\n\nlabel_return:\n\treturn(ret);\n}\n\nbool\nctl_boot(void)\n{\n\n\tif (malloc_mutex_init(&ctl_mtx))\n\t\treturn (true);\n\n\tctl_initialized = false;\n\n\treturn (false);\n}\n\nvoid\nctl_prefork(void)\n{\n\n\tmalloc_mutex_lock(&ctl_mtx);\n}\n\nvoid\nctl_postfork_parent(void)\n{\n\n\tmalloc_mutex_postfork_parent(&ctl_mtx);\n}\n\nvoid\nctl_postfork_child(void)\n{\n\n\tmalloc_mutex_postfork_child(&ctl_mtx);\n}\n\n/******************************************************************************/\n/* *_ctl() functions. */\n\n#define\tREADONLY()\tdo {\t\t\t\t\t\t\\\n\tif (newp != NULL || newlen != 0) {\t\t\t\t\\\n\t\tret = EPERM;\t\t\t\t\t\t\\\n\t\tgoto label_return;\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n} while (0)\n\n#define\tWRITEONLY()\tdo {\t\t\t\t\t\t\\\n\tif (oldp != NULL || oldlenp != NULL) {\t\t\t\t\\\n\t\tret = EPERM;\t\t\t\t\t\t\\\n\t\tgoto label_return;\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n} while (0)\n\n#define\tREAD(v, t)\tdo {\t\t\t\t\t\t\\\n\tif (oldp != NULL && oldlenp != NULL) {\t\t\t\t\\\n\t\tif (*oldlenp != sizeof(t)) {\t\t\t\t\\\n\t\t\tsize_t\tcopylen = (sizeof(t) <= *oldlenp)\t\\\n\t\t\t    ? sizeof(t) : *oldlenp;\t\t\t\\\n\t\t\tmemcpy(oldp, (void *)&v, copylen);\t\t\\\n\t\t\tret = EINVAL;\t\t\t\t\t\\\n\t\t\tgoto label_return;\t\t\t\t\\\n\t\t} else\t\t\t\t\t\t\t\\\n\t\t\t*(t *)oldp = v;\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n} while (0)\n\n#define\tWRITE(v, t)\tdo {\t\t\t\t\t\t\\\n\tif (newp != NULL) {\t\t\t\t\t\t\\\n\t\tif (newlen != sizeof(t)) {\t\t\t\t\\\n\t\t\tret = EINVAL;\t\t\t\t\t\\\n\t\t\tgoto label_return;\t\t\t\t\\\n\t\t}\t\t\t\t\t\t\t\\\n\t\tv = *(t *)newp;\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n} while (0)\n\n/*\n * There's a lot of code duplication in the following macros due to limitations\n * in how nested cpp macros are expanded.\n */\n#define\tCTL_RO_CLGEN(c, l, n, v, t)\t\t\t\t\t\\\nstatic int\t\t\t\t\t\t\t\t\\\nn##_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,\t\\\n    void *newp, size_t newlen)\t\t\t\t\t\t\\\n{\t\t\t\t\t\t\t\t\t\\\n\tint ret;\t\t\t\t\t\t\t\\\n\tt oldval;\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\tif ((c) == false)\t\t\t\t\t\t\\\n\t\treturn (ENOENT);\t\t\t\t\t\\\n\tif (l)\t\t\t\t\t\t\t\t\\\n\t\tmalloc_mutex_lock(&ctl_mtx);\t\t\t\t\\\n\tREADONLY();\t\t\t\t\t\t\t\\\n\toldval = v;\t\t\t\t\t\t\t\\\n\tREAD(oldval, t);\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\tret = 0;\t\t\t\t\t\t\t\\\nlabel_return:\t\t\t\t\t\t\t\t\\\n\tif (l)\t\t\t\t\t\t\t\t\\\n\t\tmalloc_mutex_unlock(&ctl_mtx);\t\t\t\t\\\n\treturn (ret);\t\t\t\t\t\t\t\\\n}\n\n#define\tCTL_RO_CGEN(c, n, v, t)\t\t\t\t\t\t\\\nstatic int\t\t\t\t\t\t\t\t\\\nn##_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,\t\\\n    void *newp, size_t newlen)\t\t\t\t\t\t\\\n{\t\t\t\t\t\t\t\t\t\\\n\tint ret;\t\t\t\t\t\t\t\\\n\tt oldval;\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\tif ((c) == false)\t\t\t\t\t\t\\\n\t\treturn (ENOENT);\t\t\t\t\t\\\n\tmalloc_mutex_lock(&ctl_mtx);\t\t\t\t\t\\\n\tREADONLY();\t\t\t\t\t\t\t\\\n\toldval = v;\t\t\t\t\t\t\t\\\n\tREAD(oldval, t);\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\tret = 0;\t\t\t\t\t\t\t\\\nlabel_return:\t\t\t\t\t\t\t\t\\\n\tmalloc_mutex_unlock(&ctl_mtx);\t\t\t\t\t\\\n\treturn (ret);\t\t\t\t\t\t\t\\\n}\n\n#define\tCTL_RO_GEN(n, v, t)\t\t\t\t\t\t\\\nstatic int\t\t\t\t\t\t\t\t\\\nn##_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,\t\\\n    void *newp, size_t newlen)\t\t\t\t\t\t\\\n{\t\t\t\t\t\t\t\t\t\\\n\tint ret;\t\t\t\t\t\t\t\\\n\tt oldval;\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\tmalloc_mutex_lock(&ctl_mtx);\t\t\t\t\t\\\n\tREADONLY();\t\t\t\t\t\t\t\\\n\toldval = v;\t\t\t\t\t\t\t\\\n\tREAD(oldval, t);\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\tret = 0;\t\t\t\t\t\t\t\\\nlabel_return:\t\t\t\t\t\t\t\t\\\n\tmalloc_mutex_unlock(&ctl_mtx);\t\t\t\t\t\\\n\treturn (ret);\t\t\t\t\t\t\t\\\n}\n\n/*\n * ctl_mtx is not acquired, under the assumption that no pertinent data will\n * mutate during the call.\n */\n#define\tCTL_RO_NL_CGEN(c, n, v, t)\t\t\t\t\t\\\nstatic int\t\t\t\t\t\t\t\t\\\nn##_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,\t\\\n    void *newp, size_t newlen)\t\t\t\t\t\t\\\n{\t\t\t\t\t\t\t\t\t\\\n\tint ret;\t\t\t\t\t\t\t\\\n\tt oldval;\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\tif ((c) == false)\t\t\t\t\t\t\\\n\t\treturn (ENOENT);\t\t\t\t\t\\\n\tREADONLY();\t\t\t\t\t\t\t\\\n\toldval = v;\t\t\t\t\t\t\t\\\n\tREAD(oldval, t);\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\tret = 0;\t\t\t\t\t\t\t\\\nlabel_return:\t\t\t\t\t\t\t\t\\\n\treturn (ret);\t\t\t\t\t\t\t\\\n}\n\n#define\tCTL_RO_NL_GEN(n, v, t)\t\t\t\t\t\t\\\nstatic int\t\t\t\t\t\t\t\t\\\nn##_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,\t\\\n    void *newp, size_t newlen)\t\t\t\t\t\t\\\n{\t\t\t\t\t\t\t\t\t\\\n\tint ret;\t\t\t\t\t\t\t\\\n\tt oldval;\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\tREADONLY();\t\t\t\t\t\t\t\\\n\toldval = v;\t\t\t\t\t\t\t\\\n\tREAD(oldval, t);\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\tret = 0;\t\t\t\t\t\t\t\\\nlabel_return:\t\t\t\t\t\t\t\t\\\n\treturn (ret);\t\t\t\t\t\t\t\\\n}\n\n#define\tCTL_RO_BOOL_CONFIG_GEN(n)\t\t\t\t\t\\\nstatic int\t\t\t\t\t\t\t\t\\\nn##_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,\t\\\n    void *newp, size_t newlen)\t\t\t\t\t\t\\\n{\t\t\t\t\t\t\t\t\t\\\n\tint ret;\t\t\t\t\t\t\t\\\n\tbool oldval;\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\tREADONLY();\t\t\t\t\t\t\t\\\n\toldval = n;\t\t\t\t\t\t\t\\\n\tREAD(oldval, bool);\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\tret = 0;\t\t\t\t\t\t\t\\\nlabel_return:\t\t\t\t\t\t\t\t\\\n\treturn (ret);\t\t\t\t\t\t\t\\\n}\n\nCTL_RO_NL_GEN(version, JEMALLOC_VERSION, const char *)\n\nstatic int\nepoch_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,\n    void *newp, size_t newlen)\n{\n\tint ret;\n\tuint64_t newval;\n\n\tmalloc_mutex_lock(&ctl_mtx);\n\tWRITE(newval, uint64_t);\n\tif (newp != NULL)\n\t\tctl_refresh();\n\tREAD(ctl_epoch, uint64_t);\n\n\tret = 0;\nlabel_return:\n\tmalloc_mutex_unlock(&ctl_mtx);\n\treturn (ret);\n}\n\nstatic int\nthread_tcache_enabled_ctl(const size_t *mib, size_t miblen, void *oldp,\n    size_t *oldlenp, void *newp, size_t newlen)\n{\n\tint ret;\n\tbool oldval;\n\n\tif (config_tcache == false)\n\t\treturn (ENOENT);\n\n\toldval = tcache_enabled_get();\n\tif (newp != NULL) {\n\t\tif (newlen != sizeof(bool)) {\n\t\t\tret = EINVAL;\n\t\t\tgoto label_return;\n\t\t}\n\t\ttcache_enabled_set(*(bool *)newp);\n\t}\n\tREAD(oldval, bool);\n\n\tret = 0;\nlabel_return:\n\treturn (ret);\n}\n\nstatic int\nthread_tcache_flush_ctl(const size_t *mib, size_t miblen, void *oldp,\n    size_t *oldlenp, void *newp, size_t newlen)\n{\n\tint ret;\n\n\tif (config_tcache == false)\n\t\treturn (ENOENT);\n\n\tREADONLY();\n\tWRITEONLY();\n\n\ttcache_flush();\n\n\tret = 0;\nlabel_return:\n\treturn (ret);\n}\n\nstatic int\nthread_arena_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,\n    void *newp, size_t newlen)\n{\n\tint ret;\n\tunsigned newind, oldind;\n\n\tmalloc_mutex_lock(&ctl_mtx);\n\tnewind = oldind = choose_arena(NULL)->ind;\n\tWRITE(newind, unsigned);\n\tREAD(oldind, unsigned);\n\tif (newind != oldind) {\n\t\tarena_t *arena;\n\n\t\tif (newind >= ctl_stats.narenas) {\n\t\t\t/* New arena index is out of range. */\n\t\t\tret = EFAULT;\n\t\t\tgoto label_return;\n\t\t}\n\n\t\t/* Initialize arena if necessary. */\n\t\tmalloc_mutex_lock(&arenas_lock);\n\t\tif ((arena = arenas[newind]) == NULL && (arena =\n\t\t    arenas_extend(newind)) == NULL) {\n\t\t\tmalloc_mutex_unlock(&arenas_lock);\n\t\t\tret = EAGAIN;\n\t\t\tgoto label_return;\n\t\t}\n\t\tassert(arena == arenas[newind]);\n\t\tarenas[oldind]->nthreads--;\n\t\tarenas[newind]->nthreads++;\n\t\tmalloc_mutex_unlock(&arenas_lock);\n\n\t\t/* Set new arena association. */\n\t\tif (config_tcache) {\n\t\t\ttcache_t *tcache;\n\t\t\tif ((uintptr_t)(tcache = *tcache_tsd_get()) >\n\t\t\t    (uintptr_t)TCACHE_STATE_MAX) {\n\t\t\t\ttcache_arena_dissociate(tcache);\n\t\t\t\ttcache_arena_associate(tcache, arena);\n\t\t\t}\n\t\t}\n\t\tarenas_tsd_set(&arena);\n\t}\n\n\tret = 0;\nlabel_return:\n\tmalloc_mutex_unlock(&ctl_mtx);\n\treturn (ret);\n}\n\nCTL_RO_NL_CGEN(config_stats, thread_allocated,\n    thread_allocated_tsd_get()->allocated, uint64_t)\nCTL_RO_NL_CGEN(config_stats, thread_allocatedp,\n    &thread_allocated_tsd_get()->allocated, uint64_t *)\nCTL_RO_NL_CGEN(config_stats, thread_deallocated,\n    thread_allocated_tsd_get()->deallocated, uint64_t)\nCTL_RO_NL_CGEN(config_stats, thread_deallocatedp,\n    &thread_allocated_tsd_get()->deallocated, uint64_t *)\n\n/******************************************************************************/\n\nCTL_RO_BOOL_CONFIG_GEN(config_debug)\nCTL_RO_BOOL_CONFIG_GEN(config_dss)\nCTL_RO_BOOL_CONFIG_GEN(config_fill)\nCTL_RO_BOOL_CONFIG_GEN(config_lazy_lock)\nCTL_RO_BOOL_CONFIG_GEN(config_mremap)\nCTL_RO_BOOL_CONFIG_GEN(config_munmap)\nCTL_RO_BOOL_CONFIG_GEN(config_prof)\nCTL_RO_BOOL_CONFIG_GEN(config_prof_libgcc)\nCTL_RO_BOOL_CONFIG_GEN(config_prof_libunwind)\nCTL_RO_BOOL_CONFIG_GEN(config_stats)\nCTL_RO_BOOL_CONFIG_GEN(config_tcache)\nCTL_RO_BOOL_CONFIG_GEN(config_tls)\nCTL_RO_BOOL_CONFIG_GEN(config_utrace)\nCTL_RO_BOOL_CONFIG_GEN(config_valgrind)\nCTL_RO_BOOL_CONFIG_GEN(config_xmalloc)\n\n/******************************************************************************/\n\nCTL_RO_NL_GEN(opt_abort, opt_abort, bool)\nCTL_RO_NL_GEN(opt_dss, opt_dss, const char *)\nCTL_RO_NL_GEN(opt_lg_chunk, opt_lg_chunk, size_t)\nCTL_RO_NL_GEN(opt_narenas, opt_narenas, size_t)\nCTL_RO_NL_GEN(opt_lg_dirty_mult, opt_lg_dirty_mult, ssize_t)\nCTL_RO_NL_GEN(opt_stats_print, opt_stats_print, bool)\nCTL_RO_NL_CGEN(config_fill, opt_junk, opt_junk, bool)\nCTL_RO_NL_CGEN(config_fill, opt_zero, opt_zero, bool)\nCTL_RO_NL_CGEN(config_fill, opt_quarantine, opt_quarantine, size_t)\nCTL_RO_NL_CGEN(config_fill, opt_redzone, opt_redzone, bool)\nCTL_RO_NL_CGEN(config_utrace, opt_utrace, opt_utrace, bool)\nCTL_RO_NL_CGEN(config_valgrind, opt_valgrind, opt_valgrind, bool)\nCTL_RO_NL_CGEN(config_xmalloc, opt_xmalloc, opt_xmalloc, bool)\nCTL_RO_NL_CGEN(config_tcache, opt_tcache, opt_tcache, bool)\nCTL_RO_NL_CGEN(config_tcache, opt_lg_tcache_max, opt_lg_tcache_max, ssize_t)\nCTL_RO_NL_CGEN(config_prof, opt_prof, opt_prof, bool)\nCTL_RO_NL_CGEN(config_prof, opt_prof_prefix, opt_prof_prefix, const char *)\nCTL_RO_CGEN(config_prof, opt_prof_active, opt_prof_active, bool) /* Mutable. */\nCTL_RO_NL_CGEN(config_prof, opt_lg_prof_sample, opt_lg_prof_sample, size_t)\nCTL_RO_NL_CGEN(config_prof, opt_lg_prof_interval, opt_lg_prof_interval, ssize_t)\nCTL_RO_NL_CGEN(config_prof, opt_prof_gdump, opt_prof_gdump, bool)\nCTL_RO_NL_CGEN(config_prof, opt_prof_final, opt_prof_final, bool)\nCTL_RO_NL_CGEN(config_prof, opt_prof_leak, opt_prof_leak, bool)\nCTL_RO_NL_CGEN(config_prof, opt_prof_accum, opt_prof_accum, bool)\n\n/******************************************************************************/\n\n/* ctl_mutex must be held during execution of this function. */\nstatic void\narena_purge(unsigned arena_ind)\n{\n\tVARIABLE_ARRAY(arena_t *, tarenas, ctl_stats.narenas);\n\n\tmalloc_mutex_lock(&arenas_lock);\n\tmemcpy(tarenas, arenas, sizeof(arena_t *) * ctl_stats.narenas);\n\tmalloc_mutex_unlock(&arenas_lock);\n\n\tif (arena_ind == ctl_stats.narenas) {\n\t\tunsigned i;\n\t\tfor (i = 0; i < ctl_stats.narenas; i++) {\n\t\t\tif (tarenas[i] != NULL)\n\t\t\t\tarena_purge_all(tarenas[i]);\n\t\t}\n\t} else {\n\t\tassert(arena_ind < ctl_stats.narenas);\n\t\tif (tarenas[arena_ind] != NULL)\n\t\t\tarena_purge_all(tarenas[arena_ind]);\n\t}\n}\n\nstatic int\narena_i_purge_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,\n    void *newp, size_t newlen)\n{\n\tint ret;\n\n\tREADONLY();\n\tWRITEONLY();\n\tmalloc_mutex_lock(&ctl_mtx);\n\tarena_purge(mib[1]);\n\tmalloc_mutex_unlock(&ctl_mtx);\n\n\tret = 0;\nlabel_return:\n\treturn (ret);\n}\n\nstatic int\narena_i_dss_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,\n    void *newp, size_t newlen)\n{\n\tint ret, i;\n\tbool match, err;\n\tconst char *dss;\n\tunsigned arena_ind = mib[1];\n\tdss_prec_t dss_prec_old = dss_prec_limit;\n\tdss_prec_t dss_prec = dss_prec_limit;\n\n\tmalloc_mutex_lock(&ctl_mtx);\n\tWRITE(dss, const char *);\n\tmatch = false;\n\tfor (i = 0; i < dss_prec_limit; i++) {\n\t\tif (strcmp(dss_prec_names[i], dss) == 0) {\n\t\t\tdss_prec = i;\n\t\t\tmatch = true;\n\t\t\tbreak;\n\t\t}\n\t}\n\tif (match == false) {\n\t\tret = EINVAL;\n\t\tgoto label_return;\n\t}\n\n\tif (arena_ind < ctl_stats.narenas) {\n\t\tarena_t *arena = arenas[arena_ind];\n\t\tif (arena != NULL) {\n\t\t\tdss_prec_old = arena_dss_prec_get(arena);\n\t\t\tarena_dss_prec_set(arena, dss_prec);\n\t\t\terr = false;\n\t\t} else\n\t\t\terr = true;\n\t} else {\n\t\tdss_prec_old = chunk_dss_prec_get();\n\t\terr = chunk_dss_prec_set(dss_prec);\n\t}\n\tdss = dss_prec_names[dss_prec_old];\n\tREAD(dss, const char *);\n\tif (err) {\n\t\tret = EFAULT;\n\t\tgoto label_return;\n\t}\n\n\tret = 0;\nlabel_return:\n\tmalloc_mutex_unlock(&ctl_mtx);\n\treturn (ret);\n}\n\nstatic const ctl_named_node_t *\narena_i_index(const size_t *mib, size_t miblen, size_t i)\n{\n\tconst ctl_named_node_t * ret;\n\n\tmalloc_mutex_lock(&ctl_mtx);\n\tif (i > ctl_stats.narenas) {\n\t\tret = NULL;\n\t\tgoto label_return;\n\t}\n\n\tret = super_arena_i_node;\nlabel_return:\n\tmalloc_mutex_unlock(&ctl_mtx);\n\treturn (ret);\n}\n\n\n/******************************************************************************/\n\nCTL_RO_NL_GEN(arenas_bin_i_size, arena_bin_info[mib[2]].reg_size, size_t)\nCTL_RO_NL_GEN(arenas_bin_i_nregs, arena_bin_info[mib[2]].nregs, uint32_t)\nCTL_RO_NL_GEN(arenas_bin_i_run_size, arena_bin_info[mib[2]].run_size, size_t)\nstatic const ctl_named_node_t *\narenas_bin_i_index(const size_t *mib, size_t miblen, size_t i)\n{\n\n\tif (i > NBINS)\n\t\treturn (NULL);\n\treturn (super_arenas_bin_i_node);\n}\n\nCTL_RO_NL_GEN(arenas_lrun_i_size, ((mib[2]+1) << LG_PAGE), size_t)\nstatic const ctl_named_node_t *\narenas_lrun_i_index(const size_t *mib, size_t miblen, size_t i)\n{\n\n\tif (i > nlclasses)\n\t\treturn (NULL);\n\treturn (super_arenas_lrun_i_node);\n}\n\nstatic int\narenas_narenas_ctl(const size_t *mib, size_t miblen, void *oldp,\n    size_t *oldlenp, void *newp, size_t newlen)\n{\n\tint ret;\n\tunsigned narenas;\n\n\tmalloc_mutex_lock(&ctl_mtx);\n\tREADONLY();\n\tif (*oldlenp != sizeof(unsigned)) {\n\t\tret = EINVAL;\n\t\tgoto label_return;\n\t}\n\tnarenas = ctl_stats.narenas;\n\tREAD(narenas, unsigned);\n\n\tret = 0;\nlabel_return:\n\tmalloc_mutex_unlock(&ctl_mtx);\n\treturn (ret);\n}\n\nstatic int\narenas_initialized_ctl(const size_t *mib, size_t miblen, void *oldp,\n    size_t *oldlenp, void *newp, size_t newlen)\n{\n\tint ret;\n\tunsigned nread, i;\n\n\tmalloc_mutex_lock(&ctl_mtx);\n\tREADONLY();\n\tif (*oldlenp != ctl_stats.narenas * sizeof(bool)) {\n\t\tret = EINVAL;\n\t\tnread = (*oldlenp < ctl_stats.narenas * sizeof(bool))\n\t\t    ? (*oldlenp / sizeof(bool)) : ctl_stats.narenas;\n\t} else {\n\t\tret = 0;\n\t\tnread = ctl_stats.narenas;\n\t}\n\n\tfor (i = 0; i < nread; i++)\n\t\t((bool *)oldp)[i] = ctl_stats.arenas[i].initialized;\n\nlabel_return:\n\tmalloc_mutex_unlock(&ctl_mtx);\n\treturn (ret);\n}\n\nCTL_RO_NL_GEN(arenas_quantum, QUANTUM, size_t)\nCTL_RO_NL_GEN(arenas_page, PAGE, size_t)\nCTL_RO_NL_CGEN(config_tcache, arenas_tcache_max, tcache_maxclass, size_t)\nCTL_RO_NL_GEN(arenas_nbins, NBINS, unsigned)\nCTL_RO_NL_CGEN(config_tcache, arenas_nhbins, nhbins, unsigned)\nCTL_RO_NL_GEN(arenas_nlruns, nlclasses, size_t)\n\nstatic int\narenas_purge_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,\n    void *newp, size_t newlen)\n{\n\tint ret;\n\tunsigned arena_ind;\n\n\tmalloc_mutex_lock(&ctl_mtx);\n\tWRITEONLY();\n\tarena_ind = UINT_MAX;\n\tWRITE(arena_ind, unsigned);\n\tif (newp != NULL && arena_ind >= ctl_stats.narenas)\n\t\tret = EFAULT;\n\telse {\n\t\tif (arena_ind == UINT_MAX)\n\t\t\tarena_ind = ctl_stats.narenas;\n\t\tarena_purge(arena_ind);\n\t\tret = 0;\n\t}\n\nlabel_return:\n\tmalloc_mutex_unlock(&ctl_mtx);\n\treturn (ret);\n}\n\nstatic int\narenas_extend_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,\n    void *newp, size_t newlen)\n{\n\tint ret;\n\n\tmalloc_mutex_lock(&ctl_mtx);\n\tREADONLY();\n\tif (ctl_grow()) {\n\t\tret = EAGAIN;\n\t\tgoto label_return;\n\t}\n\tREAD(ctl_stats.narenas - 1, unsigned);\n\n\tret = 0;\nlabel_return:\n\tmalloc_mutex_unlock(&ctl_mtx);\n\treturn (ret);\n}\n\n/******************************************************************************/\n\nstatic int\nprof_active_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,\n    void *newp, size_t newlen)\n{\n\tint ret;\n\tbool oldval;\n\n\tif (config_prof == false)\n\t\treturn (ENOENT);\n\n\tmalloc_mutex_lock(&ctl_mtx); /* Protect opt_prof_active. */\n\toldval = opt_prof_active;\n\tif (newp != NULL) {\n\t\t/*\n\t\t * The memory barriers will tend to make opt_prof_active\n\t\t * propagate faster on systems with weak memory ordering.\n\t\t */\n\t\tmb_write();\n\t\tWRITE(opt_prof_active, bool);\n\t\tmb_write();\n\t}\n\tREAD(oldval, bool);\n\n\tret = 0;\nlabel_return:\n\tmalloc_mutex_unlock(&ctl_mtx);\n\treturn (ret);\n}\n\nstatic int\nprof_dump_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,\n    void *newp, size_t newlen)\n{\n\tint ret;\n\tconst char *filename = NULL;\n\n\tif (config_prof == false)\n\t\treturn (ENOENT);\n\n\tWRITEONLY();\n\tWRITE(filename, const char *);\n\n\tif (prof_mdump(filename)) {\n\t\tret = EFAULT;\n\t\tgoto label_return;\n\t}\n\n\tret = 0;\nlabel_return:\n\treturn (ret);\n}\n\nCTL_RO_NL_CGEN(config_prof, prof_interval, prof_interval, uint64_t)\n\n/******************************************************************************/\n\nCTL_RO_CGEN(config_stats, stats_chunks_current, ctl_stats.chunks.current,\n    size_t)\nCTL_RO_CGEN(config_stats, stats_chunks_total, ctl_stats.chunks.total, uint64_t)\nCTL_RO_CGEN(config_stats, stats_chunks_high, ctl_stats.chunks.high, size_t)\nCTL_RO_CGEN(config_stats, stats_huge_allocated, huge_allocated, size_t)\nCTL_RO_CGEN(config_stats, stats_huge_nmalloc, huge_nmalloc, uint64_t)\nCTL_RO_CGEN(config_stats, stats_huge_ndalloc, huge_ndalloc, uint64_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_small_allocated,\n    ctl_stats.arenas[mib[2]].allocated_small, size_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_small_nmalloc,\n    ctl_stats.arenas[mib[2]].nmalloc_small, uint64_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_small_ndalloc,\n    ctl_stats.arenas[mib[2]].ndalloc_small, uint64_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_small_nrequests,\n    ctl_stats.arenas[mib[2]].nrequests_small, uint64_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_large_allocated,\n    ctl_stats.arenas[mib[2]].astats.allocated_large, size_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_large_nmalloc,\n    ctl_stats.arenas[mib[2]].astats.nmalloc_large, uint64_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_large_ndalloc,\n    ctl_stats.arenas[mib[2]].astats.ndalloc_large, uint64_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_large_nrequests,\n    ctl_stats.arenas[mib[2]].astats.nrequests_large, uint64_t)\n\nCTL_RO_CGEN(config_stats, stats_arenas_i_bins_j_allocated,\n    ctl_stats.arenas[mib[2]].bstats[mib[4]].allocated, size_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_bins_j_nmalloc,\n    ctl_stats.arenas[mib[2]].bstats[mib[4]].nmalloc, uint64_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_bins_j_ndalloc,\n    ctl_stats.arenas[mib[2]].bstats[mib[4]].ndalloc, uint64_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_bins_j_nrequests,\n    ctl_stats.arenas[mib[2]].bstats[mib[4]].nrequests, uint64_t)\nCTL_RO_CGEN(config_stats && config_tcache, stats_arenas_i_bins_j_nfills,\n    ctl_stats.arenas[mib[2]].bstats[mib[4]].nfills, uint64_t)\nCTL_RO_CGEN(config_stats && config_tcache, stats_arenas_i_bins_j_nflushes,\n    ctl_stats.arenas[mib[2]].bstats[mib[4]].nflushes, uint64_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_bins_j_nruns,\n    ctl_stats.arenas[mib[2]].bstats[mib[4]].nruns, uint64_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_bins_j_nreruns,\n    ctl_stats.arenas[mib[2]].bstats[mib[4]].reruns, uint64_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_bins_j_curruns,\n    ctl_stats.arenas[mib[2]].bstats[mib[4]].curruns, size_t)\n\nstatic const ctl_named_node_t *\nstats_arenas_i_bins_j_index(const size_t *mib, size_t miblen, size_t j)\n{\n\n\tif (j > NBINS)\n\t\treturn (NULL);\n\treturn (super_stats_arenas_i_bins_j_node);\n}\n\nCTL_RO_CGEN(config_stats, stats_arenas_i_lruns_j_nmalloc,\n    ctl_stats.arenas[mib[2]].lstats[mib[4]].nmalloc, uint64_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_lruns_j_ndalloc,\n    ctl_stats.arenas[mib[2]].lstats[mib[4]].ndalloc, uint64_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_lruns_j_nrequests,\n    ctl_stats.arenas[mib[2]].lstats[mib[4]].nrequests, uint64_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_lruns_j_curruns,\n    ctl_stats.arenas[mib[2]].lstats[mib[4]].curruns, size_t)\n\nstatic const ctl_named_node_t *\nstats_arenas_i_lruns_j_index(const size_t *mib, size_t miblen, size_t j)\n{\n\n\tif (j > nlclasses)\n\t\treturn (NULL);\n\treturn (super_stats_arenas_i_lruns_j_node);\n}\n\nCTL_RO_GEN(stats_arenas_i_nthreads, ctl_stats.arenas[mib[2]].nthreads, unsigned)\nCTL_RO_GEN(stats_arenas_i_dss, ctl_stats.arenas[mib[2]].dss, const char *)\nCTL_RO_GEN(stats_arenas_i_pactive, ctl_stats.arenas[mib[2]].pactive, size_t)\nCTL_RO_GEN(stats_arenas_i_pdirty, ctl_stats.arenas[mib[2]].pdirty, size_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_mapped,\n    ctl_stats.arenas[mib[2]].astats.mapped, size_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_npurge,\n    ctl_stats.arenas[mib[2]].astats.npurge, uint64_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_nmadvise,\n    ctl_stats.arenas[mib[2]].astats.nmadvise, uint64_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_purged,\n    ctl_stats.arenas[mib[2]].astats.purged, uint64_t)\n\nstatic const ctl_named_node_t *\nstats_arenas_i_index(const size_t *mib, size_t miblen, size_t i)\n{\n\tconst ctl_named_node_t * ret;\n\n\tmalloc_mutex_lock(&ctl_mtx);\n\tif (i > ctl_stats.narenas || ctl_stats.arenas[i].initialized == false) {\n\t\tret = NULL;\n\t\tgoto label_return;\n\t}\n\n\tret = super_stats_arenas_i_node;\nlabel_return:\n\tmalloc_mutex_unlock(&ctl_mtx);\n\treturn (ret);\n}\n\nCTL_RO_CGEN(config_stats, stats_cactive, &stats_cactive, size_t *)\nCTL_RO_CGEN(config_stats, stats_allocated, ctl_stats.allocated, size_t)\nCTL_RO_CGEN(config_stats, stats_active, ctl_stats.active, size_t)\nCTL_RO_CGEN(config_stats, stats_mapped, ctl_stats.mapped, size_t)\n"
  },
  {
    "path": "deps/jemalloc/src/extent.c",
    "content": "#define\tJEMALLOC_EXTENT_C_\n#include \"jemalloc/internal/jemalloc_internal.h\"\n\n/******************************************************************************/\n\nstatic inline int\nextent_szad_comp(extent_node_t *a, extent_node_t *b)\n{\n\tint ret;\n\tsize_t a_size = a->size;\n\tsize_t b_size = b->size;\n\n\tret = (a_size > b_size) - (a_size < b_size);\n\tif (ret == 0) {\n\t\tuintptr_t a_addr = (uintptr_t)a->addr;\n\t\tuintptr_t b_addr = (uintptr_t)b->addr;\n\n\t\tret = (a_addr > b_addr) - (a_addr < b_addr);\n\t}\n\n\treturn (ret);\n}\n\n/* Generate red-black tree functions. */\nrb_gen(, extent_tree_szad_, extent_tree_t, extent_node_t, link_szad,\n    extent_szad_comp)\n\nstatic inline int\nextent_ad_comp(extent_node_t *a, extent_node_t *b)\n{\n\tuintptr_t a_addr = (uintptr_t)a->addr;\n\tuintptr_t b_addr = (uintptr_t)b->addr;\n\n\treturn ((a_addr > b_addr) - (a_addr < b_addr));\n}\n\n/* Generate red-black tree functions. */\nrb_gen(, extent_tree_ad_, extent_tree_t, extent_node_t, link_ad,\n    extent_ad_comp)\n"
  },
  {
    "path": "deps/jemalloc/src/hash.c",
    "content": "#define\tJEMALLOC_HASH_C_\n#include \"jemalloc/internal/jemalloc_internal.h\"\n"
  },
  {
    "path": "deps/jemalloc/src/huge.c",
    "content": "#define\tJEMALLOC_HUGE_C_\n#include \"jemalloc/internal/jemalloc_internal.h\"\n\n/******************************************************************************/\n/* Data. */\n\nuint64_t\thuge_nmalloc;\nuint64_t\thuge_ndalloc;\nsize_t\t\thuge_allocated;\n\nmalloc_mutex_t\thuge_mtx;\n\n/******************************************************************************/\n\n/* Tree of chunks that are stand-alone huge allocations. */\nstatic extent_tree_t\thuge;\n\nvoid *\nhuge_malloc(size_t size, bool zero)\n{\n\n\treturn (huge_palloc(size, chunksize, zero));\n}\n\nvoid *\nhuge_palloc(size_t size, size_t alignment, bool zero)\n{\n\tvoid *ret;\n\tsize_t csize;\n\textent_node_t *node;\n\tbool is_zeroed;\n\n\t/* Allocate one or more contiguous chunks for this request. */\n\n\tcsize = CHUNK_CEILING(size);\n\tif (csize == 0) {\n\t\t/* size is large enough to cause size_t wrap-around. */\n\t\treturn (NULL);\n\t}\n\n\t/* Allocate an extent node with which to track the chunk. */\n\tnode = base_node_alloc();\n\tif (node == NULL)\n\t\treturn (NULL);\n\n\t/*\n\t * Copy zero into is_zeroed and pass the copy to chunk_alloc(), so that\n\t * it is possible to make correct junk/zero fill decisions below.\n\t */\n\tis_zeroed = zero;\n\tret = chunk_alloc(csize, alignment, false, &is_zeroed,\n\t    chunk_dss_prec_get());\n\tif (ret == NULL) {\n\t\tbase_node_dealloc(node);\n\t\treturn (NULL);\n\t}\n\n\t/* Insert node into huge. */\n\tnode->addr = ret;\n\tnode->size = csize;\n\n\tmalloc_mutex_lock(&huge_mtx);\n\textent_tree_ad_insert(&huge, node);\n\tif (config_stats) {\n\t\tstats_cactive_add(csize);\n\t\thuge_nmalloc++;\n\t\thuge_allocated += csize;\n\t}\n\tmalloc_mutex_unlock(&huge_mtx);\n\n\tif (config_fill && zero == false) {\n\t\tif (opt_junk)\n\t\t\tmemset(ret, 0xa5, csize);\n\t\telse if (opt_zero && is_zeroed == false)\n\t\t\tmemset(ret, 0, csize);\n\t}\n\n\treturn (ret);\n}\n\nvoid *\nhuge_ralloc_no_move(void *ptr, size_t oldsize, size_t size, size_t extra)\n{\n\n\t/*\n\t * Avoid moving the allocation if the size class can be left the same.\n\t */\n\tif (oldsize > arena_maxclass\n\t    && CHUNK_CEILING(oldsize) >= CHUNK_CEILING(size)\n\t    && CHUNK_CEILING(oldsize) <= CHUNK_CEILING(size+extra)) {\n\t\tassert(CHUNK_CEILING(oldsize) == oldsize);\n\t\tif (config_fill && opt_junk && size < oldsize) {\n\t\t\tmemset((void *)((uintptr_t)ptr + size), 0x5a,\n\t\t\t    oldsize - size);\n\t\t}\n\t\treturn (ptr);\n\t}\n\n\t/* Reallocation would require a move. */\n\treturn (NULL);\n}\n\nvoid *\nhuge_ralloc(void *ptr, size_t oldsize, size_t size, size_t extra,\n    size_t alignment, bool zero, bool try_tcache_dalloc)\n{\n\tvoid *ret;\n\tsize_t copysize;\n\n\t/* Try to avoid moving the allocation. */\n\tret = huge_ralloc_no_move(ptr, oldsize, size, extra);\n\tif (ret != NULL)\n\t\treturn (ret);\n\n\t/*\n\t * size and oldsize are different enough that we need to use a\n\t * different size class.  In that case, fall back to allocating new\n\t * space and copying.\n\t */\n\tif (alignment > chunksize)\n\t\tret = huge_palloc(size + extra, alignment, zero);\n\telse\n\t\tret = huge_malloc(size + extra, zero);\n\n\tif (ret == NULL) {\n\t\tif (extra == 0)\n\t\t\treturn (NULL);\n\t\t/* Try again, this time without extra. */\n\t\tif (alignment > chunksize)\n\t\t\tret = huge_palloc(size, alignment, zero);\n\t\telse\n\t\t\tret = huge_malloc(size, zero);\n\n\t\tif (ret == NULL)\n\t\t\treturn (NULL);\n\t}\n\n\t/*\n\t * Copy at most size bytes (not size+extra), since the caller has no\n\t * expectation that the extra bytes will be reliably preserved.\n\t */\n\tcopysize = (size < oldsize) ? size : oldsize;\n\n#ifdef JEMALLOC_MREMAP\n\t/*\n\t * Use mremap(2) if this is a huge-->huge reallocation, and neither the\n\t * source nor the destination are in dss.\n\t */\n\tif (oldsize >= chunksize && (config_dss == false || (chunk_in_dss(ptr)\n\t    == false && chunk_in_dss(ret) == false))) {\n\t\tsize_t newsize = huge_salloc(ret);\n\n\t\t/*\n\t\t * Remove ptr from the tree of huge allocations before\n\t\t * performing the remap operation, in order to avoid the\n\t\t * possibility of another thread acquiring that mapping before\n\t\t * this one removes it from the tree.\n\t\t */\n\t\thuge_dalloc(ptr, false);\n\t\tif (mremap(ptr, oldsize, newsize, MREMAP_MAYMOVE|MREMAP_FIXED,\n\t\t    ret) == MAP_FAILED) {\n\t\t\t/*\n\t\t\t * Assuming no chunk management bugs in the allocator,\n\t\t\t * the only documented way an error can occur here is\n\t\t\t * if the application changed the map type for a\n\t\t\t * portion of the old allocation.  This is firmly in\n\t\t\t * undefined behavior territory, so write a diagnostic\n\t\t\t * message, and optionally abort.\n\t\t\t */\n\t\t\tchar buf[BUFERROR_BUF];\n\n\t\t\tbuferror(buf, sizeof(buf));\n\t\t\tmalloc_printf(\"<jemalloc>: Error in mremap(): %s\\n\",\n\t\t\t    buf);\n\t\t\tif (opt_abort)\n\t\t\t\tabort();\n\t\t\tmemcpy(ret, ptr, copysize);\n\t\t\tchunk_dealloc_mmap(ptr, oldsize);\n\t\t}\n\t} else\n#endif\n\t{\n\t\tmemcpy(ret, ptr, copysize);\n\t\tiqallocx(ptr, try_tcache_dalloc);\n\t}\n\treturn (ret);\n}\n\nvoid\nhuge_dalloc(void *ptr, bool unmap)\n{\n\textent_node_t *node, key;\n\n\tmalloc_mutex_lock(&huge_mtx);\n\n\t/* Extract from tree of huge allocations. */\n\tkey.addr = ptr;\n\tnode = extent_tree_ad_search(&huge, &key);\n\tassert(node != NULL);\n\tassert(node->addr == ptr);\n\textent_tree_ad_remove(&huge, node);\n\n\tif (config_stats) {\n\t\tstats_cactive_sub(node->size);\n\t\thuge_ndalloc++;\n\t\thuge_allocated -= node->size;\n\t}\n\n\tmalloc_mutex_unlock(&huge_mtx);\n\n\tif (unmap && config_fill && config_dss && opt_junk)\n\t\tmemset(node->addr, 0x5a, node->size);\n\n\tchunk_dealloc(node->addr, node->size, unmap);\n\n\tbase_node_dealloc(node);\n}\n\nsize_t\nhuge_salloc(const void *ptr)\n{\n\tsize_t ret;\n\textent_node_t *node, key;\n\n\tmalloc_mutex_lock(&huge_mtx);\n\n\t/* Extract from tree of huge allocations. */\n\tkey.addr = __DECONST(void *, ptr);\n\tnode = extent_tree_ad_search(&huge, &key);\n\tassert(node != NULL);\n\n\tret = node->size;\n\n\tmalloc_mutex_unlock(&huge_mtx);\n\n\treturn (ret);\n}\n\nprof_ctx_t *\nhuge_prof_ctx_get(const void *ptr)\n{\n\tprof_ctx_t *ret;\n\textent_node_t *node, key;\n\n\tmalloc_mutex_lock(&huge_mtx);\n\n\t/* Extract from tree of huge allocations. */\n\tkey.addr = __DECONST(void *, ptr);\n\tnode = extent_tree_ad_search(&huge, &key);\n\tassert(node != NULL);\n\n\tret = node->prof_ctx;\n\n\tmalloc_mutex_unlock(&huge_mtx);\n\n\treturn (ret);\n}\n\nvoid\nhuge_prof_ctx_set(const void *ptr, prof_ctx_t *ctx)\n{\n\textent_node_t *node, key;\n\n\tmalloc_mutex_lock(&huge_mtx);\n\n\t/* Extract from tree of huge allocations. */\n\tkey.addr = __DECONST(void *, ptr);\n\tnode = extent_tree_ad_search(&huge, &key);\n\tassert(node != NULL);\n\n\tnode->prof_ctx = ctx;\n\n\tmalloc_mutex_unlock(&huge_mtx);\n}\n\nbool\nhuge_boot(void)\n{\n\n\t/* Initialize chunks data. */\n\tif (malloc_mutex_init(&huge_mtx))\n\t\treturn (true);\n\textent_tree_ad_new(&huge);\n\n\tif (config_stats) {\n\t\thuge_nmalloc = 0;\n\t\thuge_ndalloc = 0;\n\t\thuge_allocated = 0;\n\t}\n\n\treturn (false);\n}\n\nvoid\nhuge_prefork(void)\n{\n\n\tmalloc_mutex_prefork(&huge_mtx);\n}\n\nvoid\nhuge_postfork_parent(void)\n{\n\n\tmalloc_mutex_postfork_parent(&huge_mtx);\n}\n\nvoid\nhuge_postfork_child(void)\n{\n\n\tmalloc_mutex_postfork_child(&huge_mtx);\n}\n"
  },
  {
    "path": "deps/jemalloc/src/jemalloc.c",
    "content": "#define\tJEMALLOC_C_\n#include \"jemalloc/internal/jemalloc_internal.h\"\n\n/******************************************************************************/\n/* Data. */\n\nmalloc_tsd_data(, arenas, arena_t *, NULL)\nmalloc_tsd_data(, thread_allocated, thread_allocated_t,\n    THREAD_ALLOCATED_INITIALIZER)\n\n/* Runtime configuration options. */\nconst char\t*je_malloc_conf;\n#ifdef JEMALLOC_DEBUG\nbool\topt_abort = true;\n#  ifdef JEMALLOC_FILL\nbool\topt_junk = true;\n#  else\nbool\topt_junk = false;\n#  endif\n#else\nbool\topt_abort = false;\nbool\topt_junk = false;\n#endif\nsize_t\topt_quarantine = ZU(0);\nbool\topt_redzone = false;\nbool\topt_utrace = false;\nbool\topt_valgrind = false;\nbool\topt_xmalloc = false;\nbool\topt_zero = false;\nsize_t\topt_narenas = 0;\n\nunsigned\tncpus;\n\nmalloc_mutex_t\t\tarenas_lock;\narena_t\t\t\t**arenas;\nunsigned\t\tnarenas_total;\nunsigned\t\tnarenas_auto;\n\n/* Set to true once the allocator has been initialized. */\nstatic bool\t\tmalloc_initialized = false;\n\n#ifdef JEMALLOC_THREADED_INIT\n/* Used to let the initializing thread recursively allocate. */\n#  define NO_INITIALIZER\t((unsigned long)0)\n#  define INITIALIZER\t\tpthread_self()\n#  define IS_INITIALIZER\t(malloc_initializer == pthread_self())\nstatic pthread_t\t\tmalloc_initializer = NO_INITIALIZER;\n#else\n#  define NO_INITIALIZER\tfalse\n#  define INITIALIZER\t\ttrue\n#  define IS_INITIALIZER\tmalloc_initializer\nstatic bool\t\t\tmalloc_initializer = NO_INITIALIZER;\n#endif\n\n/* Used to avoid initialization races. */\n#ifdef _WIN32\nstatic malloc_mutex_t\tinit_lock;\n\nJEMALLOC_ATTR(constructor)\nstatic void WINAPI\n_init_init_lock(void)\n{\n\n\tmalloc_mutex_init(&init_lock);\n}\n\n#ifdef _MSC_VER\n#  pragma section(\".CRT$XCU\", read)\nJEMALLOC_SECTION(\".CRT$XCU\") JEMALLOC_ATTR(used)\nstatic const void (WINAPI *init_init_lock)(void) = _init_init_lock;\n#endif\n\n#else\nstatic malloc_mutex_t\tinit_lock = MALLOC_MUTEX_INITIALIZER;\n#endif\n\ntypedef struct {\n\tvoid\t*p;\t/* Input pointer (as in realloc(p, s)). */\n\tsize_t\ts;\t/* Request size. */\n\tvoid\t*r;\t/* Result pointer. */\n} malloc_utrace_t;\n\n#ifdef JEMALLOC_UTRACE\n#  define UTRACE(a, b, c) do {\t\t\t\t\t\t\\\n\tif (opt_utrace) {\t\t\t\t\t\t\\\n\t\tmalloc_utrace_t ut;\t\t\t\t\t\\\n\t\tut.p = (a);\t\t\t\t\t\t\\\n\t\tut.s = (b);\t\t\t\t\t\t\\\n\t\tut.r = (c);\t\t\t\t\t\t\\\n\t\tutrace(&ut, sizeof(ut));\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n} while (0)\n#else\n#  define UTRACE(a, b, c)\n#endif\n\n/******************************************************************************/\n/* Function prototypes for non-inline static functions. */\n\nstatic void\tstats_print_atexit(void);\nstatic unsigned\tmalloc_ncpus(void);\nstatic bool\tmalloc_conf_next(char const **opts_p, char const **k_p,\n    size_t *klen_p, char const **v_p, size_t *vlen_p);\nstatic void\tmalloc_conf_error(const char *msg, const char *k, size_t klen,\n    const char *v, size_t vlen);\nstatic void\tmalloc_conf_init(void);\nstatic bool\tmalloc_init_hard(void);\nstatic int\timemalign(void **memptr, size_t alignment, size_t size,\n    size_t min_alignment);\n\n/******************************************************************************/\n/*\n * Begin miscellaneous support functions.\n */\n\n/* Create a new arena and insert it into the arenas array at index ind. */\narena_t *\narenas_extend(unsigned ind)\n{\n\tarena_t *ret;\n\n\tret = (arena_t *)base_alloc(sizeof(arena_t));\n\tif (ret != NULL && arena_new(ret, ind) == false) {\n\t\tarenas[ind] = ret;\n\t\treturn (ret);\n\t}\n\t/* Only reached if there is an OOM error. */\n\n\t/*\n\t * OOM here is quite inconvenient to propagate, since dealing with it\n\t * would require a check for failure in the fast path.  Instead, punt\n\t * by using arenas[0].  In practice, this is an extremely unlikely\n\t * failure.\n\t */\n\tmalloc_write(\"<jemalloc>: Error initializing arena\\n\");\n\tif (opt_abort)\n\t\tabort();\n\n\treturn (arenas[0]);\n}\n\n/* Slow path, called only by choose_arena(). */\narena_t *\nchoose_arena_hard(void)\n{\n\tarena_t *ret;\n\n\tif (narenas_auto > 1) {\n\t\tunsigned i, choose, first_null;\n\n\t\tchoose = 0;\n\t\tfirst_null = narenas_auto;\n\t\tmalloc_mutex_lock(&arenas_lock);\n\t\tassert(arenas[0] != NULL);\n\t\tfor (i = 1; i < narenas_auto; i++) {\n\t\t\tif (arenas[i] != NULL) {\n\t\t\t\t/*\n\t\t\t\t * Choose the first arena that has the lowest\n\t\t\t\t * number of threads assigned to it.\n\t\t\t\t */\n\t\t\t\tif (arenas[i]->nthreads <\n\t\t\t\t    arenas[choose]->nthreads)\n\t\t\t\t\tchoose = i;\n\t\t\t} else if (first_null == narenas_auto) {\n\t\t\t\t/*\n\t\t\t\t * Record the index of the first uninitialized\n\t\t\t\t * arena, in case all extant arenas are in use.\n\t\t\t\t *\n\t\t\t\t * NB: It is possible for there to be\n\t\t\t\t * discontinuities in terms of initialized\n\t\t\t\t * versus uninitialized arenas, due to the\n\t\t\t\t * \"thread.arena\" mallctl.\n\t\t\t\t */\n\t\t\t\tfirst_null = i;\n\t\t\t}\n\t\t}\n\n\t\tif (arenas[choose]->nthreads == 0\n\t\t    || first_null == narenas_auto) {\n\t\t\t/*\n\t\t\t * Use an unloaded arena, or the least loaded arena if\n\t\t\t * all arenas are already initialized.\n\t\t\t */\n\t\t\tret = arenas[choose];\n\t\t} else {\n\t\t\t/* Initialize a new arena. */\n\t\t\tret = arenas_extend(first_null);\n\t\t}\n\t\tret->nthreads++;\n\t\tmalloc_mutex_unlock(&arenas_lock);\n\t} else {\n\t\tret = arenas[0];\n\t\tmalloc_mutex_lock(&arenas_lock);\n\t\tret->nthreads++;\n\t\tmalloc_mutex_unlock(&arenas_lock);\n\t}\n\n\tarenas_tsd_set(&ret);\n\n\treturn (ret);\n}\n\nstatic void\nstats_print_atexit(void)\n{\n\n\tif (config_tcache && config_stats) {\n\t\tunsigned narenas, i;\n\n\t\t/*\n\t\t * Merge stats from extant threads.  This is racy, since\n\t\t * individual threads do not lock when recording tcache stats\n\t\t * events.  As a consequence, the final stats may be slightly\n\t\t * out of date by the time they are reported, if other threads\n\t\t * continue to allocate.\n\t\t */\n\t\tfor (i = 0, narenas = narenas_total_get(); i < narenas; i++) {\n\t\t\tarena_t *arena = arenas[i];\n\t\t\tif (arena != NULL) {\n\t\t\t\ttcache_t *tcache;\n\n\t\t\t\t/*\n\t\t\t\t * tcache_stats_merge() locks bins, so if any\n\t\t\t\t * code is introduced that acquires both arena\n\t\t\t\t * and bin locks in the opposite order,\n\t\t\t\t * deadlocks may result.\n\t\t\t\t */\n\t\t\t\tmalloc_mutex_lock(&arena->lock);\n\t\t\t\tql_foreach(tcache, &arena->tcache_ql, link) {\n\t\t\t\t\ttcache_stats_merge(tcache, arena);\n\t\t\t\t}\n\t\t\t\tmalloc_mutex_unlock(&arena->lock);\n\t\t\t}\n\t\t}\n\t}\n\tje_malloc_stats_print(NULL, NULL, NULL);\n}\n\n/*\n * End miscellaneous support functions.\n */\n/******************************************************************************/\n/*\n * Begin initialization functions.\n */\n\nstatic unsigned\nmalloc_ncpus(void)\n{\n\tunsigned ret;\n\tlong result;\n\n#ifdef _WIN32\n\tSYSTEM_INFO si;\n\tGetSystemInfo(&si);\n\tresult = si.dwNumberOfProcessors;\n#else\n\tresult = sysconf(_SC_NPROCESSORS_ONLN);\n#endif\n\tif (result == -1) {\n\t\t/* Error. */\n\t\tret = 1;\n\t}  else {\n    ret = (unsigned)result;\n  }\n\n\treturn (ret);\n}\n\nvoid\narenas_cleanup(void *arg)\n{\n\tarena_t *arena = *(arena_t **)arg;\n\n\tmalloc_mutex_lock(&arenas_lock);\n\tarena->nthreads--;\n\tmalloc_mutex_unlock(&arenas_lock);\n}\n\nstatic inline bool\nmalloc_init(void)\n{\n\n\tif (malloc_initialized == false)\n\t\treturn (malloc_init_hard());\n\n\treturn (false);\n}\n\nstatic bool\nmalloc_conf_next(char const **opts_p, char const **k_p, size_t *klen_p,\n    char const **v_p, size_t *vlen_p)\n{\n\tbool accept;\n\tconst char *opts = *opts_p;\n\n\t*k_p = opts;\n\n\tfor (accept = false; accept == false;) {\n\t\tswitch (*opts) {\n\t\tcase 'A': case 'B': case 'C': case 'D': case 'E': case 'F':\n\t\tcase 'G': case 'H': case 'I': case 'J': case 'K': case 'L':\n\t\tcase 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':\n\t\tcase 'S': case 'T': case 'U': case 'V': case 'W': case 'X':\n\t\tcase 'Y': case 'Z':\n\t\tcase 'a': case 'b': case 'c': case 'd': case 'e': case 'f':\n\t\tcase 'g': case 'h': case 'i': case 'j': case 'k': case 'l':\n\t\tcase 'm': case 'n': case 'o': case 'p': case 'q': case 'r':\n\t\tcase 's': case 't': case 'u': case 'v': case 'w': case 'x':\n\t\tcase 'y': case 'z':\n\t\tcase '0': case '1': case '2': case '3': case '4': case '5':\n\t\tcase '6': case '7': case '8': case '9':\n\t\tcase '_':\n\t\t\topts++;\n\t\t\tbreak;\n\t\tcase ':':\n\t\t\topts++;\n\t\t\t*klen_p = (uintptr_t)opts - 1 - (uintptr_t)*k_p;\n\t\t\t*v_p = opts;\n\t\t\taccept = true;\n\t\t\tbreak;\n\t\tcase '\\0':\n\t\t\tif (opts != *opts_p) {\n\t\t\t\tmalloc_write(\"<jemalloc>: Conf string ends \"\n\t\t\t\t    \"with key\\n\");\n\t\t\t}\n\t\t\treturn (true);\n\t\tdefault:\n\t\t\tmalloc_write(\"<jemalloc>: Malformed conf string\\n\");\n\t\t\treturn (true);\n\t\t}\n\t}\n\n\tfor (accept = false; accept == false;) {\n\t\tswitch (*opts) {\n\t\tcase ',':\n\t\t\topts++;\n\t\t\t/*\n\t\t\t * Look ahead one character here, because the next time\n\t\t\t * this function is called, it will assume that end of\n\t\t\t * input has been cleanly reached if no input remains,\n\t\t\t * but we have optimistically already consumed the\n\t\t\t * comma if one exists.\n\t\t\t */\n\t\t\tif (*opts == '\\0') {\n\t\t\t\tmalloc_write(\"<jemalloc>: Conf string ends \"\n\t\t\t\t    \"with comma\\n\");\n\t\t\t}\n\t\t\t*vlen_p = (uintptr_t)opts - 1 - (uintptr_t)*v_p;\n\t\t\taccept = true;\n\t\t\tbreak;\n\t\tcase '\\0':\n\t\t\t*vlen_p = (uintptr_t)opts - (uintptr_t)*v_p;\n\t\t\taccept = true;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\topts++;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\t*opts_p = opts;\n\treturn (false);\n}\n\nstatic void\nmalloc_conf_error(const char *msg, const char *k, size_t klen, const char *v,\n    size_t vlen)\n{\n\n\tmalloc_printf(\"<jemalloc>: %s: %.*s:%.*s\\n\", msg, (int)klen, k,\n\t    (int)vlen, v);\n}\n\nstatic void\nmalloc_conf_init(void)\n{\n\tunsigned i;\n\tchar buf[PATH_MAX + 1];\n\tconst char *opts, *k, *v;\n\tsize_t klen, vlen;\n\n\t/*\n\t * Automatically configure valgrind before processing options.  The\n\t * valgrind option remains in jemalloc 3.x for compatibility reasons.\n\t */\n\tif (config_valgrind) {\n\t\topt_valgrind = (RUNNING_ON_VALGRIND != 0) ? true : false;\n\t\tif (config_fill && opt_valgrind) {\n\t\t\topt_junk = false;\n\t\t\tassert(opt_zero == false);\n\t\t\topt_quarantine = JEMALLOC_VALGRIND_QUARANTINE_DEFAULT;\n\t\t\topt_redzone = true;\n\t\t}\n\t\tif (config_tcache && opt_valgrind)\n\t\t\topt_tcache = false;\n\t}\n\n\tfor (i = 0; i < 3; i++) {\n\t\t/* Get runtime configuration. */\n\t\tswitch (i) {\n\t\tcase 0:\n\t\t\tif (je_malloc_conf != NULL) {\n\t\t\t\t/*\n\t\t\t\t * Use options that were compiled into the\n\t\t\t\t * program.\n\t\t\t\t */\n\t\t\t\topts = je_malloc_conf;\n\t\t\t} else {\n\t\t\t\t/* No configuration specified. */\n\t\t\t\tbuf[0] = '\\0';\n\t\t\t\topts = buf;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 1: {\n#ifndef _WIN32\n\t\t\tint linklen;\n\t\t\tconst char *linkname =\n#  ifdef JEMALLOC_PREFIX\n\t\t\t    \"/etc/\"JEMALLOC_PREFIX\"malloc.conf\"\n#  else\n\t\t\t    \"/etc/malloc.conf\"\n#  endif\n\t\t\t    ;\n\n\t\t\tif ((linklen = readlink(linkname, buf,\n\t\t\t    sizeof(buf) - 1)) != -1) {\n\t\t\t\t/*\n\t\t\t\t * Use the contents of the \"/etc/malloc.conf\"\n\t\t\t\t * symbolic link's name.\n\t\t\t\t */\n\t\t\t\tbuf[linklen] = '\\0';\n\t\t\t\topts = buf;\n\t\t\t} else\n#endif\n\t\t\t{\n\t\t\t\t/* No configuration specified. */\n\t\t\t\tbuf[0] = '\\0';\n\t\t\t\topts = buf;\n\t\t\t}\n\t\t\tbreak;\n\t\t} case 2: {\n\t\t\tconst char *envname =\n#ifdef JEMALLOC_PREFIX\n\t\t\t    JEMALLOC_CPREFIX\"MALLOC_CONF\"\n#else\n\t\t\t    \"MALLOC_CONF\"\n#endif\n\t\t\t    ;\n\n\t\t\tif ((opts = getenv(envname)) != NULL) {\n\t\t\t\t/*\n\t\t\t\t * Do nothing; opts is already initialized to\n\t\t\t\t * the value of the MALLOC_CONF environment\n\t\t\t\t * variable.\n\t\t\t\t */\n\t\t\t} else {\n\t\t\t\t/* No configuration specified. */\n\t\t\t\tbuf[0] = '\\0';\n\t\t\t\topts = buf;\n\t\t\t}\n\t\t\tbreak;\n\t\t} default:\n\t\t\t/* NOTREACHED */\n\t\t\tassert(false);\n\t\t\tbuf[0] = '\\0';\n\t\t\topts = buf;\n\t\t}\n\n\t\twhile (*opts != '\\0' && malloc_conf_next(&opts, &k, &klen, &v,\n\t\t    &vlen) == false) {\n#define\tCONF_HANDLE_BOOL_HIT(o, n, hit)\t\t\t\t\t\\\n\t\t\tif (sizeof(n)-1 == klen && strncmp(n, k,\t\\\n\t\t\t    klen) == 0) {\t\t\t\t\\\n\t\t\t\tif (strncmp(\"true\", v, vlen) == 0 &&\t\\\n\t\t\t\t    vlen == sizeof(\"true\")-1)\t\t\\\n\t\t\t\t\to = true;\t\t\t\\\n\t\t\t\telse if (strncmp(\"false\", v, vlen) ==\t\\\n\t\t\t\t    0 && vlen == sizeof(\"false\")-1)\t\\\n\t\t\t\t\to = false;\t\t\t\\\n\t\t\t\telse {\t\t\t\t\t\\\n\t\t\t\t\tmalloc_conf_error(\t\t\\\n\t\t\t\t\t    \"Invalid conf value\",\t\\\n\t\t\t\t\t    k, klen, v, vlen);\t\t\\\n\t\t\t\t}\t\t\t\t\t\\\n\t\t\t\thit = true;\t\t\t\t\\\n\t\t\t} else\t\t\t\t\t\t\\\n\t\t\t\thit = false;\n#define\tCONF_HANDLE_BOOL(o, n) {\t\t\t\t\t\\\n\t\t\tbool hit;\t\t\t\t\t\\\n\t\t\tCONF_HANDLE_BOOL_HIT(o, n, hit);\t\t\\\n\t\t\tif (hit)\t\t\t\t\t\\\n\t\t\t\tcontinue;\t\t\t\t\\\n}\n#define\tCONF_HANDLE_SIZE_T(o, n, min, max)\t\t\t\t\\\n\t\t\tif (sizeof(n)-1 == klen && strncmp(n, k,\t\\\n\t\t\t    klen) == 0) {\t\t\t\t\\\n\t\t\t\tuintmax_t um;\t\t\t\t\\\n\t\t\t\tchar *end;\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\t\t\t\tset_errno(0);\t\t\t\t\\\n\t\t\t\tum = malloc_strtoumax(v, &end, 0);\t\\\n\t\t\t\tif (get_errno() != 0 || (uintptr_t)end -\\\n\t\t\t\t    (uintptr_t)v != vlen) {\t\t\\\n\t\t\t\t\tmalloc_conf_error(\t\t\\\n\t\t\t\t\t    \"Invalid conf value\",\t\\\n\t\t\t\t\t    k, klen, v, vlen);\t\t\\\n\t\t\t\t} else if (um < min || um > max) {\t\\\n\t\t\t\t\tmalloc_conf_error(\t\t\\\n\t\t\t\t\t    \"Out-of-range conf value\",\t\\\n\t\t\t\t\t    k, klen, v, vlen);\t\t\\\n\t\t\t\t} else\t\t\t\t\t\\\n\t\t\t\t\to = um;\t\t\t\t\\\n\t\t\t\tcontinue;\t\t\t\t\\\n\t\t\t}\n#define\tCONF_HANDLE_SSIZE_T(o, n, min, max)\t\t\t\t\\\n\t\t\tif (sizeof(n)-1 == klen && strncmp(n, k,\t\\\n\t\t\t    klen) == 0) {\t\t\t\t\\\n\t\t\t\tlong l;\t\t\t\t\t\\\n\t\t\t\tchar *end;\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\t\t\t\tset_errno(0);\t\t\t\t\\\n\t\t\t\tl = strtol(v, &end, 0);\t\t\t\\\n\t\t\t\tif (get_errno() != 0 || (uintptr_t)end -\\\n\t\t\t\t    (uintptr_t)v != vlen) {\t\t\\\n\t\t\t\t\tmalloc_conf_error(\t\t\\\n\t\t\t\t\t    \"Invalid conf value\",\t\\\n\t\t\t\t\t    k, klen, v, vlen);\t\t\\\n\t\t\t\t} else if (l < (ssize_t)min || l >\t\\\n\t\t\t\t    (ssize_t)max) {\t\t\t\\\n\t\t\t\t\tmalloc_conf_error(\t\t\\\n\t\t\t\t\t    \"Out-of-range conf value\",\t\\\n\t\t\t\t\t    k, klen, v, vlen);\t\t\\\n\t\t\t\t} else\t\t\t\t\t\\\n\t\t\t\t\to = l;\t\t\t\t\\\n\t\t\t\tcontinue;\t\t\t\t\\\n\t\t\t}\n#define\tCONF_HANDLE_CHAR_P(o, n, d)\t\t\t\t\t\\\n\t\t\tif (sizeof(n)-1 == klen && strncmp(n, k,\t\\\n\t\t\t    klen) == 0) {\t\t\t\t\\\n\t\t\t\tsize_t cpylen = (vlen <=\t\t\\\n\t\t\t\t    sizeof(o)-1) ? vlen :\t\t\\\n\t\t\t\t    sizeof(o)-1;\t\t\t\\\n\t\t\t\tstrncpy(o, v, cpylen);\t\t\t\\\n\t\t\t\to[cpylen] = '\\0';\t\t\t\\\n\t\t\t\tcontinue;\t\t\t\t\\\n\t\t\t}\n\n\t\t\tCONF_HANDLE_BOOL(opt_abort, \"abort\")\n\t\t\t/*\n\t\t\t * Chunks always require at least one header page, plus\n\t\t\t * one data page in the absence of redzones, or three\n\t\t\t * pages in the presence of redzones.  In order to\n\t\t\t * simplify options processing, fix the limit based on\n\t\t\t * config_fill.\n\t\t\t */\n\t\t\tCONF_HANDLE_SIZE_T(opt_lg_chunk, \"lg_chunk\", LG_PAGE +\n\t\t\t    (config_fill ? 2 : 1), (sizeof(size_t) << 3) - 1)\n\t\t\tif (strncmp(\"dss\", k, klen) == 0) {\n\t\t\t\tint i;\n\t\t\t\tbool match = false;\n\t\t\t\tfor (i = 0; i < dss_prec_limit; i++) {\n\t\t\t\t\tif (strncmp(dss_prec_names[i], v, vlen)\n\t\t\t\t\t    == 0) {\n\t\t\t\t\t\tif (chunk_dss_prec_set(i)) {\n\t\t\t\t\t\t\tmalloc_conf_error(\n\t\t\t\t\t\t\t    \"Error setting dss\",\n\t\t\t\t\t\t\t    k, klen, v, vlen);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\topt_dss =\n\t\t\t\t\t\t\t    dss_prec_names[i];\n\t\t\t\t\t\t\tmatch = true;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (match == false) {\n\t\t\t\t\tmalloc_conf_error(\"Invalid conf value\",\n\t\t\t\t\t    k, klen, v, vlen);\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tCONF_HANDLE_SIZE_T(opt_narenas, \"narenas\", 1,\n\t\t\t    SIZE_T_MAX)\n\t\t\tCONF_HANDLE_SSIZE_T(opt_lg_dirty_mult, \"lg_dirty_mult\",\n\t\t\t    -1, (sizeof(size_t) << 3) - 1)\n\t\t\tCONF_HANDLE_BOOL(opt_stats_print, \"stats_print\")\n\t\t\tif (config_fill) {\n\t\t\t\tCONF_HANDLE_BOOL(opt_junk, \"junk\")\n\t\t\t\tCONF_HANDLE_SIZE_T(opt_quarantine, \"quarantine\",\n\t\t\t\t    0, SIZE_T_MAX)\n\t\t\t\tCONF_HANDLE_BOOL(opt_redzone, \"redzone\")\n\t\t\t\tCONF_HANDLE_BOOL(opt_zero, \"zero\")\n\t\t\t}\n\t\t\tif (config_utrace) {\n\t\t\t\tCONF_HANDLE_BOOL(opt_utrace, \"utrace\")\n\t\t\t}\n\t\t\tif (config_valgrind) {\n\t\t\t\tCONF_HANDLE_BOOL(opt_valgrind, \"valgrind\")\n\t\t\t}\n\t\t\tif (config_xmalloc) {\n\t\t\t\tCONF_HANDLE_BOOL(opt_xmalloc, \"xmalloc\")\n\t\t\t}\n\t\t\tif (config_tcache) {\n\t\t\t\tCONF_HANDLE_BOOL(opt_tcache, \"tcache\")\n\t\t\t\tCONF_HANDLE_SSIZE_T(opt_lg_tcache_max,\n\t\t\t\t    \"lg_tcache_max\", -1,\n\t\t\t\t    (sizeof(size_t) << 3) - 1)\n\t\t\t}\n\t\t\tif (config_prof) {\n\t\t\t\tCONF_HANDLE_BOOL(opt_prof, \"prof\")\n\t\t\t\tCONF_HANDLE_CHAR_P(opt_prof_prefix,\n\t\t\t\t    \"prof_prefix\", \"jeprof\")\n\t\t\t\tCONF_HANDLE_BOOL(opt_prof_active, \"prof_active\")\n\t\t\t\tCONF_HANDLE_SSIZE_T(opt_lg_prof_sample,\n\t\t\t\t    \"lg_prof_sample\", 0,\n\t\t\t\t    (sizeof(uint64_t) << 3) - 1)\n\t\t\t\tCONF_HANDLE_BOOL(opt_prof_accum, \"prof_accum\")\n\t\t\t\tCONF_HANDLE_SSIZE_T(opt_lg_prof_interval,\n\t\t\t\t    \"lg_prof_interval\", -1,\n\t\t\t\t    (sizeof(uint64_t) << 3) - 1)\n\t\t\t\tCONF_HANDLE_BOOL(opt_prof_gdump, \"prof_gdump\")\n\t\t\t\tCONF_HANDLE_BOOL(opt_prof_final, \"prof_final\")\n\t\t\t\tCONF_HANDLE_BOOL(opt_prof_leak, \"prof_leak\")\n\t\t\t}\n\t\t\tmalloc_conf_error(\"Invalid conf pair\", k, klen, v,\n\t\t\t    vlen);\n#undef CONF_HANDLE_BOOL\n#undef CONF_HANDLE_SIZE_T\n#undef CONF_HANDLE_SSIZE_T\n#undef CONF_HANDLE_CHAR_P\n\t\t}\n\t}\n}\n\nstatic bool\nmalloc_init_hard(void)\n{\n\tarena_t *init_arenas[1];\n\n\tmalloc_mutex_lock(&init_lock);\n\tif (malloc_initialized || IS_INITIALIZER) {\n\t\t/*\n\t\t * Another thread initialized the allocator before this one\n\t\t * acquired init_lock, or this thread is the initializing\n\t\t * thread, and it is recursively allocating.\n\t\t */\n\t\tmalloc_mutex_unlock(&init_lock);\n\t\treturn (false);\n\t}\n#ifdef JEMALLOC_THREADED_INIT\n\tif (malloc_initializer != NO_INITIALIZER && IS_INITIALIZER == false) {\n\t\t/* Busy-wait until the initializing thread completes. */\n\t\tdo {\n\t\t\tmalloc_mutex_unlock(&init_lock);\n\t\t\tCPU_SPINWAIT;\n\t\t\tmalloc_mutex_lock(&init_lock);\n\t\t} while (malloc_initialized == false);\n\t\tmalloc_mutex_unlock(&init_lock);\n\t\treturn (false);\n\t}\n#endif\n\tmalloc_initializer = INITIALIZER;\n\n\tmalloc_tsd_boot();\n\tif (config_prof)\n\t\tprof_boot0();\n\n\tmalloc_conf_init();\n\n#if (!defined(JEMALLOC_MUTEX_INIT_CB) && !defined(JEMALLOC_ZONE) \\\n    && !defined(_WIN32))\n\t/* Register fork handlers. */\n\tif (pthread_atfork(jemalloc_prefork, jemalloc_postfork_parent,\n\t    jemalloc_postfork_child) != 0) {\n\t\tmalloc_write(\"<jemalloc>: Error in pthread_atfork()\\n\");\n\t\tif (opt_abort)\n\t\t\tabort();\n\t}\n#endif\n\n\tif (opt_stats_print) {\n\t\t/* Print statistics at exit. */\n\t\tif (atexit(stats_print_atexit) != 0) {\n\t\t\tmalloc_write(\"<jemalloc>: Error in atexit()\\n\");\n\t\t\tif (opt_abort)\n\t\t\t\tabort();\n\t\t}\n\t}\n\n\tif (base_boot()) {\n\t\tmalloc_mutex_unlock(&init_lock);\n\t\treturn (true);\n\t}\n\n\tif (chunk_boot()) {\n\t\tmalloc_mutex_unlock(&init_lock);\n\t\treturn (true);\n\t}\n\n\tif (ctl_boot()) {\n\t\tmalloc_mutex_unlock(&init_lock);\n\t\treturn (true);\n\t}\n\n\tif (config_prof)\n\t\tprof_boot1();\n\n\tarena_boot();\n\n\tif (config_tcache && tcache_boot0()) {\n\t\tmalloc_mutex_unlock(&init_lock);\n\t\treturn (true);\n\t}\n\n\tif (huge_boot()) {\n\t\tmalloc_mutex_unlock(&init_lock);\n\t\treturn (true);\n\t}\n\n\tif (malloc_mutex_init(&arenas_lock))\n\t\treturn (true);\n\n\t/*\n\t * Create enough scaffolding to allow recursive allocation in\n\t * malloc_ncpus().\n\t */\n\tnarenas_total = narenas_auto = 1;\n\tarenas = init_arenas;\n\tmemset(arenas, 0, sizeof(arena_t *) * narenas_auto);\n\n\t/*\n\t * Initialize one arena here.  The rest are lazily created in\n\t * choose_arena_hard().\n\t */\n\tarenas_extend(0);\n\tif (arenas[0] == NULL) {\n\t\tmalloc_mutex_unlock(&init_lock);\n\t\treturn (true);\n\t}\n\n\t/* Initialize allocation counters before any allocations can occur. */\n\tif (config_stats && thread_allocated_tsd_boot()) {\n\t\tmalloc_mutex_unlock(&init_lock);\n\t\treturn (true);\n\t}\n\n\tif (arenas_tsd_boot()) {\n\t\tmalloc_mutex_unlock(&init_lock);\n\t\treturn (true);\n\t}\n\n\tif (config_tcache && tcache_boot1()) {\n\t\tmalloc_mutex_unlock(&init_lock);\n\t\treturn (true);\n\t}\n\n\tif (config_fill && quarantine_boot()) {\n\t\tmalloc_mutex_unlock(&init_lock);\n\t\treturn (true);\n\t}\n\n\tif (config_prof && prof_boot2()) {\n\t\tmalloc_mutex_unlock(&init_lock);\n\t\treturn (true);\n\t}\n\n\t/* Get number of CPUs. */\n\tmalloc_mutex_unlock(&init_lock);\n\tncpus = malloc_ncpus();\n\tmalloc_mutex_lock(&init_lock);\n\n\tif (mutex_boot()) {\n\t\tmalloc_mutex_unlock(&init_lock);\n\t\treturn (true);\n\t}\n\n\tif (opt_narenas == 0) {\n\t\t/*\n\t\t * For SMP systems, create more than one arena per CPU by\n\t\t * default.\n\t\t */\n\t\tif (ncpus > 1)\n\t\t\topt_narenas = ncpus << 2;\n\t\telse\n\t\t\topt_narenas = 1;\n\t}\n\tnarenas_auto = opt_narenas;\n\t/*\n\t * Make sure that the arenas array can be allocated.  In practice, this\n\t * limit is enough to allow the allocator to function, but the ctl\n\t * machinery will fail to allocate memory at far lower limits.\n\t */\n\tif (narenas_auto > chunksize / sizeof(arena_t *)) {\n\t\tnarenas_auto = chunksize / sizeof(arena_t *);\n\t\tmalloc_printf(\"<jemalloc>: Reducing narenas to limit (%d)\\n\",\n\t\t    narenas_auto);\n\t}\n\tnarenas_total = narenas_auto;\n\n\t/* Allocate and initialize arenas. */\n\tarenas = (arena_t **)base_alloc(sizeof(arena_t *) * narenas_total);\n\tif (arenas == NULL) {\n\t\tmalloc_mutex_unlock(&init_lock);\n\t\treturn (true);\n\t}\n\t/*\n\t * Zero the array.  In practice, this should always be pre-zeroed,\n\t * since it was just mmap()ed, but let's be sure.\n\t */\n\tmemset(arenas, 0, sizeof(arena_t *) * narenas_total);\n\t/* Copy the pointer to the one arena that was already initialized. */\n\tarenas[0] = init_arenas[0];\n\n\tmalloc_initialized = true;\n\tmalloc_mutex_unlock(&init_lock);\n\treturn (false);\n}\n\n/*\n * End initialization functions.\n */\n/******************************************************************************/\n/*\n * Begin malloc(3)-compatible functions.\n */\n\nvoid *\nje_malloc(size_t size)\n{\n\tvoid *ret;\n\tsize_t usize JEMALLOC_CC_SILENCE_INIT(0);\n\tprof_thr_cnt_t *cnt JEMALLOC_CC_SILENCE_INIT(NULL);\n\n\tif (malloc_init()) {\n\t\tret = NULL;\n\t\tgoto label_oom;\n\t}\n\n\tif (size == 0)\n\t\tsize = 1;\n\n\tif (config_prof && opt_prof) {\n\t\tusize = s2u(size);\n\t\tPROF_ALLOC_PREP(1, usize, cnt);\n\t\tif (cnt == NULL) {\n\t\t\tret = NULL;\n\t\t\tgoto label_oom;\n\t\t}\n\t\tif (prof_promote && (uintptr_t)cnt != (uintptr_t)1U && usize <=\n\t\t    SMALL_MAXCLASS) {\n\t\t\tret = imalloc(SMALL_MAXCLASS+1);\n\t\t\tif (ret != NULL)\n\t\t\t\tarena_prof_promoted(ret, usize);\n\t\t} else\n\t\t\tret = imalloc(size);\n\t} else {\n\t\tif (config_stats || (config_valgrind && opt_valgrind))\n\t\t\tusize = s2u(size);\n\t\tret = imalloc(size);\n\t}\n\nlabel_oom:\n\tif (ret == NULL) {\n\t\tif (config_xmalloc && opt_xmalloc) {\n\t\t\tmalloc_write(\"<jemalloc>: Error in malloc(): \"\n\t\t\t    \"out of memory\\n\");\n\t\t\tabort();\n\t\t}\n\t\tset_errno(ENOMEM);\n\t}\n\tif (config_prof && opt_prof && ret != NULL)\n\t\tprof_malloc(ret, usize, cnt);\n\tif (config_stats && ret != NULL) {\n\t\tassert(usize == isalloc(ret, config_prof));\n\t\tthread_allocated_tsd_get()->allocated += usize;\n\t}\n\tUTRACE(0, size, ret);\n\tJEMALLOC_VALGRIND_MALLOC(ret != NULL, ret, usize, false);\n\treturn (ret);\n}\n\nJEMALLOC_ATTR(nonnull(1))\n#ifdef JEMALLOC_PROF\n/*\n * Avoid any uncertainty as to how many backtrace frames to ignore in\n * PROF_ALLOC_PREP().\n */\nJEMALLOC_ATTR(noinline)\n#endif\nstatic int\nimemalign(void **memptr, size_t alignment, size_t size,\n    size_t min_alignment)\n{\n\tint ret;\n\tsize_t usize;\n\tvoid *result;\n\tprof_thr_cnt_t *cnt JEMALLOC_CC_SILENCE_INIT(NULL);\n\n\tassert(min_alignment != 0);\n\n\tif (malloc_init())\n\t\tresult = NULL;\n\telse {\n\t\tif (size == 0)\n\t\t\tsize = 1;\n\n\t\t/* Make sure that alignment is a large enough power of 2. */\n\t\tif (((alignment - 1) & alignment) != 0\n\t\t    || (alignment < min_alignment)) {\n\t\t\tif (config_xmalloc && opt_xmalloc) {\n\t\t\t\tmalloc_write(\"<jemalloc>: Error allocating \"\n\t\t\t\t    \"aligned memory: invalid alignment\\n\");\n\t\t\t\tabort();\n\t\t\t}\n\t\t\tresult = NULL;\n\t\t\tret = EINVAL;\n\t\t\tgoto label_return;\n\t\t}\n\n\t\tusize = sa2u(size, alignment);\n\t\tif (usize == 0) {\n\t\t\tresult = NULL;\n\t\t\tret = ENOMEM;\n\t\t\tgoto label_return;\n\t\t}\n\n\t\tif (config_prof && opt_prof) {\n\t\t\tPROF_ALLOC_PREP(2, usize, cnt);\n\t\t\tif (cnt == NULL) {\n\t\t\t\tresult = NULL;\n\t\t\t\tret = EINVAL;\n\t\t\t} else {\n\t\t\t\tif (prof_promote && (uintptr_t)cnt !=\n\t\t\t\t    (uintptr_t)1U && usize <= SMALL_MAXCLASS) {\n\t\t\t\t\tassert(sa2u(SMALL_MAXCLASS+1,\n\t\t\t\t\t    alignment) != 0);\n\t\t\t\t\tresult = ipalloc(sa2u(SMALL_MAXCLASS+1,\n\t\t\t\t\t    alignment), alignment, false);\n\t\t\t\t\tif (result != NULL) {\n\t\t\t\t\t\tarena_prof_promoted(result,\n\t\t\t\t\t\t    usize);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tresult = ipalloc(usize, alignment,\n\t\t\t\t\t    false);\n\t\t\t\t}\n\t\t\t}\n\t\t} else\n\t\t\tresult = ipalloc(usize, alignment, false);\n\t}\n\n\tif (result == NULL) {\n\t\tif (config_xmalloc && opt_xmalloc) {\n\t\t\tmalloc_write(\"<jemalloc>: Error allocating aligned \"\n\t\t\t    \"memory: out of memory\\n\");\n\t\t\tabort();\n\t\t}\n\t\tret = ENOMEM;\n\t\tgoto label_return;\n\t}\n\n\t*memptr = result;\n\tret = 0;\n\nlabel_return:\n\tif (config_stats && result != NULL) {\n\t\tassert(usize == isalloc(result, config_prof));\n\t\tthread_allocated_tsd_get()->allocated += usize;\n\t}\n\tif (config_prof && opt_prof && result != NULL)\n\t\tprof_malloc(result, usize, cnt);\n\tUTRACE(0, size, result);\n\treturn (ret);\n}\n\nint\nje_posix_memalign(void **memptr, size_t alignment, size_t size)\n{\n\tint ret = imemalign(memptr, alignment, size, sizeof(void *));\n\tJEMALLOC_VALGRIND_MALLOC(ret == 0, *memptr, isalloc(*memptr,\n\t    config_prof), false);\n\treturn (ret);\n}\n\nvoid *\nje_aligned_alloc(size_t alignment, size_t size)\n{\n\tvoid *ret;\n\tint err;\n\n\tif ((err = imemalign(&ret, alignment, size, 1)) != 0) {\n\t\tret = NULL;\n\t\tset_errno(err);\n\t}\n\tJEMALLOC_VALGRIND_MALLOC(err == 0, ret, isalloc(ret, config_prof),\n\t    false);\n\treturn (ret);\n}\n\nvoid *\nje_calloc(size_t num, size_t size)\n{\n\tvoid *ret;\n\tsize_t num_size;\n\tsize_t usize JEMALLOC_CC_SILENCE_INIT(0);\n\tprof_thr_cnt_t *cnt JEMALLOC_CC_SILENCE_INIT(NULL);\n\n\tif (malloc_init()) {\n\t\tnum_size = 0;\n\t\tret = NULL;\n\t\tgoto label_return;\n\t}\n\n\tnum_size = num * size;\n\tif (num_size == 0) {\n\t\tif (num == 0 || size == 0)\n\t\t\tnum_size = 1;\n\t\telse {\n\t\t\tret = NULL;\n\t\t\tgoto label_return;\n\t\t}\n\t/*\n\t * Try to avoid division here.  We know that it isn't possible to\n\t * overflow during multiplication if neither operand uses any of the\n\t * most significant half of the bits in a size_t.\n\t */\n\t} else if (((num | size) & (SIZE_T_MAX << (sizeof(size_t) << 2)))\n\t    && (num_size / size != num)) {\n\t\t/* size_t overflow. */\n\t\tret = NULL;\n\t\tgoto label_return;\n\t}\n\n\tif (config_prof && opt_prof) {\n\t\tusize = s2u(num_size);\n\t\tPROF_ALLOC_PREP(1, usize, cnt);\n\t\tif (cnt == NULL) {\n\t\t\tret = NULL;\n\t\t\tgoto label_return;\n\t\t}\n\t\tif (prof_promote && (uintptr_t)cnt != (uintptr_t)1U && usize\n\t\t    <= SMALL_MAXCLASS) {\n\t\t\tret = icalloc(SMALL_MAXCLASS+1);\n\t\t\tif (ret != NULL)\n\t\t\t\tarena_prof_promoted(ret, usize);\n\t\t} else\n\t\t\tret = icalloc(num_size);\n\t} else {\n\t\tif (config_stats || (config_valgrind && opt_valgrind))\n\t\t\tusize = s2u(num_size);\n\t\tret = icalloc(num_size);\n\t}\n\nlabel_return:\n\tif (ret == NULL) {\n\t\tif (config_xmalloc && opt_xmalloc) {\n\t\t\tmalloc_write(\"<jemalloc>: Error in calloc(): out of \"\n\t\t\t    \"memory\\n\");\n\t\t\tabort();\n\t\t}\n\t\tset_errno(ENOMEM);\n\t}\n\n\tif (config_prof && opt_prof && ret != NULL)\n\t\tprof_malloc(ret, usize, cnt);\n\tif (config_stats && ret != NULL) {\n\t\tassert(usize == isalloc(ret, config_prof));\n\t\tthread_allocated_tsd_get()->allocated += usize;\n\t}\n\tUTRACE(0, num_size, ret);\n\tJEMALLOC_VALGRIND_MALLOC(ret != NULL, ret, usize, true);\n\treturn (ret);\n}\n\nvoid *\nje_realloc(void *ptr, size_t size)\n{\n\tvoid *ret;\n\tsize_t usize JEMALLOC_CC_SILENCE_INIT(0);\n\tsize_t old_size = 0;\n\tsize_t old_rzsize JEMALLOC_CC_SILENCE_INIT(0);\n\tprof_thr_cnt_t *cnt JEMALLOC_CC_SILENCE_INIT(NULL);\n\tprof_ctx_t *old_ctx JEMALLOC_CC_SILENCE_INIT(NULL);\n\n\tif (size == 0) {\n\t\tif (ptr != NULL) {\n\t\t\t/* realloc(ptr, 0) is equivalent to free(p). */\n\t\t\tif (config_prof) {\n\t\t\t\told_size = isalloc(ptr, true);\n\t\t\t\tif (config_valgrind && opt_valgrind)\n\t\t\t\t\told_rzsize = p2rz(ptr);\n\t\t\t} else if (config_stats) {\n\t\t\t\told_size = isalloc(ptr, false);\n\t\t\t\tif (config_valgrind && opt_valgrind)\n\t\t\t\t\told_rzsize = u2rz(old_size);\n\t\t\t} else if (config_valgrind && opt_valgrind) {\n\t\t\t\told_size = isalloc(ptr, false);\n\t\t\t\told_rzsize = u2rz(old_size);\n\t\t\t}\n\t\t\tif (config_prof && opt_prof) {\n\t\t\t\told_ctx = prof_ctx_get(ptr);\n\t\t\t\tcnt = NULL;\n\t\t\t}\n\t\t\tiqalloc(ptr);\n\t\t\tret = NULL;\n\t\t\tgoto label_return;\n\t\t} else\n\t\t\tsize = 1;\n\t}\n\n\tif (ptr != NULL) {\n\t\tassert(malloc_initialized || IS_INITIALIZER);\n\n\t\tif (config_prof) {\n\t\t\told_size = isalloc(ptr, true);\n\t\t\tif (config_valgrind && opt_valgrind)\n\t\t\t\told_rzsize = p2rz(ptr);\n\t\t} else if (config_stats) {\n\t\t\told_size = isalloc(ptr, false);\n\t\t\tif (config_valgrind && opt_valgrind)\n\t\t\t\told_rzsize = u2rz(old_size);\n\t\t} else if (config_valgrind && opt_valgrind) {\n\t\t\told_size = isalloc(ptr, false);\n\t\t\told_rzsize = u2rz(old_size);\n\t\t}\n\t\tif (config_prof && opt_prof) {\n\t\t\tusize = s2u(size);\n\t\t\told_ctx = prof_ctx_get(ptr);\n\t\t\tPROF_ALLOC_PREP(1, usize, cnt);\n\t\t\tif (cnt == NULL) {\n\t\t\t\told_ctx = NULL;\n\t\t\t\tret = NULL;\n\t\t\t\tgoto label_oom;\n\t\t\t}\n\t\t\tif (prof_promote && (uintptr_t)cnt != (uintptr_t)1U &&\n\t\t\t    usize <= SMALL_MAXCLASS) {\n\t\t\t\tret = iralloc(ptr, SMALL_MAXCLASS+1, 0, 0,\n\t\t\t\t    false, false);\n\t\t\t\tif (ret != NULL)\n\t\t\t\t\tarena_prof_promoted(ret, usize);\n\t\t\t\telse\n\t\t\t\t\told_ctx = NULL;\n\t\t\t} else {\n\t\t\t\tret = iralloc(ptr, size, 0, 0, false, false);\n\t\t\t\tif (ret == NULL)\n\t\t\t\t\told_ctx = NULL;\n\t\t\t}\n\t\t} else {\n\t\t\tif (config_stats || (config_valgrind && opt_valgrind))\n\t\t\t\tusize = s2u(size);\n\t\t\tret = iralloc(ptr, size, 0, 0, false, false);\n\t\t}\n\nlabel_oom:\n\t\tif (ret == NULL) {\n\t\t\tif (config_xmalloc && opt_xmalloc) {\n\t\t\t\tmalloc_write(\"<jemalloc>: Error in realloc(): \"\n\t\t\t\t    \"out of memory\\n\");\n\t\t\t\tabort();\n\t\t\t}\n\t\t\tset_errno(ENOMEM);\n\t\t}\n\t} else {\n\t\t/* realloc(NULL, size) is equivalent to malloc(size). */\n\t\tif (config_prof && opt_prof)\n\t\t\told_ctx = NULL;\n\t\tif (malloc_init()) {\n\t\t\tif (config_prof && opt_prof)\n\t\t\t\tcnt = NULL;\n\t\t\tret = NULL;\n\t\t} else {\n\t\t\tif (config_prof && opt_prof) {\n\t\t\t\tusize = s2u(size);\n\t\t\t\tPROF_ALLOC_PREP(1, usize, cnt);\n\t\t\t\tif (cnt == NULL)\n\t\t\t\t\tret = NULL;\n\t\t\t\telse {\n\t\t\t\t\tif (prof_promote && (uintptr_t)cnt !=\n\t\t\t\t\t    (uintptr_t)1U && usize <=\n\t\t\t\t\t    SMALL_MAXCLASS) {\n\t\t\t\t\t\tret = imalloc(SMALL_MAXCLASS+1);\n\t\t\t\t\t\tif (ret != NULL) {\n\t\t\t\t\t\t\tarena_prof_promoted(ret,\n\t\t\t\t\t\t\t    usize);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else\n\t\t\t\t\t\tret = imalloc(size);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (config_stats || (config_valgrind &&\n\t\t\t\t    opt_valgrind))\n\t\t\t\t\tusize = s2u(size);\n\t\t\t\tret = imalloc(size);\n\t\t\t}\n\t\t}\n\n\t\tif (ret == NULL) {\n\t\t\tif (config_xmalloc && opt_xmalloc) {\n\t\t\t\tmalloc_write(\"<jemalloc>: Error in realloc(): \"\n\t\t\t\t    \"out of memory\\n\");\n\t\t\t\tabort();\n\t\t\t}\n\t\t\tset_errno(ENOMEM);\n\t\t}\n\t}\n\nlabel_return:\n\tif (config_prof && opt_prof)\n\t\tprof_realloc(ret, usize, cnt, old_size, old_ctx);\n\tif (config_stats && ret != NULL) {\n\t\tthread_allocated_t *ta;\n\t\tassert(usize == isalloc(ret, config_prof));\n\t\tta = thread_allocated_tsd_get();\n\t\tta->allocated += usize;\n\t\tta->deallocated += old_size;\n\t}\n\tUTRACE(ptr, size, ret);\n\tJEMALLOC_VALGRIND_REALLOC(ret, usize, ptr, old_size, old_rzsize, false);\n\treturn (ret);\n}\n\nvoid\nje_free(void *ptr)\n{\n\n\tUTRACE(ptr, 0, 0);\n\tif (ptr != NULL) {\n\t\tsize_t usize;\n\t\tsize_t rzsize JEMALLOC_CC_SILENCE_INIT(0);\n\n\t\tassert(malloc_initialized || IS_INITIALIZER);\n\n\t\tif (config_prof && opt_prof) {\n\t\t\tusize = isalloc(ptr, config_prof);\n\t\t\tprof_free(ptr, usize);\n\t\t} else if (config_stats || config_valgrind)\n\t\t\tusize = isalloc(ptr, config_prof);\n\t\tif (config_stats)\n\t\t\tthread_allocated_tsd_get()->deallocated += usize;\n\t\tif (config_valgrind && opt_valgrind)\n\t\t\trzsize = p2rz(ptr);\n\t\tiqalloc(ptr);\n\t\tJEMALLOC_VALGRIND_FREE(ptr, rzsize);\n\t}\n}\n\n/*\n * End malloc(3)-compatible functions.\n */\n/******************************************************************************/\n/*\n * Begin non-standard override functions.\n */\n\n#ifdef JEMALLOC_OVERRIDE_MEMALIGN\nvoid *\nje_memalign(size_t alignment, size_t size)\n{\n\tvoid *ret JEMALLOC_CC_SILENCE_INIT(NULL);\n\timemalign(&ret, alignment, size, 1);\n\tJEMALLOC_VALGRIND_MALLOC(ret != NULL, ret, size, false);\n\treturn (ret);\n}\n#endif\n\n#ifdef JEMALLOC_OVERRIDE_VALLOC\nvoid *\nje_valloc(size_t size)\n{\n\tvoid *ret JEMALLOC_CC_SILENCE_INIT(NULL);\n\timemalign(&ret, PAGE, size, 1);\n\tJEMALLOC_VALGRIND_MALLOC(ret != NULL, ret, size, false);\n\treturn (ret);\n}\n#endif\n\n/*\n * is_malloc(je_malloc) is some macro magic to detect if jemalloc_defs.h has\n * #define je_malloc malloc\n */\n#define\tmalloc_is_malloc 1\n#define\tis_malloc_(a) malloc_is_ ## a\n#define\tis_malloc(a) is_malloc_(a)\n\n#if ((is_malloc(je_malloc) == 1) && defined(__GLIBC__) && !defined(__UCLIBC__))\n/*\n * glibc provides the RTLD_DEEPBIND flag for dlopen which can make it possible\n * to inconsistently reference libc's malloc(3)-compatible functions\n * (https://bugzilla.mozilla.org/show_bug.cgi?id=493541).\n *\n * These definitions interpose hooks in glibc.  The functions are actually\n * passed an extra argument for the caller return address, which will be\n * ignored.\n */\nJEMALLOC_EXPORT void (* __free_hook)(void *ptr) = je_free;\nJEMALLOC_EXPORT void *(* __malloc_hook)(size_t size) = je_malloc;\nJEMALLOC_EXPORT void *(* __realloc_hook)(void *ptr, size_t size) = je_realloc;\nJEMALLOC_EXPORT void *(* __memalign_hook)(size_t alignment, size_t size) =\n    je_memalign;\n#endif\n\n/*\n * End non-standard override functions.\n */\n/******************************************************************************/\n/*\n * Begin non-standard functions.\n */\n\nsize_t\nje_malloc_usable_size(JEMALLOC_USABLE_SIZE_CONST void *ptr)\n{\n\tsize_t ret;\n\n\tassert(malloc_initialized || IS_INITIALIZER);\n\n\tif (config_ivsalloc)\n\t\tret = ivsalloc(ptr, config_prof);\n\telse\n\t\tret = (ptr != NULL) ? isalloc(ptr, config_prof) : 0;\n\n\treturn (ret);\n}\n\nvoid\nje_malloc_stats_print(void (*write_cb)(void *, const char *), void *cbopaque,\n    const char *opts)\n{\n\n\tstats_print(write_cb, cbopaque, opts);\n}\n\nint\nje_mallctl(const char *name, void *oldp, size_t *oldlenp, void *newp,\n    size_t newlen)\n{\n\n\tif (malloc_init())\n\t\treturn (EAGAIN);\n\n\treturn (ctl_byname(name, oldp, oldlenp, newp, newlen));\n}\n\nint\nje_mallctlnametomib(const char *name, size_t *mibp, size_t *miblenp)\n{\n\n\tif (malloc_init())\n\t\treturn (EAGAIN);\n\n\treturn (ctl_nametomib(name, mibp, miblenp));\n}\n\nint\nje_mallctlbymib(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,\n  void *newp, size_t newlen)\n{\n\n\tif (malloc_init())\n\t\treturn (EAGAIN);\n\n\treturn (ctl_bymib(mib, miblen, oldp, oldlenp, newp, newlen));\n}\n\n/*\n * End non-standard functions.\n */\n/******************************************************************************/\n/*\n * Begin experimental functions.\n */\n#ifdef JEMALLOC_EXPERIMENTAL\n\nJEMALLOC_INLINE void *\niallocm(size_t usize, size_t alignment, bool zero, bool try_tcache,\n    arena_t *arena)\n{\n\n\tassert(usize == ((alignment == 0) ? s2u(usize) : sa2u(usize,\n\t    alignment)));\n\n\tif (alignment != 0)\n\t\treturn (ipallocx(usize, alignment, zero, try_tcache, arena));\n\telse if (zero)\n\t\treturn (icallocx(usize, try_tcache, arena));\n\telse\n\t\treturn (imallocx(usize, try_tcache, arena));\n}\n\nint\nje_allocm(void **ptr, size_t *rsize, size_t size, int flags)\n{\n\tvoid *p;\n\tsize_t usize;\n\tsize_t alignment = (ZU(1) << (flags & ALLOCM_LG_ALIGN_MASK)\n\t    & (SIZE_T_MAX-1));\n\tbool zero = flags & ALLOCM_ZERO;\n\tunsigned arena_ind = ((unsigned)(flags >> 8)) - 1;\n\tarena_t *arena;\n\tbool try_tcache;\n\n\tassert(ptr != NULL);\n\tassert(size != 0);\n\n\tif (malloc_init())\n\t\tgoto label_oom;\n\n\tif (arena_ind != UINT_MAX) {\n\t\tarena = arenas[arena_ind];\n\t\ttry_tcache = false;\n\t} else {\n\t\tarena = NULL;\n\t\ttry_tcache = true;\n\t}\n\n\tusize = (alignment == 0) ? s2u(size) : sa2u(size, alignment);\n\tif (usize == 0)\n\t\tgoto label_oom;\n\n\tif (config_prof && opt_prof) {\n\t\tprof_thr_cnt_t *cnt;\n\n\t\tPROF_ALLOC_PREP(1, usize, cnt);\n\t\tif (cnt == NULL)\n\t\t\tgoto label_oom;\n\t\tif (prof_promote && (uintptr_t)cnt != (uintptr_t)1U && usize <=\n\t\t    SMALL_MAXCLASS) {\n\t\t\tsize_t usize_promoted = (alignment == 0) ?\n\t\t\t    s2u(SMALL_MAXCLASS+1) : sa2u(SMALL_MAXCLASS+1,\n\t\t\t    alignment);\n\t\t\tassert(usize_promoted != 0);\n\t\t\tp = iallocm(usize_promoted, alignment, zero,\n\t\t\t    try_tcache, arena);\n\t\t\tif (p == NULL)\n\t\t\t\tgoto label_oom;\n\t\t\tarena_prof_promoted(p, usize);\n\t\t} else {\n\t\t\tp = iallocm(usize, alignment, zero, try_tcache, arena);\n\t\t\tif (p == NULL)\n\t\t\t\tgoto label_oom;\n\t\t}\n\t\tprof_malloc(p, usize, cnt);\n\t} else {\n\t\tp = iallocm(usize, alignment, zero, try_tcache, arena);\n\t\tif (p == NULL)\n\t\t\tgoto label_oom;\n\t}\n\tif (rsize != NULL)\n\t\t*rsize = usize;\n\n\t*ptr = p;\n\tif (config_stats) {\n\t\tassert(usize == isalloc(p, config_prof));\n\t\tthread_allocated_tsd_get()->allocated += usize;\n\t}\n\tUTRACE(0, size, p);\n\tJEMALLOC_VALGRIND_MALLOC(true, p, usize, zero);\n\treturn (ALLOCM_SUCCESS);\nlabel_oom:\n\tif (config_xmalloc && opt_xmalloc) {\n\t\tmalloc_write(\"<jemalloc>: Error in allocm(): \"\n\t\t    \"out of memory\\n\");\n\t\tabort();\n\t}\n\t*ptr = NULL;\n\tUTRACE(0, size, 0);\n\treturn (ALLOCM_ERR_OOM);\n}\n\nint\nje_rallocm(void **ptr, size_t *rsize, size_t size, size_t extra, int flags)\n{\n\tvoid *p, *q;\n\tsize_t usize;\n\tsize_t old_size;\n\tsize_t old_rzsize JEMALLOC_CC_SILENCE_INIT(0);\n\tsize_t alignment = (ZU(1) << (flags & ALLOCM_LG_ALIGN_MASK)\n\t    & (SIZE_T_MAX-1));\n\tbool zero = flags & ALLOCM_ZERO;\n\tbool no_move = flags & ALLOCM_NO_MOVE;\n\tunsigned arena_ind = ((unsigned)(flags >> 8)) - 1;\n\tbool try_tcache_alloc, try_tcache_dalloc;\n\tarena_t *arena;\n\n\tassert(ptr != NULL);\n\tassert(*ptr != NULL);\n\tassert(size != 0);\n\tassert(SIZE_T_MAX - size >= extra);\n\tassert(malloc_initialized || IS_INITIALIZER);\n\n\tif (arena_ind != UINT_MAX) {\n\t\tarena_chunk_t *chunk;\n\t\ttry_tcache_alloc = true;\n\t\tchunk = (arena_chunk_t *)CHUNK_ADDR2BASE(*ptr);\n\t\ttry_tcache_dalloc = (chunk == *ptr || chunk->arena !=\n\t\t    arenas[arena_ind]);\n\t\tarena = arenas[arena_ind];\n\t} else {\n\t\ttry_tcache_alloc = true;\n\t\ttry_tcache_dalloc = true;\n\t\tarena = NULL;\n\t}\n\n\tp = *ptr;\n\tif (config_prof && opt_prof) {\n\t\tprof_thr_cnt_t *cnt;\n\n\t\t/*\n\t\t * usize isn't knowable before iralloc() returns when extra is\n\t\t * non-zero.  Therefore, compute its maximum possible value and\n\t\t * use that in PROF_ALLOC_PREP() to decide whether to capture a\n\t\t * backtrace.  prof_realloc() will use the actual usize to\n\t\t * decide whether to sample.\n\t\t */\n\t\tsize_t max_usize = (alignment == 0) ? s2u(size+extra) :\n\t\t    sa2u(size+extra, alignment);\n\t\tprof_ctx_t *old_ctx = prof_ctx_get(p);\n\t\told_size = isalloc(p, true);\n\t\tif (config_valgrind && opt_valgrind)\n\t\t\told_rzsize = p2rz(p);\n\t\tPROF_ALLOC_PREP(1, max_usize, cnt);\n\t\tif (cnt == NULL)\n\t\t\tgoto label_oom;\n\t\t/*\n\t\t * Use minimum usize to determine whether promotion may happen.\n\t\t */\n\t\tif (prof_promote && (uintptr_t)cnt != (uintptr_t)1U\n\t\t    && ((alignment == 0) ? s2u(size) : sa2u(size, alignment))\n\t\t    <= SMALL_MAXCLASS) {\n\t\t\tq = irallocx(p, SMALL_MAXCLASS+1, (SMALL_MAXCLASS+1 >=\n\t\t\t    size+extra) ? 0 : size+extra - (SMALL_MAXCLASS+1),\n\t\t\t    alignment, zero, no_move, try_tcache_alloc,\n\t\t\t    try_tcache_dalloc, arena);\n\t\t\tif (q == NULL)\n\t\t\t\tgoto label_err;\n\t\t\tif (max_usize < PAGE) {\n\t\t\t\tusize = max_usize;\n\t\t\t\tarena_prof_promoted(q, usize);\n\t\t\t} else\n\t\t\t\tusize = isalloc(q, config_prof);\n\t\t} else {\n\t\t\tq = irallocx(p, size, extra, alignment, zero, no_move,\n\t\t\t    try_tcache_alloc, try_tcache_dalloc, arena);\n\t\t\tif (q == NULL)\n\t\t\t\tgoto label_err;\n\t\t\tusize = isalloc(q, config_prof);\n\t\t}\n\t\tprof_realloc(q, usize, cnt, old_size, old_ctx);\n\t\tif (rsize != NULL)\n\t\t\t*rsize = usize;\n\t} else {\n\t\tif (config_stats) {\n\t\t\told_size = isalloc(p, false);\n\t\t\tif (config_valgrind && opt_valgrind)\n\t\t\t\told_rzsize = u2rz(old_size);\n\t\t} else if (config_valgrind && opt_valgrind) {\n\t\t\told_size = isalloc(p, false);\n\t\t\told_rzsize = u2rz(old_size);\n\t\t}\n\t\tq = irallocx(p, size, extra, alignment, zero, no_move,\n\t\t    try_tcache_alloc, try_tcache_dalloc, arena);\n\t\tif (q == NULL)\n\t\t\tgoto label_err;\n\t\tif (config_stats)\n\t\t\tusize = isalloc(q, config_prof);\n\t\tif (rsize != NULL) {\n\t\t\tif (config_stats == false)\n\t\t\t\tusize = isalloc(q, config_prof);\n\t\t\t*rsize = usize;\n\t\t}\n\t}\n\n\t*ptr = q;\n\tif (config_stats) {\n\t\tthread_allocated_t *ta;\n\t\tta = thread_allocated_tsd_get();\n\t\tta->allocated += usize;\n\t\tta->deallocated += old_size;\n\t}\n\tUTRACE(p, size, q);\n\tJEMALLOC_VALGRIND_REALLOC(q, usize, p, old_size, old_rzsize, zero);\n\treturn (ALLOCM_SUCCESS);\nlabel_err:\n\tif (no_move) {\n\t\tUTRACE(p, size, q);\n\t\treturn (ALLOCM_ERR_NOT_MOVED);\n\t}\nlabel_oom:\n\tif (config_xmalloc && opt_xmalloc) {\n\t\tmalloc_write(\"<jemalloc>: Error in rallocm(): \"\n\t\t    \"out of memory\\n\");\n\t\tabort();\n\t}\n\tUTRACE(p, size, 0);\n\treturn (ALLOCM_ERR_OOM);\n}\n\nint\nje_sallocm(const void *ptr, size_t *rsize, int flags)\n{\n\tsize_t sz;\n\n\tassert(malloc_initialized || IS_INITIALIZER);\n\n\tif (config_ivsalloc)\n\t\tsz = ivsalloc(ptr, config_prof);\n\telse {\n\t\tassert(ptr != NULL);\n\t\tsz = isalloc(ptr, config_prof);\n\t}\n\tassert(rsize != NULL);\n\t*rsize = sz;\n\n\treturn (ALLOCM_SUCCESS);\n}\n\nint\nje_dallocm(void *ptr, int flags)\n{\n\tsize_t usize;\n\tsize_t rzsize JEMALLOC_CC_SILENCE_INIT(0);\n\tunsigned arena_ind = ((unsigned)(flags >> 8)) - 1;\n\tbool try_tcache;\n\n\tassert(ptr != NULL);\n\tassert(malloc_initialized || IS_INITIALIZER);\n\n\tif (arena_ind != UINT_MAX) {\n\t\tarena_chunk_t *chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);\n\t\ttry_tcache = (chunk == ptr || chunk->arena !=\n\t\t    arenas[arena_ind]);\n\t} else\n\t\ttry_tcache = true;\n\n\tUTRACE(ptr, 0, 0);\n\tif (config_stats || config_valgrind)\n\t\tusize = isalloc(ptr, config_prof);\n\tif (config_prof && opt_prof) {\n\t\tif (config_stats == false && config_valgrind == false)\n\t\t\tusize = isalloc(ptr, config_prof);\n\t\tprof_free(ptr, usize);\n\t}\n\tif (config_stats)\n\t\tthread_allocated_tsd_get()->deallocated += usize;\n\tif (config_valgrind && opt_valgrind)\n\t\trzsize = p2rz(ptr);\n\tiqallocx(ptr, try_tcache);\n\tJEMALLOC_VALGRIND_FREE(ptr, rzsize);\n\n\treturn (ALLOCM_SUCCESS);\n}\n\nint\nje_nallocm(size_t *rsize, size_t size, int flags)\n{\n\tsize_t usize;\n\tsize_t alignment = (ZU(1) << (flags & ALLOCM_LG_ALIGN_MASK)\n\t    & (SIZE_T_MAX-1));\n\n\tassert(size != 0);\n\n\tif (malloc_init())\n\t\treturn (ALLOCM_ERR_OOM);\n\n\tusize = (alignment == 0) ? s2u(size) : sa2u(size, alignment);\n\tif (usize == 0)\n\t\treturn (ALLOCM_ERR_OOM);\n\n\tif (rsize != NULL)\n\t\t*rsize = usize;\n\treturn (ALLOCM_SUCCESS);\n}\n\n#endif\n/*\n * End experimental functions.\n */\n/******************************************************************************/\n/*\n * The following functions are used by threading libraries for protection of\n * malloc during fork().\n */\n\n/*\n * If an application creates a thread before doing any allocation in the main\n * thread, then calls fork(2) in the main thread followed by memory allocation\n * in the child process, a race can occur that results in deadlock within the\n * child: the main thread may have forked while the created thread had\n * partially initialized the allocator.  Ordinarily jemalloc prevents\n * fork/malloc races via the following functions it registers during\n * initialization using pthread_atfork(), but of course that does no good if\n * the allocator isn't fully initialized at fork time.  The following library\n * constructor is a partial solution to this problem.  It may still possible to\n * trigger the deadlock described above, but doing so would involve forking via\n * a library constructor that runs before jemalloc's runs.\n */\nJEMALLOC_ATTR(constructor)\nstatic void\njemalloc_constructor(void)\n{\n\n\tmalloc_init();\n}\n\n#ifndef JEMALLOC_MUTEX_INIT_CB\nvoid\njemalloc_prefork(void)\n#else\nJEMALLOC_EXPORT void\n_malloc_prefork(void)\n#endif\n{\n\tunsigned i;\n\n#ifdef JEMALLOC_MUTEX_INIT_CB\n\tif (malloc_initialized == false)\n\t\treturn;\n#endif\n\tassert(malloc_initialized);\n\n\t/* Acquire all mutexes in a safe order. */\n\tctl_prefork();\n\tmalloc_mutex_prefork(&arenas_lock);\n\tfor (i = 0; i < narenas_total; i++) {\n\t\tif (arenas[i] != NULL)\n\t\t\tarena_prefork(arenas[i]);\n\t}\n\tprof_prefork();\n\tchunk_prefork();\n\tbase_prefork();\n\thuge_prefork();\n}\n\n#ifndef JEMALLOC_MUTEX_INIT_CB\nvoid\njemalloc_postfork_parent(void)\n#else\nJEMALLOC_EXPORT void\n_malloc_postfork(void)\n#endif\n{\n\tunsigned i;\n\n#ifdef JEMALLOC_MUTEX_INIT_CB\n\tif (malloc_initialized == false)\n\t\treturn;\n#endif\n\tassert(malloc_initialized);\n\n\t/* Release all mutexes, now that fork() has completed. */\n\thuge_postfork_parent();\n\tbase_postfork_parent();\n\tchunk_postfork_parent();\n\tprof_postfork_parent();\n\tfor (i = 0; i < narenas_total; i++) {\n\t\tif (arenas[i] != NULL)\n\t\t\tarena_postfork_parent(arenas[i]);\n\t}\n\tmalloc_mutex_postfork_parent(&arenas_lock);\n\tctl_postfork_parent();\n}\n\nvoid\njemalloc_postfork_child(void)\n{\n\tunsigned i;\n\n\tassert(malloc_initialized);\n\n\t/* Release all mutexes, now that fork() has completed. */\n\thuge_postfork_child();\n\tbase_postfork_child();\n\tchunk_postfork_child();\n\tprof_postfork_child();\n\tfor (i = 0; i < narenas_total; i++) {\n\t\tif (arenas[i] != NULL)\n\t\t\tarena_postfork_child(arenas[i]);\n\t}\n\tmalloc_mutex_postfork_child(&arenas_lock);\n\tctl_postfork_child();\n}\n\n/******************************************************************************/\n/*\n * The following functions are used for TLS allocation/deallocation in static\n * binaries on FreeBSD.  The primary difference between these and i[mcd]alloc()\n * is that these avoid accessing TLS variables.\n */\n\nstatic void *\na0alloc(size_t size, bool zero)\n{\n\n\tif (malloc_init())\n\t\treturn (NULL);\n\n\tif (size == 0)\n\t\tsize = 1;\n\n\tif (size <= arena_maxclass)\n\t\treturn (arena_malloc(arenas[0], size, zero, false));\n\telse\n\t\treturn (huge_malloc(size, zero));\n}\n\nvoid *\na0malloc(size_t size)\n{\n\n\treturn (a0alloc(size, false));\n}\n\nvoid *\na0calloc(size_t num, size_t size)\n{\n\n\treturn (a0alloc(num * size, true));\n}\n\nvoid\na0free(void *ptr)\n{\n\tarena_chunk_t *chunk;\n\n\tif (ptr == NULL)\n\t\treturn;\n\n\tchunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);\n\tif (chunk != ptr)\n\t\tarena_dalloc(chunk->arena, chunk, ptr, false);\n\telse\n\t\thuge_dalloc(ptr, true);\n}\n\n/******************************************************************************/\n"
  },
  {
    "path": "deps/jemalloc/src/mb.c",
    "content": "#define\tJEMALLOC_MB_C_\n#include \"jemalloc/internal/jemalloc_internal.h\"\n"
  },
  {
    "path": "deps/jemalloc/src/mutex.c",
    "content": "#define\tJEMALLOC_MUTEX_C_\n#include \"jemalloc/internal/jemalloc_internal.h\"\n\n#if defined(JEMALLOC_LAZY_LOCK) && !defined(_WIN32)\n#include <dlfcn.h>\n#endif\n\n#ifndef _CRT_SPINCOUNT\n#define _CRT_SPINCOUNT 4000\n#endif\n\n/******************************************************************************/\n/* Data. */\n\n#ifdef JEMALLOC_LAZY_LOCK\nbool isthreaded = false;\n#endif\n#ifdef JEMALLOC_MUTEX_INIT_CB\nstatic bool\t\tpostpone_init = true;\nstatic malloc_mutex_t\t*postponed_mutexes = NULL;\n#endif\n\n#if defined(JEMALLOC_LAZY_LOCK) && !defined(_WIN32)\nstatic void\tpthread_create_once(void);\n#endif\n\n/******************************************************************************/\n/*\n * We intercept pthread_create() calls in order to toggle isthreaded if the\n * process goes multi-threaded.\n */\n\n#if defined(JEMALLOC_LAZY_LOCK) && !defined(_WIN32)\nstatic int (*pthread_create_fptr)(pthread_t *__restrict, const pthread_attr_t *,\n    void *(*)(void *), void *__restrict);\n\nstatic void\npthread_create_once(void)\n{\n\n\tpthread_create_fptr = dlsym(RTLD_NEXT, \"pthread_create\");\n\tif (pthread_create_fptr == NULL) {\n\t\tmalloc_write(\"<jemalloc>: Error in dlsym(RTLD_NEXT, \"\n\t\t    \"\\\"pthread_create\\\")\\n\");\n\t\tabort();\n\t}\n\n\tisthreaded = true;\n}\n\nJEMALLOC_EXPORT int\npthread_create(pthread_t *__restrict thread,\n    const pthread_attr_t *__restrict attr, void *(*start_routine)(void *),\n    void *__restrict arg)\n{\n\tstatic pthread_once_t once_control = PTHREAD_ONCE_INIT;\n\n\tpthread_once(&once_control, pthread_create_once);\n\n\treturn (pthread_create_fptr(thread, attr, start_routine, arg));\n}\n#endif\n\n/******************************************************************************/\n\n#ifdef JEMALLOC_MUTEX_INIT_CB\nJEMALLOC_EXPORT int\t_pthread_mutex_init_calloc_cb(pthread_mutex_t *mutex,\n    void *(calloc_cb)(size_t, size_t));\n#endif\n\nbool\nmalloc_mutex_init(malloc_mutex_t *mutex)\n{\n\n#ifdef _WIN32\n\tif (!InitializeCriticalSectionAndSpinCount(&mutex->lock,\n\t    _CRT_SPINCOUNT))\n\t\treturn (true);\n#elif (defined(JEMALLOC_OSSPIN))\n\tmutex->lock = 0;\n#elif (defined(JEMALLOC_MUTEX_INIT_CB))\n\tif (postpone_init) {\n\t\tmutex->postponed_next = postponed_mutexes;\n\t\tpostponed_mutexes = mutex;\n\t} else {\n\t\tif (_pthread_mutex_init_calloc_cb(&mutex->lock, base_calloc) !=\n\t\t    0)\n\t\t\treturn (true);\n\t}\n#else\n\tpthread_mutexattr_t attr;\n\n\tif (pthread_mutexattr_init(&attr) != 0)\n\t\treturn (true);\n\tpthread_mutexattr_settype(&attr, MALLOC_MUTEX_TYPE);\n\tif (pthread_mutex_init(&mutex->lock, &attr) != 0) {\n\t\tpthread_mutexattr_destroy(&attr);\n\t\treturn (true);\n\t}\n\tpthread_mutexattr_destroy(&attr);\n#endif\n\treturn (false);\n}\n\nvoid\nmalloc_mutex_prefork(malloc_mutex_t *mutex)\n{\n\n\tmalloc_mutex_lock(mutex);\n}\n\nvoid\nmalloc_mutex_postfork_parent(malloc_mutex_t *mutex)\n{\n\n\tmalloc_mutex_unlock(mutex);\n}\n\nvoid\nmalloc_mutex_postfork_child(malloc_mutex_t *mutex)\n{\n\n#ifdef JEMALLOC_MUTEX_INIT_CB\n\tmalloc_mutex_unlock(mutex);\n#else\n\tif (malloc_mutex_init(mutex)) {\n\t\tmalloc_printf(\"<jemalloc>: Error re-initializing mutex in \"\n\t\t    \"child\\n\");\n\t\tif (opt_abort)\n\t\t\tabort();\n\t}\n#endif\n}\n\nbool\nmutex_boot(void)\n{\n\n#ifdef JEMALLOC_MUTEX_INIT_CB\n\tpostpone_init = false;\n\twhile (postponed_mutexes != NULL) {\n\t\tif (_pthread_mutex_init_calloc_cb(&postponed_mutexes->lock,\n\t\t    base_calloc) != 0)\n\t\t\treturn (true);\n\t\tpostponed_mutexes = postponed_mutexes->postponed_next;\n\t}\n#endif\n\treturn (false);\n}\n"
  },
  {
    "path": "deps/jemalloc/src/prof.c",
    "content": "#define\tJEMALLOC_PROF_C_\n#include \"jemalloc/internal/jemalloc_internal.h\"\n/******************************************************************************/\n\n#ifdef JEMALLOC_PROF_LIBUNWIND\n#define\tUNW_LOCAL_ONLY\n#include <libunwind.h>\n#endif\n\n#ifdef JEMALLOC_PROF_LIBGCC\n#include <unwind.h>\n#endif\n\n/******************************************************************************/\n/* Data. */\n\nmalloc_tsd_data(, prof_tdata, prof_tdata_t *, NULL)\n\nbool\t\topt_prof = false;\nbool\t\topt_prof_active = true;\nsize_t\t\topt_lg_prof_sample = LG_PROF_SAMPLE_DEFAULT;\nssize_t\t\topt_lg_prof_interval = LG_PROF_INTERVAL_DEFAULT;\nbool\t\topt_prof_gdump = false;\nbool\t\topt_prof_final = true;\nbool\t\topt_prof_leak = false;\nbool\t\topt_prof_accum = false;\nchar\t\topt_prof_prefix[PATH_MAX + 1];\n\nuint64_t\tprof_interval;\nbool\t\tprof_promote;\n\n/*\n * Table of mutexes that are shared among ctx's.  These are leaf locks, so\n * there is no problem with using them for more than one ctx at the same time.\n * The primary motivation for this sharing though is that ctx's are ephemeral,\n * and destroying mutexes causes complications for systems that allocate when\n * creating/destroying mutexes.\n */\nstatic malloc_mutex_t\t*ctx_locks;\nstatic unsigned\t\tcum_ctxs; /* Atomic counter. */\n\n/*\n * Global hash of (prof_bt_t *)-->(prof_ctx_t *).  This is the master data\n * structure that knows about all backtraces currently captured.\n */\nstatic ckh_t\t\tbt2ctx;\nstatic malloc_mutex_t\tbt2ctx_mtx;\n\nstatic malloc_mutex_t\tprof_dump_seq_mtx;\nstatic uint64_t\t\tprof_dump_seq;\nstatic uint64_t\t\tprof_dump_iseq;\nstatic uint64_t\t\tprof_dump_mseq;\nstatic uint64_t\t\tprof_dump_useq;\n\n/*\n * This buffer is rather large for stack allocation, so use a single buffer for\n * all profile dumps.  The buffer is implicitly protected by bt2ctx_mtx, since\n * it must be locked anyway during dumping.\n */\nstatic char\t\tprof_dump_buf[PROF_DUMP_BUFSIZE];\nstatic unsigned\t\tprof_dump_buf_end;\nstatic int\t\tprof_dump_fd;\n\n/* Do not dump any profiles until bootstrapping is complete. */\nstatic bool\t\tprof_booted = false;\n\n/******************************************************************************/\n/* Function prototypes for non-inline static functions. */\n\nstatic prof_bt_t\t*bt_dup(prof_bt_t *bt);\nstatic void\tbt_destroy(prof_bt_t *bt);\n#ifdef JEMALLOC_PROF_LIBGCC\nstatic _Unwind_Reason_Code\tprof_unwind_init_callback(\n    struct _Unwind_Context *context, void *arg);\nstatic _Unwind_Reason_Code\tprof_unwind_callback(\n    struct _Unwind_Context *context, void *arg);\n#endif\nstatic bool\tprof_flush(bool propagate_err);\nstatic bool\tprof_write(bool propagate_err, const char *s);\nstatic bool\tprof_printf(bool propagate_err, const char *format, ...)\n    JEMALLOC_ATTR(format(printf, 2, 3));\nstatic void\tprof_ctx_sum(prof_ctx_t *ctx, prof_cnt_t *cnt_all,\n    size_t *leak_nctx);\nstatic void\tprof_ctx_destroy(prof_ctx_t *ctx);\nstatic void\tprof_ctx_merge(prof_ctx_t *ctx, prof_thr_cnt_t *cnt);\nstatic bool\tprof_dump_ctx(bool propagate_err, prof_ctx_t *ctx,\n    prof_bt_t *bt);\nstatic bool\tprof_dump_maps(bool propagate_err);\nstatic bool\tprof_dump(bool propagate_err, const char *filename,\n    bool leakcheck);\nstatic void\tprof_dump_filename(char *filename, char v, int64_t vseq);\nstatic void\tprof_fdump(void);\nstatic void\tprof_bt_hash(const void *key, unsigned minbits, size_t *hash1,\n    size_t *hash2);\nstatic bool\tprof_bt_keycomp(const void *k1, const void *k2);\nstatic malloc_mutex_t\t*prof_ctx_mutex_choose(void);\n\n/******************************************************************************/\n\nvoid\nbt_init(prof_bt_t *bt, void **vec)\n{\n\n\tcassert(config_prof);\n\n\tbt->vec = vec;\n\tbt->len = 0;\n}\n\nstatic void\nbt_destroy(prof_bt_t *bt)\n{\n\n\tcassert(config_prof);\n\n\tidalloc(bt);\n}\n\nstatic prof_bt_t *\nbt_dup(prof_bt_t *bt)\n{\n\tprof_bt_t *ret;\n\n\tcassert(config_prof);\n\n\t/*\n\t * Create a single allocation that has space for vec immediately\n\t * following the prof_bt_t structure.  The backtraces that get\n\t * stored in the backtrace caches are copied from stack-allocated\n\t * temporary variables, so size is known at creation time.  Making this\n\t * a contiguous object improves cache locality.\n\t */\n\tret = (prof_bt_t *)imalloc(QUANTUM_CEILING(sizeof(prof_bt_t)) +\n\t    (bt->len * sizeof(void *)));\n\tif (ret == NULL)\n\t\treturn (NULL);\n\tret->vec = (void **)((uintptr_t)ret +\n\t    QUANTUM_CEILING(sizeof(prof_bt_t)));\n\tmemcpy(ret->vec, bt->vec, bt->len * sizeof(void *));\n\tret->len = bt->len;\n\n\treturn (ret);\n}\n\nstatic inline void\nprof_enter(prof_tdata_t *prof_tdata)\n{\n\n\tcassert(config_prof);\n\n\tassert(prof_tdata->enq == false);\n\tprof_tdata->enq = true;\n\n\tmalloc_mutex_lock(&bt2ctx_mtx);\n}\n\nstatic inline void\nprof_leave(prof_tdata_t *prof_tdata)\n{\n\tbool idump, gdump;\n\n\tcassert(config_prof);\n\n\tmalloc_mutex_unlock(&bt2ctx_mtx);\n\n\tassert(prof_tdata->enq);\n\tprof_tdata->enq = false;\n\tidump = prof_tdata->enq_idump;\n\tprof_tdata->enq_idump = false;\n\tgdump = prof_tdata->enq_gdump;\n\tprof_tdata->enq_gdump = false;\n\n\tif (idump)\n\t\tprof_idump();\n\tif (gdump)\n\t\tprof_gdump();\n}\n\n#ifdef JEMALLOC_PROF_LIBUNWIND\nvoid\nprof_backtrace(prof_bt_t *bt, unsigned nignore)\n{\n\tunw_context_t uc;\n\tunw_cursor_t cursor;\n\tunsigned i;\n\tint err;\n\n\tcassert(config_prof);\n\tassert(bt->len == 0);\n\tassert(bt->vec != NULL);\n\n\tunw_getcontext(&uc);\n\tunw_init_local(&cursor, &uc);\n\n\t/* Throw away (nignore+1) stack frames, if that many exist. */\n\tfor (i = 0; i < nignore + 1; i++) {\n\t\terr = unw_step(&cursor);\n\t\tif (err <= 0)\n\t\t\treturn;\n\t}\n\n\t/*\n\t * Iterate over stack frames until there are no more, or until no space\n\t * remains in bt.\n\t */\n\tfor (i = 0; i < PROF_BT_MAX; i++) {\n\t\tunw_get_reg(&cursor, UNW_REG_IP, (unw_word_t *)&bt->vec[i]);\n\t\tbt->len++;\n\t\terr = unw_step(&cursor);\n\t\tif (err <= 0)\n\t\t\tbreak;\n\t}\n}\n#elif (defined(JEMALLOC_PROF_LIBGCC))\nstatic _Unwind_Reason_Code\nprof_unwind_init_callback(struct _Unwind_Context *context, void *arg)\n{\n\n\tcassert(config_prof);\n\n\treturn (_URC_NO_REASON);\n}\n\nstatic _Unwind_Reason_Code\nprof_unwind_callback(struct _Unwind_Context *context, void *arg)\n{\n\tprof_unwind_data_t *data = (prof_unwind_data_t *)arg;\n\n\tcassert(config_prof);\n\n\tif (data->nignore > 0)\n\t\tdata->nignore--;\n\telse {\n\t\tdata->bt->vec[data->bt->len] = (void *)_Unwind_GetIP(context);\n\t\tdata->bt->len++;\n\t\tif (data->bt->len == data->max)\n\t\t\treturn (_URC_END_OF_STACK);\n\t}\n\n\treturn (_URC_NO_REASON);\n}\n\nvoid\nprof_backtrace(prof_bt_t *bt, unsigned nignore)\n{\n\tprof_unwind_data_t data = {bt, nignore, PROF_BT_MAX};\n\n\tcassert(config_prof);\n\n\t_Unwind_Backtrace(prof_unwind_callback, &data);\n}\n#elif (defined(JEMALLOC_PROF_GCC))\nvoid\nprof_backtrace(prof_bt_t *bt, unsigned nignore)\n{\n#define\tBT_FRAME(i)\t\t\t\t\t\t\t\\\n\tif ((i) < nignore + PROF_BT_MAX) {\t\t\t\t\\\n\t\tvoid *p;\t\t\t\t\t\t\\\n\t\tif (__builtin_frame_address(i) == 0)\t\t\t\\\n\t\t\treturn;\t\t\t\t\t\t\\\n\t\tp = __builtin_return_address(i);\t\t\t\\\n\t\tif (p == NULL)\t\t\t\t\t\t\\\n\t\t\treturn;\t\t\t\t\t\t\\\n\t\tif (i >= nignore) {\t\t\t\t\t\\\n\t\t\tbt->vec[(i) - nignore] = p;\t\t\t\\\n\t\t\tbt->len = (i) - nignore + 1;\t\t\t\\\n\t\t}\t\t\t\t\t\t\t\\\n\t} else\t\t\t\t\t\t\t\t\\\n\t\treturn;\n\n\tcassert(config_prof);\n\tassert(nignore <= 3);\n\n\tBT_FRAME(0)\n\tBT_FRAME(1)\n\tBT_FRAME(2)\n\tBT_FRAME(3)\n\tBT_FRAME(4)\n\tBT_FRAME(5)\n\tBT_FRAME(6)\n\tBT_FRAME(7)\n\tBT_FRAME(8)\n\tBT_FRAME(9)\n\n\tBT_FRAME(10)\n\tBT_FRAME(11)\n\tBT_FRAME(12)\n\tBT_FRAME(13)\n\tBT_FRAME(14)\n\tBT_FRAME(15)\n\tBT_FRAME(16)\n\tBT_FRAME(17)\n\tBT_FRAME(18)\n\tBT_FRAME(19)\n\n\tBT_FRAME(20)\n\tBT_FRAME(21)\n\tBT_FRAME(22)\n\tBT_FRAME(23)\n\tBT_FRAME(24)\n\tBT_FRAME(25)\n\tBT_FRAME(26)\n\tBT_FRAME(27)\n\tBT_FRAME(28)\n\tBT_FRAME(29)\n\n\tBT_FRAME(30)\n\tBT_FRAME(31)\n\tBT_FRAME(32)\n\tBT_FRAME(33)\n\tBT_FRAME(34)\n\tBT_FRAME(35)\n\tBT_FRAME(36)\n\tBT_FRAME(37)\n\tBT_FRAME(38)\n\tBT_FRAME(39)\n\n\tBT_FRAME(40)\n\tBT_FRAME(41)\n\tBT_FRAME(42)\n\tBT_FRAME(43)\n\tBT_FRAME(44)\n\tBT_FRAME(45)\n\tBT_FRAME(46)\n\tBT_FRAME(47)\n\tBT_FRAME(48)\n\tBT_FRAME(49)\n\n\tBT_FRAME(50)\n\tBT_FRAME(51)\n\tBT_FRAME(52)\n\tBT_FRAME(53)\n\tBT_FRAME(54)\n\tBT_FRAME(55)\n\tBT_FRAME(56)\n\tBT_FRAME(57)\n\tBT_FRAME(58)\n\tBT_FRAME(59)\n\n\tBT_FRAME(60)\n\tBT_FRAME(61)\n\tBT_FRAME(62)\n\tBT_FRAME(63)\n\tBT_FRAME(64)\n\tBT_FRAME(65)\n\tBT_FRAME(66)\n\tBT_FRAME(67)\n\tBT_FRAME(68)\n\tBT_FRAME(69)\n\n\tBT_FRAME(70)\n\tBT_FRAME(71)\n\tBT_FRAME(72)\n\tBT_FRAME(73)\n\tBT_FRAME(74)\n\tBT_FRAME(75)\n\tBT_FRAME(76)\n\tBT_FRAME(77)\n\tBT_FRAME(78)\n\tBT_FRAME(79)\n\n\tBT_FRAME(80)\n\tBT_FRAME(81)\n\tBT_FRAME(82)\n\tBT_FRAME(83)\n\tBT_FRAME(84)\n\tBT_FRAME(85)\n\tBT_FRAME(86)\n\tBT_FRAME(87)\n\tBT_FRAME(88)\n\tBT_FRAME(89)\n\n\tBT_FRAME(90)\n\tBT_FRAME(91)\n\tBT_FRAME(92)\n\tBT_FRAME(93)\n\tBT_FRAME(94)\n\tBT_FRAME(95)\n\tBT_FRAME(96)\n\tBT_FRAME(97)\n\tBT_FRAME(98)\n\tBT_FRAME(99)\n\n\tBT_FRAME(100)\n\tBT_FRAME(101)\n\tBT_FRAME(102)\n\tBT_FRAME(103)\n\tBT_FRAME(104)\n\tBT_FRAME(105)\n\tBT_FRAME(106)\n\tBT_FRAME(107)\n\tBT_FRAME(108)\n\tBT_FRAME(109)\n\n\tBT_FRAME(110)\n\tBT_FRAME(111)\n\tBT_FRAME(112)\n\tBT_FRAME(113)\n\tBT_FRAME(114)\n\tBT_FRAME(115)\n\tBT_FRAME(116)\n\tBT_FRAME(117)\n\tBT_FRAME(118)\n\tBT_FRAME(119)\n\n\tBT_FRAME(120)\n\tBT_FRAME(121)\n\tBT_FRAME(122)\n\tBT_FRAME(123)\n\tBT_FRAME(124)\n\tBT_FRAME(125)\n\tBT_FRAME(126)\n\tBT_FRAME(127)\n\n\t/* Extras to compensate for nignore. */\n\tBT_FRAME(128)\n\tBT_FRAME(129)\n\tBT_FRAME(130)\n#undef BT_FRAME\n}\n#else\nvoid\nprof_backtrace(prof_bt_t *bt, unsigned nignore)\n{\n\n\tcassert(config_prof);\n\tassert(false);\n}\n#endif\n\nprof_thr_cnt_t *\nprof_lookup(prof_bt_t *bt)\n{\n\tunion {\n\t\tprof_thr_cnt_t\t*p;\n\t\tvoid\t\t*v;\n\t} ret;\n\tprof_tdata_t *prof_tdata;\n\n\tcassert(config_prof);\n\n\tprof_tdata = prof_tdata_get();\n\tif ((uintptr_t)prof_tdata <= (uintptr_t)PROF_TDATA_STATE_MAX)\n\t\treturn (NULL);\n\n\tif (ckh_search(&prof_tdata->bt2cnt, bt, NULL, &ret.v)) {\n\t\tunion {\n\t\t\tprof_bt_t\t*p;\n\t\t\tvoid\t\t*v;\n\t\t} btkey;\n\t\tunion {\n\t\t\tprof_ctx_t\t*p;\n\t\t\tvoid\t\t*v;\n\t\t} ctx;\n\t\tbool new_ctx;\n\n\t\t/*\n\t\t * This thread's cache lacks bt.  Look for it in the global\n\t\t * cache.\n\t\t */\n\t\tprof_enter(prof_tdata);\n\t\tif (ckh_search(&bt2ctx, bt, &btkey.v, &ctx.v)) {\n\t\t\t/* bt has never been seen before.  Insert it. */\n\t\t\tctx.v = imalloc(sizeof(prof_ctx_t));\n\t\t\tif (ctx.v == NULL) {\n\t\t\t\tprof_leave(prof_tdata);\n\t\t\t\treturn (NULL);\n\t\t\t}\n\t\t\tbtkey.p = bt_dup(bt);\n\t\t\tif (btkey.v == NULL) {\n\t\t\t\tprof_leave(prof_tdata);\n\t\t\t\tidalloc(ctx.v);\n\t\t\t\treturn (NULL);\n\t\t\t}\n\t\t\tctx.p->bt = btkey.p;\n\t\t\tctx.p->lock = prof_ctx_mutex_choose();\n\t\t\t/*\n\t\t\t * Set nlimbo to 1, in order to avoid a race condition\n\t\t\t * with prof_ctx_merge()/prof_ctx_destroy().\n\t\t\t */\n\t\t\tctx.p->nlimbo = 1;\n\t\t\tmemset(&ctx.p->cnt_merged, 0, sizeof(prof_cnt_t));\n\t\t\tql_new(&ctx.p->cnts_ql);\n\t\t\tif (ckh_insert(&bt2ctx, btkey.v, ctx.v)) {\n\t\t\t\t/* OOM. */\n\t\t\t\tprof_leave(prof_tdata);\n\t\t\t\tidalloc(btkey.v);\n\t\t\t\tidalloc(ctx.v);\n\t\t\t\treturn (NULL);\n\t\t\t}\n\t\t\tnew_ctx = true;\n\t\t} else {\n\t\t\t/*\n\t\t\t * Increment nlimbo, in order to avoid a race condition\n\t\t\t * with prof_ctx_merge()/prof_ctx_destroy().\n\t\t\t */\n\t\t\tmalloc_mutex_lock(ctx.p->lock);\n\t\t\tctx.p->nlimbo++;\n\t\t\tmalloc_mutex_unlock(ctx.p->lock);\n\t\t\tnew_ctx = false;\n\t\t}\n\t\tprof_leave(prof_tdata);\n\n\t\t/* Link a prof_thd_cnt_t into ctx for this thread. */\n\t\tif (ckh_count(&prof_tdata->bt2cnt) == PROF_TCMAX) {\n\t\t\tassert(ckh_count(&prof_tdata->bt2cnt) > 0);\n\t\t\t/*\n\t\t\t * Flush the least recently used cnt in order to keep\n\t\t\t * bt2cnt from becoming too large.\n\t\t\t */\n\t\t\tret.p = ql_last(&prof_tdata->lru_ql, lru_link);\n\t\t\tassert(ret.v != NULL);\n\t\t\tif (ckh_remove(&prof_tdata->bt2cnt, ret.p->ctx->bt,\n\t\t\t    NULL, NULL))\n\t\t\t\tassert(false);\n\t\t\tql_remove(&prof_tdata->lru_ql, ret.p, lru_link);\n\t\t\tprof_ctx_merge(ret.p->ctx, ret.p);\n\t\t\t/* ret can now be re-used. */\n\t\t} else {\n\t\t\tassert(ckh_count(&prof_tdata->bt2cnt) < PROF_TCMAX);\n\t\t\t/* Allocate and partially initialize a new cnt. */\n\t\t\tret.v = imalloc(sizeof(prof_thr_cnt_t));\n\t\t\tif (ret.p == NULL) {\n\t\t\t\tif (new_ctx)\n\t\t\t\t\tprof_ctx_destroy(ctx.p);\n\t\t\t\treturn (NULL);\n\t\t\t}\n\t\t\tql_elm_new(ret.p, cnts_link);\n\t\t\tql_elm_new(ret.p, lru_link);\n\t\t}\n\t\t/* Finish initializing ret. */\n\t\tret.p->ctx = ctx.p;\n\t\tret.p->epoch = 0;\n\t\tmemset(&ret.p->cnts, 0, sizeof(prof_cnt_t));\n\t\tif (ckh_insert(&prof_tdata->bt2cnt, btkey.v, ret.v)) {\n\t\t\tif (new_ctx)\n\t\t\t\tprof_ctx_destroy(ctx.p);\n\t\t\tidalloc(ret.v);\n\t\t\treturn (NULL);\n\t\t}\n\t\tql_head_insert(&prof_tdata->lru_ql, ret.p, lru_link);\n\t\tmalloc_mutex_lock(ctx.p->lock);\n\t\tql_tail_insert(&ctx.p->cnts_ql, ret.p, cnts_link);\n\t\tctx.p->nlimbo--;\n\t\tmalloc_mutex_unlock(ctx.p->lock);\n\t} else {\n\t\t/* Move ret to the front of the LRU. */\n\t\tql_remove(&prof_tdata->lru_ql, ret.p, lru_link);\n\t\tql_head_insert(&prof_tdata->lru_ql, ret.p, lru_link);\n\t}\n\n\treturn (ret.p);\n}\n\nstatic bool\nprof_flush(bool propagate_err)\n{\n\tbool ret = false;\n\tssize_t err;\n\n\tcassert(config_prof);\n\n\terr = write(prof_dump_fd, prof_dump_buf, prof_dump_buf_end);\n\tif (err == -1) {\n\t\tif (propagate_err == false) {\n\t\t\tmalloc_write(\"<jemalloc>: write() failed during heap \"\n\t\t\t    \"profile flush\\n\");\n\t\t\tif (opt_abort)\n\t\t\t\tabort();\n\t\t}\n\t\tret = true;\n\t}\n\tprof_dump_buf_end = 0;\n\n\treturn (ret);\n}\n\nstatic bool\nprof_write(bool propagate_err, const char *s)\n{\n\tunsigned i, slen, n;\n\n\tcassert(config_prof);\n\n\ti = 0;\n\tslen = strlen(s);\n\twhile (i < slen) {\n\t\t/* Flush the buffer if it is full. */\n\t\tif (prof_dump_buf_end == PROF_DUMP_BUFSIZE)\n\t\t\tif (prof_flush(propagate_err) && propagate_err)\n\t\t\t\treturn (true);\n\n\t\tif (prof_dump_buf_end + slen <= PROF_DUMP_BUFSIZE) {\n\t\t\t/* Finish writing. */\n\t\t\tn = slen - i;\n\t\t} else {\n\t\t\t/* Write as much of s as will fit. */\n\t\t\tn = PROF_DUMP_BUFSIZE - prof_dump_buf_end;\n\t\t}\n\t\tmemcpy(&prof_dump_buf[prof_dump_buf_end], &s[i], n);\n\t\tprof_dump_buf_end += n;\n\t\ti += n;\n\t}\n\n\treturn (false);\n}\n\nJEMALLOC_ATTR(format(printf, 2, 3))\nstatic bool\nprof_printf(bool propagate_err, const char *format, ...)\n{\n\tbool ret;\n\tva_list ap;\n\tchar buf[PROF_PRINTF_BUFSIZE];\n\n\tva_start(ap, format);\n\tmalloc_vsnprintf(buf, sizeof(buf), format, ap);\n\tva_end(ap);\n\tret = prof_write(propagate_err, buf);\n\n\treturn (ret);\n}\n\nstatic void\nprof_ctx_sum(prof_ctx_t *ctx, prof_cnt_t *cnt_all, size_t *leak_nctx)\n{\n\tprof_thr_cnt_t *thr_cnt;\n\tprof_cnt_t tcnt;\n\n\tcassert(config_prof);\n\n\tmalloc_mutex_lock(ctx->lock);\n\n\tmemcpy(&ctx->cnt_summed, &ctx->cnt_merged, sizeof(prof_cnt_t));\n\tql_foreach(thr_cnt, &ctx->cnts_ql, cnts_link) {\n\t\tvolatile unsigned *epoch = &thr_cnt->epoch;\n\n\t\twhile (true) {\n\t\t\tunsigned epoch0 = *epoch;\n\n\t\t\t/* Make sure epoch is even. */\n\t\t\tif (epoch0 & 1U)\n\t\t\t\tcontinue;\n\n\t\t\tmemcpy(&tcnt, &thr_cnt->cnts, sizeof(prof_cnt_t));\n\n\t\t\t/* Terminate if epoch didn't change while reading. */\n\t\t\tif (*epoch == epoch0)\n\t\t\t\tbreak;\n\t\t}\n\n\t\tctx->cnt_summed.curobjs += tcnt.curobjs;\n\t\tctx->cnt_summed.curbytes += tcnt.curbytes;\n\t\tif (opt_prof_accum) {\n\t\t\tctx->cnt_summed.accumobjs += tcnt.accumobjs;\n\t\t\tctx->cnt_summed.accumbytes += tcnt.accumbytes;\n\t\t}\n\t}\n\n\tif (ctx->cnt_summed.curobjs != 0)\n\t\t(*leak_nctx)++;\n\n\t/* Add to cnt_all. */\n\tcnt_all->curobjs += ctx->cnt_summed.curobjs;\n\tcnt_all->curbytes += ctx->cnt_summed.curbytes;\n\tif (opt_prof_accum) {\n\t\tcnt_all->accumobjs += ctx->cnt_summed.accumobjs;\n\t\tcnt_all->accumbytes += ctx->cnt_summed.accumbytes;\n\t}\n\n\tmalloc_mutex_unlock(ctx->lock);\n}\n\nstatic void\nprof_ctx_destroy(prof_ctx_t *ctx)\n{\n\tprof_tdata_t *prof_tdata;\n\n\tcassert(config_prof);\n\n\t/*\n\t * Check that ctx is still unused by any thread cache before destroying\n\t * it.  prof_lookup() increments ctx->nlimbo in order to avoid a race\n\t * condition with this function, as does prof_ctx_merge() in order to\n\t * avoid a race between the main body of prof_ctx_merge() and entry\n\t * into this function.\n\t */\n\tprof_tdata = *prof_tdata_tsd_get();\n\tassert((uintptr_t)prof_tdata > (uintptr_t)PROF_TDATA_STATE_MAX);\n\tprof_enter(prof_tdata);\n\tmalloc_mutex_lock(ctx->lock);\n\tif (ql_first(&ctx->cnts_ql) == NULL && ctx->cnt_merged.curobjs == 0 &&\n\t    ctx->nlimbo == 1) {\n\t\tassert(ctx->cnt_merged.curbytes == 0);\n\t\tassert(ctx->cnt_merged.accumobjs == 0);\n\t\tassert(ctx->cnt_merged.accumbytes == 0);\n\t\t/* Remove ctx from bt2ctx. */\n\t\tif (ckh_remove(&bt2ctx, ctx->bt, NULL, NULL))\n\t\t\tassert(false);\n\t\tprof_leave(prof_tdata);\n\t\t/* Destroy ctx. */\n\t\tmalloc_mutex_unlock(ctx->lock);\n\t\tbt_destroy(ctx->bt);\n\t\tidalloc(ctx);\n\t} else {\n\t\t/*\n\t\t * Compensate for increment in prof_ctx_merge() or\n\t\t * prof_lookup().\n\t\t */\n\t\tctx->nlimbo--;\n\t\tmalloc_mutex_unlock(ctx->lock);\n\t\tprof_leave(prof_tdata);\n\t}\n}\n\nstatic void\nprof_ctx_merge(prof_ctx_t *ctx, prof_thr_cnt_t *cnt)\n{\n\tbool destroy;\n\n\tcassert(config_prof);\n\n\t/* Merge cnt stats and detach from ctx. */\n\tmalloc_mutex_lock(ctx->lock);\n\tctx->cnt_merged.curobjs += cnt->cnts.curobjs;\n\tctx->cnt_merged.curbytes += cnt->cnts.curbytes;\n\tctx->cnt_merged.accumobjs += cnt->cnts.accumobjs;\n\tctx->cnt_merged.accumbytes += cnt->cnts.accumbytes;\n\tql_remove(&ctx->cnts_ql, cnt, cnts_link);\n\tif (opt_prof_accum == false && ql_first(&ctx->cnts_ql) == NULL &&\n\t    ctx->cnt_merged.curobjs == 0 && ctx->nlimbo == 0) {\n\t\t/*\n\t\t * Increment ctx->nlimbo in order to keep another thread from\n\t\t * winning the race to destroy ctx while this one has ctx->lock\n\t\t * dropped.  Without this, it would be possible for another\n\t\t * thread to:\n\t\t *\n\t\t * 1) Sample an allocation associated with ctx.\n\t\t * 2) Deallocate the sampled object.\n\t\t * 3) Successfully prof_ctx_destroy(ctx).\n\t\t *\n\t\t * The result would be that ctx no longer exists by the time\n\t\t * this thread accesses it in prof_ctx_destroy().\n\t\t */\n\t\tctx->nlimbo++;\n\t\tdestroy = true;\n\t} else\n\t\tdestroy = false;\n\tmalloc_mutex_unlock(ctx->lock);\n\tif (destroy)\n\t\tprof_ctx_destroy(ctx);\n}\n\nstatic bool\nprof_dump_ctx(bool propagate_err, prof_ctx_t *ctx, prof_bt_t *bt)\n{\n\tunsigned i;\n\n\tcassert(config_prof);\n\n\t/*\n\t * Current statistics can sum to 0 as a result of unmerged per thread\n\t * statistics.  Additionally, interval- and growth-triggered dumps can\n\t * occur between the time a ctx is created and when its statistics are\n\t * filled in.  Avoid dumping any ctx that is an artifact of either\n\t * implementation detail.\n\t */\n\tif ((opt_prof_accum == false && ctx->cnt_summed.curobjs == 0) ||\n\t    (opt_prof_accum && ctx->cnt_summed.accumobjs == 0)) {\n\t\tassert(ctx->cnt_summed.curobjs == 0);\n\t\tassert(ctx->cnt_summed.curbytes == 0);\n\t\tassert(ctx->cnt_summed.accumobjs == 0);\n\t\tassert(ctx->cnt_summed.accumbytes == 0);\n\t\treturn (false);\n\t}\n\n\tif (prof_printf(propagate_err, \"%\"PRId64\": %\"PRId64\n\t    \" [%\"PRIu64\": %\"PRIu64\"] @\",\n\t    ctx->cnt_summed.curobjs, ctx->cnt_summed.curbytes,\n\t    ctx->cnt_summed.accumobjs, ctx->cnt_summed.accumbytes))\n\t\treturn (true);\n\n\tfor (i = 0; i < bt->len; i++) {\n\t\tif (prof_printf(propagate_err, \" %#\"PRIxPTR,\n\t\t    (uintptr_t)bt->vec[i]))\n\t\t\treturn (true);\n\t}\n\n\tif (prof_write(propagate_err, \"\\n\"))\n\t\treturn (true);\n\n\treturn (false);\n}\n\nstatic bool\nprof_dump_maps(bool propagate_err)\n{\n\tint mfd;\n\tchar filename[PATH_MAX + 1];\n\n\tcassert(config_prof);\n\n\tmalloc_snprintf(filename, sizeof(filename), \"/proc/%d/maps\",\n\t    (int)getpid());\n\tmfd = open(filename, O_RDONLY);\n\tif (mfd != -1) {\n\t\tssize_t nread;\n\n\t\tif (prof_write(propagate_err, \"\\nMAPPED_LIBRARIES:\\n\") &&\n\t\t    propagate_err)\n\t\t\treturn (true);\n\t\tnread = 0;\n\t\tdo {\n\t\t\tprof_dump_buf_end += nread;\n\t\t\tif (prof_dump_buf_end == PROF_DUMP_BUFSIZE) {\n\t\t\t\t/* Make space in prof_dump_buf before read(). */\n\t\t\t\tif (prof_flush(propagate_err) && propagate_err)\n\t\t\t\t\treturn (true);\n\t\t\t}\n\t\t\tnread = read(mfd, &prof_dump_buf[prof_dump_buf_end],\n\t\t\t    PROF_DUMP_BUFSIZE - prof_dump_buf_end);\n\t\t} while (nread > 0);\n\t\tclose(mfd);\n\t} else\n\t\treturn (true);\n\n\treturn (false);\n}\n\nstatic bool\nprof_dump(bool propagate_err, const char *filename, bool leakcheck)\n{\n\tprof_tdata_t *prof_tdata;\n\tprof_cnt_t cnt_all;\n\tsize_t tabind;\n\tunion {\n\t\tprof_bt_t\t*p;\n\t\tvoid\t\t*v;\n\t} bt;\n\tunion {\n\t\tprof_ctx_t\t*p;\n\t\tvoid\t\t*v;\n\t} ctx;\n\tsize_t leak_nctx;\n\n\tcassert(config_prof);\n\n\tprof_tdata = prof_tdata_get();\n\tif ((uintptr_t)prof_tdata <= (uintptr_t)PROF_TDATA_STATE_MAX)\n\t\treturn (true);\n\tprof_enter(prof_tdata);\n\tprof_dump_fd = creat(filename, 0644);\n\tif (prof_dump_fd == -1) {\n\t\tif (propagate_err == false) {\n\t\t\tmalloc_printf(\n\t\t\t    \"<jemalloc>: creat(\\\"%s\\\"), 0644) failed\\n\",\n\t\t\t    filename);\n\t\t\tif (opt_abort)\n\t\t\t\tabort();\n\t\t}\n\t\tgoto label_error;\n\t}\n\n\t/* Merge per thread profile stats, and sum them in cnt_all. */\n\tmemset(&cnt_all, 0, sizeof(prof_cnt_t));\n\tleak_nctx = 0;\n\tfor (tabind = 0; ckh_iter(&bt2ctx, &tabind, NULL, &ctx.v) == false;)\n\t\tprof_ctx_sum(ctx.p, &cnt_all, &leak_nctx);\n\n\t/* Dump profile header. */\n\tif (opt_lg_prof_sample == 0) {\n\t\tif (prof_printf(propagate_err,\n\t\t    \"heap profile: %\"PRId64\": %\"PRId64\n\t\t    \" [%\"PRIu64\": %\"PRIu64\"] @ heapprofile\\n\",\n\t\t    cnt_all.curobjs, cnt_all.curbytes,\n\t\t    cnt_all.accumobjs, cnt_all.accumbytes))\n\t\t\tgoto label_error;\n\t} else {\n\t\tif (prof_printf(propagate_err,\n\t\t    \"heap profile: %\"PRId64\": %\"PRId64\n\t\t    \" [%\"PRIu64\": %\"PRIu64\"] @ heap_v2/%\"PRIu64\"\\n\",\n\t\t    cnt_all.curobjs, cnt_all.curbytes,\n\t\t    cnt_all.accumobjs, cnt_all.accumbytes,\n\t\t    ((uint64_t)1U << opt_lg_prof_sample)))\n\t\t\tgoto label_error;\n\t}\n\n\t/* Dump  per ctx profile stats. */\n\tfor (tabind = 0; ckh_iter(&bt2ctx, &tabind, &bt.v, &ctx.v)\n\t    == false;) {\n\t\tif (prof_dump_ctx(propagate_err, ctx.p, bt.p))\n\t\t\tgoto label_error;\n\t}\n\n\t/* Dump /proc/<pid>/maps if possible. */\n\tif (prof_dump_maps(propagate_err))\n\t\tgoto label_error;\n\n\tif (prof_flush(propagate_err))\n\t\tgoto label_error;\n\tclose(prof_dump_fd);\n\tprof_leave(prof_tdata);\n\n\tif (leakcheck && cnt_all.curbytes != 0) {\n\t\tmalloc_printf(\"<jemalloc>: Leak summary: %\"PRId64\" byte%s, %\"\n\t\t    PRId64\" object%s, %zu context%s\\n\",\n\t\t    cnt_all.curbytes, (cnt_all.curbytes != 1) ? \"s\" : \"\",\n\t\t    cnt_all.curobjs, (cnt_all.curobjs != 1) ? \"s\" : \"\",\n\t\t    leak_nctx, (leak_nctx != 1) ? \"s\" : \"\");\n\t\tmalloc_printf(\n\t\t    \"<jemalloc>: Run pprof on \\\"%s\\\" for leak detail\\n\",\n\t\t    filename);\n\t}\n\n\treturn (false);\nlabel_error:\n\tprof_leave(prof_tdata);\n\treturn (true);\n}\n\n#define\tDUMP_FILENAME_BUFSIZE\t(PATH_MAX + 1)\nstatic void\nprof_dump_filename(char *filename, char v, int64_t vseq)\n{\n\n\tcassert(config_prof);\n\n\tif (vseq != UINT64_C(0xffffffffffffffff)) {\n\t        /* \"<prefix>.<pid>.<seq>.v<vseq>.heap\" */\n\t\tmalloc_snprintf(filename, DUMP_FILENAME_BUFSIZE,\n\t\t    \"%s.%d.%\"PRIu64\".%c%\"PRId64\".heap\",\n\t\t    opt_prof_prefix, (int)getpid(), prof_dump_seq, v, vseq);\n\t} else {\n\t        /* \"<prefix>.<pid>.<seq>.<v>.heap\" */\n\t\tmalloc_snprintf(filename, DUMP_FILENAME_BUFSIZE,\n\t\t    \"%s.%d.%\"PRIu64\".%c.heap\",\n\t\t    opt_prof_prefix, (int)getpid(), prof_dump_seq, v);\n\t}\n\tprof_dump_seq++;\n}\n\nstatic void\nprof_fdump(void)\n{\n\tchar filename[DUMP_FILENAME_BUFSIZE];\n\n\tcassert(config_prof);\n\n\tif (prof_booted == false)\n\t\treturn;\n\n\tif (opt_prof_final && opt_prof_prefix[0] != '\\0') {\n\t\tmalloc_mutex_lock(&prof_dump_seq_mtx);\n\t\tprof_dump_filename(filename, 'f', UINT64_C(0xffffffffffffffff));\n\t\tmalloc_mutex_unlock(&prof_dump_seq_mtx);\n\t\tprof_dump(false, filename, opt_prof_leak);\n\t}\n}\n\nvoid\nprof_idump(void)\n{\n\tprof_tdata_t *prof_tdata;\n\tchar filename[PATH_MAX + 1];\n\n\tcassert(config_prof);\n\n\tif (prof_booted == false)\n\t\treturn;\n\t/*\n\t * Don't call prof_tdata_get() here, because it could cause recursive\n\t * allocation.\n\t */\n\tprof_tdata = *prof_tdata_tsd_get();\n\tif ((uintptr_t)prof_tdata <= (uintptr_t)PROF_TDATA_STATE_MAX)\n\t\treturn;\n\tif (prof_tdata->enq) {\n\t\tprof_tdata->enq_idump = true;\n\t\treturn;\n\t}\n\n\tif (opt_prof_prefix[0] != '\\0') {\n\t\tmalloc_mutex_lock(&prof_dump_seq_mtx);\n\t\tprof_dump_filename(filename, 'i', prof_dump_iseq);\n\t\tprof_dump_iseq++;\n\t\tmalloc_mutex_unlock(&prof_dump_seq_mtx);\n\t\tprof_dump(false, filename, false);\n\t}\n}\n\nbool\nprof_mdump(const char *filename)\n{\n\tchar filename_buf[DUMP_FILENAME_BUFSIZE];\n\n\tcassert(config_prof);\n\n\tif (opt_prof == false || prof_booted == false)\n\t\treturn (true);\n\n\tif (filename == NULL) {\n\t\t/* No filename specified, so automatically generate one. */\n\t\tif (opt_prof_prefix[0] == '\\0')\n\t\t\treturn (true);\n\t\tmalloc_mutex_lock(&prof_dump_seq_mtx);\n\t\tprof_dump_filename(filename_buf, 'm', prof_dump_mseq);\n\t\tprof_dump_mseq++;\n\t\tmalloc_mutex_unlock(&prof_dump_seq_mtx);\n\t\tfilename = filename_buf;\n\t}\n\treturn (prof_dump(true, filename, false));\n}\n\nvoid\nprof_gdump(void)\n{\n\tprof_tdata_t *prof_tdata;\n\tchar filename[DUMP_FILENAME_BUFSIZE];\n\n\tcassert(config_prof);\n\n\tif (prof_booted == false)\n\t\treturn;\n\t/*\n\t * Don't call prof_tdata_get() here, because it could cause recursive\n\t * allocation.\n\t */\n\tprof_tdata = *prof_tdata_tsd_get();\n\tif ((uintptr_t)prof_tdata <= (uintptr_t)PROF_TDATA_STATE_MAX)\n\t\treturn;\n\tif (prof_tdata->enq) {\n\t\tprof_tdata->enq_gdump = true;\n\t\treturn;\n\t}\n\n\tif (opt_prof_prefix[0] != '\\0') {\n\t\tmalloc_mutex_lock(&prof_dump_seq_mtx);\n\t\tprof_dump_filename(filename, 'u', prof_dump_useq);\n\t\tprof_dump_useq++;\n\t\tmalloc_mutex_unlock(&prof_dump_seq_mtx);\n\t\tprof_dump(false, filename, false);\n\t}\n}\n\nstatic void\nprof_bt_hash(const void *key, unsigned minbits, size_t *hash1, size_t *hash2)\n{\n\tsize_t ret1, ret2;\n\tuint64_t h;\n\tprof_bt_t *bt = (prof_bt_t *)key;\n\n\tcassert(config_prof);\n\tassert(minbits <= 32 || (SIZEOF_PTR == 8 && minbits <= 64));\n\tassert(hash1 != NULL);\n\tassert(hash2 != NULL);\n\n\th = hash(bt->vec, bt->len * sizeof(void *),\n\t    UINT64_C(0x94122f335b332aea));\n\tif (minbits <= 32) {\n\t\t/*\n\t\t * Avoid doing multiple hashes, since a single hash provides\n\t\t * enough bits.\n\t\t */\n\t\tret1 = h & ZU(0xffffffffU);\n\t\tret2 = h >> 32;\n\t} else {\n\t\tret1 = h;\n\t\tret2 = hash(bt->vec, bt->len * sizeof(void *),\n\t\t    UINT64_C(0x8432a476666bbc13));\n\t}\n\n\t*hash1 = ret1;\n\t*hash2 = ret2;\n}\n\nstatic bool\nprof_bt_keycomp(const void *k1, const void *k2)\n{\n\tconst prof_bt_t *bt1 = (prof_bt_t *)k1;\n\tconst prof_bt_t *bt2 = (prof_bt_t *)k2;\n\n\tcassert(config_prof);\n\n\tif (bt1->len != bt2->len)\n\t\treturn (false);\n\treturn (memcmp(bt1->vec, bt2->vec, bt1->len * sizeof(void *)) == 0);\n}\n\nstatic malloc_mutex_t *\nprof_ctx_mutex_choose(void)\n{\n\tunsigned nctxs = atomic_add_u(&cum_ctxs, 1);\n\n\treturn (&ctx_locks[(nctxs - 1) % PROF_NCTX_LOCKS]);\n}\n\nprof_tdata_t *\nprof_tdata_init(void)\n{\n\tprof_tdata_t *prof_tdata;\n\n\tcassert(config_prof);\n\n\t/* Initialize an empty cache for this thread. */\n\tprof_tdata = (prof_tdata_t *)imalloc(sizeof(prof_tdata_t));\n\tif (prof_tdata == NULL)\n\t\treturn (NULL);\n\n\tif (ckh_new(&prof_tdata->bt2cnt, PROF_CKH_MINITEMS,\n\t    prof_bt_hash, prof_bt_keycomp)) {\n\t\tidalloc(prof_tdata);\n\t\treturn (NULL);\n\t}\n\tql_new(&prof_tdata->lru_ql);\n\n\tprof_tdata->vec = imalloc(sizeof(void *) * PROF_BT_MAX);\n\tif (prof_tdata->vec == NULL) {\n\t\tckh_delete(&prof_tdata->bt2cnt);\n\t\tidalloc(prof_tdata);\n\t\treturn (NULL);\n\t}\n\n\tprof_tdata->prng_state = 0;\n\tprof_tdata->threshold = 0;\n\tprof_tdata->accum = 0;\n\n\tprof_tdata->enq = false;\n\tprof_tdata->enq_idump = false;\n\tprof_tdata->enq_gdump = false;\n\n\tprof_tdata_tsd_set(&prof_tdata);\n\n\treturn (prof_tdata);\n}\n\nvoid\nprof_tdata_cleanup(void *arg)\n{\n\tprof_thr_cnt_t *cnt;\n\tprof_tdata_t *prof_tdata = *(prof_tdata_t **)arg;\n\n\tcassert(config_prof);\n\n\tif (prof_tdata == PROF_TDATA_STATE_REINCARNATED) {\n\t\t/*\n\t\t * Another destructor deallocated memory after this destructor\n\t\t * was called.  Reset prof_tdata to PROF_TDATA_STATE_PURGATORY\n\t\t * in order to receive another callback.\n\t\t */\n\t\tprof_tdata = PROF_TDATA_STATE_PURGATORY;\n\t\tprof_tdata_tsd_set(&prof_tdata);\n\t} else if (prof_tdata == PROF_TDATA_STATE_PURGATORY) {\n\t\t/*\n\t\t * The previous time this destructor was called, we set the key\n\t\t * to PROF_TDATA_STATE_PURGATORY so that other destructors\n\t\t * wouldn't cause re-creation of the prof_tdata.  This time, do\n\t\t * nothing, so that the destructor will not be called again.\n\t\t */\n\t} else if (prof_tdata != NULL) {\n\t\t/*\n\t\t * Delete the hash table.  All of its contents can still be\n\t\t * iterated over via the LRU.\n\t\t */\n\t\tckh_delete(&prof_tdata->bt2cnt);\n\t\t/*\n\t\t * Iteratively merge cnt's into the global stats and delete\n\t\t * them.\n\t\t */\n\t\twhile ((cnt = ql_last(&prof_tdata->lru_ql, lru_link)) != NULL) {\n\t\t\tql_remove(&prof_tdata->lru_ql, cnt, lru_link);\n\t\t\tprof_ctx_merge(cnt->ctx, cnt);\n\t\t\tidalloc(cnt);\n\t\t}\n\t\tidalloc(prof_tdata->vec);\n\t\tidalloc(prof_tdata);\n\t\tprof_tdata = PROF_TDATA_STATE_PURGATORY;\n\t\tprof_tdata_tsd_set(&prof_tdata);\n\t}\n}\n\nvoid\nprof_boot0(void)\n{\n\n\tcassert(config_prof);\n\n\tmemcpy(opt_prof_prefix, PROF_PREFIX_DEFAULT,\n\t    sizeof(PROF_PREFIX_DEFAULT));\n}\n\nvoid\nprof_boot1(void)\n{\n\n\tcassert(config_prof);\n\n\t/*\n\t * opt_prof and prof_promote must be in their final state before any\n\t * arenas are initialized, so this function must be executed early.\n\t */\n\n\tif (opt_prof_leak && opt_prof == false) {\n\t\t/*\n\t\t * Enable opt_prof, but in such a way that profiles are never\n\t\t * automatically dumped.\n\t\t */\n\t\topt_prof = true;\n\t\topt_prof_gdump = false;\n\t\tprof_interval = 0;\n\t} else if (opt_prof) {\n\t\tif (opt_lg_prof_interval >= 0) {\n\t\t\tprof_interval = (((uint64_t)1U) <<\n\t\t\t    opt_lg_prof_interval);\n\t\t} else\n\t\t\tprof_interval = 0;\n\t}\n\n\tprof_promote = (opt_prof && opt_lg_prof_sample > LG_PAGE);\n}\n\nbool\nprof_boot2(void)\n{\n\n\tcassert(config_prof);\n\n\tif (opt_prof) {\n\t\tunsigned i;\n\n\t\tif (ckh_new(&bt2ctx, PROF_CKH_MINITEMS, prof_bt_hash,\n\t\t    prof_bt_keycomp))\n\t\t\treturn (true);\n\t\tif (malloc_mutex_init(&bt2ctx_mtx))\n\t\t\treturn (true);\n\t\tif (prof_tdata_tsd_boot()) {\n\t\t\tmalloc_write(\n\t\t\t    \"<jemalloc>: Error in pthread_key_create()\\n\");\n\t\t\tabort();\n\t\t}\n\n\t\tif (malloc_mutex_init(&prof_dump_seq_mtx))\n\t\t\treturn (true);\n\n\t\tif (atexit(prof_fdump) != 0) {\n\t\t\tmalloc_write(\"<jemalloc>: Error in atexit()\\n\");\n\t\t\tif (opt_abort)\n\t\t\t\tabort();\n\t\t}\n\n\t\tctx_locks = (malloc_mutex_t *)base_alloc(PROF_NCTX_LOCKS *\n\t\t    sizeof(malloc_mutex_t));\n\t\tif (ctx_locks == NULL)\n\t\t\treturn (true);\n\t\tfor (i = 0; i < PROF_NCTX_LOCKS; i++) {\n\t\t\tif (malloc_mutex_init(&ctx_locks[i]))\n\t\t\t\treturn (true);\n\t\t}\n\t}\n\n#ifdef JEMALLOC_PROF_LIBGCC\n\t/*\n\t * Cause the backtracing machinery to allocate its internal state\n\t * before enabling profiling.\n\t */\n\t_Unwind_Backtrace(prof_unwind_init_callback, NULL);\n#endif\n\n\tprof_booted = true;\n\n\treturn (false);\n}\n\nvoid\nprof_prefork(void)\n{\n\n\tif (opt_prof) {\n\t\tunsigned i;\n\n\t\tmalloc_mutex_lock(&bt2ctx_mtx);\n\t\tmalloc_mutex_lock(&prof_dump_seq_mtx);\n\t\tfor (i = 0; i < PROF_NCTX_LOCKS; i++)\n\t\t\tmalloc_mutex_lock(&ctx_locks[i]);\n\t}\n}\n\nvoid\nprof_postfork_parent(void)\n{\n\n\tif (opt_prof) {\n\t\tunsigned i;\n\n\t\tfor (i = 0; i < PROF_NCTX_LOCKS; i++)\n\t\t\tmalloc_mutex_postfork_parent(&ctx_locks[i]);\n\t\tmalloc_mutex_postfork_parent(&prof_dump_seq_mtx);\n\t\tmalloc_mutex_postfork_parent(&bt2ctx_mtx);\n\t}\n}\n\nvoid\nprof_postfork_child(void)\n{\n\n\tif (opt_prof) {\n\t\tunsigned i;\n\n\t\tfor (i = 0; i < PROF_NCTX_LOCKS; i++)\n\t\t\tmalloc_mutex_postfork_child(&ctx_locks[i]);\n\t\tmalloc_mutex_postfork_child(&prof_dump_seq_mtx);\n\t\tmalloc_mutex_postfork_child(&bt2ctx_mtx);\n\t}\n}\n\n/******************************************************************************/\n"
  },
  {
    "path": "deps/jemalloc/src/quarantine.c",
    "content": "#include \"jemalloc/internal/jemalloc_internal.h\"\n\n/*\n * quarantine pointers close to NULL are used to encode state information that\n * is used for cleaning up during thread shutdown.\n */\n#define\tQUARANTINE_STATE_REINCARNATED\t((quarantine_t *)(uintptr_t)1)\n#define\tQUARANTINE_STATE_PURGATORY\t((quarantine_t *)(uintptr_t)2)\n#define\tQUARANTINE_STATE_MAX\t\tQUARANTINE_STATE_PURGATORY\n\n/******************************************************************************/\n/* Data. */\n\ntypedef struct quarantine_obj_s quarantine_obj_t;\ntypedef struct quarantine_s quarantine_t;\n\nstruct quarantine_obj_s {\n\tvoid\t*ptr;\n\tsize_t\tusize;\n};\n\nstruct quarantine_s {\n\tsize_t\t\t\tcurbytes;\n\tsize_t\t\t\tcurobjs;\n\tsize_t\t\t\tfirst;\n#define\tLG_MAXOBJS_INIT 10\n\tsize_t\t\t\tlg_maxobjs;\n\tquarantine_obj_t\tobjs[1]; /* Dynamically sized ring buffer. */\n};\n\nstatic void\tquarantine_cleanup(void *arg);\n\nmalloc_tsd_data(static, quarantine, quarantine_t *, NULL)\nmalloc_tsd_funcs(JEMALLOC_INLINE, quarantine, quarantine_t *, NULL,\n    quarantine_cleanup)\n\n/******************************************************************************/\n/* Function prototypes for non-inline static functions. */\n\nstatic quarantine_t\t*quarantine_init(size_t lg_maxobjs);\nstatic quarantine_t\t*quarantine_grow(quarantine_t *quarantine);\nstatic void\tquarantine_drain(quarantine_t *quarantine, size_t upper_bound);\n\n/******************************************************************************/\n\nstatic quarantine_t *\nquarantine_init(size_t lg_maxobjs)\n{\n\tquarantine_t *quarantine;\n\n\tquarantine = (quarantine_t *)imalloc(offsetof(quarantine_t, objs) +\n\t    ((ZU(1) << lg_maxobjs) * sizeof(quarantine_obj_t)));\n\tif (quarantine == NULL)\n\t\treturn (NULL);\n\tquarantine->curbytes = 0;\n\tquarantine->curobjs = 0;\n\tquarantine->first = 0;\n\tquarantine->lg_maxobjs = lg_maxobjs;\n\n\tquarantine_tsd_set(&quarantine);\n\n\treturn (quarantine);\n}\n\nstatic quarantine_t *\nquarantine_grow(quarantine_t *quarantine)\n{\n\tquarantine_t *ret;\n\n\tret = quarantine_init(quarantine->lg_maxobjs + 1);\n\tif (ret == NULL)\n\t\treturn (quarantine);\n\n\tret->curbytes = quarantine->curbytes;\n\tret->curobjs = quarantine->curobjs;\n\tif (quarantine->first + quarantine->curobjs <= (ZU(1) <<\n\t    quarantine->lg_maxobjs)) {\n\t\t/* objs ring buffer data are contiguous. */\n\t\tmemcpy(ret->objs, &quarantine->objs[quarantine->first],\n\t\t    quarantine->curobjs * sizeof(quarantine_obj_t));\n\t} else {\n\t\t/* objs ring buffer data wrap around. */\n\t\tsize_t ncopy_a = (ZU(1) << quarantine->lg_maxobjs) -\n\t\t    quarantine->first;\n\t\tsize_t ncopy_b = quarantine->curobjs - ncopy_a;\n\n\t\tmemcpy(ret->objs, &quarantine->objs[quarantine->first], ncopy_a\n\t\t    * sizeof(quarantine_obj_t));\n\t\tmemcpy(&ret->objs[ncopy_a], quarantine->objs, ncopy_b *\n\t\t    sizeof(quarantine_obj_t));\n\t}\n\n\treturn (ret);\n}\n\nstatic void\nquarantine_drain(quarantine_t *quarantine, size_t upper_bound)\n{\n\n\twhile (quarantine->curbytes > upper_bound && quarantine->curobjs > 0) {\n\t\tquarantine_obj_t *obj = &quarantine->objs[quarantine->first];\n\t\tassert(obj->usize == isalloc(obj->ptr, config_prof));\n\t\tidalloc(obj->ptr);\n\t\tquarantine->curbytes -= obj->usize;\n\t\tquarantine->curobjs--;\n\t\tquarantine->first = (quarantine->first + 1) & ((ZU(1) <<\n\t\t    quarantine->lg_maxobjs) - 1);\n\t}\n}\n\nvoid\nquarantine(void *ptr)\n{\n\tquarantine_t *quarantine;\n\tsize_t usize = isalloc(ptr, config_prof);\n\n\tcassert(config_fill);\n\tassert(opt_quarantine);\n\n\tquarantine = *quarantine_tsd_get();\n\tif ((uintptr_t)quarantine <= (uintptr_t)QUARANTINE_STATE_MAX) {\n\t\tif (quarantine == NULL) {\n\t\t\tif ((quarantine = quarantine_init(LG_MAXOBJS_INIT)) ==\n\t\t\t    NULL) {\n\t\t\t\tidalloc(ptr);\n\t\t\t\treturn;\n\t\t\t}\n\t\t} else {\n\t\t\tif (quarantine == QUARANTINE_STATE_PURGATORY) {\n\t\t\t\t/*\n\t\t\t\t * Make a note that quarantine() was called\n\t\t\t\t * after quarantine_cleanup() was called.\n\t\t\t\t */\n\t\t\t\tquarantine = QUARANTINE_STATE_REINCARNATED;\n\t\t\t\tquarantine_tsd_set(&quarantine);\n\t\t\t}\n\t\t\tidalloc(ptr);\n\t\t\treturn;\n\t\t}\n\t}\n\t/*\n\t * Drain one or more objects if the quarantine size limit would be\n\t * exceeded by appending ptr.\n\t */\n\tif (quarantine->curbytes + usize > opt_quarantine) {\n\t\tsize_t upper_bound = (opt_quarantine >= usize) ? opt_quarantine\n\t\t    - usize : 0;\n\t\tquarantine_drain(quarantine, upper_bound);\n\t}\n\t/* Grow the quarantine ring buffer if it's full. */\n\tif (quarantine->curobjs == (ZU(1) << quarantine->lg_maxobjs))\n\t\tquarantine = quarantine_grow(quarantine);\n\t/* quarantine_grow() must free a slot if it fails to grow. */\n\tassert(quarantine->curobjs < (ZU(1) << quarantine->lg_maxobjs));\n\t/* Append ptr if its size doesn't exceed the quarantine size. */\n\tif (quarantine->curbytes + usize <= opt_quarantine) {\n\t\tsize_t offset = (quarantine->first + quarantine->curobjs) &\n\t\t    ((ZU(1) << quarantine->lg_maxobjs) - 1);\n\t\tquarantine_obj_t *obj = &quarantine->objs[offset];\n\t\tobj->ptr = ptr;\n\t\tobj->usize = usize;\n\t\tquarantine->curbytes += usize;\n\t\tquarantine->curobjs++;\n\t\tif (opt_junk)\n\t\t\tmemset(ptr, 0x5a, usize);\n\t} else {\n\t\tassert(quarantine->curbytes == 0);\n\t\tidalloc(ptr);\n\t}\n}\n\nstatic void\nquarantine_cleanup(void *arg)\n{\n\tquarantine_t *quarantine = *(quarantine_t **)arg;\n\n\tif (quarantine == QUARANTINE_STATE_REINCARNATED) {\n\t\t/*\n\t\t * Another destructor deallocated memory after this destructor\n\t\t * was called.  Reset quarantine to QUARANTINE_STATE_PURGATORY\n\t\t * in order to receive another callback.\n\t\t */\n\t\tquarantine = QUARANTINE_STATE_PURGATORY;\n\t\tquarantine_tsd_set(&quarantine);\n\t} else if (quarantine == QUARANTINE_STATE_PURGATORY) {\n\t\t/*\n\t\t * The previous time this destructor was called, we set the key\n\t\t * to QUARANTINE_STATE_PURGATORY so that other destructors\n\t\t * wouldn't cause re-creation of the quarantine.  This time, do\n\t\t * nothing, so that the destructor will not be called again.\n\t\t */\n\t} else if (quarantine != NULL) {\n\t\tquarantine_drain(quarantine, 0);\n\t\tidalloc(quarantine);\n\t\tquarantine = QUARANTINE_STATE_PURGATORY;\n\t\tquarantine_tsd_set(&quarantine);\n\t}\n}\n\nbool\nquarantine_boot(void)\n{\n\n\tcassert(config_fill);\n\n\tif (quarantine_tsd_boot())\n\t\treturn (true);\n\n\treturn (false);\n}\n"
  },
  {
    "path": "deps/jemalloc/src/rtree.c",
    "content": "#define\tJEMALLOC_RTREE_C_\n#include \"jemalloc/internal/jemalloc_internal.h\"\n\nrtree_t *\nrtree_new(unsigned bits)\n{\n\trtree_t *ret;\n\tunsigned bits_per_level, height, i;\n\n\tbits_per_level = ffs(pow2_ceil((RTREE_NODESIZE / sizeof(void *)))) - 1;\n\theight = bits / bits_per_level;\n\tif (height * bits_per_level != bits)\n\t\theight++;\n\tassert(height * bits_per_level >= bits);\n\n\tret = (rtree_t*)base_alloc(offsetof(rtree_t, level2bits) +\n\t    (sizeof(unsigned) * height));\n\tif (ret == NULL)\n\t\treturn (NULL);\n\tmemset(ret, 0, offsetof(rtree_t, level2bits) + (sizeof(unsigned) *\n\t    height));\n\n\tif (malloc_mutex_init(&ret->mutex)) {\n\t\t/* Leak the rtree. */\n\t\treturn (NULL);\n\t}\n\tret->height = height;\n\tif (bits_per_level * height > bits)\n\t\tret->level2bits[0] = bits % bits_per_level;\n\telse\n\t\tret->level2bits[0] = bits_per_level;\n\tfor (i = 1; i < height; i++)\n\t\tret->level2bits[i] = bits_per_level;\n\n\tret->root = (void**)base_alloc(sizeof(void *) << ret->level2bits[0]);\n\tif (ret->root == NULL) {\n\t\t/*\n\t\t * We leak the rtree here, since there's no generic base\n\t\t * deallocation.\n\t\t */\n\t\treturn (NULL);\n\t}\n\tmemset(ret->root, 0, sizeof(void *) << ret->level2bits[0]);\n\n\treturn (ret);\n}\n\nvoid\nrtree_prefork(rtree_t *rtree)\n{\n\n\tmalloc_mutex_prefork(&rtree->mutex);\n}\n\nvoid\nrtree_postfork_parent(rtree_t *rtree)\n{\n\n\tmalloc_mutex_postfork_parent(&rtree->mutex);\n}\n\nvoid\nrtree_postfork_child(rtree_t *rtree)\n{\n\n\tmalloc_mutex_postfork_child(&rtree->mutex);\n}\n"
  },
  {
    "path": "deps/jemalloc/src/stats.c",
    "content": "#define\tJEMALLOC_STATS_C_\n#include \"jemalloc/internal/jemalloc_internal.h\"\n\n#define\tCTL_GET(n, v, t) do {\t\t\t\t\t\t\\\n\tsize_t sz = sizeof(t);\t\t\t\t\t\t\\\n\txmallctl(n, v, &sz, NULL, 0);\t\t\t\t\t\\\n} while (0)\n\n#define\tCTL_I_GET(n, v, t) do {\t\t\t\t\t\t\\\n\tsize_t mib[6];\t\t\t\t\t\t\t\\\n\tsize_t miblen = sizeof(mib) / sizeof(size_t);\t\t\t\\\n\tsize_t sz = sizeof(t);\t\t\t\t\t\t\\\n\txmallctlnametomib(n, mib, &miblen);\t\t\t\t\\\n\tmib[2] = i;\t\t\t\t\t\t\t\\\n\txmallctlbymib(mib, miblen, v, &sz, NULL, 0);\t\t\t\\\n} while (0)\n\n#define\tCTL_J_GET(n, v, t) do {\t\t\t\t\t\t\\\n\tsize_t mib[6];\t\t\t\t\t\t\t\\\n\tsize_t miblen = sizeof(mib) / sizeof(size_t);\t\t\t\\\n\tsize_t sz = sizeof(t);\t\t\t\t\t\t\\\n\txmallctlnametomib(n, mib, &miblen);\t\t\t\t\\\n\tmib[2] = j;\t\t\t\t\t\t\t\\\n\txmallctlbymib(mib, miblen, v, &sz, NULL, 0);\t\t\t\\\n} while (0)\n\n#define\tCTL_IJ_GET(n, v, t) do {\t\t\t\t\t\\\n\tsize_t mib[6];\t\t\t\t\t\t\t\\\n\tsize_t miblen = sizeof(mib) / sizeof(size_t);\t\t\t\\\n\tsize_t sz = sizeof(t);\t\t\t\t\t\t\\\n\txmallctlnametomib(n, mib, &miblen);\t\t\t\t\\\n\tmib[2] = i;\t\t\t\t\t\t\t\\\n\tmib[4] = j;\t\t\t\t\t\t\t\\\n\txmallctlbymib(mib, miblen, v, &sz, NULL, 0);\t\t\t\\\n} while (0)\n\n/******************************************************************************/\n/* Data. */\n\nbool\topt_stats_print = false;\n\nsize_t\tstats_cactive = 0;\n\n/******************************************************************************/\n/* Function prototypes for non-inline static functions. */\n\nstatic void\tstats_arena_bins_print(void (*write_cb)(void *, const char *),\n    void *cbopaque, unsigned i);\nstatic void\tstats_arena_lruns_print(void (*write_cb)(void *, const char *),\n    void *cbopaque, unsigned i);\nstatic void\tstats_arena_print(void (*write_cb)(void *, const char *),\n    void *cbopaque, unsigned i, bool bins, bool large);\n\n/******************************************************************************/\n\nstatic void\nstats_arena_bins_print(void (*write_cb)(void *, const char *), void *cbopaque,\n    unsigned i)\n{\n\tsize_t page;\n\tbool config_tcache;\n\tunsigned nbins, j, gap_start;\n\n\tCTL_GET(\"arenas.page\", &page, size_t);\n\n\tCTL_GET(\"config.tcache\", &config_tcache, bool);\n\tif (config_tcache) {\n\t\tmalloc_cprintf(write_cb, cbopaque,\n\t\t    \"bins:     bin  size regs pgs    allocated      nmalloc\"\n\t\t    \"      ndalloc    nrequests       nfills     nflushes\"\n\t\t    \"      newruns       reruns      curruns\\n\");\n\t} else {\n\t\tmalloc_cprintf(write_cb, cbopaque,\n\t\t    \"bins:     bin  size regs pgs    allocated      nmalloc\"\n\t\t    \"      ndalloc      newruns       reruns      curruns\\n\");\n\t}\n\tCTL_GET(\"arenas.nbins\", &nbins, unsigned);\n\tfor (j = 0, gap_start = UINT_MAX; j < nbins; j++) {\n\t\tuint64_t nruns;\n\n\t\tCTL_IJ_GET(\"stats.arenas.0.bins.0.nruns\", &nruns, uint64_t);\n\t\tif (nruns == 0) {\n\t\t\tif (gap_start == UINT_MAX)\n\t\t\t\tgap_start = j;\n\t\t} else {\n\t\t\tsize_t reg_size, run_size, allocated;\n\t\t\tuint32_t nregs;\n\t\t\tuint64_t nmalloc, ndalloc, nrequests, nfills, nflushes;\n\t\t\tuint64_t reruns;\n\t\t\tsize_t curruns;\n\n\t\t\tif (gap_start != UINT_MAX) {\n\t\t\t\tif (j > gap_start + 1) {\n\t\t\t\t\t/* Gap of more than one size class. */\n\t\t\t\t\tmalloc_cprintf(write_cb, cbopaque,\n\t\t\t\t\t    \"[%u..%u]\\n\", gap_start,\n\t\t\t\t\t    j - 1);\n\t\t\t\t} else {\n\t\t\t\t\t/* Gap of one size class. */\n\t\t\t\t\tmalloc_cprintf(write_cb, cbopaque,\n\t\t\t\t\t    \"[%u]\\n\", gap_start);\n\t\t\t\t}\n\t\t\t\tgap_start = UINT_MAX;\n\t\t\t}\n\t\t\tCTL_J_GET(\"arenas.bin.0.size\", &reg_size, size_t);\n\t\t\tCTL_J_GET(\"arenas.bin.0.nregs\", &nregs, uint32_t);\n\t\t\tCTL_J_GET(\"arenas.bin.0.run_size\", &run_size, size_t);\n\t\t\tCTL_IJ_GET(\"stats.arenas.0.bins.0.allocated\",\n\t\t\t    &allocated, size_t);\n\t\t\tCTL_IJ_GET(\"stats.arenas.0.bins.0.nmalloc\",\n\t\t\t    &nmalloc, uint64_t);\n\t\t\tCTL_IJ_GET(\"stats.arenas.0.bins.0.ndalloc\",\n\t\t\t    &ndalloc, uint64_t);\n\t\t\tif (config_tcache) {\n\t\t\t\tCTL_IJ_GET(\"stats.arenas.0.bins.0.nrequests\",\n\t\t\t\t    &nrequests, uint64_t);\n\t\t\t\tCTL_IJ_GET(\"stats.arenas.0.bins.0.nfills\",\n\t\t\t\t    &nfills, uint64_t);\n\t\t\t\tCTL_IJ_GET(\"stats.arenas.0.bins.0.nflushes\",\n\t\t\t\t    &nflushes, uint64_t);\n\t\t\t}\n\t\t\tCTL_IJ_GET(\"stats.arenas.0.bins.0.nreruns\", &reruns,\n\t\t\t    uint64_t);\n\t\t\tCTL_IJ_GET(\"stats.arenas.0.bins.0.curruns\", &curruns,\n\t\t\t    size_t);\n\t\t\tif (config_tcache) {\n\t\t\t\tmalloc_cprintf(write_cb, cbopaque,\n\t\t\t\t    \"%13u %5zu %4u %3zu %12zu %12\"PRIu64\n\t\t\t\t    \" %12\"PRIu64\" %12\"PRIu64\" %12\"PRIu64\n\t\t\t\t    \" %12\"PRIu64\" %12\"PRIu64\" %12\"PRIu64\n\t\t\t\t    \" %12zu\\n\",\n\t\t\t\t    j, reg_size, nregs, run_size / page,\n\t\t\t\t    allocated, nmalloc, ndalloc, nrequests,\n\t\t\t\t    nfills, nflushes, nruns, reruns, curruns);\n\t\t\t} else {\n\t\t\t\tmalloc_cprintf(write_cb, cbopaque,\n\t\t\t\t    \"%13u %5zu %4u %3zu %12zu %12\"PRIu64\n\t\t\t\t    \" %12\"PRIu64\" %12\"PRIu64\" %12\"PRIu64\n\t\t\t\t    \" %12zu\\n\",\n\t\t\t\t    j, reg_size, nregs, run_size / page,\n\t\t\t\t    allocated, nmalloc, ndalloc, nruns, reruns,\n\t\t\t\t    curruns);\n\t\t\t}\n\t\t}\n\t}\n\tif (gap_start != UINT_MAX) {\n\t\tif (j > gap_start + 1) {\n\t\t\t/* Gap of more than one size class. */\n\t\t\tmalloc_cprintf(write_cb, cbopaque, \"[%u..%u]\\n\",\n\t\t\t    gap_start, j - 1);\n\t\t} else {\n\t\t\t/* Gap of one size class. */\n\t\t\tmalloc_cprintf(write_cb, cbopaque, \"[%u]\\n\", gap_start);\n\t\t}\n\t}\n}\n\nstatic void\nstats_arena_lruns_print(void (*write_cb)(void *, const char *), void *cbopaque,\n    unsigned i)\n{\n\tsize_t page, nlruns, j;\n\tssize_t gap_start;\n\n\tCTL_GET(\"arenas.page\", &page, size_t);\n\n\tmalloc_cprintf(write_cb, cbopaque,\n\t    \"large:   size pages      nmalloc      ndalloc    nrequests\"\n\t    \"      curruns\\n\");\n\tCTL_GET(\"arenas.nlruns\", &nlruns, size_t);\n\tfor (j = 0, gap_start = -1; j < nlruns; j++) {\n\t\tuint64_t nmalloc, ndalloc, nrequests;\n\t\tsize_t run_size, curruns;\n\n\t\tCTL_IJ_GET(\"stats.arenas.0.lruns.0.nmalloc\", &nmalloc,\n\t\t    uint64_t);\n\t\tCTL_IJ_GET(\"stats.arenas.0.lruns.0.ndalloc\", &ndalloc,\n\t\t    uint64_t);\n\t\tCTL_IJ_GET(\"stats.arenas.0.lruns.0.nrequests\", &nrequests,\n\t\t    uint64_t);\n\t\tif (nrequests == 0) {\n\t\t\tif (gap_start == -1)\n\t\t\t\tgap_start = j;\n\t\t} else {\n\t\t\tCTL_J_GET(\"arenas.lrun.0.size\", &run_size, size_t);\n\t\t\tCTL_IJ_GET(\"stats.arenas.0.lruns.0.curruns\", &curruns,\n\t\t\t    size_t);\n\t\t\tif (gap_start != -1) {\n\t\t\t\tmalloc_cprintf(write_cb, cbopaque, \"[%zu]\\n\",\n\t\t\t\t    j - gap_start);\n\t\t\t\tgap_start = -1;\n\t\t\t}\n\t\t\tmalloc_cprintf(write_cb, cbopaque,\n\t\t\t    \"%13zu %5zu %12\"PRIu64\" %12\"PRIu64\" %12\"PRIu64\n\t\t\t    \" %12zu\\n\",\n\t\t\t    run_size, run_size / page, nmalloc, ndalloc,\n\t\t\t    nrequests, curruns);\n\t\t}\n\t}\n\tif (gap_start != -1)\n\t\tmalloc_cprintf(write_cb, cbopaque, \"[%zu]\\n\", j - gap_start);\n}\n\nstatic void\nstats_arena_print(void (*write_cb)(void *, const char *), void *cbopaque,\n    unsigned i, bool bins, bool large)\n{\n\tunsigned nthreads;\n\tconst char *dss;\n\tsize_t page, pactive, pdirty, mapped;\n\tuint64_t npurge, nmadvise, purged;\n\tsize_t small_allocated;\n\tuint64_t small_nmalloc, small_ndalloc, small_nrequests;\n\tsize_t large_allocated;\n\tuint64_t large_nmalloc, large_ndalloc, large_nrequests;\n\n\tCTL_GET(\"arenas.page\", &page, size_t);\n\n\tCTL_I_GET(\"stats.arenas.0.nthreads\", &nthreads, unsigned);\n\tmalloc_cprintf(write_cb, cbopaque,\n\t    \"assigned threads: %u\\n\", nthreads);\n\tCTL_I_GET(\"stats.arenas.0.dss\", &dss, const char *);\n\tmalloc_cprintf(write_cb, cbopaque, \"dss allocation precedence: %s\\n\",\n\t    dss);\n\tCTL_I_GET(\"stats.arenas.0.pactive\", &pactive, size_t);\n\tCTL_I_GET(\"stats.arenas.0.pdirty\", &pdirty, size_t);\n\tCTL_I_GET(\"stats.arenas.0.npurge\", &npurge, uint64_t);\n\tCTL_I_GET(\"stats.arenas.0.nmadvise\", &nmadvise, uint64_t);\n\tCTL_I_GET(\"stats.arenas.0.purged\", &purged, uint64_t);\n\tmalloc_cprintf(write_cb, cbopaque,\n\t    \"dirty pages: %zu:%zu active:dirty, %\"PRIu64\" sweep%s,\"\n\t    \" %\"PRIu64\" madvise%s, %\"PRIu64\" purged\\n\",\n\t    pactive, pdirty, npurge, npurge == 1 ? \"\" : \"s\",\n\t    nmadvise, nmadvise == 1 ? \"\" : \"s\", purged);\n\n\tmalloc_cprintf(write_cb, cbopaque,\n\t    \"            allocated      nmalloc      ndalloc    nrequests\\n\");\n\tCTL_I_GET(\"stats.arenas.0.small.allocated\", &small_allocated, size_t);\n\tCTL_I_GET(\"stats.arenas.0.small.nmalloc\", &small_nmalloc, uint64_t);\n\tCTL_I_GET(\"stats.arenas.0.small.ndalloc\", &small_ndalloc, uint64_t);\n\tCTL_I_GET(\"stats.arenas.0.small.nrequests\", &small_nrequests, uint64_t);\n\tmalloc_cprintf(write_cb, cbopaque,\n\t    \"small:   %12zu %12\"PRIu64\" %12\"PRIu64\" %12\"PRIu64\"\\n\",\n\t    small_allocated, small_nmalloc, small_ndalloc, small_nrequests);\n\tCTL_I_GET(\"stats.arenas.0.large.allocated\", &large_allocated, size_t);\n\tCTL_I_GET(\"stats.arenas.0.large.nmalloc\", &large_nmalloc, uint64_t);\n\tCTL_I_GET(\"stats.arenas.0.large.ndalloc\", &large_ndalloc, uint64_t);\n\tCTL_I_GET(\"stats.arenas.0.large.nrequests\", &large_nrequests, uint64_t);\n\tmalloc_cprintf(write_cb, cbopaque,\n\t    \"large:   %12zu %12\"PRIu64\" %12\"PRIu64\" %12\"PRIu64\"\\n\",\n\t    large_allocated, large_nmalloc, large_ndalloc, large_nrequests);\n\tmalloc_cprintf(write_cb, cbopaque,\n\t    \"total:   %12zu %12\"PRIu64\" %12\"PRIu64\" %12\"PRIu64\"\\n\",\n\t    small_allocated + large_allocated,\n\t    small_nmalloc + large_nmalloc,\n\t    small_ndalloc + large_ndalloc,\n\t    small_nrequests + large_nrequests);\n\tmalloc_cprintf(write_cb, cbopaque, \"active:  %12zu\\n\", pactive * page);\n\tCTL_I_GET(\"stats.arenas.0.mapped\", &mapped, size_t);\n\tmalloc_cprintf(write_cb, cbopaque, \"mapped:  %12zu\\n\", mapped);\n\n\tif (bins)\n\t\tstats_arena_bins_print(write_cb, cbopaque, i);\n\tif (large)\n\t\tstats_arena_lruns_print(write_cb, cbopaque, i);\n}\n\nvoid\nstats_print(void (*write_cb)(void *, const char *), void *cbopaque,\n    const char *opts)\n{\n\tint err;\n\tuint64_t epoch;\n\tsize_t u64sz;\n\tbool general = true;\n\tbool merged = true;\n\tbool unmerged = true;\n\tbool bins = true;\n\tbool large = true;\n\n\t/*\n\t * Refresh stats, in case mallctl() was called by the application.\n\t *\n\t * Check for OOM here, since refreshing the ctl cache can trigger\n\t * allocation.  In practice, none of the subsequent mallctl()-related\n\t * calls in this function will cause OOM if this one succeeds.\n\t * */\n\tepoch = 1;\n\tu64sz = sizeof(uint64_t);\n\terr = je_mallctl(\"epoch\", &epoch, &u64sz, &epoch, sizeof(uint64_t));\n\tif (err != 0) {\n\t\tif (err == EAGAIN) {\n\t\t\tmalloc_write(\"<jemalloc>: Memory allocation failure in \"\n\t\t\t    \"mallctl(\\\"epoch\\\", ...)\\n\");\n\t\t\treturn;\n\t\t}\n\t\tmalloc_write(\"<jemalloc>: Failure in mallctl(\\\"epoch\\\", \"\n\t\t    \"...)\\n\");\n\t\tabort();\n\t}\n\n\tif (opts != NULL) {\n\t\tunsigned i;\n\n\t\tfor (i = 0; opts[i] != '\\0'; i++) {\n\t\t\tswitch (opts[i]) {\n\t\t\tcase 'g':\n\t\t\t\tgeneral = false;\n\t\t\t\tbreak;\n\t\t\tcase 'm':\n\t\t\t\tmerged = false;\n\t\t\t\tbreak;\n\t\t\tcase 'a':\n\t\t\t\tunmerged = false;\n\t\t\t\tbreak;\n\t\t\tcase 'b':\n\t\t\t\tbins = false;\n\t\t\t\tbreak;\n\t\t\tcase 'l':\n\t\t\t\tlarge = false;\n\t\t\t\tbreak;\n\t\t\tdefault:;\n\t\t\t}\n\t\t}\n\t}\n\n\tmalloc_cprintf(write_cb, cbopaque,\n\t    \"___ Begin jemalloc statistics ___\\n\");\n\tif (general) {\n\t\tint err;\n\t\tconst char *cpv;\n\t\tbool bv;\n\t\tunsigned uv;\n\t\tssize_t ssv;\n\t\tsize_t sv, bsz, ssz, sssz, cpsz;\n\n\t\tbsz = sizeof(bool);\n\t\tssz = sizeof(size_t);\n\t\tsssz = sizeof(ssize_t);\n\t\tcpsz = sizeof(const char *);\n\n\t\tCTL_GET(\"version\", &cpv, const char *);\n\t\tmalloc_cprintf(write_cb, cbopaque, \"Version: %s\\n\", cpv);\n\t\tCTL_GET(\"config.debug\", &bv, bool);\n\t\tmalloc_cprintf(write_cb, cbopaque, \"Assertions %s\\n\",\n\t\t    bv ? \"enabled\" : \"disabled\");\n\n#define OPT_WRITE_BOOL(n)\t\t\t\t\t\t\\\n\t\tif ((err = je_mallctl(\"opt.\"#n, &bv, &bsz, NULL, 0))\t\\\n\t\t    == 0) {\t\t\t\t\t\t\\\n\t\t\tmalloc_cprintf(write_cb, cbopaque,\t\t\\\n\t\t\t    \"  opt.\"#n\": %s\\n\", bv ? \"true\" : \"false\");\t\\\n\t\t}\n#define OPT_WRITE_SIZE_T(n)\t\t\t\t\t\t\\\n\t\tif ((err = je_mallctl(\"opt.\"#n, &sv, &ssz, NULL, 0))\t\\\n\t\t    == 0) {\t\t\t\t\t\t\\\n\t\t\tmalloc_cprintf(write_cb, cbopaque,\t\t\\\n\t\t\t\"  opt.\"#n\": %zu\\n\", sv);\t\t\t\\\n\t\t}\n#define OPT_WRITE_SSIZE_T(n)\t\t\t\t\t\t\\\n\t\tif ((err = je_mallctl(\"opt.\"#n, &ssv, &sssz, NULL, 0))\t\\\n\t\t    == 0) {\t\t\t\t\t\t\\\n\t\t\tmalloc_cprintf(write_cb, cbopaque,\t\t\\\n\t\t\t    \"  opt.\"#n\": %zd\\n\", ssv);\t\t\t\\\n\t\t}\n#define OPT_WRITE_CHAR_P(n)\t\t\t\t\t\t\\\n\t\tif ((err = je_mallctl(\"opt.\"#n, &cpv, &cpsz, NULL, 0))\t\\\n\t\t    == 0) {\t\t\t\t\t\t\\\n\t\t\tmalloc_cprintf(write_cb, cbopaque,\t\t\\\n\t\t\t    \"  opt.\"#n\": \\\"%s\\\"\\n\", cpv);\t\t\\\n\t\t}\n\n\t\tmalloc_cprintf(write_cb, cbopaque,\n\t\t    \"Run-time option settings:\\n\");\n\t\tOPT_WRITE_BOOL(abort)\n\t\tOPT_WRITE_SIZE_T(lg_chunk)\n\t\tOPT_WRITE_CHAR_P(dss)\n\t\tOPT_WRITE_SIZE_T(narenas)\n\t\tOPT_WRITE_SSIZE_T(lg_dirty_mult)\n\t\tOPT_WRITE_BOOL(stats_print)\n\t\tOPT_WRITE_BOOL(junk)\n\t\tOPT_WRITE_SIZE_T(quarantine)\n\t\tOPT_WRITE_BOOL(redzone)\n\t\tOPT_WRITE_BOOL(zero)\n\t\tOPT_WRITE_BOOL(utrace)\n\t\tOPT_WRITE_BOOL(valgrind)\n\t\tOPT_WRITE_BOOL(xmalloc)\n\t\tOPT_WRITE_BOOL(tcache)\n\t\tOPT_WRITE_SSIZE_T(lg_tcache_max)\n\t\tOPT_WRITE_BOOL(prof)\n\t\tOPT_WRITE_CHAR_P(prof_prefix)\n\t\tOPT_WRITE_BOOL(prof_active)\n\t\tOPT_WRITE_SSIZE_T(lg_prof_sample)\n\t\tOPT_WRITE_BOOL(prof_accum)\n\t\tOPT_WRITE_SSIZE_T(lg_prof_interval)\n\t\tOPT_WRITE_BOOL(prof_gdump)\n\t\tOPT_WRITE_BOOL(prof_final)\n\t\tOPT_WRITE_BOOL(prof_leak)\n\n#undef OPT_WRITE_BOOL\n#undef OPT_WRITE_SIZE_T\n#undef OPT_WRITE_SSIZE_T\n#undef OPT_WRITE_CHAR_P\n\n\t\tmalloc_cprintf(write_cb, cbopaque, \"CPUs: %u\\n\", ncpus);\n\n\t\tCTL_GET(\"arenas.narenas\", &uv, unsigned);\n\t\tmalloc_cprintf(write_cb, cbopaque, \"Arenas: %u\\n\", uv);\n\n\t\tmalloc_cprintf(write_cb, cbopaque, \"Pointer size: %zu\\n\",\n\t\t    sizeof(void *));\n\n\t\tCTL_GET(\"arenas.quantum\", &sv, size_t);\n\t\tmalloc_cprintf(write_cb, cbopaque, \"Quantum size: %zu\\n\", sv);\n\n\t\tCTL_GET(\"arenas.page\", &sv, size_t);\n\t\tmalloc_cprintf(write_cb, cbopaque, \"Page size: %zu\\n\", sv);\n\n\t\tCTL_GET(\"opt.lg_dirty_mult\", &ssv, ssize_t);\n\t\tif (ssv >= 0) {\n\t\t\tmalloc_cprintf(write_cb, cbopaque,\n\t\t\t    \"Min active:dirty page ratio per arena: %u:1\\n\",\n\t\t\t    (1U << ssv));\n\t\t} else {\n\t\t\tmalloc_cprintf(write_cb, cbopaque,\n\t\t\t    \"Min active:dirty page ratio per arena: N/A\\n\");\n\t\t}\n\t\tif ((err = je_mallctl(\"arenas.tcache_max\", &sv, &ssz, NULL, 0))\n\t\t    == 0) {\n\t\t\tmalloc_cprintf(write_cb, cbopaque,\n\t\t\t    \"Maximum thread-cached size class: %zu\\n\", sv);\n\t\t}\n\t\tif ((err = je_mallctl(\"opt.prof\", &bv, &bsz, NULL, 0)) == 0 &&\n\t\t    bv) {\n\t\t\tCTL_GET(\"opt.lg_prof_sample\", &sv, size_t);\n\t\t\tmalloc_cprintf(write_cb, cbopaque,\n\t\t\t    \"Average profile sample interval: %\"PRIu64\n\t\t\t    \" (2^%zu)\\n\", (((uint64_t)1U) << sv), sv);\n\n\t\t\tCTL_GET(\"opt.lg_prof_interval\", &ssv, ssize_t);\n\t\t\tif (ssv >= 0) {\n\t\t\t\tmalloc_cprintf(write_cb, cbopaque,\n\t\t\t\t    \"Average profile dump interval: %\"PRIu64\n\t\t\t\t    \" (2^%zd)\\n\",\n\t\t\t\t    (((uint64_t)1U) << ssv), ssv);\n\t\t\t} else {\n\t\t\t\tmalloc_cprintf(write_cb, cbopaque,\n\t\t\t\t    \"Average profile dump interval: N/A\\n\");\n\t\t\t}\n\t\t}\n\t\tCTL_GET(\"opt.lg_chunk\", &sv, size_t);\n\t\tmalloc_cprintf(write_cb, cbopaque, \"Chunk size: %zu (2^%zu)\\n\",\n\t\t    (ZU(1) << sv), sv);\n\t}\n\n\tif (config_stats) {\n\t\tsize_t *cactive;\n\t\tsize_t allocated, active, mapped;\n\t\tsize_t chunks_current, chunks_high;\n\t\tuint64_t chunks_total;\n\t\tsize_t huge_allocated;\n\t\tuint64_t huge_nmalloc, huge_ndalloc;\n\n\t\tCTL_GET(\"stats.cactive\", &cactive, size_t *);\n\t\tCTL_GET(\"stats.allocated\", &allocated, size_t);\n\t\tCTL_GET(\"stats.active\", &active, size_t);\n\t\tCTL_GET(\"stats.mapped\", &mapped, size_t);\n\t\tmalloc_cprintf(write_cb, cbopaque,\n\t\t    \"Allocated: %zu, active: %zu, mapped: %zu\\n\",\n\t\t    allocated, active, mapped);\n\t\tmalloc_cprintf(write_cb, cbopaque,\n\t\t    \"Current active ceiling: %zu\\n\", atomic_read_z(cactive));\n\n\t\t/* Print chunk stats. */\n\t\tCTL_GET(\"stats.chunks.total\", &chunks_total, uint64_t);\n\t\tCTL_GET(\"stats.chunks.high\", &chunks_high, size_t);\n\t\tCTL_GET(\"stats.chunks.current\", &chunks_current, size_t);\n\t\tmalloc_cprintf(write_cb, cbopaque, \"chunks: nchunks   \"\n\t\t    \"highchunks    curchunks\\n\");\n\t\tmalloc_cprintf(write_cb, cbopaque,\n\t\t    \"  %13\"PRIu64\" %12zu %12zu\\n\",\n\t\t    chunks_total, chunks_high, chunks_current);\n\n\t\t/* Print huge stats. */\n\t\tCTL_GET(\"stats.huge.nmalloc\", &huge_nmalloc, uint64_t);\n\t\tCTL_GET(\"stats.huge.ndalloc\", &huge_ndalloc, uint64_t);\n\t\tCTL_GET(\"stats.huge.allocated\", &huge_allocated, size_t);\n\t\tmalloc_cprintf(write_cb, cbopaque,\n\t\t    \"huge: nmalloc      ndalloc    allocated\\n\");\n\t\tmalloc_cprintf(write_cb, cbopaque,\n\t\t    \" %12\"PRIu64\" %12\"PRIu64\" %12zu\\n\",\n\t\t    huge_nmalloc, huge_ndalloc, huge_allocated);\n\n\t\tif (merged) {\n\t\t\tunsigned narenas;\n\n\t\t\tCTL_GET(\"arenas.narenas\", &narenas, unsigned);\n\t\t\t{\n\t\t\t\tVARIABLE_ARRAY(bool, initialized, narenas);\n\t\t\t\tsize_t isz;\n\t\t\t\tunsigned i, ninitialized;\n\n\t\t\t\tisz = sizeof(bool) * narenas;\n\t\t\t\txmallctl(\"arenas.initialized\", initialized,\n\t\t\t\t    &isz, NULL, 0);\n\t\t\t\tfor (i = ninitialized = 0; i < narenas; i++) {\n\t\t\t\t\tif (initialized[i])\n\t\t\t\t\t\tninitialized++;\n\t\t\t\t}\n\n\t\t\t\tif (ninitialized > 1 || unmerged == false) {\n\t\t\t\t\t/* Print merged arena stats. */\n\t\t\t\t\tmalloc_cprintf(write_cb, cbopaque,\n\t\t\t\t\t    \"\\nMerged arenas stats:\\n\");\n\t\t\t\t\tstats_arena_print(write_cb, cbopaque,\n\t\t\t\t\t    narenas, bins, large);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (unmerged) {\n\t\t\tunsigned narenas;\n\n\t\t\t/* Print stats for each arena. */\n\n\t\t\tCTL_GET(\"arenas.narenas\", &narenas, unsigned);\n\t\t\t{\n\t\t\t\tVARIABLE_ARRAY(bool, initialized, narenas);\n\t\t\t\tsize_t isz;\n\t\t\t\tunsigned i;\n\n\t\t\t\tisz = sizeof(bool) * narenas;\n\t\t\t\txmallctl(\"arenas.initialized\", initialized,\n\t\t\t\t    &isz, NULL, 0);\n\n\t\t\t\tfor (i = 0; i < narenas; i++) {\n\t\t\t\t\tif (initialized[i]) {\n\t\t\t\t\t\tmalloc_cprintf(write_cb,\n\t\t\t\t\t\t    cbopaque,\n\t\t\t\t\t\t    \"\\narenas[%u]:\\n\", i);\n\t\t\t\t\t\tstats_arena_print(write_cb,\n\t\t\t\t\t\t    cbopaque, i, bins, large);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tmalloc_cprintf(write_cb, cbopaque, \"--- End jemalloc statistics ---\\n\");\n}\n"
  },
  {
    "path": "deps/jemalloc/src/tcache.c",
    "content": "#define\tJEMALLOC_TCACHE_C_\n#include \"jemalloc/internal/jemalloc_internal.h\"\n\n/******************************************************************************/\n/* Data. */\n\nmalloc_tsd_data(, tcache, tcache_t *, NULL)\nmalloc_tsd_data(, tcache_enabled, tcache_enabled_t, tcache_enabled_default)\n\nbool\topt_tcache = true;\nssize_t\topt_lg_tcache_max = LG_TCACHE_MAXCLASS_DEFAULT;\n\ntcache_bin_info_t\t*tcache_bin_info;\nstatic unsigned\t\tstack_nelms; /* Total stack elms per tcache. */\n\nsize_t\t\t\tnhbins;\nsize_t\t\t\ttcache_maxclass;\n\n/******************************************************************************/\n\nsize_t\ttcache_salloc(const void *ptr)\n{\n\n\treturn (arena_salloc(ptr, false));\n}\n\nvoid\ntcache_event_hard(tcache_t *tcache)\n{\n\tsize_t binind = tcache->next_gc_bin;\n\ttcache_bin_t *tbin = &tcache->tbins[binind];\n\ttcache_bin_info_t *tbin_info = &tcache_bin_info[binind];\n\n\tif (tbin->low_water > 0) {\n\t\t/*\n\t\t * Flush (ceiling) 3/4 of the objects below the low water mark.\n\t\t */\n\t\tif (binind < NBINS) {\n\t\t\ttcache_bin_flush_small(tbin, binind, tbin->ncached -\n\t\t\t    tbin->low_water + (tbin->low_water >> 2), tcache);\n\t\t} else {\n\t\t\ttcache_bin_flush_large(tbin, binind, tbin->ncached -\n\t\t\t    tbin->low_water + (tbin->low_water >> 2), tcache);\n\t\t}\n\t\t/*\n\t\t * Reduce fill count by 2X.  Limit lg_fill_div such that the\n\t\t * fill count is always at least 1.\n\t\t */\n\t\tif ((tbin_info->ncached_max >> (tbin->lg_fill_div+1)) >= 1)\n\t\t\ttbin->lg_fill_div++;\n\t} else if (tbin->low_water < 0) {\n\t\t/*\n\t\t * Increase fill count by 2X.  Make sure lg_fill_div stays\n\t\t * greater than 0.\n\t\t */\n\t\tif (tbin->lg_fill_div > 1)\n\t\t\ttbin->lg_fill_div--;\n\t}\n\ttbin->low_water = tbin->ncached;\n\n\ttcache->next_gc_bin++;\n\tif (tcache->next_gc_bin == nhbins)\n\t\ttcache->next_gc_bin = 0;\n\ttcache->ev_cnt = 0;\n}\n\nvoid *\ntcache_alloc_small_hard(tcache_t *tcache, tcache_bin_t *tbin, size_t binind)\n{\n\tvoid *ret;\n\n\tarena_tcache_fill_small(tcache->arena, tbin, binind,\n\t    config_prof ? tcache->prof_accumbytes : 0);\n\tif (config_prof)\n\t\ttcache->prof_accumbytes = 0;\n\tret = tcache_alloc_easy(tbin);\n\n\treturn (ret);\n}\n\nvoid\ntcache_bin_flush_small(tcache_bin_t *tbin, size_t binind, unsigned rem,\n    tcache_t *tcache)\n{\n\tvoid *ptr;\n\tunsigned i, nflush, ndeferred;\n\tbool merged_stats = false;\n\n\tassert(binind < NBINS);\n\tassert(rem <= tbin->ncached);\n\n\tfor (nflush = tbin->ncached - rem; nflush > 0; nflush = ndeferred) {\n\t\t/* Lock the arena bin associated with the first object. */\n\t\tarena_chunk_t *chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(\n\t\t    tbin->avail[0]);\n\t\tarena_t *arena = chunk->arena;\n\t\tarena_bin_t *bin = &arena->bins[binind];\n\n\t\tif (config_prof && arena == tcache->arena) {\n\t\t\tmalloc_mutex_lock(&arena->lock);\n\t\t\tarena_prof_accum(arena, tcache->prof_accumbytes);\n\t\t\tmalloc_mutex_unlock(&arena->lock);\n\t\t\ttcache->prof_accumbytes = 0;\n\t\t}\n\n\t\tmalloc_mutex_lock(&bin->lock);\n\t\tif (config_stats && arena == tcache->arena) {\n\t\t\tassert(merged_stats == false);\n\t\t\tmerged_stats = true;\n\t\t\tbin->stats.nflushes++;\n\t\t\tbin->stats.nrequests += tbin->tstats.nrequests;\n\t\t\ttbin->tstats.nrequests = 0;\n\t\t}\n\t\tndeferred = 0;\n\t\tfor (i = 0; i < nflush; i++) {\n\t\t\tptr = tbin->avail[i];\n\t\t\tassert(ptr != NULL);\n\t\t\tchunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);\n\t\t\tif (chunk->arena == arena) {\n\t\t\t\tsize_t pageind = ((uintptr_t)ptr -\n\t\t\t\t    (uintptr_t)chunk) >> LG_PAGE;\n\t\t\t\tarena_chunk_map_t *mapelm =\n\t\t\t\t    arena_mapp_get(chunk, pageind);\n\t\t\t\tif (config_fill && opt_junk) {\n\t\t\t\t\tarena_alloc_junk_small(ptr,\n\t\t\t\t\t    &arena_bin_info[binind], true);\n\t\t\t\t}\n\t\t\t\tarena_dalloc_bin_locked(arena, chunk, ptr,\n\t\t\t\t    mapelm);\n\t\t\t} else {\n\t\t\t\t/*\n\t\t\t\t * This object was allocated via a different\n\t\t\t\t * arena bin than the one that is currently\n\t\t\t\t * locked.  Stash the object, so that it can be\n\t\t\t\t * handled in a future pass.\n\t\t\t\t */\n\t\t\t\ttbin->avail[ndeferred] = ptr;\n\t\t\t\tndeferred++;\n\t\t\t}\n\t\t}\n\t\tmalloc_mutex_unlock(&bin->lock);\n\t}\n\tif (config_stats && merged_stats == false) {\n\t\t/*\n\t\t * The flush loop didn't happen to flush to this thread's\n\t\t * arena, so the stats didn't get merged.  Manually do so now.\n\t\t */\n\t\tarena_bin_t *bin = &tcache->arena->bins[binind];\n\t\tmalloc_mutex_lock(&bin->lock);\n\t\tbin->stats.nflushes++;\n\t\tbin->stats.nrequests += tbin->tstats.nrequests;\n\t\ttbin->tstats.nrequests = 0;\n\t\tmalloc_mutex_unlock(&bin->lock);\n\t}\n\n\tmemmove(tbin->avail, &tbin->avail[tbin->ncached - rem],\n\t    rem * sizeof(void *));\n\ttbin->ncached = rem;\n\tif ((int)tbin->ncached < tbin->low_water)\n\t\ttbin->low_water = tbin->ncached;\n}\n\nvoid\ntcache_bin_flush_large(tcache_bin_t *tbin, size_t binind, unsigned rem,\n    tcache_t *tcache)\n{\n\tvoid *ptr;\n\tunsigned i, nflush, ndeferred;\n\tbool merged_stats = false;\n\n\tassert(binind < nhbins);\n\tassert(rem <= tbin->ncached);\n\n\tfor (nflush = tbin->ncached - rem; nflush > 0; nflush = ndeferred) {\n\t\t/* Lock the arena associated with the first object. */\n\t\tarena_chunk_t *chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(\n\t\t    tbin->avail[0]);\n\t\tarena_t *arena = chunk->arena;\n\n\t\tmalloc_mutex_lock(&arena->lock);\n\t\tif ((config_prof || config_stats) && arena == tcache->arena) {\n\t\t\tif (config_prof) {\n\t\t\t\tarena_prof_accum(arena,\n\t\t\t\t    tcache->prof_accumbytes);\n\t\t\t\ttcache->prof_accumbytes = 0;\n\t\t\t}\n\t\t\tif (config_stats) {\n\t\t\t\tmerged_stats = true;\n\t\t\t\tarena->stats.nrequests_large +=\n\t\t\t\t    tbin->tstats.nrequests;\n\t\t\t\tarena->stats.lstats[binind - NBINS].nrequests +=\n\t\t\t\t    tbin->tstats.nrequests;\n\t\t\t\ttbin->tstats.nrequests = 0;\n\t\t\t}\n\t\t}\n\t\tndeferred = 0;\n\t\tfor (i = 0; i < nflush; i++) {\n\t\t\tptr = tbin->avail[i];\n\t\t\tassert(ptr != NULL);\n\t\t\tchunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);\n\t\t\tif (chunk->arena == arena)\n\t\t\t\tarena_dalloc_large_locked(arena, chunk, ptr);\n\t\t\telse {\n\t\t\t\t/*\n\t\t\t\t * This object was allocated via a different\n\t\t\t\t * arena than the one that is currently locked.\n\t\t\t\t * Stash the object, so that it can be handled\n\t\t\t\t * in a future pass.\n\t\t\t\t */\n\t\t\t\ttbin->avail[ndeferred] = ptr;\n\t\t\t\tndeferred++;\n\t\t\t}\n\t\t}\n\t\tmalloc_mutex_unlock(&arena->lock);\n\t}\n\tif (config_stats && merged_stats == false) {\n\t\t/*\n\t\t * The flush loop didn't happen to flush to this thread's\n\t\t * arena, so the stats didn't get merged.  Manually do so now.\n\t\t */\n\t\tarena_t *arena = tcache->arena;\n\t\tmalloc_mutex_lock(&arena->lock);\n\t\tarena->stats.nrequests_large += tbin->tstats.nrequests;\n\t\tarena->stats.lstats[binind - NBINS].nrequests +=\n\t\t    tbin->tstats.nrequests;\n\t\ttbin->tstats.nrequests = 0;\n\t\tmalloc_mutex_unlock(&arena->lock);\n\t}\n\n\tmemmove(tbin->avail, &tbin->avail[tbin->ncached - rem],\n\t    rem * sizeof(void *));\n\ttbin->ncached = rem;\n\tif ((int)tbin->ncached < tbin->low_water)\n\t\ttbin->low_water = tbin->ncached;\n}\n\nvoid\ntcache_arena_associate(tcache_t *tcache, arena_t *arena)\n{\n\n\tif (config_stats) {\n\t\t/* Link into list of extant tcaches. */\n\t\tmalloc_mutex_lock(&arena->lock);\n\t\tql_elm_new(tcache, link);\n\t\tql_tail_insert(&arena->tcache_ql, tcache, link);\n\t\tmalloc_mutex_unlock(&arena->lock);\n\t}\n\ttcache->arena = arena;\n}\n\nvoid\ntcache_arena_dissociate(tcache_t *tcache)\n{\n\n\tif (config_stats) {\n\t\t/* Unlink from list of extant tcaches. */\n\t\tmalloc_mutex_lock(&tcache->arena->lock);\n\t\tql_remove(&tcache->arena->tcache_ql, tcache, link);\n\t\tmalloc_mutex_unlock(&tcache->arena->lock);\n\t\ttcache_stats_merge(tcache, tcache->arena);\n\t}\n}\n\ntcache_t *\ntcache_create(arena_t *arena)\n{\n\ttcache_t *tcache;\n\tsize_t size, stack_offset;\n\tunsigned i;\n\n\tsize = offsetof(tcache_t, tbins) + (sizeof(tcache_bin_t) * nhbins);\n\t/* Naturally align the pointer stacks. */\n\tsize = PTR_CEILING(size);\n\tstack_offset = size;\n\tsize += stack_nelms * sizeof(void *);\n\t/*\n\t * Round up to the nearest multiple of the cacheline size, in order to\n\t * avoid the possibility of false cacheline sharing.\n\t *\n\t * That this works relies on the same logic as in ipalloc(), but we\n\t * cannot directly call ipalloc() here due to tcache bootstrapping\n\t * issues.\n\t */\n\tsize = (size + CACHELINE_MASK) & (-CACHELINE);\n\n\tif (size <= SMALL_MAXCLASS)\n\t\ttcache = (tcache_t *)arena_malloc_small(arena, size, true);\n\telse if (size <= tcache_maxclass)\n\t\ttcache = (tcache_t *)arena_malloc_large(arena, size, true);\n\telse\n\t\ttcache = (tcache_t *)icallocx(size, false, arena);\n\n\tif (tcache == NULL)\n\t\treturn (NULL);\n\n\ttcache_arena_associate(tcache, arena);\n\n\tassert((TCACHE_NSLOTS_SMALL_MAX & 1U) == 0);\n\tfor (i = 0; i < nhbins; i++) {\n\t\ttcache->tbins[i].lg_fill_div = 1;\n\t\ttcache->tbins[i].avail = (void **)((uintptr_t)tcache +\n\t\t    (uintptr_t)stack_offset);\n\t\tstack_offset += tcache_bin_info[i].ncached_max * sizeof(void *);\n\t}\n\n\ttcache_tsd_set(&tcache);\n\n\treturn (tcache);\n}\n\nvoid\ntcache_destroy(tcache_t *tcache)\n{\n\tunsigned i;\n\tsize_t tcache_size;\n\n\ttcache_arena_dissociate(tcache);\n\n\tfor (i = 0; i < NBINS; i++) {\n\t\ttcache_bin_t *tbin = &tcache->tbins[i];\n\t\ttcache_bin_flush_small(tbin, i, 0, tcache);\n\n\t\tif (config_stats && tbin->tstats.nrequests != 0) {\n\t\t\tarena_t *arena = tcache->arena;\n\t\t\tarena_bin_t *bin = &arena->bins[i];\n\t\t\tmalloc_mutex_lock(&bin->lock);\n\t\t\tbin->stats.nrequests += tbin->tstats.nrequests;\n\t\t\tmalloc_mutex_unlock(&bin->lock);\n\t\t}\n\t}\n\n\tfor (; i < nhbins; i++) {\n\t\ttcache_bin_t *tbin = &tcache->tbins[i];\n\t\ttcache_bin_flush_large(tbin, i, 0, tcache);\n\n\t\tif (config_stats && tbin->tstats.nrequests != 0) {\n\t\t\tarena_t *arena = tcache->arena;\n\t\t\tmalloc_mutex_lock(&arena->lock);\n\t\t\tarena->stats.nrequests_large += tbin->tstats.nrequests;\n\t\t\tarena->stats.lstats[i - NBINS].nrequests +=\n\t\t\t    tbin->tstats.nrequests;\n\t\t\tmalloc_mutex_unlock(&arena->lock);\n\t\t}\n\t}\n\n\tif (config_prof && tcache->prof_accumbytes > 0) {\n\t\tmalloc_mutex_lock(&tcache->arena->lock);\n\t\tarena_prof_accum(tcache->arena, tcache->prof_accumbytes);\n\t\tmalloc_mutex_unlock(&tcache->arena->lock);\n\t}\n\n\ttcache_size = arena_salloc(tcache, false);\n\tif (tcache_size <= SMALL_MAXCLASS) {\n\t\tarena_chunk_t *chunk = CHUNK_ADDR2BASE(tcache);\n\t\tarena_t *arena = chunk->arena;\n\t\tsize_t pageind = ((uintptr_t)tcache - (uintptr_t)chunk) >>\n\t\t    LG_PAGE;\n\t\tarena_chunk_map_t *mapelm = arena_mapp_get(chunk, pageind);\n\n\t\tarena_dalloc_bin(arena, chunk, tcache, pageind, mapelm);\n\t} else if (tcache_size <= tcache_maxclass) {\n\t\tarena_chunk_t *chunk = CHUNK_ADDR2BASE(tcache);\n\t\tarena_t *arena = chunk->arena;\n\n\t\tarena_dalloc_large(arena, chunk, tcache);\n\t} else\n\t\tidallocx(tcache, false);\n}\n\nvoid\ntcache_thread_cleanup(void *arg)\n{\n\ttcache_t *tcache = *(tcache_t **)arg;\n\n\tif (tcache == TCACHE_STATE_DISABLED) {\n\t\t/* Do nothing. */\n\t} else if (tcache == TCACHE_STATE_REINCARNATED) {\n\t\t/*\n\t\t * Another destructor called an allocator function after this\n\t\t * destructor was called.  Reset tcache to\n\t\t * TCACHE_STATE_PURGATORY in order to receive another callback.\n\t\t */\n\t\ttcache = TCACHE_STATE_PURGATORY;\n\t\ttcache_tsd_set(&tcache);\n\t} else if (tcache == TCACHE_STATE_PURGATORY) {\n\t\t/*\n\t\t * The previous time this destructor was called, we set the key\n\t\t * to TCACHE_STATE_PURGATORY so that other destructors wouldn't\n\t\t * cause re-creation of the tcache.  This time, do nothing, so\n\t\t * that the destructor will not be called again.\n\t\t */\n\t} else if (tcache != NULL) {\n\t\tassert(tcache != TCACHE_STATE_PURGATORY);\n\t\ttcache_destroy(tcache);\n\t\ttcache = TCACHE_STATE_PURGATORY;\n\t\ttcache_tsd_set(&tcache);\n\t}\n}\n\nvoid\ntcache_stats_merge(tcache_t *tcache, arena_t *arena)\n{\n\tunsigned i;\n\n\t/* Merge and reset tcache stats. */\n\tfor (i = 0; i < NBINS; i++) {\n\t\tarena_bin_t *bin = &arena->bins[i];\n\t\ttcache_bin_t *tbin = &tcache->tbins[i];\n\t\tmalloc_mutex_lock(&bin->lock);\n\t\tbin->stats.nrequests += tbin->tstats.nrequests;\n\t\tmalloc_mutex_unlock(&bin->lock);\n\t\ttbin->tstats.nrequests = 0;\n\t}\n\n\tfor (; i < nhbins; i++) {\n\t\tmalloc_large_stats_t *lstats = &arena->stats.lstats[i - NBINS];\n\t\ttcache_bin_t *tbin = &tcache->tbins[i];\n\t\tarena->stats.nrequests_large += tbin->tstats.nrequests;\n\t\tlstats->nrequests += tbin->tstats.nrequests;\n\t\ttbin->tstats.nrequests = 0;\n\t}\n}\n\nbool\ntcache_boot0(void)\n{\n\tunsigned i;\n\n\t/*\n\t * If necessary, clamp opt_lg_tcache_max, now that arena_maxclass is\n\t * known.\n\t */\n\tif (opt_lg_tcache_max < 0 || (1U << opt_lg_tcache_max) < SMALL_MAXCLASS)\n\t\ttcache_maxclass = SMALL_MAXCLASS;\n\telse if ((1U << opt_lg_tcache_max) > arena_maxclass)\n\t\ttcache_maxclass = arena_maxclass;\n\telse\n\t\ttcache_maxclass = (1U << opt_lg_tcache_max);\n\n\tnhbins = NBINS + (tcache_maxclass >> LG_PAGE);\n\n\t/* Initialize tcache_bin_info. */\n\ttcache_bin_info = (tcache_bin_info_t *)base_alloc(nhbins *\n\t    sizeof(tcache_bin_info_t));\n\tif (tcache_bin_info == NULL)\n\t\treturn (true);\n\tstack_nelms = 0;\n\tfor (i = 0; i < NBINS; i++) {\n\t\tif ((arena_bin_info[i].nregs << 1) <= TCACHE_NSLOTS_SMALL_MAX) {\n\t\t\ttcache_bin_info[i].ncached_max =\n\t\t\t    (arena_bin_info[i].nregs << 1);\n\t\t} else {\n\t\t\ttcache_bin_info[i].ncached_max =\n\t\t\t    TCACHE_NSLOTS_SMALL_MAX;\n\t\t}\n\t\tstack_nelms += tcache_bin_info[i].ncached_max;\n\t}\n\tfor (; i < nhbins; i++) {\n\t\ttcache_bin_info[i].ncached_max = TCACHE_NSLOTS_LARGE;\n\t\tstack_nelms += tcache_bin_info[i].ncached_max;\n\t}\n\n\treturn (false);\n}\n\nbool\ntcache_boot1(void)\n{\n\n\tif (tcache_tsd_boot() || tcache_enabled_tsd_boot())\n\t\treturn (true);\n\n\treturn (false);\n}\n"
  },
  {
    "path": "deps/jemalloc/src/tsd.c",
    "content": "#define\tJEMALLOC_TSD_C_\n#include \"jemalloc/internal/jemalloc_internal.h\"\n\n/******************************************************************************/\n/* Data. */\n\nstatic unsigned ncleanups;\nstatic malloc_tsd_cleanup_t cleanups[MALLOC_TSD_CLEANUPS_MAX];\n\n/******************************************************************************/\n\nvoid *\nmalloc_tsd_malloc(size_t size)\n{\n\n\t/* Avoid choose_arena() in order to dodge bootstrapping issues. */\n\treturn (arena_malloc(arenas[0], size, false, false));\n}\n\nvoid\nmalloc_tsd_dalloc(void *wrapper)\n{\n\n\tidalloc(wrapper);\n}\n\nvoid\nmalloc_tsd_no_cleanup(void *arg)\n{\n\n\tnot_reached();\n}\n\n#if defined(JEMALLOC_MALLOC_THREAD_CLEANUP) || defined(_WIN32)\n#ifndef _WIN32\nJEMALLOC_EXPORT\n#endif\nvoid\n_malloc_thread_cleanup(void)\n{\n\tbool pending[MALLOC_TSD_CLEANUPS_MAX], again;\n\tunsigned i;\n\n\tfor (i = 0; i < ncleanups; i++)\n\t\tpending[i] = true;\n\n\tdo {\n\t\tagain = false;\n\t\tfor (i = 0; i < ncleanups; i++) {\n\t\t\tif (pending[i]) {\n\t\t\t\tpending[i] = cleanups[i]();\n\t\t\t\tif (pending[i])\n\t\t\t\t\tagain = true;\n\t\t\t}\n\t\t}\n\t} while (again);\n}\n#endif\n\nvoid\nmalloc_tsd_cleanup_register(bool (*f)(void))\n{\n\n\tassert(ncleanups < MALLOC_TSD_CLEANUPS_MAX);\n\tcleanups[ncleanups] = f;\n\tncleanups++;\n}\n\nvoid\nmalloc_tsd_boot(void)\n{\n\n\tncleanups = 0;\n}\n\n#ifdef _WIN32\nstatic BOOL WINAPI\n_tls_callback(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)\n{\n\n\tswitch (fdwReason) {\n#ifdef JEMALLOC_LAZY_LOCK\n\tcase DLL_THREAD_ATTACH:\n\t\tisthreaded = true;\n\t\tbreak;\n#endif\n\tcase DLL_THREAD_DETACH:\n\t\t_malloc_thread_cleanup();\n\t\tbreak;\n\tdefault:\n\t\tbreak;\n\t}\n\treturn (true);\n}\n\n#ifdef _MSC_VER\n#  ifdef _M_IX86\n#    pragma comment(linker, \"/INCLUDE:__tls_used\")\n#  else\n#    pragma comment(linker, \"/INCLUDE:_tls_used\")\n#  endif\n#  pragma section(\".CRT$XLY\",long,read)\n#endif\nJEMALLOC_SECTION(\".CRT$XLY\") JEMALLOC_ATTR(used)\nstatic const BOOL\t(WINAPI *tls_callback)(HINSTANCE hinstDLL,\n    DWORD fdwReason, LPVOID lpvReserved) = _tls_callback;\n#endif\n"
  },
  {
    "path": "deps/jemalloc/src/util.c",
    "content": "#define\tassert(e) do {\t\t\t\t\t\t\t\\\n\tif (config_debug && !(e)) {\t\t\t\t\t\\\n\t\tmalloc_write(\"<jemalloc>: Failed assertion\\n\");\t\t\\\n\t\tabort();\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n} while (0)\n\n#define\tnot_reached() do {\t\t\t\t\t\t\\\n\tif (config_debug) {\t\t\t\t\t\t\\\n\t\tmalloc_write(\"<jemalloc>: Unreachable code reached\\n\");\t\\\n\t\tabort();\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n} while (0)\n\n#define\tnot_implemented() do {\t\t\t\t\t\t\\\n\tif (config_debug) {\t\t\t\t\t\t\\\n\t\tmalloc_write(\"<jemalloc>: Not implemented\\n\");\t\t\\\n\t\tabort();\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n} while (0)\n\n#define\tJEMALLOC_UTIL_C_\n#include \"jemalloc/internal/jemalloc_internal.h\"\n\n/******************************************************************************/\n/* Function prototypes for non-inline static functions. */\n\nstatic void\twrtmessage(void *cbopaque, const char *s);\n#define\tU2S_BUFSIZE\t((1U << (LG_SIZEOF_INTMAX_T + 3)) + 1)\nstatic char\t*u2s(uintmax_t x, unsigned base, bool uppercase, char *s,\n    size_t *slen_p);\n#define\tD2S_BUFSIZE\t(1 + U2S_BUFSIZE)\nstatic char\t*d2s(intmax_t x, char sign, char *s, size_t *slen_p);\n#define\tO2S_BUFSIZE\t(1 + U2S_BUFSIZE)\nstatic char\t*o2s(uintmax_t x, bool alt_form, char *s, size_t *slen_p);\n#define\tX2S_BUFSIZE\t(2 + U2S_BUFSIZE)\nstatic char\t*x2s(uintmax_t x, bool alt_form, bool uppercase, char *s,\n    size_t *slen_p);\n\n/******************************************************************************/\n\n/* malloc_message() setup. */\nstatic void\nwrtmessage(void *cbopaque, const char *s)\n{\n\n#ifdef SYS_write\n\t/*\n\t * Use syscall(2) rather than write(2) when possible in order to avoid\n\t * the possibility of memory allocation within libc.  This is necessary\n\t * on FreeBSD; most operating systems do not have this problem though.\n\t */\n\tUNUSED int result = syscall(SYS_write, STDERR_FILENO, s, strlen(s));\n#else\n\tUNUSED int result = write(STDERR_FILENO, s, strlen(s));\n#endif\n}\n\nJEMALLOC_EXPORT void\t(*je_malloc_message)(void *, const char *s);\n\n/*\n * Wrapper around malloc_message() that avoids the need for\n * je_malloc_message(...) throughout the code.\n */\nvoid\nmalloc_write(const char *s)\n{\n\n\tif (je_malloc_message != NULL)\n\t\tje_malloc_message(NULL, s);\n\telse\n\t\twrtmessage(NULL, s);\n}\n\n/*\n * glibc provides a non-standard strerror_r() when _GNU_SOURCE is defined, so\n * provide a wrapper.\n */\nint\nbuferror(char *buf, size_t buflen)\n{\n\n#ifdef _WIN32\n\tFormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 0,\n\t    (LPSTR)buf, buflen, NULL);\n\treturn (0);\n#elif defined(_GNU_SOURCE)\n\tchar *b = strerror_r(errno, buf, buflen);\n\tif (b != buf) {\n\t\tstrncpy(buf, b, buflen);\n\t\tbuf[buflen-1] = '\\0';\n\t}\n\treturn (0);\n#else\n\treturn (strerror_r(errno, buf, buflen));\n#endif\n}\n\nuintmax_t\nmalloc_strtoumax(const char *nptr, char **endptr, int base)\n{\n\tuintmax_t ret, digit;\n\tint b;\n\tbool neg;\n\tconst char *p, *ns;\n\n\tif (base < 0 || base == 1 || base > 36) {\n\t\tset_errno(EINVAL);\n\t\treturn (UINTMAX_MAX);\n\t}\n\tb = base;\n\n\t/* Swallow leading whitespace and get sign, if any. */\n\tneg = false;\n\tp = nptr;\n\twhile (true) {\n\t\tswitch (*p) {\n\t\tcase '\\t': case '\\n': case '\\v': case '\\f': case '\\r': case ' ':\n\t\t\tp++;\n\t\t\tbreak;\n\t\tcase '-':\n\t\t\tneg = true;\n\t\t\t/* Fall through. */\n\t\tcase '+':\n\t\t\tp++;\n\t\t\t/* Fall through. */\n\t\tdefault:\n\t\t\tgoto label_prefix;\n\t\t}\n\t}\n\n\t/* Get prefix, if any. */\n\tlabel_prefix:\n\t/*\n\t * Note where the first non-whitespace/sign character is so that it is\n\t * possible to tell whether any digits are consumed (e.g., \"  0\" vs.\n\t * \"  -x\").\n\t */\n\tns = p;\n\tif (*p == '0') {\n\t\tswitch (p[1]) {\n\t\tcase '0': case '1': case '2': case '3': case '4': case '5':\n\t\tcase '6': case '7':\n\t\t\tif (b == 0)\n\t\t\t\tb = 8;\n\t\t\tif (b == 8)\n\t\t\t\tp++;\n\t\t\tbreak;\n\t\tcase 'x':\n\t\t\tswitch (p[2]) {\n\t\t\tcase '0': case '1': case '2': case '3': case '4':\n\t\t\tcase '5': case '6': case '7': case '8': case '9':\n\t\t\tcase 'A': case 'B': case 'C': case 'D': case 'E':\n\t\t\tcase 'F':\n\t\t\tcase 'a': case 'b': case 'c': case 'd': case 'e':\n\t\t\tcase 'f':\n\t\t\t\tif (b == 0)\n\t\t\t\t\tb = 16;\n\t\t\t\tif (b == 16)\n\t\t\t\t\tp += 2;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\t}\n\tif (b == 0)\n\t\tb = 10;\n\n\t/* Convert. */\n\tret = 0;\n\twhile ((*p >= '0' && *p <= '9' && (digit = *p - '0') < b)\n\t    || (*p >= 'A' && *p <= 'Z' && (digit = 10 + *p - 'A') < b)\n\t    || (*p >= 'a' && *p <= 'z' && (digit = 10 + *p - 'a') < b)) {\n\t\tuintmax_t pret = ret;\n\t\tret *= b;\n\t\tret += digit;\n\t\tif (ret < pret) {\n\t\t\t/* Overflow. */\n\t\t\tset_errno(ERANGE);\n\t\t\treturn (UINTMAX_MAX);\n\t\t}\n\t\tp++;\n\t}\n\tif (neg)\n\t\tret = -ret;\n\n\tif (endptr != NULL) {\n\t\tif (p == ns) {\n\t\t\t/* No characters were converted. */\n\t\t\t*endptr = (char *)nptr;\n\t\t} else\n\t\t\t*endptr = (char *)p;\n\t}\n\n\treturn (ret);\n}\n\nstatic char *\nu2s(uintmax_t x, unsigned base, bool uppercase, char *s, size_t *slen_p)\n{\n\tunsigned i;\n\n\ti = U2S_BUFSIZE - 1;\n\ts[i] = '\\0';\n\tswitch (base) {\n\tcase 10:\n\t\tdo {\n\t\t\ti--;\n\t\t\ts[i] = \"0123456789\"[x % (uint64_t)10];\n\t\t\tx /= (uint64_t)10;\n\t\t} while (x > 0);\n\t\tbreak;\n\tcase 16: {\n\t\tconst char *digits = (uppercase)\n\t\t    ? \"0123456789ABCDEF\"\n\t\t    : \"0123456789abcdef\";\n\n\t\tdo {\n\t\t\ti--;\n\t\t\ts[i] = digits[x & 0xf];\n\t\t\tx >>= 4;\n\t\t} while (x > 0);\n\t\tbreak;\n\t} default: {\n\t\tconst char *digits = (uppercase)\n\t\t    ? \"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ\"\n\t\t    : \"0123456789abcdefghijklmnopqrstuvwxyz\";\n\n\t\tassert(base >= 2 && base <= 36);\n\t\tdo {\n\t\t\ti--;\n\t\t\ts[i] = digits[x % (uint64_t)base];\n\t\t\tx /= (uint64_t)base;\n\t\t} while (x > 0);\n\t}}\n\n\t*slen_p = U2S_BUFSIZE - 1 - i;\n\treturn (&s[i]);\n}\n\nstatic char *\nd2s(intmax_t x, char sign, char *s, size_t *slen_p)\n{\n\tbool neg;\n\n\tif ((neg = (x < 0)))\n\t\tx = -x;\n\ts = u2s(x, 10, false, s, slen_p);\n\tif (neg)\n\t\tsign = '-';\n\tswitch (sign) {\n\tcase '-':\n\t\tif (neg == false)\n\t\t\tbreak;\n\t\t/* Fall through. */\n\tcase ' ':\n\tcase '+':\n\t\ts--;\n\t\t(*slen_p)++;\n\t\t*s = sign;\n\t\tbreak;\n\tdefault: not_reached();\n\t}\n\treturn (s);\n}\n\nstatic char *\no2s(uintmax_t x, bool alt_form, char *s, size_t *slen_p)\n{\n\n\ts = u2s(x, 8, false, s, slen_p);\n\tif (alt_form && *s != '0') {\n\t\ts--;\n\t\t(*slen_p)++;\n\t\t*s = '0';\n\t}\n\treturn (s);\n}\n\nstatic char *\nx2s(uintmax_t x, bool alt_form, bool uppercase, char *s, size_t *slen_p)\n{\n\n\ts = u2s(x, 16, uppercase, s, slen_p);\n\tif (alt_form) {\n\t\ts -= 2;\n\t\t(*slen_p) += 2;\n\t\tmemcpy(s, uppercase ? \"0X\" : \"0x\", 2);\n\t}\n\treturn (s);\n}\n\nint\nmalloc_vsnprintf(char *str, size_t size, const char *format, va_list ap)\n{\n\tint ret;\n\tsize_t i;\n\tconst char *f;\n\n#define\tAPPEND_C(c) do {\t\t\t\t\t\t\\\n\tif (i < size)\t\t\t\t\t\t\t\\\n\t\tstr[i] = (c);\t\t\t\t\t\t\\\n\ti++;\t\t\t\t\t\t\t\t\\\n} while (0)\n#define\tAPPEND_S(s, slen) do {\t\t\t\t\t\t\\\n\tif (i < size) {\t\t\t\t\t\t\t\\\n\t\tsize_t cpylen = (slen <= size - i) ? slen : size - i;\t\\\n\t\tmemcpy(&str[i], s, cpylen);\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\ti += slen;\t\t\t\t\t\t\t\\\n} while (0)\n#define\tAPPEND_PADDED_S(s, slen, width, left_justify) do {\t\t\\\n\t/* Left padding. */\t\t\t\t\t\t\\\n\tsize_t pad_len = (width == -1) ? 0 : ((slen < (size_t)width) ?\t\\\n\t    (size_t)width - slen : 0);\t\t\t\t\t\\\n\tif (left_justify == false && pad_len != 0) {\t\t\t\\\n\t\tsize_t j;\t\t\t\t\t\t\\\n\t\tfor (j = 0; j < pad_len; j++)\t\t\t\t\\\n\t\t\tAPPEND_C(' ');\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\t/* Value. */\t\t\t\t\t\t\t\\\n\tAPPEND_S(s, slen);\t\t\t\t\t\t\\\n\t/* Right padding. */\t\t\t\t\t\t\\\n\tif (left_justify && pad_len != 0) {\t\t\t\t\\\n\t\tsize_t j;\t\t\t\t\t\t\\\n\t\tfor (j = 0; j < pad_len; j++)\t\t\t\t\\\n\t\t\tAPPEND_C(' ');\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n} while (0)\n#define GET_ARG_NUMERIC(val, len) do {\t\t\t\t\t\\\n\tswitch (len) {\t\t\t\t\t\t\t\\\n\tcase '?':\t\t\t\t\t\t\t\\\n\t\tval = va_arg(ap, int);\t\t\t\t\t\\\n\t\tbreak;\t\t\t\t\t\t\t\\\n\tcase '?' | 0x80:\t\t\t\t\t\t\\\n\t\tval = va_arg(ap, unsigned int);\t\t\t\t\\\n\t\tbreak;\t\t\t\t\t\t\t\\\n\tcase 'l':\t\t\t\t\t\t\t\\\n\t\tval = va_arg(ap, long);\t\t\t\t\t\\\n\t\tbreak;\t\t\t\t\t\t\t\\\n\tcase 'l' | 0x80:\t\t\t\t\t\t\\\n\t\tval = va_arg(ap, unsigned long);\t\t\t\\\n\t\tbreak;\t\t\t\t\t\t\t\\\n\tcase 'q':\t\t\t\t\t\t\t\\\n\t\tval = va_arg(ap, long long);\t\t\t\t\\\n\t\tbreak;\t\t\t\t\t\t\t\\\n\tcase 'q' | 0x80:\t\t\t\t\t\t\\\n\t\tval = va_arg(ap, unsigned long long);\t\t\t\\\n\t\tbreak;\t\t\t\t\t\t\t\\\n\tcase 'j':\t\t\t\t\t\t\t\\\n\t\tval = va_arg(ap, intmax_t);\t\t\t\t\\\n\t\tbreak;\t\t\t\t\t\t\t\\\n\tcase 't':\t\t\t\t\t\t\t\\\n\t\tval = va_arg(ap, ptrdiff_t);\t\t\t\t\\\n\t\tbreak;\t\t\t\t\t\t\t\\\n\tcase 'z':\t\t\t\t\t\t\t\\\n\t\tval = va_arg(ap, ssize_t);\t\t\t\t\\\n\t\tbreak;\t\t\t\t\t\t\t\\\n\tcase 'z' | 0x80:\t\t\t\t\t\t\\\n\t\tval = va_arg(ap, size_t);\t\t\t\t\\\n\t\tbreak;\t\t\t\t\t\t\t\\\n\tcase 'p': /* Synthetic; used for %p. */\t\t\t\t\\\n\t\tval = va_arg(ap, uintptr_t);\t\t\t\t\\\n\t\tbreak;\t\t\t\t\t\t\t\\\n\tdefault: not_reached();\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n} while (0)\n\n\ti = 0;\n\tf = format;\n\twhile (true) {\n\t\tswitch (*f) {\n\t\tcase '\\0': goto label_out;\n\t\tcase '%': {\n\t\t\tbool alt_form = false;\n\t\t\tbool left_justify = false;\n\t\t\tbool plus_space = false;\n\t\t\tbool plus_plus = false;\n\t\t\tint prec = -1;\n\t\t\tint width = -1;\n\t\t\tunsigned char len = '?';\n\n\t\t\tf++;\n\t\t\tif (*f == '%') {\n\t\t\t\t/* %% */\n\t\t\t\tAPPEND_C(*f);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\t/* Flags. */\n\t\t\twhile (true) {\n\t\t\t\tswitch (*f) {\n\t\t\t\tcase '#':\n\t\t\t\t\tassert(alt_form == false);\n\t\t\t\t\talt_form = true;\n\t\t\t\t\tbreak;\n\t\t\t\tcase '-':\n\t\t\t\t\tassert(left_justify == false);\n\t\t\t\t\tleft_justify = true;\n\t\t\t\t\tbreak;\n\t\t\t\tcase ' ':\n\t\t\t\t\tassert(plus_space == false);\n\t\t\t\t\tplus_space = true;\n\t\t\t\t\tbreak;\n\t\t\t\tcase '+':\n\t\t\t\t\tassert(plus_plus == false);\n\t\t\t\t\tplus_plus = true;\n\t\t\t\t\tbreak;\n\t\t\t\tdefault: goto label_width;\n\t\t\t\t}\n\t\t\t\tf++;\n\t\t\t}\n\t\t\t/* Width. */\n\t\t\tlabel_width:\n\t\t\tswitch (*f) {\n\t\t\tcase '*':\n\t\t\t\twidth = va_arg(ap, int);\n\t\t\t\tf++;\n\t\t\t\tbreak;\n\t\t\tcase '0': case '1': case '2': case '3': case '4':\n\t\t\tcase '5': case '6': case '7': case '8': case '9': {\n\t\t\t\tuintmax_t uwidth;\n\t\t\t\tset_errno(0);\n\t\t\t\tuwidth = malloc_strtoumax(f, (char **)&f, 10);\n\t\t\t\tassert(uwidth != UINTMAX_MAX || get_errno() !=\n\t\t\t\t    ERANGE);\n\t\t\t\twidth = (int)uwidth;\n\t\t\t\tif (*f == '.') {\n\t\t\t\t\tf++;\n\t\t\t\t\tgoto label_precision;\n\t\t\t\t} else\n\t\t\t\t\tgoto label_length;\n\t\t\t\tbreak;\n\t\t\t} case '.':\n\t\t\t\tf++;\n\t\t\t\tgoto label_precision;\n\t\t\tdefault: goto label_length;\n\t\t\t}\n\t\t\t/* Precision. */\n\t\t\tlabel_precision:\n\t\t\tswitch (*f) {\n\t\t\tcase '*':\n\t\t\t\tprec = va_arg(ap, int);\n\t\t\t\tf++;\n\t\t\t\tbreak;\n\t\t\tcase '0': case '1': case '2': case '3': case '4':\n\t\t\tcase '5': case '6': case '7': case '8': case '9': {\n\t\t\t\tuintmax_t uprec;\n\t\t\t\tset_errno(0);\n\t\t\t\tuprec = malloc_strtoumax(f, (char **)&f, 10);\n\t\t\t\tassert(uprec != UINTMAX_MAX || get_errno() !=\n\t\t\t\t    ERANGE);\n\t\t\t\tprec = (int)uprec;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tdefault: break;\n\t\t\t}\n\t\t\t/* Length. */\n\t\t\tlabel_length:\n\t\t\tswitch (*f) {\n\t\t\tcase 'l':\n\t\t\t\tf++;\n\t\t\t\tif (*f == 'l') {\n\t\t\t\t\tlen = 'q';\n\t\t\t\t\tf++;\n\t\t\t\t} else\n\t\t\t\t\tlen = 'l';\n\t\t\t\tbreak;\n\t\t\tcase 'j':\n\t\t\t\tlen = 'j';\n\t\t\t\tf++;\n\t\t\t\tbreak;\n\t\t\tcase 't':\n\t\t\t\tlen = 't';\n\t\t\t\tf++;\n\t\t\t\tbreak;\n\t\t\tcase 'z':\n\t\t\t\tlen = 'z';\n\t\t\t\tf++;\n\t\t\t\tbreak;\n\t\t\tdefault: break;\n\t\t\t}\n\t\t\t/* Conversion specifier. */\n\t\t\tswitch (*f) {\n\t\t\t\tchar *s;\n\t\t\t\tsize_t slen;\n\t\t\tcase 'd': case 'i': {\n\t\t\t\tintmax_t val JEMALLOC_CC_SILENCE_INIT(0);\n\t\t\t\tchar buf[D2S_BUFSIZE];\n\n\t\t\t\tGET_ARG_NUMERIC(val, len);\n\t\t\t\ts = d2s(val, (plus_plus ? '+' : (plus_space ?\n\t\t\t\t    ' ' : '-')), buf, &slen);\n\t\t\t\tAPPEND_PADDED_S(s, slen, width, left_justify);\n\t\t\t\tf++;\n\t\t\t\tbreak;\n\t\t\t} case 'o': {\n\t\t\t\tuintmax_t val JEMALLOC_CC_SILENCE_INIT(0);\n\t\t\t\tchar buf[O2S_BUFSIZE];\n\n\t\t\t\tGET_ARG_NUMERIC(val, len | 0x80);\n\t\t\t\ts = o2s(val, alt_form, buf, &slen);\n\t\t\t\tAPPEND_PADDED_S(s, slen, width, left_justify);\n\t\t\t\tf++;\n\t\t\t\tbreak;\n\t\t\t} case 'u': {\n\t\t\t\tuintmax_t val JEMALLOC_CC_SILENCE_INIT(0);\n\t\t\t\tchar buf[U2S_BUFSIZE];\n\n\t\t\t\tGET_ARG_NUMERIC(val, len | 0x80);\n\t\t\t\ts = u2s(val, 10, false, buf, &slen);\n\t\t\t\tAPPEND_PADDED_S(s, slen, width, left_justify);\n\t\t\t\tf++;\n\t\t\t\tbreak;\n\t\t\t} case 'x': case 'X': {\n\t\t\t\tuintmax_t val JEMALLOC_CC_SILENCE_INIT(0);\n\t\t\t\tchar buf[X2S_BUFSIZE];\n\n\t\t\t\tGET_ARG_NUMERIC(val, len | 0x80);\n\t\t\t\ts = x2s(val, alt_form, *f == 'X', buf, &slen);\n\t\t\t\tAPPEND_PADDED_S(s, slen, width, left_justify);\n\t\t\t\tf++;\n\t\t\t\tbreak;\n\t\t\t} case 'c': {\n\t\t\t\tunsigned char val;\n\t\t\t\tchar buf[2];\n\n\t\t\t\tassert(len == '?' || len == 'l');\n\t\t\t\tassert_not_implemented(len != 'l');\n\t\t\t\tval = va_arg(ap, int);\n\t\t\t\tbuf[0] = val;\n\t\t\t\tbuf[1] = '\\0';\n\t\t\t\tAPPEND_PADDED_S(buf, 1, width, left_justify);\n\t\t\t\tf++;\n\t\t\t\tbreak;\n\t\t\t} case 's':\n\t\t\t\tassert(len == '?' || len == 'l');\n\t\t\t\tassert_not_implemented(len != 'l');\n\t\t\t\ts = va_arg(ap, char *);\n\t\t\t\tslen = (prec == -1) ? strlen(s) : prec;\n\t\t\t\tAPPEND_PADDED_S(s, slen, width, left_justify);\n\t\t\t\tf++;\n\t\t\t\tbreak;\n\t\t\tcase 'p': {\n\t\t\t\tuintmax_t val;\n\t\t\t\tchar buf[X2S_BUFSIZE];\n\n\t\t\t\tGET_ARG_NUMERIC(val, 'p');\n\t\t\t\ts = x2s(val, true, false, buf, &slen);\n\t\t\t\tAPPEND_PADDED_S(s, slen, width, left_justify);\n\t\t\t\tf++;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tdefault: not_implemented();\n\t\t\t}\n\t\t\tbreak;\n\t\t} default: {\n\t\t\tAPPEND_C(*f);\n\t\t\tf++;\n\t\t\tbreak;\n\t\t}}\n\t}\n\tlabel_out:\n\tif (i < size)\n\t\tstr[i] = '\\0';\n\telse\n\t\tstr[size - 1] = '\\0';\n\tret = i;\n\n#undef APPEND_C\n#undef APPEND_S\n#undef APPEND_PADDED_S\n#undef GET_ARG_NUMERIC\n\treturn (ret);\n}\n\nJEMALLOC_ATTR(format(printf, 3, 4))\nint\nmalloc_snprintf(char *str, size_t size, const char *format, ...)\n{\n\tint ret;\n\tva_list ap;\n\n\tva_start(ap, format);\n\tret = malloc_vsnprintf(str, size, format, ap);\n\tva_end(ap);\n\n\treturn (ret);\n}\n\nvoid\nmalloc_vcprintf(void (*write_cb)(void *, const char *), void *cbopaque,\n    const char *format, va_list ap)\n{\n\tchar buf[MALLOC_PRINTF_BUFSIZE];\n\n\tif (write_cb == NULL) {\n\t\t/*\n\t\t * The caller did not provide an alternate write_cb callback\n\t\t * function, so use the default one.  malloc_write() is an\n\t\t * inline function, so use malloc_message() directly here.\n\t\t */\n\t\twrite_cb = (je_malloc_message != NULL) ? je_malloc_message :\n\t\t    wrtmessage;\n\t\tcbopaque = NULL;\n\t}\n\n\tmalloc_vsnprintf(buf, sizeof(buf), format, ap);\n\twrite_cb(cbopaque, buf);\n}\n\n/*\n * Print to a callback function in such a way as to (hopefully) avoid memory\n * allocation.\n */\nJEMALLOC_ATTR(format(printf, 3, 4))\nvoid\nmalloc_cprintf(void (*write_cb)(void *, const char *), void *cbopaque,\n    const char *format, ...)\n{\n\tva_list ap;\n\n\tva_start(ap, format);\n\tmalloc_vcprintf(write_cb, cbopaque, format, ap);\n\tva_end(ap);\n}\n\n/* Print to stderr in such a way as to avoid memory allocation. */\nJEMALLOC_ATTR(format(printf, 1, 2))\nvoid\nmalloc_printf(const char *format, ...)\n{\n\tva_list ap;\n\n\tva_start(ap, format);\n\tmalloc_vcprintf(NULL, NULL, format, ap);\n\tva_end(ap);\n}\n"
  },
  {
    "path": "deps/jemalloc/src/zone.c",
    "content": "#include \"jemalloc/internal/jemalloc_internal.h\"\n#ifndef JEMALLOC_ZONE\n#  error \"This source file is for zones on Darwin (OS X).\"\n#endif\n\n/*\n * The malloc_default_purgeable_zone function is only available on >= 10.6.\n * We need to check whether it is present at runtime, thus the weak_import.\n */\nextern malloc_zone_t *malloc_default_purgeable_zone(void)\nJEMALLOC_ATTR(weak_import);\n\n/******************************************************************************/\n/* Data. */\n\nstatic malloc_zone_t zone;\nstatic struct malloc_introspection_t zone_introspect;\n\n/******************************************************************************/\n/* Function prototypes for non-inline static functions. */\n\nstatic size_t\tzone_size(malloc_zone_t *zone, void *ptr);\nstatic void\t*zone_malloc(malloc_zone_t *zone, size_t size);\nstatic void\t*zone_calloc(malloc_zone_t *zone, size_t num, size_t size);\nstatic void\t*zone_valloc(malloc_zone_t *zone, size_t size);\nstatic void\tzone_free(malloc_zone_t *zone, void *ptr);\nstatic void\t*zone_realloc(malloc_zone_t *zone, void *ptr, size_t size);\n#if (JEMALLOC_ZONE_VERSION >= 5)\nstatic void\t*zone_memalign(malloc_zone_t *zone, size_t alignment,\n#endif\n#if (JEMALLOC_ZONE_VERSION >= 6)\n    size_t size);\nstatic void\tzone_free_definite_size(malloc_zone_t *zone, void *ptr,\n    size_t size);\n#endif\nstatic void\t*zone_destroy(malloc_zone_t *zone);\nstatic size_t\tzone_good_size(malloc_zone_t *zone, size_t size);\nstatic void\tzone_force_lock(malloc_zone_t *zone);\nstatic void\tzone_force_unlock(malloc_zone_t *zone);\n\n/******************************************************************************/\n/*\n * Functions.\n */\n\nstatic size_t\nzone_size(malloc_zone_t *zone, void *ptr)\n{\n\n\t/*\n\t * There appear to be places within Darwin (such as setenv(3)) that\n\t * cause calls to this function with pointers that *no* zone owns.  If\n\t * we knew that all pointers were owned by *some* zone, we could split\n\t * our zone into two parts, and use one as the default allocator and\n\t * the other as the default deallocator/reallocator.  Since that will\n\t * not work in practice, we must check all pointers to assure that they\n\t * reside within a mapped chunk before determining size.\n\t */\n\treturn (ivsalloc(ptr, config_prof));\n}\n\nstatic void *\nzone_malloc(malloc_zone_t *zone, size_t size)\n{\n\n\treturn (je_malloc(size));\n}\n\nstatic void *\nzone_calloc(malloc_zone_t *zone, size_t num, size_t size)\n{\n\n\treturn (je_calloc(num, size));\n}\n\nstatic void *\nzone_valloc(malloc_zone_t *zone, size_t size)\n{\n\tvoid *ret = NULL; /* Assignment avoids useless compiler warning. */\n\n\tje_posix_memalign(&ret, PAGE, size);\n\n\treturn (ret);\n}\n\nstatic void\nzone_free(malloc_zone_t *zone, void *ptr)\n{\n\n\tif (ivsalloc(ptr, config_prof) != 0) {\n\t\tje_free(ptr);\n\t\treturn;\n\t}\n\n\tfree(ptr);\n}\n\nstatic void *\nzone_realloc(malloc_zone_t *zone, void *ptr, size_t size)\n{\n\n\tif (ivsalloc(ptr, config_prof) != 0)\n\t\treturn (je_realloc(ptr, size));\n\n\treturn (realloc(ptr, size));\n}\n\n#if (JEMALLOC_ZONE_VERSION >= 5)\nstatic void *\nzone_memalign(malloc_zone_t *zone, size_t alignment, size_t size)\n{\n\tvoid *ret = NULL; /* Assignment avoids useless compiler warning. */\n\n\tje_posix_memalign(&ret, alignment, size);\n\n\treturn (ret);\n}\n#endif\n\n#if (JEMALLOC_ZONE_VERSION >= 6)\nstatic void\nzone_free_definite_size(malloc_zone_t *zone, void *ptr, size_t size)\n{\n\n\tif (ivsalloc(ptr, config_prof) != 0) {\n\t\tassert(ivsalloc(ptr, config_prof) == size);\n\t\tje_free(ptr);\n\t\treturn;\n\t}\n\n\tfree(ptr);\n}\n#endif\n\nstatic void *\nzone_destroy(malloc_zone_t *zone)\n{\n\n\t/* This function should never be called. */\n\tassert(false);\n\treturn (NULL);\n}\n\nstatic size_t\nzone_good_size(malloc_zone_t *zone, size_t size)\n{\n\n\tif (size == 0)\n\t\tsize = 1;\n\treturn (s2u(size));\n}\n\nstatic void\nzone_force_lock(malloc_zone_t *zone)\n{\n\n\tif (isthreaded)\n\t\tjemalloc_prefork();\n}\n\nstatic void\nzone_force_unlock(malloc_zone_t *zone)\n{\n\n\tif (isthreaded)\n\t\tjemalloc_postfork_parent();\n}\n\nJEMALLOC_ATTR(constructor)\nvoid\nregister_zone(void)\n{\n\n\t/*\n\t * If something else replaced the system default zone allocator, don't\n\t * register jemalloc's.\n\t */\n\tmalloc_zone_t *default_zone = malloc_default_zone();\n\tif (!default_zone->zone_name ||\n\t    strcmp(default_zone->zone_name, \"DefaultMallocZone\") != 0) {\n\t\treturn;\n\t}\n\n\tzone.size = (void *)zone_size;\n\tzone.malloc = (void *)zone_malloc;\n\tzone.calloc = (void *)zone_calloc;\n\tzone.valloc = (void *)zone_valloc;\n\tzone.free = (void *)zone_free;\n\tzone.realloc = (void *)zone_realloc;\n\tzone.destroy = (void *)zone_destroy;\n\tzone.zone_name = \"jemalloc_zone\";\n\tzone.batch_malloc = NULL;\n\tzone.batch_free = NULL;\n\tzone.introspect = &zone_introspect;\n\tzone.version = JEMALLOC_ZONE_VERSION;\n#if (JEMALLOC_ZONE_VERSION >= 5)\n\tzone.memalign = zone_memalign;\n#endif\n#if (JEMALLOC_ZONE_VERSION >= 6)\n\tzone.free_definite_size = zone_free_definite_size;\n#endif\n#if (JEMALLOC_ZONE_VERSION >= 8)\n\tzone.pressure_relief = NULL;\n#endif\n\n\tzone_introspect.enumerator = NULL;\n\tzone_introspect.good_size = (void *)zone_good_size;\n\tzone_introspect.check = NULL;\n\tzone_introspect.print = NULL;\n\tzone_introspect.log = NULL;\n\tzone_introspect.force_lock = (void *)zone_force_lock;\n\tzone_introspect.force_unlock = (void *)zone_force_unlock;\n\tzone_introspect.statistics = NULL;\n#if (JEMALLOC_ZONE_VERSION >= 6)\n\tzone_introspect.zone_locked = NULL;\n#endif\n#if (JEMALLOC_ZONE_VERSION >= 7)\n\tzone_introspect.enable_discharge_checking = NULL;\n\tzone_introspect.disable_discharge_checking = NULL;\n\tzone_introspect.discharge = NULL;\n#ifdef __BLOCKS__\n\tzone_introspect.enumerate_discharged_pointers = NULL;\n#else\n\tzone_introspect.enumerate_unavailable_without_blocks = NULL;\n#endif\n#endif\n\n\t/*\n\t * The default purgeable zone is created lazily by OSX's libc.  It uses\n\t * the default zone when it is created for \"small\" allocations\n\t * (< 15 KiB), but assumes the default zone is a scalable_zone.  This\n\t * obviously fails when the default zone is the jemalloc zone, so\n\t * malloc_default_purgeable_zone is called beforehand so that the\n\t * default purgeable zone is created when the default zone is still\n\t * a scalable_zone.  As purgeable zones only exist on >= 10.6, we need\n\t * to check for the existence of malloc_default_purgeable_zone() at\n\t * run time.\n\t */\n\tif (malloc_default_purgeable_zone != NULL)\n\t\tmalloc_default_purgeable_zone();\n\n\t/* Register the custom zone.  At this point it won't be the default. */\n\tmalloc_zone_register(&zone);\n\n\t/*\n\t * Unregister and reregister the default zone.  On OSX >= 10.6,\n\t * unregistering takes the last registered zone and places it at the\n\t * location of the specified zone.  Unregistering the default zone thus\n\t * makes the last registered one the default.  On OSX < 10.6,\n\t * unregistering shifts all registered zones.  The first registered zone\n\t * then becomes the default.\n\t */\n\tdo {\n\t\tdefault_zone = malloc_default_zone();\n\t\tmalloc_zone_unregister(default_zone);\n\t\tmalloc_zone_register(default_zone);\n\t} while (malloc_default_zone() != &zone);\n}\n"
  },
  {
    "path": "deps/jemalloc/test/ALLOCM_ARENA.c",
    "content": "#define\tJEMALLOC_MANGLE\n#include \"jemalloc_test.h\"\n\n#define\tNTHREADS 10\n\nvoid *\nje_thread_start(void *arg)\n{\n\tunsigned thread_ind = (unsigned)(uintptr_t)arg;\n\tunsigned arena_ind;\n\tint r;\n\tvoid *p;\n\tsize_t rsz, sz;\n\n\tsz = sizeof(arena_ind);\n\tif (mallctl(\"arenas.extend\", &arena_ind, &sz, NULL, 0)\n\t    != 0) {\n\t\tmalloc_printf(\"Error in arenas.extend\\n\");\n\t\tabort();\n\t}\n\n\tif (thread_ind % 4 != 3) {\n\t\tsize_t mib[3];\n\t\tsize_t miblen = sizeof(mib) / sizeof(size_t);\n\t\tconst char *dss_precs[] = {\"disabled\", \"primary\", \"secondary\"};\n\t\tconst char *dss = dss_precs[thread_ind % 4];\n\t\tif (mallctlnametomib(\"arena.0.dss\", mib, &miblen) != 0) {\n\t\t\tmalloc_printf(\"Error in mallctlnametomib()\\n\");\n\t\t\tabort();\n\t\t}\n\t\tmib[1] = arena_ind;\n\t\tif (mallctlbymib(mib, miblen, NULL, NULL, (void *)&dss,\n\t\t    sizeof(const char *))) {\n\t\t\tmalloc_printf(\"Error in mallctlbymib()\\n\");\n\t\t\tabort();\n\t\t}\n\t}\n\n\tr = allocm(&p, &rsz, 1, ALLOCM_ARENA(arena_ind));\n\tif (r != ALLOCM_SUCCESS) {\n\t\tmalloc_printf(\"Unexpected allocm() error\\n\");\n\t\tabort();\n\t}\n\n\treturn (NULL);\n}\n\nint\nmain(void)\n{\n\tje_thread_t threads[NTHREADS];\n\tunsigned i;\n\n\tmalloc_printf(\"Test begin\\n\");\n\n\tfor (i = 0; i < NTHREADS; i++) {\n\t\tje_thread_create(&threads[i], je_thread_start,\n\t\t    (void *)(uintptr_t)i);\n\t}\n\n\tfor (i = 0; i < NTHREADS; i++)\n\t\tje_thread_join(threads[i], NULL);\n\n\tmalloc_printf(\"Test end\\n\");\n\treturn (0);\n}\n"
  },
  {
    "path": "deps/jemalloc/test/ALLOCM_ARENA.exp",
    "content": "Test begin\nTest end\n"
  },
  {
    "path": "deps/jemalloc/test/aligned_alloc.c",
    "content": "#define\tJEMALLOC_MANGLE\n#include \"jemalloc_test.h\"\n\n#define CHUNK 0x400000\n/* #define MAXALIGN ((size_t)UINT64_C(0x80000000000)) */\n#define MAXALIGN ((size_t)0x2000000LU)\n#define NITER 4\n\nint\nmain(void)\n{\n\tsize_t alignment, size, total;\n\tunsigned i;\n\tvoid *p, *ps[NITER];\n\n\tmalloc_printf(\"Test begin\\n\");\n\n\t/* Test error conditions. */\n\talignment = 0;\n\tset_errno(0);\n\tp = aligned_alloc(alignment, 1);\n\tif (p != NULL || get_errno() != EINVAL) {\n\t\tmalloc_printf(\n\t\t    \"Expected error for invalid alignment %zu\\n\", alignment);\n\t}\n\n\tfor (alignment = sizeof(size_t); alignment < MAXALIGN;\n\t    alignment <<= 1) {\n\t\tset_errno(0);\n\t\tp = aligned_alloc(alignment + 1, 1);\n\t\tif (p != NULL || get_errno() != EINVAL) {\n\t\t\tmalloc_printf(\n\t\t\t    \"Expected error for invalid alignment %zu\\n\",\n\t\t\t    alignment + 1);\n\t\t}\n\t}\n\n#if LG_SIZEOF_PTR == 3\n\talignment = UINT64_C(0x8000000000000000);\n\tsize      = UINT64_C(0x8000000000000000);\n#else\n\talignment = 0x80000000LU;\n\tsize      = 0x80000000LU;\n#endif\n\tset_errno(0);\n\tp = aligned_alloc(alignment, size);\n\tif (p != NULL || get_errno() != ENOMEM) {\n\t\tmalloc_printf(\n\t\t    \"Expected error for aligned_alloc(%zu, %zu)\\n\",\n\t\t    alignment, size);\n\t}\n\n#if LG_SIZEOF_PTR == 3\n\talignment = UINT64_C(0x4000000000000000);\n\tsize      = UINT64_C(0x8400000000000001);\n#else\n\talignment = 0x40000000LU;\n\tsize      = 0x84000001LU;\n#endif\n\tset_errno(0);\n\tp = aligned_alloc(alignment, size);\n\tif (p != NULL || get_errno() != ENOMEM) {\n\t\tmalloc_printf(\n\t\t    \"Expected error for aligned_alloc(%zu, %zu)\\n\",\n\t\t    alignment, size);\n\t}\n\n\talignment = 0x10LU;\n#if LG_SIZEOF_PTR == 3\n\tsize = UINT64_C(0xfffffffffffffff0);\n#else\n\tsize = 0xfffffff0LU;\n#endif\n\tset_errno(0);\n\tp = aligned_alloc(alignment, size);\n\tif (p != NULL || get_errno() != ENOMEM) {\n\t\tmalloc_printf(\n\t\t    \"Expected error for aligned_alloc(&p, %zu, %zu)\\n\",\n\t\t    alignment, size);\n\t}\n\n\tfor (i = 0; i < NITER; i++)\n\t\tps[i] = NULL;\n\n\tfor (alignment = 8;\n\t    alignment <= MAXALIGN;\n\t    alignment <<= 1) {\n\t\ttotal = 0;\n\t\tmalloc_printf(\"Alignment: %zu\\n\", alignment);\n\t\tfor (size = 1;\n\t\t    size < 3 * alignment && size < (1U << 31);\n\t\t    size += (alignment >> (LG_SIZEOF_PTR-1)) - 1) {\n\t\t\tfor (i = 0; i < NITER; i++) {\n\t\t\t\tps[i] = aligned_alloc(alignment, size);\n\t\t\t\tif (ps[i] == NULL) {\n\t\t\t\t\tchar buf[BUFERROR_BUF];\n\n\t\t\t\t\tbuferror(buf, sizeof(buf));\n\t\t\t\t\tmalloc_printf(\n\t\t\t\t\t    \"Error for size %zu (%#zx): %s\\n\",\n\t\t\t\t\t    size, size, buf);\n\t\t\t\t\texit(1);\n\t\t\t\t}\n\t\t\t\ttotal += malloc_usable_size(ps[i]);\n\t\t\t\tif (total >= (MAXALIGN << 1))\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tfor (i = 0; i < NITER; i++) {\n\t\t\t\tif (ps[i] != NULL) {\n\t\t\t\t\tfree(ps[i]);\n\t\t\t\t\tps[i] = NULL;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tmalloc_printf(\"Test end\\n\");\n\treturn (0);\n}\n"
  },
  {
    "path": "deps/jemalloc/test/aligned_alloc.exp",
    "content": "Test begin\nAlignment: 8\nAlignment: 16\nAlignment: 32\nAlignment: 64\nAlignment: 128\nAlignment: 256\nAlignment: 512\nAlignment: 1024\nAlignment: 2048\nAlignment: 4096\nAlignment: 8192\nAlignment: 16384\nAlignment: 32768\nAlignment: 65536\nAlignment: 131072\nAlignment: 262144\nAlignment: 524288\nAlignment: 1048576\nAlignment: 2097152\nAlignment: 4194304\nAlignment: 8388608\nAlignment: 16777216\nAlignment: 33554432\nTest end\n"
  },
  {
    "path": "deps/jemalloc/test/allocated.c",
    "content": "#define\tJEMALLOC_MANGLE\n#include \"jemalloc_test.h\"\n\nvoid *\nje_thread_start(void *arg)\n{\n\tint err;\n\tvoid *p;\n\tuint64_t a0, a1, d0, d1;\n\tuint64_t *ap0, *ap1, *dp0, *dp1;\n\tsize_t sz, usize;\n\n\tsz = sizeof(a0);\n\tif ((err = mallctl(\"thread.allocated\", &a0, &sz, NULL, 0))) {\n\t\tif (err == ENOENT) {\n#ifdef JEMALLOC_STATS\n\t\t\tassert(false);\n#endif\n\t\t\tgoto label_return;\n\t\t}\n\t\tmalloc_printf(\"%s(): Error in mallctl(): %s\\n\", __func__,\n\t\t    strerror(err));\n\t\texit(1);\n\t}\n\tsz = sizeof(ap0);\n\tif ((err = mallctl(\"thread.allocatedp\", &ap0, &sz, NULL, 0))) {\n\t\tif (err == ENOENT) {\n#ifdef JEMALLOC_STATS\n\t\t\tassert(false);\n#endif\n\t\t\tgoto label_return;\n\t\t}\n\t\tmalloc_printf(\"%s(): Error in mallctl(): %s\\n\", __func__,\n\t\t    strerror(err));\n\t\texit(1);\n\t}\n\tassert(*ap0 == a0);\n\n\tsz = sizeof(d0);\n\tif ((err = mallctl(\"thread.deallocated\", &d0, &sz, NULL, 0))) {\n\t\tif (err == ENOENT) {\n#ifdef JEMALLOC_STATS\n\t\t\tassert(false);\n#endif\n\t\t\tgoto label_return;\n\t\t}\n\t\tmalloc_printf(\"%s(): Error in mallctl(): %s\\n\", __func__,\n\t\t    strerror(err));\n\t\texit(1);\n\t}\n\tsz = sizeof(dp0);\n\tif ((err = mallctl(\"thread.deallocatedp\", &dp0, &sz, NULL, 0))) {\n\t\tif (err == ENOENT) {\n#ifdef JEMALLOC_STATS\n\t\t\tassert(false);\n#endif\n\t\t\tgoto label_return;\n\t\t}\n\t\tmalloc_printf(\"%s(): Error in mallctl(): %s\\n\", __func__,\n\t\t    strerror(err));\n\t\texit(1);\n\t}\n\tassert(*dp0 == d0);\n\n\tp = malloc(1);\n\tif (p == NULL) {\n\t\tmalloc_printf(\"%s(): Error in malloc()\\n\", __func__);\n\t\texit(1);\n\t}\n\n\tsz = sizeof(a1);\n\tmallctl(\"thread.allocated\", &a1, &sz, NULL, 0);\n\tsz = sizeof(ap1);\n\tmallctl(\"thread.allocatedp\", &ap1, &sz, NULL, 0);\n\tassert(*ap1 == a1);\n\tassert(ap0 == ap1);\n\n\tusize = malloc_usable_size(p);\n\tassert(a0 + usize <= a1);\n\n\tfree(p);\n\n\tsz = sizeof(d1);\n\tmallctl(\"thread.deallocated\", &d1, &sz, NULL, 0);\n\tsz = sizeof(dp1);\n\tmallctl(\"thread.deallocatedp\", &dp1, &sz, NULL, 0);\n\tassert(*dp1 == d1);\n\tassert(dp0 == dp1);\n\n\tassert(d0 + usize <= d1);\n\nlabel_return:\n\treturn (NULL);\n}\n\nint\nmain(void)\n{\n\tint ret = 0;\n\tje_thread_t thread;\n\n\tmalloc_printf(\"Test begin\\n\");\n\n\tje_thread_start(NULL);\n\n\tje_thread_create(&thread, je_thread_start, NULL);\n\tje_thread_join(thread, (void *)&ret);\n\n\tje_thread_start(NULL);\n\n\tje_thread_create(&thread, je_thread_start, NULL);\n\tje_thread_join(thread, (void *)&ret);\n\n\tje_thread_start(NULL);\n\n\tmalloc_printf(\"Test end\\n\");\n\treturn (ret);\n}\n"
  },
  {
    "path": "deps/jemalloc/test/allocated.exp",
    "content": "Test begin\nTest end\n"
  },
  {
    "path": "deps/jemalloc/test/allocm.c",
    "content": "#define\tJEMALLOC_MANGLE\n#include \"jemalloc_test.h\"\n\n#define CHUNK 0x400000\n/* #define MAXALIGN ((size_t)UINT64_C(0x80000000000)) */\n#define MAXALIGN ((size_t)0x2000000LU)\n#define NITER 4\n\nint\nmain(void)\n{\n\tint r;\n\tvoid *p;\n\tsize_t nsz, rsz, sz, alignment, total;\n\tunsigned i;\n\tvoid *ps[NITER];\n\n\tmalloc_printf(\"Test begin\\n\");\n\n\tsz = 42;\n\tnsz = 0;\n\tr = nallocm(&nsz, sz, 0);\n\tif (r != ALLOCM_SUCCESS) {\n\t\tmalloc_printf(\"Unexpected nallocm() error\\n\");\n\t\tabort();\n\t}\n\trsz = 0;\n\tr = allocm(&p, &rsz, sz, 0);\n\tif (r != ALLOCM_SUCCESS) {\n\t\tmalloc_printf(\"Unexpected allocm() error\\n\");\n\t\tabort();\n\t}\n\tif (rsz < sz)\n\t\tmalloc_printf(\"Real size smaller than expected\\n\");\n\tif (nsz != rsz)\n\t\tmalloc_printf(\"nallocm()/allocm() rsize mismatch\\n\");\n\tif (dallocm(p, 0) != ALLOCM_SUCCESS)\n\t\tmalloc_printf(\"Unexpected dallocm() error\\n\");\n\n\tr = allocm(&p, NULL, sz, 0);\n\tif (r != ALLOCM_SUCCESS) {\n\t\tmalloc_printf(\"Unexpected allocm() error\\n\");\n\t\tabort();\n\t}\n\tif (dallocm(p, 0) != ALLOCM_SUCCESS)\n\t\tmalloc_printf(\"Unexpected dallocm() error\\n\");\n\n\tnsz = 0;\n\tr = nallocm(&nsz, sz, ALLOCM_ZERO);\n\tif (r != ALLOCM_SUCCESS) {\n\t\tmalloc_printf(\"Unexpected nallocm() error\\n\");\n\t\tabort();\n\t}\n\trsz = 0;\n\tr = allocm(&p, &rsz, sz, ALLOCM_ZERO);\n\tif (r != ALLOCM_SUCCESS) {\n\t\tmalloc_printf(\"Unexpected allocm() error\\n\");\n\t\tabort();\n\t}\n\tif (nsz != rsz)\n\t\tmalloc_printf(\"nallocm()/allocm() rsize mismatch\\n\");\n\tif (dallocm(p, 0) != ALLOCM_SUCCESS)\n\t\tmalloc_printf(\"Unexpected dallocm() error\\n\");\n\n#if LG_SIZEOF_PTR == 3\n\talignment = UINT64_C(0x8000000000000000);\n\tsz        = UINT64_C(0x8000000000000000);\n#else\n\talignment = 0x80000000LU;\n\tsz        = 0x80000000LU;\n#endif\n\tnsz = 0;\n\tr = nallocm(&nsz, sz, ALLOCM_ALIGN(alignment));\n\tif (r == ALLOCM_SUCCESS) {\n\t\tmalloc_printf(\n\t\t    \"Expected error for nallocm(&nsz, %zu, %#x)\\n\",\n\t\t    sz, ALLOCM_ALIGN(alignment));\n\t}\n\trsz = 0;\n\tr = allocm(&p, &rsz, sz, ALLOCM_ALIGN(alignment));\n\tif (r == ALLOCM_SUCCESS) {\n\t\tmalloc_printf(\n\t\t    \"Expected error for allocm(&p, %zu, %#x)\\n\",\n\t\t    sz, ALLOCM_ALIGN(alignment));\n\t}\n\tif (nsz != rsz)\n\t\tmalloc_printf(\"nallocm()/allocm() rsize mismatch\\n\");\n\n#if LG_SIZEOF_PTR == 3\n\talignment = UINT64_C(0x4000000000000000);\n\tsz        = UINT64_C(0x8400000000000001);\n#else\n\talignment = 0x40000000LU;\n\tsz        = 0x84000001LU;\n#endif\n\tnsz = 0;\n\tr = nallocm(&nsz, sz, ALLOCM_ALIGN(alignment));\n\tif (r != ALLOCM_SUCCESS)\n\t\tmalloc_printf(\"Unexpected nallocm() error\\n\");\n\trsz = 0;\n\tr = allocm(&p, &rsz, sz, ALLOCM_ALIGN(alignment));\n\tif (r == ALLOCM_SUCCESS) {\n\t\tmalloc_printf(\n\t\t    \"Expected error for allocm(&p, %zu, %#x)\\n\",\n\t\t    sz, ALLOCM_ALIGN(alignment));\n\t}\n\n\talignment = 0x10LU;\n#if LG_SIZEOF_PTR == 3\n\tsz = UINT64_C(0xfffffffffffffff0);\n#else\n\tsz = 0xfffffff0LU;\n#endif\n\tnsz = 0;\n\tr = nallocm(&nsz, sz, ALLOCM_ALIGN(alignment));\n\tif (r == ALLOCM_SUCCESS) {\n\t\tmalloc_printf(\n\t\t    \"Expected error for nallocm(&nsz, %zu, %#x)\\n\",\n\t\t    sz, ALLOCM_ALIGN(alignment));\n\t}\n\trsz = 0;\n\tr = allocm(&p, &rsz, sz, ALLOCM_ALIGN(alignment));\n\tif (r == ALLOCM_SUCCESS) {\n\t\tmalloc_printf(\n\t\t    \"Expected error for allocm(&p, %zu, %#x)\\n\",\n\t\t    sz, ALLOCM_ALIGN(alignment));\n\t}\n\tif (nsz != rsz)\n\t\tmalloc_printf(\"nallocm()/allocm() rsize mismatch\\n\");\n\n\tfor (i = 0; i < NITER; i++)\n\t\tps[i] = NULL;\n\n\tfor (alignment = 8;\n\t    alignment <= MAXALIGN;\n\t    alignment <<= 1) {\n\t\ttotal = 0;\n\t\tmalloc_printf(\"Alignment: %zu\\n\", alignment);\n\t\tfor (sz = 1;\n\t\t    sz < 3 * alignment && sz < (1U << 31);\n\t\t    sz += (alignment >> (LG_SIZEOF_PTR-1)) - 1) {\n\t\t\tfor (i = 0; i < NITER; i++) {\n\t\t\t\tnsz = 0;\n\t\t\t\tr = nallocm(&nsz, sz,\n\t\t\t\t    ALLOCM_ALIGN(alignment) | ALLOCM_ZERO);\n\t\t\t\tif (r != ALLOCM_SUCCESS) {\n\t\t\t\t\tmalloc_printf(\n\t\t\t\t\t    \"nallocm() error for size %zu\"\n\t\t\t\t\t    \" (%#zx): %d\\n\",\n\t\t\t\t\t    sz, sz, r);\n\t\t\t\t\texit(1);\n\t\t\t\t}\n\t\t\t\trsz = 0;\n\t\t\t\tr = allocm(&ps[i], &rsz, sz,\n\t\t\t\t    ALLOCM_ALIGN(alignment) | ALLOCM_ZERO);\n\t\t\t\tif (r != ALLOCM_SUCCESS) {\n\t\t\t\t\tmalloc_printf(\n\t\t\t\t\t    \"allocm() error for size %zu\"\n\t\t\t\t\t    \" (%#zx): %d\\n\",\n\t\t\t\t\t    sz, sz, r);\n\t\t\t\t\texit(1);\n\t\t\t\t}\n\t\t\t\tif (rsz < sz) {\n\t\t\t\t\tmalloc_printf(\n\t\t\t\t\t    \"Real size smaller than\"\n\t\t\t\t\t    \" expected\\n\");\n\t\t\t\t}\n\t\t\t\tif (nsz != rsz) {\n\t\t\t\t\tmalloc_printf(\n\t\t\t\t\t    \"nallocm()/allocm() rsize\"\n\t\t\t\t\t    \" mismatch\\n\");\n\t\t\t\t}\n\t\t\t\tif ((uintptr_t)p & (alignment-1)) {\n\t\t\t\t\tmalloc_printf(\n\t\t\t\t\t    \"%p inadequately aligned for\"\n\t\t\t\t\t    \" alignment: %zu\\n\", p, alignment);\n\t\t\t\t}\n\t\t\t\tsallocm(ps[i], &rsz, 0);\n\t\t\t\ttotal += rsz;\n\t\t\t\tif (total >= (MAXALIGN << 1))\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tfor (i = 0; i < NITER; i++) {\n\t\t\t\tif (ps[i] != NULL) {\n\t\t\t\t\tdallocm(ps[i], 0);\n\t\t\t\t\tps[i] = NULL;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tmalloc_printf(\"Test end\\n\");\n\treturn (0);\n}\n"
  },
  {
    "path": "deps/jemalloc/test/allocm.exp",
    "content": "Test begin\nAlignment: 8\nAlignment: 16\nAlignment: 32\nAlignment: 64\nAlignment: 128\nAlignment: 256\nAlignment: 512\nAlignment: 1024\nAlignment: 2048\nAlignment: 4096\nAlignment: 8192\nAlignment: 16384\nAlignment: 32768\nAlignment: 65536\nAlignment: 131072\nAlignment: 262144\nAlignment: 524288\nAlignment: 1048576\nAlignment: 2097152\nAlignment: 4194304\nAlignment: 8388608\nAlignment: 16777216\nAlignment: 33554432\nTest end\n"
  },
  {
    "path": "deps/jemalloc/test/bitmap.c",
    "content": "#define\tJEMALLOC_MANGLE\n#include \"jemalloc_test.h\"\n\n#if (LG_BITMAP_MAXBITS > 12)\n#  define MAXBITS\t4500\n#else\n#  define MAXBITS\t(1U << LG_BITMAP_MAXBITS)\n#endif\n\nstatic void\ntest_bitmap_size(void)\n{\n\tsize_t i, prev_size;\n\n\tprev_size = 0;\n\tfor (i = 1; i <= MAXBITS; i++) {\n\t\tsize_t size = bitmap_size(i);\n\t\tassert(size >= prev_size);\n\t\tprev_size = size;\n\t}\n}\n\nstatic void\ntest_bitmap_init(void)\n{\n\tsize_t i;\n\n\tfor (i = 1; i <= MAXBITS; i++) {\n\t\tbitmap_info_t binfo;\n\t\tbitmap_info_init(&binfo, i);\n\t\t{\n\t\t\tsize_t j;\n\t\t\tbitmap_t *bitmap = malloc(sizeof(bitmap_t) *\n\t\t\t\tbitmap_info_ngroups(&binfo));\n\t\t\tbitmap_init(bitmap, &binfo);\n\n\t\t\tfor (j = 0; j < i; j++)\n\t\t\t\tassert(bitmap_get(bitmap, &binfo, j) == false);\n\t\t\tfree(bitmap);\n\n\t\t}\n\t}\n}\n\nstatic void\ntest_bitmap_set(void)\n{\n\tsize_t i;\n\n\tfor (i = 1; i <= MAXBITS; i++) {\n\t\tbitmap_info_t binfo;\n\t\tbitmap_info_init(&binfo, i);\n\t\t{\n\t\t\tsize_t j;\n\t\t\tbitmap_t *bitmap = malloc(sizeof(bitmap_t) *\n\t\t\t\tbitmap_info_ngroups(&binfo));\n\t\t\tbitmap_init(bitmap, &binfo);\n\n\t\t\tfor (j = 0; j < i; j++)\n\t\t\t\tbitmap_set(bitmap, &binfo, j);\n\t\t\tassert(bitmap_full(bitmap, &binfo));\n\t\t\tfree(bitmap);\n\t\t}\n\t}\n}\n\nstatic void\ntest_bitmap_unset(void)\n{\n\tsize_t i;\n\n\tfor (i = 1; i <= MAXBITS; i++) {\n\t\tbitmap_info_t binfo;\n\t\tbitmap_info_init(&binfo, i);\n\t\t{\n\t\t\tsize_t j;\n\t\t\tbitmap_t *bitmap = malloc(sizeof(bitmap_t) *\n\t\t\t\tbitmap_info_ngroups(&binfo));\n\t\t\tbitmap_init(bitmap, &binfo);\n\n\t\t\tfor (j = 0; j < i; j++)\n\t\t\t\tbitmap_set(bitmap, &binfo, j);\n\t\t\tassert(bitmap_full(bitmap, &binfo));\n\t\t\tfor (j = 0; j < i; j++)\n\t\t\t\tbitmap_unset(bitmap, &binfo, j);\n\t\t\tfor (j = 0; j < i; j++)\n\t\t\t\tbitmap_set(bitmap, &binfo, j);\n\t\t\tassert(bitmap_full(bitmap, &binfo));\n\t\t\tfree(bitmap);\n\t\t}\n\t}\n}\n\nstatic void\ntest_bitmap_sfu(void)\n{\n\tsize_t i;\n\n\tfor (i = 1; i <= MAXBITS; i++) {\n\t\tbitmap_info_t binfo;\n\t\tbitmap_info_init(&binfo, i);\n\t\t{\n\t\t\tssize_t j;\n\t\t\tbitmap_t *bitmap = malloc(sizeof(bitmap_t) *\n\t\t\t\tbitmap_info_ngroups(&binfo));\n\t\t\tbitmap_init(bitmap, &binfo);\n\n\t\t\t/* Iteratively set bits starting at the beginning. */\n\t\t\tfor (j = 0; j < i; j++)\n\t\t\t\tassert(bitmap_sfu(bitmap, &binfo) == j);\n\t\t\tassert(bitmap_full(bitmap, &binfo));\n\n\t\t\t/*\n\t\t\t * Iteratively unset bits starting at the end, and\n\t\t\t * verify that bitmap_sfu() reaches the unset bits.\n\t\t\t */\n\t\t\tfor (j = i - 1; j >= 0; j--) {\n\t\t\t\tbitmap_unset(bitmap, &binfo, j);\n\t\t\t\tassert(bitmap_sfu(bitmap, &binfo) == j);\n\t\t\t\tbitmap_unset(bitmap, &binfo, j);\n\t\t\t}\n\t\t\tassert(bitmap_get(bitmap, &binfo, 0) == false);\n\n\t\t\t/*\n\t\t\t * Iteratively set bits starting at the beginning, and\n\t\t\t * verify that bitmap_sfu() looks past them.\n\t\t\t */\n\t\t\tfor (j = 1; j < i; j++) {\n\t\t\t\tbitmap_set(bitmap, &binfo, j - 1);\n\t\t\t\tassert(bitmap_sfu(bitmap, &binfo) == j);\n\t\t\t\tbitmap_unset(bitmap, &binfo, j);\n\t\t\t}\n\t\t\tassert(bitmap_sfu(bitmap, &binfo) == i - 1);\n\t\t\tassert(bitmap_full(bitmap, &binfo));\n\t\t\tfree(bitmap);\n\t\t}\n\t}\n}\n\nint\nmain(void)\n{\n\tmalloc_printf(\"Test begin\\n\");\n\n\ttest_bitmap_size();\n\ttest_bitmap_init();\n\ttest_bitmap_set();\n\ttest_bitmap_unset();\n\ttest_bitmap_sfu();\n\n\tmalloc_printf(\"Test end\\n\");\n\treturn (0);\n}\n"
  },
  {
    "path": "deps/jemalloc/test/bitmap.exp",
    "content": "Test begin\nTest end\n"
  },
  {
    "path": "deps/jemalloc/test/jemalloc_test.h.in",
    "content": "/*\n * This header should be included by tests, rather than directly including\n * jemalloc/jemalloc.h, because --with-install-suffix may cause the header to\n * have a different name.\n */\n#include \"jemalloc/jemalloc@install_suffix@.h\"\n#include \"jemalloc/internal/jemalloc_internal.h\"\n\n/* Abstraction layer for threading in tests */\n#ifdef _WIN32\n#include <windows.h>\n\ntypedef HANDLE je_thread_t;\n\nvoid\nje_thread_create(je_thread_t *thread, void *(*proc)(void *), void *arg)\n{\n\tLPTHREAD_START_ROUTINE routine = (LPTHREAD_START_ROUTINE)proc;\n\t*thread = CreateThread(NULL, 0, routine, arg, 0, NULL);\n\tif (*thread == NULL) {\n\t\tmalloc_printf(\"Error in CreateThread()\\n\");\n\t\texit(1);\n\t}\n}\n\nvoid\nje_thread_join(je_thread_t thread, void **ret)\n{\n\tWaitForSingleObject(thread, INFINITE);\n}\n\n#else\n#include <pthread.h>\n\ntypedef pthread_t je_thread_t;\n\nvoid\nje_thread_create(je_thread_t *thread, void *(*proc)(void *), void *arg)\n{\n\n\tif (pthread_create(thread, NULL, proc, arg) != 0) {\n\t\tmalloc_printf(\"Error in pthread_create()\\n\");\n\t\texit(1);\n\t}\n}\n\nvoid\nje_thread_join(je_thread_t thread, void **ret)\n{\n\n\tpthread_join(thread, ret);\n}\n#endif\n"
  },
  {
    "path": "deps/jemalloc/test/mremap.c",
    "content": "#define\tJEMALLOC_MANGLE\n#include \"jemalloc_test.h\"\n\nint\nmain(void)\n{\n\tint ret, err;\n\tsize_t sz, lg_chunk, chunksize, i;\n\tchar *p, *q;\n\n\tmalloc_printf(\"Test begin\\n\");\n\n\tsz = sizeof(lg_chunk);\n\tif ((err = mallctl(\"opt.lg_chunk\", &lg_chunk, &sz, NULL, 0))) {\n\t\tassert(err != ENOENT);\n\t\tmalloc_printf(\"%s(): Error in mallctl(): %s\\n\", __func__,\n\t\t    strerror(err));\n\t\tret = 1;\n\t\tgoto label_return;\n\t}\n\tchunksize = ((size_t)1U) << lg_chunk;\n\n\tp = (char *)malloc(chunksize);\n\tif (p == NULL) {\n\t\tmalloc_printf(\"malloc(%zu) --> %p\\n\", chunksize, p);\n\t\tret = 1;\n\t\tgoto label_return;\n\t}\n\tmemset(p, 'a', chunksize);\n\n\tq = (char *)realloc(p, chunksize * 2);\n\tif (q == NULL) {\n\t\tmalloc_printf(\"realloc(%p, %zu) --> %p\\n\", p, chunksize * 2,\n\t\t    q);\n\t\tret = 1;\n\t\tgoto label_return;\n\t}\n\tfor (i = 0; i < chunksize; i++) {\n\t\tassert(q[i] == 'a');\n\t}\n\n\tp = q;\n\n\tq = (char *)realloc(p, chunksize);\n\tif (q == NULL) {\n\t\tmalloc_printf(\"realloc(%p, %zu) --> %p\\n\", p, chunksize, q);\n\t\tret = 1;\n\t\tgoto label_return;\n\t}\n\tfor (i = 0; i < chunksize; i++) {\n\t\tassert(q[i] == 'a');\n\t}\n\n\tfree(q);\n\n\tret = 0;\nlabel_return:\n\tmalloc_printf(\"Test end\\n\");\n\treturn (ret);\n}\n"
  },
  {
    "path": "deps/jemalloc/test/mremap.exp",
    "content": "Test begin\nTest end\n"
  },
  {
    "path": "deps/jemalloc/test/posix_memalign.c",
    "content": "#define\tJEMALLOC_MANGLE\n#include \"jemalloc_test.h\"\n\n#define CHUNK 0x400000\n/* #define MAXALIGN ((size_t)UINT64_C(0x80000000000)) */\n#define MAXALIGN ((size_t)0x2000000LU)\n#define NITER 4\n\nint\nmain(void)\n{\n\tsize_t alignment, size, total;\n\tunsigned i;\n\tint err;\n\tvoid *p, *ps[NITER];\n\n\tmalloc_printf(\"Test begin\\n\");\n\n\t/* Test error conditions. */\n\tfor (alignment = 0; alignment < sizeof(void *); alignment++) {\n\t\terr = posix_memalign(&p, alignment, 1);\n\t\tif (err != EINVAL) {\n\t\t\tmalloc_printf(\n\t\t\t    \"Expected error for invalid alignment %zu\\n\",\n\t\t\t    alignment);\n\t\t}\n\t}\n\n\tfor (alignment = sizeof(size_t); alignment < MAXALIGN;\n\t    alignment <<= 1) {\n\t\terr = posix_memalign(&p, alignment + 1, 1);\n\t\tif (err == 0) {\n\t\t\tmalloc_printf(\n\t\t\t    \"Expected error for invalid alignment %zu\\n\",\n\t\t\t    alignment + 1);\n\t\t}\n\t}\n\n#if LG_SIZEOF_PTR == 3\n\talignment = UINT64_C(0x8000000000000000);\n\tsize      = UINT64_C(0x8000000000000000);\n#else\n\talignment = 0x80000000LU;\n\tsize      = 0x80000000LU;\n#endif\n\terr = posix_memalign(&p, alignment, size);\n\tif (err == 0) {\n\t\tmalloc_printf(\n\t\t    \"Expected error for posix_memalign(&p, %zu, %zu)\\n\",\n\t\t    alignment, size);\n\t}\n\n#if LG_SIZEOF_PTR == 3\n\talignment = UINT64_C(0x4000000000000000);\n\tsize      = UINT64_C(0x8400000000000001);\n#else\n\talignment = 0x40000000LU;\n\tsize      = 0x84000001LU;\n#endif\n\terr = posix_memalign(&p, alignment, size);\n\tif (err == 0) {\n\t\tmalloc_printf(\n\t\t    \"Expected error for posix_memalign(&p, %zu, %zu)\\n\",\n\t\t    alignment, size);\n\t}\n\n\talignment = 0x10LU;\n#if LG_SIZEOF_PTR == 3\n\tsize = UINT64_C(0xfffffffffffffff0);\n#else\n\tsize = 0xfffffff0LU;\n#endif\n\terr = posix_memalign(&p, alignment, size);\n\tif (err == 0) {\n\t\tmalloc_printf(\n\t\t    \"Expected error for posix_memalign(&p, %zu, %zu)\\n\",\n\t\t    alignment, size);\n\t}\n\n\tfor (i = 0; i < NITER; i++)\n\t\tps[i] = NULL;\n\n\tfor (alignment = 8;\n\t    alignment <= MAXALIGN;\n\t    alignment <<= 1) {\n\t\ttotal = 0;\n\t\tmalloc_printf(\"Alignment: %zu\\n\", alignment);\n\t\tfor (size = 1;\n\t\t    size < 3 * alignment && size < (1U << 31);\n\t\t    size += (alignment >> (LG_SIZEOF_PTR-1)) - 1) {\n\t\t\tfor (i = 0; i < NITER; i++) {\n\t\t\t\terr = posix_memalign(&ps[i],\n\t\t\t\t    alignment, size);\n\t\t\t\tif (err) {\n\t\t\t\t\tmalloc_printf(\n\t\t\t\t\t    \"Error for size %zu (%#zx): %s\\n\",\n\t\t\t\t\t    size, size, strerror(err));\n\t\t\t\t\texit(1);\n\t\t\t\t}\n\t\t\t\ttotal += malloc_usable_size(ps[i]);\n\t\t\t\tif (total >= (MAXALIGN << 1))\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tfor (i = 0; i < NITER; i++) {\n\t\t\t\tif (ps[i] != NULL) {\n\t\t\t\t\tfree(ps[i]);\n\t\t\t\t\tps[i] = NULL;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tmalloc_printf(\"Test end\\n\");\n\treturn (0);\n}\n"
  },
  {
    "path": "deps/jemalloc/test/posix_memalign.exp",
    "content": "Test begin\nAlignment: 8\nAlignment: 16\nAlignment: 32\nAlignment: 64\nAlignment: 128\nAlignment: 256\nAlignment: 512\nAlignment: 1024\nAlignment: 2048\nAlignment: 4096\nAlignment: 8192\nAlignment: 16384\nAlignment: 32768\nAlignment: 65536\nAlignment: 131072\nAlignment: 262144\nAlignment: 524288\nAlignment: 1048576\nAlignment: 2097152\nAlignment: 4194304\nAlignment: 8388608\nAlignment: 16777216\nAlignment: 33554432\nTest end\n"
  },
  {
    "path": "deps/jemalloc/test/rallocm.c",
    "content": "#define\tJEMALLOC_MANGLE\n#include \"jemalloc_test.h\"\n\nint\nmain(void)\n{\n\tsize_t pagesize;\n\tvoid *p, *q;\n\tsize_t sz, tsz;\n\tint r;\n\n\tmalloc_printf(\"Test begin\\n\");\n\n\t/* Get page size. */\n\t{\n#ifdef _WIN32\n\t\tSYSTEM_INFO si;\n\t\tGetSystemInfo(&si);\n\t\tpagesize = (size_t)si.dwPageSize;\n#else\n\t\tlong result = sysconf(_SC_PAGESIZE);\n\t\tassert(result != -1);\n\t\tpagesize = (size_t)result;\n#endif\n\t}\n\n\tr = allocm(&p, &sz, 42, 0);\n\tif (r != ALLOCM_SUCCESS) {\n\t\tmalloc_printf(\"Unexpected allocm() error\\n\");\n\t\tabort();\n\t}\n\n\tq = p;\n\tr = rallocm(&q, &tsz, sz, 0, ALLOCM_NO_MOVE);\n\tif (r != ALLOCM_SUCCESS)\n\t\tmalloc_printf(\"Unexpected rallocm() error\\n\");\n\tif (q != p)\n\t\tmalloc_printf(\"Unexpected object move\\n\");\n\tif (tsz != sz) {\n\t\tmalloc_printf(\"Unexpected size change: %zu --> %zu\\n\",\n\t\t    sz, tsz);\n\t}\n\n\tq = p;\n\tr = rallocm(&q, &tsz, sz, 5, ALLOCM_NO_MOVE);\n\tif (r != ALLOCM_SUCCESS)\n\t\tmalloc_printf(\"Unexpected rallocm() error\\n\");\n\tif (q != p)\n\t\tmalloc_printf(\"Unexpected object move\\n\");\n\tif (tsz != sz) {\n\t\tmalloc_printf(\"Unexpected size change: %zu --> %zu\\n\",\n\t\t    sz, tsz);\n\t}\n\n\tq = p;\n\tr = rallocm(&q, &tsz, sz + 5, 0, ALLOCM_NO_MOVE);\n\tif (r != ALLOCM_ERR_NOT_MOVED)\n\t\tmalloc_printf(\"Unexpected rallocm() result\\n\");\n\tif (q != p)\n\t\tmalloc_printf(\"Unexpected object move\\n\");\n\tif (tsz != sz) {\n\t\tmalloc_printf(\"Unexpected size change: %zu --> %zu\\n\",\n\t\t    sz, tsz);\n\t}\n\n\tq = p;\n\tr = rallocm(&q, &tsz, sz + 5, 0, 0);\n\tif (r != ALLOCM_SUCCESS)\n\t\tmalloc_printf(\"Unexpected rallocm() error\\n\");\n\tif (q == p)\n\t\tmalloc_printf(\"Expected object move\\n\");\n\tif (tsz == sz) {\n\t\tmalloc_printf(\"Expected size change: %zu --> %zu\\n\",\n\t\t    sz, tsz);\n\t}\n\tp = q;\n\tsz = tsz;\n\n\tr = rallocm(&q, &tsz, pagesize*2, 0, 0);\n\tif (r != ALLOCM_SUCCESS)\n\t\tmalloc_printf(\"Unexpected rallocm() error\\n\");\n\tif (q == p)\n\t\tmalloc_printf(\"Expected object move\\n\");\n\tif (tsz == sz) {\n\t\tmalloc_printf(\"Expected size change: %zu --> %zu\\n\",\n\t\t    sz, tsz);\n\t}\n\tp = q;\n\tsz = tsz;\n\n\tr = rallocm(&q, &tsz, pagesize*4, 0, 0);\n\tif (r != ALLOCM_SUCCESS)\n\t\tmalloc_printf(\"Unexpected rallocm() error\\n\");\n\tif (tsz == sz) {\n\t\tmalloc_printf(\"Expected size change: %zu --> %zu\\n\",\n\t\t    sz, tsz);\n\t}\n\tp = q;\n\tsz = tsz;\n\n\tr = rallocm(&q, &tsz, pagesize*2, 0, ALLOCM_NO_MOVE);\n\tif (r != ALLOCM_SUCCESS)\n\t\tmalloc_printf(\"Unexpected rallocm() error\\n\");\n\tif (q != p)\n\t\tmalloc_printf(\"Unexpected object move\\n\");\n\tif (tsz == sz) {\n\t\tmalloc_printf(\"Expected size change: %zu --> %zu\\n\",\n\t\t    sz, tsz);\n\t}\n\tsz = tsz;\n\n\tr = rallocm(&q, &tsz, pagesize*4, 0, ALLOCM_NO_MOVE);\n\tif (r != ALLOCM_SUCCESS)\n\t\tmalloc_printf(\"Unexpected rallocm() error\\n\");\n\tif (q != p)\n\t\tmalloc_printf(\"Unexpected object move\\n\");\n\tif (tsz == sz) {\n\t\tmalloc_printf(\"Expected size change: %zu --> %zu\\n\",\n\t\t    sz, tsz);\n\t}\n\tsz = tsz;\n\n\tdallocm(p, 0);\n\n\tmalloc_printf(\"Test end\\n\");\n\treturn (0);\n}\n"
  },
  {
    "path": "deps/jemalloc/test/rallocm.exp",
    "content": "Test begin\nTest end\n"
  },
  {
    "path": "deps/jemalloc/test/thread_arena.c",
    "content": "#define\tJEMALLOC_MANGLE\n#include \"jemalloc_test.h\"\n\n#define\tNTHREADS 10\n\nvoid *\nje_thread_start(void *arg)\n{\n\tunsigned main_arena_ind = *(unsigned *)arg;\n\tvoid *p;\n\tunsigned arena_ind;\n\tsize_t size;\n\tint err;\n\n\tp = malloc(1);\n\tif (p == NULL) {\n\t\tmalloc_printf(\"%s(): Error in malloc()\\n\", __func__);\n\t\treturn (void *)1;\n\t}\n\n\tsize = sizeof(arena_ind);\n\tif ((err = mallctl(\"thread.arena\", &arena_ind, &size, &main_arena_ind,\n\t    sizeof(main_arena_ind)))) {\n\t\tmalloc_printf(\"%s(): Error in mallctl(): %s\\n\", __func__,\n\t\t    strerror(err));\n\t\treturn (void *)1;\n\t}\n\n\tsize = sizeof(arena_ind);\n\tif ((err = mallctl(\"thread.arena\", &arena_ind, &size, NULL,\n\t    0))) {\n\t\tmalloc_printf(\"%s(): Error in mallctl(): %s\\n\", __func__,\n\t\t    strerror(err));\n\t\treturn (void *)1;\n\t}\n\tassert(arena_ind == main_arena_ind);\n\n\treturn (NULL);\n}\n\nint\nmain(void)\n{\n\tint ret = 0;\n\tvoid *p;\n\tunsigned arena_ind;\n\tsize_t size;\n\tint err;\n\tje_thread_t threads[NTHREADS];\n\tunsigned i;\n\n\tmalloc_printf(\"Test begin\\n\");\n\n\tp = malloc(1);\n\tif (p == NULL) {\n\t\tmalloc_printf(\"%s(): Error in malloc()\\n\", __func__);\n\t\tret = 1;\n\t\tgoto label_return;\n\t}\n\n\tsize = sizeof(arena_ind);\n\tif ((err = mallctl(\"thread.arena\", &arena_ind, &size, NULL, 0))) {\n\t\tmalloc_printf(\"%s(): Error in mallctl(): %s\\n\", __func__,\n\t\t    strerror(err));\n\t\tret = 1;\n\t\tgoto label_return;\n\t}\n\n\tfor (i = 0; i < NTHREADS; i++) {\n\t\tje_thread_create(&threads[i], je_thread_start,\n\t\t    (void *)&arena_ind);\n\t}\n\n\tfor (i = 0; i < NTHREADS; i++)\n\t\tje_thread_join(threads[i], (void *)&ret);\n\nlabel_return:\n\tmalloc_printf(\"Test end\\n\");\n\treturn (ret);\n}\n"
  },
  {
    "path": "deps/jemalloc/test/thread_arena.exp",
    "content": "Test begin\nTest end\n"
  },
  {
    "path": "deps/jemalloc/test/thread_tcache_enabled.c",
    "content": "#define\tJEMALLOC_MANGLE\n#include \"jemalloc_test.h\"\n\nvoid *\nje_thread_start(void *arg)\n{\n\tint err;\n\tsize_t sz;\n\tbool e0, e1;\n\n\tsz = sizeof(bool);\n\tif ((err = mallctl(\"thread.tcache.enabled\", &e0, &sz, NULL, 0))) {\n\t\tif (err == ENOENT) {\n#ifdef JEMALLOC_TCACHE\n\t\t\tassert(false);\n#endif\n\t\t}\n\t\tgoto label_return;\n\t}\n\n\tif (e0) {\n\t\te1 = false;\n\t\tassert(mallctl(\"thread.tcache.enabled\", &e0, &sz, &e1, sz)\n\t\t    == 0);\n\t\tassert(e0);\n\t}\n\n\te1 = true;\n\tassert(mallctl(\"thread.tcache.enabled\", &e0, &sz, &e1, sz) == 0);\n\tassert(e0 == false);\n\n\te1 = true;\n\tassert(mallctl(\"thread.tcache.enabled\", &e0, &sz, &e1, sz) == 0);\n\tassert(e0);\n\n\te1 = false;\n\tassert(mallctl(\"thread.tcache.enabled\", &e0, &sz, &e1, sz) == 0);\n\tassert(e0);\n\n\te1 = false;\n\tassert(mallctl(\"thread.tcache.enabled\", &e0, &sz, &e1, sz) == 0);\n\tassert(e0 == false);\n\n\tfree(malloc(1));\n\te1 = true;\n\tassert(mallctl(\"thread.tcache.enabled\", &e0, &sz, &e1, sz) == 0);\n\tassert(e0 == false);\n\n\tfree(malloc(1));\n\te1 = true;\n\tassert(mallctl(\"thread.tcache.enabled\", &e0, &sz, &e1, sz) == 0);\n\tassert(e0);\n\n\tfree(malloc(1));\n\te1 = false;\n\tassert(mallctl(\"thread.tcache.enabled\", &e0, &sz, &e1, sz) == 0);\n\tassert(e0);\n\n\tfree(malloc(1));\n\te1 = false;\n\tassert(mallctl(\"thread.tcache.enabled\", &e0, &sz, &e1, sz) == 0);\n\tassert(e0 == false);\n\n\tfree(malloc(1));\nlabel_return:\n\treturn (NULL);\n}\n\nint\nmain(void)\n{\n\tint ret = 0;\n\tje_thread_t thread;\n\n\tmalloc_printf(\"Test begin\\n\");\n\n\tje_thread_start(NULL);\n\n\tje_thread_create(&thread, je_thread_start, NULL);\n\tje_thread_join(thread, (void *)&ret);\n\n\tje_thread_start(NULL);\n\n\tje_thread_create(&thread, je_thread_start, NULL);\n\tje_thread_join(thread, (void *)&ret);\n\n\tje_thread_start(NULL);\n\n\tmalloc_printf(\"Test end\\n\");\n\treturn (ret);\n}\n"
  },
  {
    "path": "deps/jemalloc/test/thread_tcache_enabled.exp",
    "content": "Test begin\nTest end\n"
  },
  {
    "path": "deps/linenoise/.gitignore",
    "content": "linenoise_example*\n"
  },
  {
    "path": "deps/linenoise/Makefile",
    "content": "STD=\nWARN= -Wall\nOPT= -Os\n\nR_CFLAGS= $(STD) $(WARN) $(OPT) $(DEBUG) $(CFLAGS)\nR_LDFLAGS= $(LDFLAGS)\nDEBUG= -g\n\nR_CC=$(CC) $(R_CFLAGS)\nR_LD=$(CC) $(R_LDFLAGS)\n\nlinenoise.o: linenoise.h linenoise.c\n\nlinenoise_example: linenoise.o example.o\n\t$(R_LD) -o $@ $^\n\n.c.o:\n\t$(R_CC) -c $<\n\nclean:\n\trm -f linenoise_example *.o\n"
  },
  {
    "path": "deps/linenoise/README.markdown",
    "content": "# Linenoise\n\nA minimal, zero-config, BSD licensed, readline replacement.\n\nNews: linenoise is now part of [Android](http://android.git.kernel.org/?p=platform/system/core.git;a=tree;f=liblinenoise;h=56450eaed7f783760e5e6a5993ef75cde2e29dea;hb=HEAD Android)!\n\n## Can a line editing library be 20k lines of code?\n\nLine editing with some support for history is a really important feature for command line utilities. Instead of retyping almost the same stuff again and again it's just much better to hit the up arrow and edit on syntax errors, or in order to try a slightly different command. But apparently code dealing with terminals is some sort of Black Magic: readline is 30k lines of code, libedit 20k. Is it reasonable to link small utilities to huge libraries just to get a minimal support for line editing?\n\nSo what usually happens is either:\n\n * Large programs with configure scripts disabling line editing if readline is not present in the system, or not supporting it at all since readline is GPL licensed and libedit (the BSD clone) is not as known and available as readline is (Readl world example of this problem: Tclsh).\n * Smaller programs not using a configure script not supporting line editing at all (A problem we had with Redis-cli for instance).\n \nThe result is a pollution of binaries without line editing support.\n\nSo I spent more or less two hours doing a reality check resulting in this little library: is it *really* needed for a line editing library to be 20k lines of code? Apparently not, it is possibe to get a very small, zero configuration, trivial to embed library, that solves the problem. Smaller programs will just include this, supporing line editing out of the box. Larger programs may use this little library or just checking with configure if readline/libedit is available and resorting to linenoise if not.\n\n## Terminals, in 2010.\n\nApparently almost every terminal you can happen to use today has some kind of support for VT100 alike escape sequences. So I tried to write a lib using just very basic VT100 features. The resulting library appears to work everywhere I tried to use it.\n\nSince it's so young I guess there are a few bugs, or the lib may not compile or work with some operating system, but it's a matter of a few weeks and eventually we'll get it right, and there will be no excuses for not shipping command line tools without built-in line editing support.\n\nThe library is currently less than 400 lines of code. In order to use it in your project just look at the *example.c* file in the source distribution, it is trivial. Linenoise is BSD code, so you can use both in free software and commercial software.\n\n## Tested with...\n\n * Linux text only console ($TERM = linux)\n * Linux KDE terminal application ($TERM = xterm)\n * Linux xterm ($TERM = xterm)\n * Mac OS X iTerm ($TERM = xterm)\n * Mac OS X default Terminal.app ($TERM = xterm)\n * OpenBSD 4.5 through an OSX Terminal.app ($TERM = screen)\n * IBM AIX 6.1\n * FreeBSD xterm ($TERM = xterm)\n\nPlease test it everywhere you can and report back!\n\n## Let's push this forward!\n\nPlease fork it and add something interesting and send me a pull request. What's especially interesting are fixes, new key bindings, completion.\n\nSend feedbacks to antirez at gmail\n"
  },
  {
    "path": "deps/linenoise/example.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include \"linenoise.h\"\n\n\nvoid completion(const char *buf, linenoiseCompletions *lc) {\n    if (buf[0] == 'h') {\n        linenoiseAddCompletion(lc,\"hello\");\n        linenoiseAddCompletion(lc,\"hello there\");\n    }\n}\n\nint main(void) {\n    char *line;\n\n    linenoiseSetCompletionCallback(completion);\n    linenoiseHistoryLoad(\"history.txt\"); /* Load the history at startup */\n    while((line = linenoise(\"hello> \")) != NULL) {\n        if (line[0] != '\\0') {\n            printf(\"echo: '%s'\\n\", line);\n            linenoiseHistoryAdd(line);\n            linenoiseHistorySave(\"history.txt\"); /* Save every new entry */\n        }\n        free(line);\n    }\n    return 0;\n}\n"
  },
  {
    "path": "deps/linenoise/linenoise.c",
    "content": "/* linenoise.c -- guerrilla line editing library against the idea that a\n * line editing lib needs to be 20,000 lines of C code.\n *\n * You can find the latest source code at:\n * \n *   http://github.com/antirez/linenoise\n *\n * Does a number of crazy assumptions that happen to be true in 99.9999% of\n * the 2010 UNIX computers around.\n *\n * ------------------------------------------------------------------------\n *\n * Copyright (c) 2010-2013, Salvatore Sanfilippo <antirez at gmail dot com>\n * Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com>\n *\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\n * met:\n * \n *  *  Redistributions of source code must retain the above copyright\n *     notice, this list of conditions and the following disclaimer.\n *\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 * \n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n * \n * ------------------------------------------------------------------------\n *\n * References:\n * - http://invisible-island.net/xterm/ctlseqs/ctlseqs.html\n * - http://www.3waylabs.com/nw/WWW/products/wizcon/vt220.html\n *\n * Todo list:\n * - Filter bogus Ctrl+<char> combinations.\n * - Win32 support\n *\n * Bloat:\n * - History search like Ctrl+r in readline?\n *\n * List of escape sequences used by this program, we do everything just\n * with three sequences. In order to be so cheap we may have some\n * flickering effect with some slow terminal, but the lesser sequences\n * the more compatible.\n *\n * CHA (Cursor Horizontal Absolute)\n *    Sequence: ESC [ n G\n *    Effect: moves cursor to column n\n *\n * EL (Erase Line)\n *    Sequence: ESC [ n K\n *    Effect: if n is 0 or missing, clear from cursor to end of line\n *    Effect: if n is 1, clear from beginning of line to cursor\n *    Effect: if n is 2, clear entire line\n *\n * CUF (CUrsor Forward)\n *    Sequence: ESC [ n C\n *    Effect: moves cursor forward of n chars\n *\n * When multi line mode is enabled, we also use an additional escape\n * sequence. However multi line editing is disabled by default.\n *\n * CUU (Cursor Up)\n *    Sequence: ESC [ n A\n *    Effect: moves cursor up of n chars.\n *\n * CUD (Cursor Down)\n *    Sequence: ESC [ n B\n *    Effect: moves cursor down of n chars.\n *\n * The following are used to clear the screen: ESC [ H ESC [ 2 J\n * This is actually composed of two sequences:\n *\n * cursorhome\n *    Sequence: ESC [ H\n *    Effect: moves the cursor to upper left corner\n *\n * ED2 (Clear entire screen)\n *    Sequence: ESC [ 2 J\n *    Effect: clear the whole screen\n * \n */\n\n#include <termios.h>\n#include <unistd.h>\n#include <stdlib.h>\n#include <stdio.h>\n#include <errno.h>\n#include <string.h>\n#include <stdlib.h>\n#include <ctype.h>\n#include <sys/types.h>\n#include <sys/ioctl.h>\n#include <unistd.h>\n#include \"linenoise.h\"\n\n#define LINENOISE_DEFAULT_HISTORY_MAX_LEN 100\n#define LINENOISE_MAX_LINE 4096\nstatic char *unsupported_term[] = {\"dumb\",\"cons25\",\"emacs\",NULL};\nstatic linenoiseCompletionCallback *completionCallback = NULL;\n\nstatic struct termios orig_termios; /* In order to restore at exit.*/\nstatic int rawmode = 0; /* For atexit() function to check if restore is needed*/\nstatic int mlmode = 0;  /* Multi line mode. Default is single line. */\nstatic int atexit_registered = 0; /* Register atexit just 1 time. */\nstatic int history_max_len = LINENOISE_DEFAULT_HISTORY_MAX_LEN;\nstatic int history_len = 0;\nstatic char **history = NULL;\n\n/* The linenoiseState structure represents the state during line editing.\n * We pass this state to functions implementing specific editing\n * functionalities. */\nstruct linenoiseState {\n    int ifd;            /* Terminal stdin file descriptor. */\n    int ofd;            /* Terminal stdout file descriptor. */\n    char *buf;          /* Edited line buffer. */\n    size_t buflen;      /* Edited line buffer size. */\n    const char *prompt; /* Prompt to display. */\n    size_t plen;        /* Prompt length. */\n    size_t pos;         /* Current cursor position. */\n    size_t oldpos;      /* Previous refresh cursor position. */\n    size_t len;         /* Current edited line length. */\n    size_t cols;        /* Number of columns in terminal. */\n    size_t maxrows;     /* Maximum num of rows used so far (multiline mode) */\n    int history_index;  /* The history index we are currently editing. */\n};\n\nenum KEY_ACTION{\n\tKEY_NULL = 0,\t    /* NULL */\n\tCTRL_A = 1,         /* Ctrl+a */\n\tCTRL_B = 2,         /* Ctrl-b */\n\tCTRL_C = 3,         /* Ctrl-c */\n\tCTRL_D = 4,         /* Ctrl-d */\n\tCTRL_E = 5,         /* Ctrl-e */\n\tCTRL_F = 6,         /* Ctrl-f */\n\tCTRL_H = 8,         /* Ctrl-h */\n\tTAB = 9,            /* Tab */\n\tCTRL_K = 11,        /* Ctrl+k */\n\tCTRL_L = 12,        /* Ctrl+l */\n\tENTER = 13,         /* Enter */\n\tCTRL_N = 14,        /* Ctrl-n */\n\tCTRL_P = 16,        /* Ctrl-p */\n\tCTRL_T = 20,        /* Ctrl-t */\n\tCTRL_U = 21,        /* Ctrl+u */\n\tCTRL_W = 23,        /* Ctrl+w */\n\tESC = 27,           /* Escape */\n\tBACKSPACE =  127    /* Backspace */\n};\n\nstatic void linenoiseAtExit(void);\nint linenoiseHistoryAdd(const char *line);\nstatic void refreshLine(struct linenoiseState *l);\n\n/* Debugging macro. */\n#if 0\nFILE *lndebug_fp = NULL;\n#define lndebug(...) \\\n    do { \\\n        if (lndebug_fp == NULL) { \\\n            lndebug_fp = fopen(\"/tmp/lndebug.txt\",\"a\"); \\\n            fprintf(lndebug_fp, \\\n            \"[%d %d %d] p: %d, rows: %d, rpos: %d, max: %d, oldmax: %d\\n\", \\\n            (int)l->len,(int)l->pos,(int)l->oldpos,plen,rows,rpos, \\\n            (int)l->maxrows,old_rows); \\\n        } \\\n        fprintf(lndebug_fp, \", \" __VA_ARGS__); \\\n        fflush(lndebug_fp); \\\n    } while (0)\n#else\n#define lndebug(fmt, ...)\n#endif\n\n/* ======================= Low level terminal handling ====================== */\n\n/* Set if to use or not the multi line mode. */\nvoid linenoiseSetMultiLine(int ml) {\n    mlmode = ml;\n}\n\n/* Return true if the terminal name is in the list of terminals we know are\n * not able to understand basic escape sequences. */\nstatic int isUnsupportedTerm(void) {\n    char *term = getenv(\"TERM\");\n    int j;\n\n    if (term == NULL) return 0;\n    for (j = 0; unsupported_term[j]; j++)\n        if (!strcasecmp(term,unsupported_term[j])) return 1;\n    return 0;\n}\n\n/* Raw mode: 1960 magic shit. */\nstatic int enableRawMode(int fd) {\n    struct termios raw;\n\n    if (!isatty(STDIN_FILENO)) goto fatal;\n    if (!atexit_registered) {\n        atexit(linenoiseAtExit);\n        atexit_registered = 1;\n    }\n    if (tcgetattr(fd,&orig_termios) == -1) goto fatal;\n\n    raw = orig_termios;  /* modify the original mode */\n    /* input modes: no break, no CR to NL, no parity check, no strip char,\n     * no start/stop output control. */\n    raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);\n    /* output modes - disable post processing */\n    raw.c_oflag &= ~(OPOST);\n    /* control modes - set 8 bit chars */\n    raw.c_cflag |= (CS8);\n    /* local modes - choing off, canonical off, no extended functions,\n     * no signal chars (^Z,^C) */\n    raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);\n    /* control chars - set return condition: min number of bytes and timer.\n     * We want read to return every single byte, without timeout. */\n    raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */\n\n    /* put terminal in raw mode after flushing */\n    if (tcsetattr(fd,TCSAFLUSH,&raw) < 0) goto fatal;\n    rawmode = 1;\n    return 0;\n\nfatal:\n    errno = ENOTTY;\n    return -1;\n}\n\nstatic void disableRawMode(int fd) {\n    /* Don't even check the return value as it's too late. */\n    if (rawmode && tcsetattr(fd,TCSAFLUSH,&orig_termios) != -1)\n        rawmode = 0;\n}\n\n/* Use the ESC [6n escape sequence to query the horizontal cursor position\n * and return it. On error -1 is returned, on success the position of the\n * cursor. */\nstatic int getCursorPosition(int ifd, int ofd) {\n    char buf[32];\n    int cols, rows;\n    unsigned int i = 0;\n\n    /* Report cursor location */\n    if (write(ofd, \"\\x1b[6n\", 4) != 4) return -1;\n\n    /* Read the response: ESC [ rows ; cols R */\n    while (i < sizeof(buf)-1) {\n        if (read(ifd,buf+i,1) != 1) break;\n        if (buf[i] == 'R') break;\n        i++;\n    }\n    buf[i] = '\\0';\n\n    /* Parse it. */\n    if (buf[0] != ESC || buf[1] != '[') return -1;\n    if (sscanf(buf+2,\"%d;%d\",&rows,&cols) != 2) return -1;\n    return cols;\n}\n\n/* Try to get the number of columns in the current terminal, or assume 80\n * if it fails. */\nstatic int getColumns(int ifd, int ofd) {\n    struct winsize ws;\n\n    if (ioctl(1, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0) {\n        /* ioctl() failed. Try to query the terminal itself. */\n        int start, cols;\n\n        /* Get the initial position so we can restore it later. */\n        start = getCursorPosition(ifd,ofd);\n        if (start == -1) goto failed;\n\n        /* Go to right margin and get position. */\n        if (write(ofd,\"\\x1b[999C\",6) != 6) goto failed;\n        cols = getCursorPosition(ifd,ofd);\n        if (cols == -1) goto failed;\n\n        /* Restore position. */\n        if (cols > start) {\n            char seq[32];\n            snprintf(seq,32,\"\\x1b[%dD\",cols-start);\n            if (write(ofd,seq,strlen(seq)) == -1) {\n                /* Can't recover... */\n            }\n        }\n        return cols;\n    } else {\n        return ws.ws_col;\n    }\n\nfailed:\n    return 80;\n}\n\n/* Clear the screen. Used to handle ctrl+l */\nvoid linenoiseClearScreen(void) {\n    if (write(STDOUT_FILENO,\"\\x1b[H\\x1b[2J\",7) <= 0) {\n        /* nothing to do, just to avoid warning. */\n    }\n}\n\n/* Beep, used for completion when there is nothing to complete or when all\n * the choices were already shown. */\nstatic void linenoiseBeep(void) {\n    fprintf(stderr, \"\\x7\");\n    fflush(stderr);\n}\n\n/* ============================== Completion ================================ */\n\n/* Free a list of completion option populated by linenoiseAddCompletion(). */\nstatic void freeCompletions(linenoiseCompletions *lc) {\n    size_t i;\n    for (i = 0; i < lc->len; i++)\n        free(lc->cvec[i]);\n    if (lc->cvec != NULL)\n        free(lc->cvec);\n}\n\n/* This is an helper function for linenoiseEdit() and is called when the\n * user types the <tab> key in order to complete the string currently in the\n * input.\n * \n * The state of the editing is encapsulated into the pointed linenoiseState\n * structure as described in the structure definition. */\nstatic int completeLine(struct linenoiseState *ls) {\n    linenoiseCompletions lc = { 0, NULL };\n    int nread, nwritten;\n    char c = 0;\n\n    completionCallback(ls->buf,&lc);\n    if (lc.len == 0) {\n        linenoiseBeep();\n    } else {\n        size_t stop = 0, i = 0;\n\n        while(!stop) {\n            /* Show completion or original buffer */\n            if (i < lc.len) {\n                struct linenoiseState saved = *ls;\n\n                ls->len = ls->pos = strlen(lc.cvec[i]);\n                ls->buf = lc.cvec[i];\n                refreshLine(ls);\n                ls->len = saved.len;\n                ls->pos = saved.pos;\n                ls->buf = saved.buf;\n            } else {\n                refreshLine(ls);\n            }\n\n            nread = read(ls->ifd,&c,1);\n            if (nread <= 0) {\n                freeCompletions(&lc);\n                return -1;\n            }\n\n            switch(c) {\n                case 9: /* tab */\n                    i = (i+1) % (lc.len+1);\n                    if (i == lc.len) linenoiseBeep();\n                    break;\n                case 27: /* escape */\n                    /* Re-show original buffer */\n                    if (i < lc.len) refreshLine(ls);\n                    stop = 1;\n                    break;\n                default:\n                    /* Update buffer and return */\n                    if (i < lc.len) {\n                        nwritten = snprintf(ls->buf,ls->buflen,\"%s\",lc.cvec[i]);\n                        ls->len = ls->pos = nwritten;\n                    }\n                    stop = 1;\n                    break;\n            }\n        }\n    }\n\n    freeCompletions(&lc);\n    return c; /* Return last read character */\n}\n\n/* Register a callback function to be called for tab-completion. */\nvoid linenoiseSetCompletionCallback(linenoiseCompletionCallback *fn) {\n    completionCallback = fn;\n}\n\n/* This function is used by the callback function registered by the user\n * in order to add completion options given the input string when the\n * user typed <tab>. See the example.c source code for a very easy to\n * understand example. */\nvoid linenoiseAddCompletion(linenoiseCompletions *lc, const char *str) {\n    size_t len = strlen(str);\n    char *copy, **cvec;\n\n    copy = malloc(len+1);\n    if (copy == NULL) return;\n    memcpy(copy,str,len+1);\n    cvec = realloc(lc->cvec,sizeof(char*)*(lc->len+1));\n    if (cvec == NULL) {\n        free(copy);\n        return;\n    }\n    lc->cvec = cvec;\n    lc->cvec[lc->len++] = copy;\n}\n\n/* =========================== Line editing ================================= */\n\n/* We define a very simple \"append buffer\" structure, that is an heap\n * allocated string where we can append to. This is useful in order to\n * write all the escape sequences in a buffer and flush them to the standard\n * output in a single call, to avoid flickering effects. */\nstruct abuf {\n    char *b;\n    int len;\n};\n\nstatic void abInit(struct abuf *ab) {\n    ab->b = NULL;\n    ab->len = 0;\n}\n\nstatic void abAppend(struct abuf *ab, const char *s, int len) {\n    char *new = realloc(ab->b,ab->len+len);\n\n    if (new == NULL) return;\n    memcpy(new+ab->len,s,len);\n    ab->b = new;\n    ab->len += len;\n}\n\nstatic void abFree(struct abuf *ab) {\n    free(ab->b);\n}\n\n/* Single line low level line refresh.\n *\n * Rewrite the currently edited line accordingly to the buffer content,\n * cursor position, and number of columns of the terminal. */\nstatic void refreshSingleLine(struct linenoiseState *l) {\n    char seq[64];\n    size_t plen = strlen(l->prompt);\n    int fd = l->ofd;\n    char *buf = l->buf;\n    size_t len = l->len;\n    size_t pos = l->pos;\n    struct abuf ab;\n    \n    while((plen+pos) >= l->cols) {\n        buf++;\n        len--;\n        pos--;\n    }\n    while (plen+len > l->cols) {\n        len--;\n    }\n\n    abInit(&ab);\n    /* Cursor to left edge */\n    snprintf(seq,64,\"\\x1b[0G\");\n    abAppend(&ab,seq,strlen(seq));\n    /* Write the prompt and the current buffer content */\n    abAppend(&ab,l->prompt,strlen(l->prompt));\n    abAppend(&ab,buf,len);\n    /* Erase to right */\n    snprintf(seq,64,\"\\x1b[0K\");\n    abAppend(&ab,seq,strlen(seq));\n    /* Move cursor to original position. */\n    snprintf(seq,64,\"\\x1b[0G\\x1b[%dC\", (int)(pos+plen));\n    abAppend(&ab,seq,strlen(seq));\n    if (write(fd,ab.b,ab.len) == -1) {} /* Can't recover from write error. */\n    abFree(&ab);\n}\n\n/* Multi line low level line refresh.\n *\n * Rewrite the currently edited line accordingly to the buffer content,\n * cursor position, and number of columns of the terminal. */\nstatic void refreshMultiLine(struct linenoiseState *l) {\n    char seq[64];\n    int plen = strlen(l->prompt);\n    int rows = (plen+l->len+l->cols-1)/l->cols; /* rows used by current buf. */\n    int rpos = (plen+l->oldpos+l->cols)/l->cols; /* cursor relative row. */\n    int rpos2; /* rpos after refresh. */\n    int old_rows = l->maxrows;\n    int fd = l->ofd, j;\n    struct abuf ab;\n\n    /* Update maxrows if needed. */\n    if (rows > (int)l->maxrows) l->maxrows = rows;\n\n    /* First step: clear all the lines used before. To do so start by\n     * going to the last row. */\n    abInit(&ab);\n    if (old_rows-rpos > 0) {\n        lndebug(\"go down %d\", old_rows-rpos);\n        snprintf(seq,64,\"\\x1b[%dB\", old_rows-rpos);\n        abAppend(&ab,seq,strlen(seq));\n    }\n\n    /* Now for every row clear it, go up. */\n    for (j = 0; j < old_rows-1; j++) {\n        lndebug(\"clear+up\");\n        snprintf(seq,64,\"\\x1b[0G\\x1b[0K\\x1b[1A\");\n        abAppend(&ab,seq,strlen(seq));\n    }\n\n    /* Clean the top line. */\n    lndebug(\"clear\");\n    snprintf(seq,64,\"\\x1b[0G\\x1b[0K\");\n    abAppend(&ab,seq,strlen(seq));\n    \n    /* Write the prompt and the current buffer content */\n    abAppend(&ab,l->prompt,strlen(l->prompt));\n    abAppend(&ab,l->buf,l->len);\n\n    /* If we are at the very end of the screen with our prompt, we need to\n     * emit a newline and move the prompt to the first column. */\n    if (l->pos &&\n        l->pos == l->len &&\n        (l->pos+plen) % l->cols == 0)\n    {\n        lndebug(\"<newline>\");\n        abAppend(&ab,\"\\n\",1);\n        snprintf(seq,64,\"\\x1b[0G\");\n        abAppend(&ab,seq,strlen(seq));\n        rows++;\n        if (rows > (int)l->maxrows) l->maxrows = rows;\n    }\n\n    /* Move cursor to right position. */\n    rpos2 = (plen+l->pos+l->cols)/l->cols; /* current cursor relative row. */\n    lndebug(\"rpos2 %d\", rpos2);\n\n    /* Go up till we reach the expected positon. */\n    if (rows-rpos2 > 0) {\n        lndebug(\"go-up %d\", rows-rpos2);\n        snprintf(seq,64,\"\\x1b[%dA\", rows-rpos2);\n        abAppend(&ab,seq,strlen(seq));\n    }\n\n    /* Set column. */\n    lndebug(\"set col %d\", 1+((plen+(int)l->pos) % (int)l->cols));\n    snprintf(seq,64,\"\\x1b[%dG\", 1+((plen+(int)l->pos) % (int)l->cols));\n    abAppend(&ab,seq,strlen(seq));\n\n    lndebug(\"\\n\");\n    l->oldpos = l->pos;\n\n    if (write(fd,ab.b,ab.len) == -1) {} /* Can't recover from write error. */\n    abFree(&ab);\n}\n\n/* Calls the two low level functions refreshSingleLine() or\n * refreshMultiLine() according to the selected mode. */\nstatic void refreshLine(struct linenoiseState *l) {\n    if (mlmode)\n        refreshMultiLine(l);\n    else\n        refreshSingleLine(l);\n}\n\n/* Insert the character 'c' at cursor current position.\n *\n * On error writing to the terminal -1 is returned, otherwise 0. */\nint linenoiseEditInsert(struct linenoiseState *l, char c) {\n    if (l->len < l->buflen) {\n        if (l->len == l->pos) {\n            l->buf[l->pos] = c;\n            l->pos++;\n            l->len++;\n            l->buf[l->len] = '\\0';\n            if ((!mlmode && l->plen+l->len < l->cols) /* || mlmode */) {\n                /* Avoid a full update of the line in the\n                 * trivial case. */\n                if (write(l->ofd,&c,1) == -1) return -1;\n            } else {\n                refreshLine(l);\n            }\n        } else {\n            memmove(l->buf+l->pos+1,l->buf+l->pos,l->len-l->pos);\n            l->buf[l->pos] = c;\n            l->len++;\n            l->pos++;\n            l->buf[l->len] = '\\0';\n            refreshLine(l);\n        }\n    }\n    return 0;\n}\n\n/* Move cursor on the left. */\nvoid linenoiseEditMoveLeft(struct linenoiseState *l) {\n    if (l->pos > 0) {\n        l->pos--;\n        refreshLine(l);\n    }\n}\n\n/* Move cursor on the right. */\nvoid linenoiseEditMoveRight(struct linenoiseState *l) {\n    if (l->pos != l->len) {\n        l->pos++;\n        refreshLine(l);\n    }\n}\n\n/* Move cursor to the start of the line. */\nvoid linenoiseEditMoveHome(struct linenoiseState *l) {\n    if (l->pos != 0) {\n        l->pos = 0;\n        refreshLine(l);\n    }\n}\n\n/* Move cursor to the end of the line. */\nvoid linenoiseEditMoveEnd(struct linenoiseState *l) {\n    if (l->pos != l->len) {\n        l->pos = l->len;\n        refreshLine(l);\n    }\n}\n\n/* Substitute the currently edited line with the next or previous history\n * entry as specified by 'dir'. */\n#define LINENOISE_HISTORY_NEXT 0\n#define LINENOISE_HISTORY_PREV 1\nvoid linenoiseEditHistoryNext(struct linenoiseState *l, int dir) {\n    if (history_len > 1) {\n        /* Update the current history entry before to\n         * overwrite it with the next one. */\n        free(history[history_len - 1 - l->history_index]);\n        history[history_len - 1 - l->history_index] = strdup(l->buf);\n        /* Show the new entry */\n        l->history_index += (dir == LINENOISE_HISTORY_PREV) ? 1 : -1;\n        if (l->history_index < 0) {\n            l->history_index = 0;\n            return;\n        } else if (l->history_index >= history_len) {\n            l->history_index = history_len-1;\n            return;\n        }\n        strncpy(l->buf,history[history_len - 1 - l->history_index],l->buflen);\n        l->buf[l->buflen-1] = '\\0';\n        l->len = l->pos = strlen(l->buf);\n        refreshLine(l);\n    }\n}\n\n/* Delete the character at the right of the cursor without altering the cursor\n * position. Basically this is what happens with the \"Delete\" keyboard key. */\nvoid linenoiseEditDelete(struct linenoiseState *l) {\n    if (l->len > 0 && l->pos < l->len) {\n        memmove(l->buf+l->pos,l->buf+l->pos+1,l->len-l->pos-1);\n        l->len--;\n        l->buf[l->len] = '\\0';\n        refreshLine(l);\n    }\n}\n\n/* Backspace implementation. */\nvoid linenoiseEditBackspace(struct linenoiseState *l) {\n    if (l->pos > 0 && l->len > 0) {\n        memmove(l->buf+l->pos-1,l->buf+l->pos,l->len-l->pos);\n        l->pos--;\n        l->len--;\n        l->buf[l->len] = '\\0';\n        refreshLine(l);\n    }\n}\n\n/* Delete the previosu word, maintaining the cursor at the start of the\n * current word. */\nvoid linenoiseEditDeletePrevWord(struct linenoiseState *l) {\n    size_t old_pos = l->pos;\n    size_t diff;\n\n    while (l->pos > 0 && l->buf[l->pos-1] == ' ')\n        l->pos--;\n    while (l->pos > 0 && l->buf[l->pos-1] != ' ')\n        l->pos--;\n    diff = old_pos - l->pos;\n    memmove(l->buf+l->pos,l->buf+old_pos,l->len-old_pos+1);\n    l->len -= diff;\n    refreshLine(l);\n}\n\n/* This function is the core of the line editing capability of linenoise.\n * It expects 'fd' to be already in \"raw mode\" so that every key pressed\n * will be returned ASAP to read().\n *\n * The resulting string is put into 'buf' when the user type enter, or\n * when ctrl+d is typed.\n *\n * The function returns the length of the current buffer. */\nstatic int linenoiseEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen, const char *prompt)\n{\n    struct linenoiseState l;\n\n    /* Populate the linenoise state that we pass to functions implementing\n     * specific editing functionalities. */\n    l.ifd = stdin_fd;\n    l.ofd = stdout_fd;\n    l.buf = buf;\n    l.buflen = buflen;\n    l.prompt = prompt;\n    l.plen = strlen(prompt);\n    l.oldpos = l.pos = 0;\n    l.len = 0;\n    l.cols = getColumns(stdin_fd, stdout_fd);\n    l.maxrows = 0;\n    l.history_index = 0;\n\n    /* Buffer starts empty. */\n    l.buf[0] = '\\0';\n    l.buflen--; /* Make sure there is always space for the nulterm */\n\n    /* The latest history entry is always our current buffer, that\n     * initially is just an empty string. */\n    linenoiseHistoryAdd(\"\");\n    \n    if (write(l.ofd,prompt,l.plen) == -1) return -1;\n    while(1) {\n        char c;\n        int nread;\n        char seq[3];\n\n        nread = read(l.ifd,&c,1);\n        if (nread <= 0) return l.len;\n\n        /* Only autocomplete when the callback is set. It returns < 0 when\n         * there was an error reading from fd. Otherwise it will return the\n         * character that should be handled next. */\n        if (c == 9 && completionCallback != NULL) {\n            c = completeLine(&l);\n            /* Return on errors */\n            if (c < 0) return l.len;\n            /* Read next character when 0 */\n            if (c == 0) continue;\n        }\n\n        switch(c) {\n        case ENTER:    /* enter */\n            history_len--;\n            free(history[history_len]);\n            return (int)l.len;\n        case CTRL_C:     /* ctrl-c */\n            errno = EAGAIN;\n            return -1;\n        case BACKSPACE:   /* backspace */\n        case 8:     /* ctrl-h */\n            linenoiseEditBackspace(&l);\n            break;\n        case CTRL_D:     /* ctrl-d, remove char at right of cursor, or of the\n                       line is empty, act as end-of-file. */\n            if (l.len > 0) {\n                linenoiseEditDelete(&l);\n            } else {\n                history_len--;\n                free(history[history_len]);\n                return -1;\n            }\n            break;\n        case CTRL_T:    /* ctrl-t, swaps current character with previous. */\n            if (l.pos > 0 && l.pos < l.len) {\n                int aux = buf[l.pos-1];\n                buf[l.pos-1] = buf[l.pos];\n                buf[l.pos] = aux;\n                if (l.pos != l.len-1) l.pos++;\n                refreshLine(&l);\n            }\n            break;\n        case CTRL_B:     /* ctrl-b */\n            linenoiseEditMoveLeft(&l);\n            break;\n        case CTRL_F:     /* ctrl-f */\n            linenoiseEditMoveRight(&l);\n            break;\n        case CTRL_P:    /* ctrl-p */\n            linenoiseEditHistoryNext(&l, LINENOISE_HISTORY_PREV);\n            break;\n        case CTRL_N:    /* ctrl-n */\n            linenoiseEditHistoryNext(&l, LINENOISE_HISTORY_NEXT);\n            break;\n        case ESC:    /* escape sequence */\n            /* Read the next two bytes representing the escape sequence.\n             * Use two calls to handle slow terminals returning the two\n             * chars at different times. */\n            if (read(l.ifd,seq,1) == -1) break;\n            if (read(l.ifd,seq+1,1) == -1) break;\n\n            /* ESC [ sequences. */\n            if (seq[0] == '[') {\n                if (seq[1] >= '0' && seq[1] <= '9') {\n                    /* Extended escape, read additional byte. */\n                    if (read(l.ifd,seq+2,1) == -1) break;\n                    if (seq[2] == '~') {\n                        switch(seq[1]) {\n                        case '3': /* Delete key. */\n                            linenoiseEditDelete(&l);\n                            break;\n                        }\n                    }\n                } else {\n                    switch(seq[1]) {\n                    case 'A': /* Up */\n                        linenoiseEditHistoryNext(&l, LINENOISE_HISTORY_PREV);\n                        break;\n                    case 'B': /* Down */\n                        linenoiseEditHistoryNext(&l, LINENOISE_HISTORY_NEXT);\n                        break;\n                    case 'C': /* Right */\n                        linenoiseEditMoveRight(&l);\n                        break;\n                    case 'D': /* Left */\n                        linenoiseEditMoveLeft(&l);\n                        break;\n                    case 'H': /* Home */\n                        linenoiseEditMoveHome(&l);\n                        break;\n                    case 'F': /* End*/\n                        linenoiseEditMoveEnd(&l);\n                        break;\n                    }\n                }\n            }\n\n            /* ESC O sequences. */\n            else if (seq[0] == 'O') {\n                switch(seq[1]) {\n                case 'H': /* Home */\n                    linenoiseEditMoveHome(&l);\n                    break;\n                case 'F': /* End*/\n                    linenoiseEditMoveEnd(&l);\n                    break;\n                }\n            }\n            break;\n        default:\n            if (linenoiseEditInsert(&l,c)) return -1;\n            break;\n        case CTRL_U: /* Ctrl+u, delete the whole line. */\n            buf[0] = '\\0';\n            l.pos = l.len = 0;\n            refreshLine(&l);\n            break;\n        case CTRL_K: /* Ctrl+k, delete from current to end of line. */\n            buf[l.pos] = '\\0';\n            l.len = l.pos;\n            refreshLine(&l);\n            break;\n        case CTRL_A: /* Ctrl+a, go to the start of the line */\n            linenoiseEditMoveHome(&l);\n            break;\n        case CTRL_E: /* ctrl+e, go to the end of the line */\n            linenoiseEditMoveEnd(&l);\n            break;\n        case CTRL_L: /* ctrl+l, clear screen */\n            linenoiseClearScreen();\n            refreshLine(&l);\n            break;\n        case CTRL_W: /* ctrl+w, delete previous word */\n            linenoiseEditDeletePrevWord(&l);\n            break;\n        }\n    }\n    return l.len;\n}\n\n/* This special mode is used by linenoise in order to print scan codes\n * on screen for debugging / development purposes. It is implemented\n * by the linenoise_example program using the --keycodes option. */\nvoid linenoisePrintKeyCodes(void) {\n    char quit[4];\n\n    printf(\"Linenoise key codes debugging mode.\\n\"\n            \"Press keys to see scan codes. Type 'quit' at any time to exit.\\n\");\n    if (enableRawMode(STDIN_FILENO) == -1) return;\n    memset(quit,' ',4);\n    while(1) {\n        char c;\n        int nread;\n\n        nread = read(STDIN_FILENO,&c,1);\n        if (nread <= 0) continue;\n        memmove(quit,quit+1,sizeof(quit)-1); /* shift string to left. */\n        quit[sizeof(quit)-1] = c; /* Insert current char on the right. */\n        if (memcmp(quit,\"quit\",sizeof(quit)) == 0) break;\n\n        printf(\"'%c' %02x (%d) (type quit to exit)\\n\",\n            isprint(c) ? c : '?', (int)c, (int)c);\n        printf(\"\\x1b[0G\"); /* Go left edge manually, we are in raw mode. */\n        fflush(stdout);\n    }\n    disableRawMode(STDIN_FILENO);\n}\n\n/* This function calls the line editing function linenoiseEdit() using\n * the STDIN file descriptor set in raw mode. */\nstatic int linenoiseRaw(char *buf, size_t buflen, const char *prompt) {\n    int count;\n\n    if (buflen == 0) {\n        errno = EINVAL;\n        return -1;\n    }\n    if (!isatty(STDIN_FILENO)) {\n        /* Not a tty: read from file / pipe. */\n        if (fgets(buf, buflen, stdin) == NULL) return -1;\n        count = strlen(buf);\n        if (count && buf[count-1] == '\\n') {\n            count--;\n            buf[count] = '\\0';\n        }\n    } else {\n        /* Interactive editing. */\n        if (enableRawMode(STDIN_FILENO) == -1) return -1;\n        count = linenoiseEdit(STDIN_FILENO, STDOUT_FILENO, buf, buflen, prompt);\n        disableRawMode(STDIN_FILENO);\n        printf(\"\\n\");\n    }\n    return count;\n}\n\n/* The high level function that is the main API of the linenoise library.\n * This function checks if the terminal has basic capabilities, just checking\n * for a blacklist of stupid terminals, and later either calls the line\n * editing function or uses dummy fgets() so that you will be able to type\n * something even in the most desperate of the conditions. */\nchar *linenoise(const char *prompt) {\n    char buf[LINENOISE_MAX_LINE];\n    int count;\n\n    if (isUnsupportedTerm()) {\n        size_t len;\n\n        printf(\"%s\",prompt);\n        fflush(stdout);\n        if (fgets(buf,LINENOISE_MAX_LINE,stdin) == NULL) return NULL;\n        len = strlen(buf);\n        while(len && (buf[len-1] == '\\n' || buf[len-1] == '\\r')) {\n            len--;\n            buf[len] = '\\0';\n        }\n        return strdup(buf);\n    } else {\n        count = linenoiseRaw(buf,LINENOISE_MAX_LINE,prompt);\n        if (count == -1) return NULL;\n        return strdup(buf);\n    }\n}\n\n/* ================================ History ================================= */\n\n/* Free the history, but does not reset it. Only used when we have to\n * exit() to avoid memory leaks are reported by valgrind & co. */\nstatic void freeHistory(void) {\n    if (history) {\n        int j;\n\n        for (j = 0; j < history_len; j++)\n            free(history[j]);\n        free(history);\n    }\n}\n\n/* At exit we'll try to fix the terminal to the initial conditions. */\nstatic void linenoiseAtExit(void) {\n    disableRawMode(STDIN_FILENO);\n    freeHistory();\n}\n\n/* This is the API call to add a new entry in the linenoise history.\n * It uses a fixed array of char pointers that are shifted (memmoved)\n * when the history max length is reached in order to remove the older\n * entry and make room for the new one, so it is not exactly suitable for huge\n * histories, but will work well for a few hundred of entries.\n *\n * Using a circular buffer is smarter, but a bit more complex to handle. */\nint linenoiseHistoryAdd(const char *line) {\n    char *linecopy;\n\n    if (history_max_len == 0) return 0;\n\n    /* Initialization on first call. */\n    if (history == NULL) {\n        history = malloc(sizeof(char*)*history_max_len);\n        if (history == NULL) return 0;\n        memset(history,0,(sizeof(char*)*history_max_len));\n    }\n\n    /* Don't add duplicated lines. */\n    if (history_len && !strcmp(history[history_len-1], line)) return 0;\n\n    /* Add an heap allocated copy of the line in the history.\n     * If we reached the max length, remove the older line. */\n    linecopy = strdup(line);\n    if (!linecopy) return 0;\n    if (history_len == history_max_len) {\n        free(history[0]);\n        memmove(history,history+1,sizeof(char*)*(history_max_len-1));\n        history_len--;\n    }\n    history[history_len] = linecopy;\n    history_len++;\n    return 1;\n}\n\n/* Set the maximum length for the history. This function can be called even\n * if there is already some history, the function will make sure to retain\n * just the latest 'len' elements if the new history length value is smaller\n * than the amount of items already inside the history. */\nint linenoiseHistorySetMaxLen(int len) {\n    char **new;\n\n    if (len < 1) return 0;\n    if (history) {\n        int tocopy = history_len;\n\n        new = malloc(sizeof(char*)*len);\n        if (new == NULL) return 0;\n\n        /* If we can't copy everything, free the elements we'll not use. */\n        if (len < tocopy) {\n            int j;\n\n            for (j = 0; j < tocopy-len; j++) free(history[j]);\n            tocopy = len;\n        }\n        memset(new,0,sizeof(char*)*len);\n        memcpy(new,history+(history_len-tocopy), sizeof(char*)*tocopy);\n        free(history);\n        history = new;\n    }\n    history_max_len = len;\n    if (history_len > history_max_len)\n        history_len = history_max_len;\n    return 1;\n}\n\n/* Save the history in the specified file. On success 0 is returned\n * otherwise -1 is returned. */\nint linenoiseHistorySave(const char *filename) {\n    FILE *fp = fopen(filename,\"w\");\n    int j;\n    \n    if (fp == NULL) return -1;\n    for (j = 0; j < history_len; j++)\n        fprintf(fp,\"%s\\n\",history[j]);\n    fclose(fp);\n    return 0;\n}\n\n/* Load the history from the specified file. If the file does not exist\n * zero is returned and no operation is performed.\n *\n * If the file exists and the operation succeeded 0 is returned, otherwise\n * on error -1 is returned. */\nint linenoiseHistoryLoad(const char *filename) {\n    FILE *fp = fopen(filename,\"r\");\n    char buf[LINENOISE_MAX_LINE];\n    \n    if (fp == NULL) return -1;\n\n    while (fgets(buf,LINENOISE_MAX_LINE,fp) != NULL) {\n        char *p;\n        \n        p = strchr(buf,'\\r');\n        if (!p) p = strchr(buf,'\\n');\n        if (p) *p = '\\0';\n        linenoiseHistoryAdd(buf);\n    }\n    fclose(fp);\n    return 0;\n}\n"
  },
  {
    "path": "deps/linenoise/linenoise.h",
    "content": "/* linenoise.h -- guerrilla line editing library against the idea that a\n * line editing lib needs to be 20,000 lines of C code.\n *\n * See linenoise.c for more information.\n *\n * ------------------------------------------------------------------------\n *\n * Copyright (c) 2010, Salvatore Sanfilippo <antirez at gmail dot com>\n * Copyright (c) 2010, Pieter Noordhuis <pcnoordhuis at gmail dot com>\n *\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\n * met:\n * \n *  *  Redistributions of source code must retain the above copyright\n *     notice, this list of conditions and the following disclaimer.\n *\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 * \n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#ifndef __LINENOISE_H\n#define __LINENOISE_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct linenoiseCompletions {\n  size_t len;\n  char **cvec;\n} linenoiseCompletions;\n\ntypedef void(linenoiseCompletionCallback)(const char *, linenoiseCompletions *);\nvoid linenoiseSetCompletionCallback(linenoiseCompletionCallback *);\nvoid linenoiseAddCompletion(linenoiseCompletions *, const char *);\n\nchar *linenoise(const char *prompt);\nint linenoiseHistoryAdd(const char *line);\nint linenoiseHistorySetMaxLen(int len);\nint linenoiseHistorySave(const char *filename);\nint linenoiseHistoryLoad(const char *filename);\nvoid linenoiseClearScreen(void);\nvoid linenoiseSetMultiLine(int ml);\nvoid linenoisePrintKeyCodes(void);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* __LINENOISE_H */\n"
  },
  {
    "path": "deps/lua/COPYRIGHT",
    "content": "Lua License\n-----------\n\nLua is licensed under the terms of the MIT license reproduced below.\nThis means that Lua is free software and can be used for both academic\nand commercial purposes at absolutely no cost.\n\nFor details and rationale, see http://www.lua.org/license.html .\n\n===============================================================================\n\nCopyright (C) 1994-2012 Lua.org, PUC-Rio.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n\n===============================================================================\n\n(end of COPYRIGHT)\n"
  },
  {
    "path": "deps/lua/HISTORY",
    "content": "HISTORY for Lua 5.1\n\n* Changes from version 5.0 to 5.1\n  -------------------------------\n  Language:\n  + new module system.\n  + new semantics for control variables of fors.\n  + new semantics for setn/getn.\n  + new syntax/semantics for varargs.\n  + new long strings and comments.\n  + new `mod' operator (`%')\n  + new length operator #t\n  + metatables for all types\n  API:\n  + new functions: lua_createtable, lua_get(set)field, lua_push(to)integer.\n  + user supplies memory allocator (lua_open becomes lua_newstate).\n  + luaopen_* functions must be called through Lua.\n  Implementation:\n  + new configuration scheme via luaconf.h.\n  + incremental garbage collection.\n  + better handling of end-of-line in the lexer.\n  + fully reentrant parser (new Lua function `load')\n  + better support for 64-bit machines.\n  + native loadlib support for Mac OS X.\n  + standard distribution in only one library (lualib.a merged into lua.a)\n\n* Changes from version 4.0 to 5.0\n  -------------------------------\n  Language:\n  + lexical scoping.\n  + Lua coroutines.\n  + standard libraries now packaged in tables.\n  + tags replaced by metatables and tag methods replaced by metamethods,\n    stored in metatables.\n  + proper tail calls.\n  + each function can have its own global table, which can be shared.\n  + new __newindex metamethod, called when we insert a new key into a table.\n  + new block comments: --[[ ... ]].\n  + new generic for.\n  + new weak tables.\n  + new boolean type.\n  + new syntax \"local function\".\n  + (f()) returns the first value returned by f.\n  + {f()} fills a table with all values returned by f.\n  + \\n ignored in [[\\n .\n  + fixed and-or priorities.\n  + more general syntax for function definition (e.g. function a.x.y:f()...end).\n  + more general syntax for function calls (e.g. (print or write)(9)).\n  + new functions (time/date, tmpfile, unpack, require, load*, etc.).\n  API:\n  + chunks are loaded by using lua_load; new luaL_loadfile and luaL_loadbuffer.\n  + introduced lightweight userdata, a simple \"void*\" without a metatable.\n  + new error handling protocol: the core no longer prints error messages;\n    all errors are reported to the caller on the stack.\n  + new lua_atpanic for host cleanup.\n  + new, signal-safe, hook scheme.\n  Implementation:\n  + new license: MIT.\n  + new, faster, register-based virtual machine.\n  + support for external multithreading and coroutines.\n  + new and consistent error message format.\n  + the core no longer needs \"stdio.h\" for anything (except for a single\n    use of sprintf to convert numbers to strings).\n  + lua.c now runs the environment variable LUA_INIT, if present. It can\n    be \"@filename\", to run a file, or the chunk itself.\n  + support for user extensions in lua.c.\n    sample implementation given for command line editing.\n  + new dynamic loading library, active by default on several platforms.\n  + safe garbage-collector metamethods.\n  + precompiled bytecodes checked for integrity (secure binary dostring).\n  + strings are fully aligned.\n  + position capture in string.find.\n  + read('*l') can read lines with embedded zeros.\n\n* Changes from version 3.2 to 4.0\n  -------------------------------\n  Language:\n  + new \"break\" and \"for\" statements (both numerical and for tables).\n  + uniform treatment of globals: globals are now stored in a Lua table.\n  + improved error messages.\n  + no more '$debug': full speed *and* full debug information.\n  + new read form: read(N) for next N bytes.\n  + general read patterns now deprecated.\n    (still available with -DCOMPAT_READPATTERNS.)\n  + all return values are passed as arguments for the last function\n    (old semantics still available with -DLUA_COMPAT_ARGRET)\n  + garbage collection tag methods for tables now deprecated.\n  + there is now only one tag method for order.\n  API:\n  + New API: fully re-entrant, simpler, and more efficient.\n  + New debug API.\n  Implementation:\n  + faster than ever: cleaner virtual machine and new hashing algorithm.\n  + non-recursive garbage-collector algorithm.\n  + reduced memory usage for programs with many strings.\n  + improved treatment for memory allocation errors.\n  + improved support for 16-bit machines (we hope).\n  + code now compiles unmodified as both ANSI C and C++.\n  + numbers in bases other than 10 are converted using strtoul.\n  + new -f option in Lua to support #! scripts.\n  + luac can now combine text and binaries.\n\n* Changes from version 3.1 to 3.2\n  -------------------------------\n  + redirected all output in Lua's core to _ERRORMESSAGE and _ALERT.\n  + increased limit on the number of constants and globals per function\n    (from 2^16 to 2^24).\n  + debugging info (lua_debug and hooks) moved into lua_state and new API\n    functions provided to get and set this info.\n  + new debug lib gives full debugging access within Lua.\n  + new table functions \"foreachi\", \"sort\", \"tinsert\", \"tremove\", \"getn\".\n  + new io functions \"flush\", \"seek\".\n\n* Changes from version 3.0 to 3.1\n  -------------------------------\n  + NEW FEATURE: anonymous functions with closures (via \"upvalues\").\n  + new syntax:\n    - local variables in chunks.\n    - better scope control with DO block END.\n    - constructors can now be also written: { record-part; list-part }.\n    - more general syntax for function calls and lvalues, e.g.:\n      f(x).y=1\n      o:f(x,y):g(z)\n      f\"string\" is sugar for f(\"string\")\n  + strings may now contain arbitrary binary data (e.g., embedded zeros).\n  + major code re-organization and clean-up; reduced module interdependecies.\n  + no arbitrary limits on the total number of constants and globals.\n  + support for multiple global contexts.\n  + better syntax error messages.\n  + new traversal functions \"foreach\" and \"foreachvar\".\n  + the default for numbers is now double.\n    changing it to use floats or longs is easy.\n  + complete debug information stored in pre-compiled chunks.\n  + sample interpreter now prompts user when run interactively, and also\n    handles control-C interruptions gracefully.\n\n* Changes from version 2.5 to 3.0\n  -------------------------------\n  + NEW CONCEPT: \"tag methods\".\n    Tag methods replace fallbacks as the meta-mechanism for extending the\n    semantics of Lua. Whereas fallbacks had a global nature, tag methods\n    work on objects having the same tag (e.g., groups of tables).\n    Existing code that uses fallbacks should work without change.\n  + new, general syntax for constructors {[exp] = exp, ... }.\n  + support for handling variable number of arguments in functions (varargs).\n  + support for conditional compilation ($if ... $else ... $end).\n  + cleaner semantics in API simplifies host code.\n  + better support for writing libraries (auxlib.h).\n  + better type checking and error messages in the standard library.\n  + luac can now also undump.\n\n* Changes from version 2.4 to 2.5\n  -------------------------------\n  + io and string libraries are now based on pattern matching;\n    the old libraries are still available for compatibility\n  + dofile and dostring can now return values (via return statement)\n  + better support for 16- and 64-bit machines\n  + expanded documentation, with more examples\n\n* Changes from version 2.2 to 2.4\n  -------------------------------\n  + external compiler creates portable binary files that can be loaded faster\n  + interface for debugging and profiling\n  + new \"getglobal\" fallback\n  + new functions for handling references to Lua objects\n  + new functions in standard lib\n  + only one copy of each string is stored\n  + expanded documentation, with more examples\n\n* Changes from version 2.1 to 2.2\n  -------------------------------\n  + functions now may be declared with any \"lvalue\" as a name\n  + garbage collection of functions\n  + support for pipes\n\n* Changes from version 1.1 to 2.1\n  -------------------------------\n  + object-oriented support\n  + fallbacks\n  + simplified syntax for tables\n  + many internal improvements\n\n(end of HISTORY)\n"
  },
  {
    "path": "deps/lua/INSTALL",
    "content": "INSTALL for Lua 5.1\n\n* Building Lua\n  ------------\n  Lua is built in the src directory, but the build process can be\n  controlled from the top-level Makefile.\n\n  Building Lua on Unix systems should be very easy. First do \"make\" and\n  see if your platform is listed. If so, just do \"make xxx\", where xxx\n  is your platform name. The platforms currently supported are:\n    aix ansi bsd freebsd generic linux macosx mingw posix solaris\n\n  If your platform is not listed, try the closest one or posix, generic,\n  ansi, in this order.\n\n  See below for customization instructions and for instructions on how\n  to build with other Windows compilers.\n\n  If you want to check that Lua has been built correctly, do \"make test\"\n  after building Lua. Also, have a look at the example programs in test.\n\n* Installing Lua\n  --------------\n  Once you have built Lua, you may want to install it in an official\n  place in your system. In this case, do \"make install\". The official\n  place and the way to install files are defined in Makefile. You must\n  have the right permissions to install files.\n\n  If you want to build and install Lua in one step, do \"make xxx install\",\n  where xxx is your platform name.\n\n  If you want to install Lua locally, then do \"make local\". This will\n  create directories bin, include, lib, man, and install Lua there as\n  follows:\n\n    bin:\tlua luac\n    include:\tlua.h luaconf.h lualib.h lauxlib.h lua.hpp\n    lib:\tliblua.a\n    man/man1:\tlua.1 luac.1\n\n  These are the only directories you need for development.\n\n  There are man pages for lua and luac, in both nroff and html, and a\n  reference manual in html in doc, some sample code in test, and some\n  useful stuff in etc. You don't need these directories for development.\n\n  If you want to install Lua locally, but in some other directory, do\n  \"make install INSTALL_TOP=xxx\", where xxx is your chosen directory.\n\n  See below for instructions for Windows and other systems.\n\n* Customization\n  -------------\n  Three things can be customized by editing a file:\n    - Where and how to install Lua -- edit Makefile.\n    - How to build Lua -- edit src/Makefile.\n    - Lua features -- edit src/luaconf.h.\n\n  You don't actually need to edit the Makefiles because you may set the\n  relevant variables when invoking make.\n\n  On the other hand, if you need to select some Lua features, you'll need\n  to edit src/luaconf.h. The edited file will be the one installed, and\n  it will be used by any Lua clients that you build, to ensure consistency.\n\n  We strongly recommend that you enable dynamic loading. This is done\n  automatically for all platforms listed above that have this feature\n  (and also Windows). See src/luaconf.h and also src/Makefile.\n\n* Building Lua on Windows and other systems\n  -----------------------------------------\n  If you're not using the usual Unix tools, then the instructions for\n  building Lua depend on the compiler you use. You'll need to create\n  projects (or whatever your compiler uses) for building the library,\n  the interpreter, and the compiler, as follows:\n\n  library:\tlapi.c lcode.c ldebug.c ldo.c ldump.c lfunc.c lgc.c llex.c\n\t\tlmem.c lobject.c lopcodes.c lparser.c lstate.c lstring.c\n\t\tltable.c ltm.c lundump.c lvm.c lzio.c\n\t\tlauxlib.c lbaselib.c ldblib.c liolib.c lmathlib.c loslib.c\n\t\tltablib.c lstrlib.c loadlib.c linit.c\n\n  interpreter:\tlibrary, lua.c\n\n  compiler:\tlibrary, luac.c print.c\n\n  If you use Visual Studio .NET, you can use etc/luavs.bat in its\n  \"Command Prompt\".\n\n  If all you want is to build the Lua interpreter, you may put all .c files\n  in a single project, except for luac.c and print.c. Or just use etc/all.c.\n\n  To use Lua as a library in your own programs, you'll need to know how to\n  create and use libraries with your compiler.\n\n  As mentioned above, you may edit luaconf.h to select some features before\n  building Lua.\n\n(end of INSTALL)\n"
  },
  {
    "path": "deps/lua/Makefile",
    "content": "# makefile for installing Lua\n# see INSTALL for installation instructions\n# see src/Makefile and src/luaconf.h for further customization\n\n# == CHANGE THE SETTINGS BELOW TO SUIT YOUR ENVIRONMENT =======================\n\n# Your platform. See PLATS for possible values.\nPLAT= none\n\n# Where to install. The installation starts in the src and doc directories,\n# so take care if INSTALL_TOP is not an absolute path.\nINSTALL_TOP= /usr/local\nINSTALL_BIN= $(INSTALL_TOP)/bin\nINSTALL_INC= $(INSTALL_TOP)/include\nINSTALL_LIB= $(INSTALL_TOP)/lib\nINSTALL_MAN= $(INSTALL_TOP)/man/man1\n#\n# You probably want to make INSTALL_LMOD and INSTALL_CMOD consistent with\n# LUA_ROOT, LUA_LDIR, and LUA_CDIR in luaconf.h (and also with etc/lua.pc).\nINSTALL_LMOD= $(INSTALL_TOP)/share/lua/$V\nINSTALL_CMOD= $(INSTALL_TOP)/lib/lua/$V\n\n# How to install. If your install program does not support \"-p\", then you\n# may have to run ranlib on the installed liblua.a (do \"make ranlib\").\nINSTALL= install -p\nINSTALL_EXEC= $(INSTALL) -m 0755\nINSTALL_DATA= $(INSTALL) -m 0644\n#\n# If you don't have install you can use cp instead.\n# INSTALL= cp -p\n# INSTALL_EXEC= $(INSTALL)\n# INSTALL_DATA= $(INSTALL)\n\n# Utilities.\nMKDIR= mkdir -p\nRANLIB= ranlib\n\n# == END OF USER SETTINGS. NO NEED TO CHANGE ANYTHING BELOW THIS LINE =========\n\n# Convenience platforms targets.\nPLATS= aix ansi bsd freebsd generic linux macosx mingw posix solaris\n\n# What to install.\nTO_BIN= lua luac\nTO_INC= lua.h luaconf.h lualib.h lauxlib.h ../etc/lua.hpp\nTO_LIB= liblua.a\nTO_MAN= lua.1 luac.1\n\n# Lua version and release.\nV= 5.1\nR= 5.1.5\n\nall:\t$(PLAT)\n\n$(PLATS) clean:\n\tcd src && $(MAKE) $@\n\ntest:\tdummy\n\tsrc/lua test/hello.lua\n\ninstall: dummy\n\tcd src && $(MKDIR) $(INSTALL_BIN) $(INSTALL_INC) $(INSTALL_LIB) $(INSTALL_MAN) $(INSTALL_LMOD) $(INSTALL_CMOD)\n\tcd src && $(INSTALL_EXEC) $(TO_BIN) $(INSTALL_BIN)\n\tcd src && $(INSTALL_DATA) $(TO_INC) $(INSTALL_INC)\n\tcd src && $(INSTALL_DATA) $(TO_LIB) $(INSTALL_LIB)\n\tcd doc && $(INSTALL_DATA) $(TO_MAN) $(INSTALL_MAN)\n\nranlib:\n\tcd src && cd $(INSTALL_LIB) && $(RANLIB) $(TO_LIB)\n\nlocal:\n\t$(MAKE) install INSTALL_TOP=..\n\nnone:\n\t@echo \"Please do\"\n\t@echo \"   make PLATFORM\"\n\t@echo \"where PLATFORM is one of these:\"\n\t@echo \"   $(PLATS)\"\n\t@echo \"See INSTALL for complete instructions.\"\n\n# make may get confused with test/ and INSTALL in a case-insensitive OS\ndummy:\n\n# echo config parameters\necho:\n\t@echo \"\"\n\t@echo \"These are the parameters currently set in src/Makefile to build Lua $R:\"\n\t@echo \"\"\n\t@cd src && $(MAKE) -s echo\n\t@echo \"\"\n\t@echo \"These are the parameters currently set in Makefile to install Lua $R:\"\n\t@echo \"\"\n\t@echo \"PLAT = $(PLAT)\"\n\t@echo \"INSTALL_TOP = $(INSTALL_TOP)\"\n\t@echo \"INSTALL_BIN = $(INSTALL_BIN)\"\n\t@echo \"INSTALL_INC = $(INSTALL_INC)\"\n\t@echo \"INSTALL_LIB = $(INSTALL_LIB)\"\n\t@echo \"INSTALL_MAN = $(INSTALL_MAN)\"\n\t@echo \"INSTALL_LMOD = $(INSTALL_LMOD)\"\n\t@echo \"INSTALL_CMOD = $(INSTALL_CMOD)\"\n\t@echo \"INSTALL_EXEC = $(INSTALL_EXEC)\"\n\t@echo \"INSTALL_DATA = $(INSTALL_DATA)\"\n\t@echo \"\"\n\t@echo \"See also src/luaconf.h .\"\n\t@echo \"\"\n\n# echo private config parameters\npecho:\n\t@echo \"V = $(V)\"\n\t@echo \"R = $(R)\"\n\t@echo \"TO_BIN = $(TO_BIN)\"\n\t@echo \"TO_INC = $(TO_INC)\"\n\t@echo \"TO_LIB = $(TO_LIB)\"\n\t@echo \"TO_MAN = $(TO_MAN)\"\n\n# echo config parameters as Lua code\n# uncomment the last sed expression if you want nil instead of empty strings\nlecho:\n\t@echo \"-- installation parameters for Lua $R\"\n\t@echo \"VERSION = '$V'\"\n\t@echo \"RELEASE = '$R'\"\n\t@$(MAKE) echo | grep = | sed -e 's/= /= \"/' -e 's/$$/\"/' #-e 's/\"\"/nil/'\n\t@echo \"-- EOF\"\n\n# list targets that do not create files (but not all makes understand .PHONY)\n.PHONY: all $(PLATS) clean test install local none dummy echo pecho lecho\n\n# (end of Makefile)\n"
  },
  {
    "path": "deps/lua/README",
    "content": "README for Lua 5.1\n\nSee INSTALL for installation instructions.\nSee HISTORY for a summary of changes since the last released version.\n\n* What is Lua?\n  ------------\n  Lua is a powerful, light-weight programming language designed for extending\n  applications. Lua is also frequently used as a general-purpose, stand-alone\n  language. Lua is free software.\n\n  For complete information, visit Lua's web site at http://www.lua.org/ .\n  For an executive summary, see http://www.lua.org/about.html .\n\n  Lua has been used in many different projects around the world.\n  For a short list, see http://www.lua.org/uses.html .\n\n* Availability\n  ------------\n  Lua is freely available for both academic and commercial purposes.\n  See COPYRIGHT and http://www.lua.org/license.html for details.\n  Lua can be downloaded at http://www.lua.org/download.html .\n\n* Installation\n  ------------\n  Lua is implemented in pure ANSI C, and compiles unmodified in all known\n  platforms that have an ANSI C compiler. In most Unix-like platforms, simply\n  do \"make\" with a suitable target. See INSTALL for detailed instructions.\n\n* Origin\n  ------\n  Lua is developed at Lua.org, a laboratory of the Department of Computer\n  Science of PUC-Rio (the Pontifical Catholic University of Rio de Janeiro\n  in Brazil).\n  For more information about the authors, see http://www.lua.org/authors.html .\n\n(end of README)\n"
  },
  {
    "path": "deps/lua/doc/contents.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n<HTML>\n<HEAD>\n<TITLE>Lua 5.1 Reference Manual - contents</TITLE>\n<LINK REL=\"stylesheet\" TYPE=\"text/css\" HREF=\"lua.css\">\n<META HTTP-EQUIV=\"content-type\" CONTENT=\"text/html; charset=utf-8\">\n<STYLE TYPE=\"text/css\">\nul {\n\tlist-style-type: none ;\n}\n</STYLE>\n</HEAD>\n\n<BODY>\n\n<HR>\n<H1>\n<A HREF=\"http://www.lua.org/\"><IMG SRC=\"logo.gif\" ALT=\"\" BORDER=0></A>\nLua 5.1 Reference Manual\n</H1>\n\n<P>\nThe reference manual is the official definition of the Lua language.\nFor a complete introduction to Lua programming, see the book\n<A HREF=\"http://www.lua.org/docs.html#pil\">Programming in Lua</A>.\n\n<P>\nThis manual is also available as a book:\n<BLOCKQUOTE>\n<A HREF=\"http://www.amazon.com/exec/obidos/ASIN/8590379833/lua-indexmanual-20\">\n<IMG SRC=\"cover.png\" ALT=\"\" TITLE=\"buy from Amazon\" BORDER=1 ALIGN=\"left\" HSPACE=12>\n</A>\n<B>Lua 5.1 Reference Manual</B>\n<BR>by R. Ierusalimschy, L. H. de Figueiredo, W. Celes\n<BR>Lua.org, August 2006\n<BR>ISBN 85-903798-3-3\n<BR CLEAR=\"all\">\n</BLOCKQUOTE>\n\n<P>\n<A HREF=\"http://www.amazon.com/exec/obidos/ASIN/8590379833/lua-indexmanual-20\">Buy a copy</A>\nof this book and\n<A HREF=\"http://www.lua.org/donations.html\">help to support</A>\nthe Lua project.\n\n<P>\n<A HREF=\"manual.html\">start</A>\n&middot;\n<A HREF=\"#contents\">contents</A>\n&middot;\n<A HREF=\"#index\">index</A>\n&middot;\n<A HREF=\"http://www.lua.org/manual/\">other versions</A>\n<HR>\n<SMALL>\nCopyright &copy; 2006&ndash;2012 Lua.org, PUC-Rio.\nFreely available under the terms of the\n<A HREF=\"http://www.lua.org/license.html\">Lua license</A>.\n</SMALL>\n\n<H2><A NAME=\"contents\">Contents</A></H2>\n<UL style=\"padding: 0\">\n<LI><A HREF=\"manual.html\">1 &ndash; Introduction</A>\n<P>\n<LI><A HREF=\"manual.html#2\">2 &ndash; The Language</A>\n<UL>\n<LI><A HREF=\"manual.html#2.1\">2.1 &ndash; Lexical Conventions</A>\n<LI><A HREF=\"manual.html#2.2\">2.2 &ndash; Values and Types</A>\n<UL>\n<LI><A HREF=\"manual.html#2.2.1\">2.2.1 &ndash; Coercion</A>\n</UL>\n<LI><A HREF=\"manual.html#2.3\">2.3 &ndash; Variables</A>\n<LI><A HREF=\"manual.html#2.4\">2.4 &ndash; Statements</A>\n<UL>\n<LI><A HREF=\"manual.html#2.4.1\">2.4.1 &ndash; Chunks</A>\n<LI><A HREF=\"manual.html#2.4.2\">2.4.2 &ndash; Blocks</A>\n<LI><A HREF=\"manual.html#2.4.3\">2.4.3 &ndash; Assignment</A>\n<LI><A HREF=\"manual.html#2.4.4\">2.4.4 &ndash; Control Structures</A>\n<LI><A HREF=\"manual.html#2.4.5\">2.4.5 &ndash; For Statement</A>\n<LI><A HREF=\"manual.html#2.4.6\">2.4.6 &ndash; Function Calls as Statements</A>\n<LI><A HREF=\"manual.html#2.4.7\">2.4.7 &ndash; Local Declarations</A>\n</UL>\n<LI><A HREF=\"manual.html#2.5\">2.5 &ndash; Expressions</A>\n<UL>\n<LI><A HREF=\"manual.html#2.5.1\">2.5.1 &ndash; Arithmetic Operators</A>\n<LI><A HREF=\"manual.html#2.5.2\">2.5.2 &ndash; Relational Operators</A>\n<LI><A HREF=\"manual.html#2.5.3\">2.5.3 &ndash; Logical Operators</A>\n<LI><A HREF=\"manual.html#2.5.4\">2.5.4 &ndash; Concatenation</A>\n<LI><A HREF=\"manual.html#2.5.5\">2.5.5 &ndash; The Length Operator</A>\n<LI><A HREF=\"manual.html#2.5.6\">2.5.6 &ndash; Precedence</A>\n<LI><A HREF=\"manual.html#2.5.7\">2.5.7 &ndash; Table Constructors</A>\n<LI><A HREF=\"manual.html#2.5.8\">2.5.8 &ndash; Function Calls</A>\n<LI><A HREF=\"manual.html#2.5.9\">2.5.9 &ndash; Function Definitions</A>\n</UL>\n<LI><A HREF=\"manual.html#2.6\">2.6 &ndash; Visibility Rules</A>\n<LI><A HREF=\"manual.html#2.7\">2.7 &ndash; Error Handling</A>\n<LI><A HREF=\"manual.html#2.8\">2.8 &ndash; Metatables</A>\n<LI><A HREF=\"manual.html#2.9\">2.9 &ndash; Environments</A>\n<LI><A HREF=\"manual.html#2.10\">2.10 &ndash; Garbage Collection</A>\n<UL>\n<LI><A HREF=\"manual.html#2.10.1\">2.10.1 &ndash; Garbage-Collection Metamethods</A>\n<LI><A HREF=\"manual.html#2.10.2\">2.10.2 &ndash; Weak Tables</A>\n</UL>\n<LI><A HREF=\"manual.html#2.11\">2.11 &ndash; Coroutines</A>\n</UL>\n<P>\n<LI><A HREF=\"manual.html#3\">3 &ndash; The Application Program Interface</A>\n<UL>\n<LI><A HREF=\"manual.html#3.1\">3.1 &ndash; The Stack</A>\n<LI><A HREF=\"manual.html#3.2\">3.2 &ndash; Stack Size</A>\n<LI><A HREF=\"manual.html#3.3\">3.3 &ndash; Pseudo-Indices</A>\n<LI><A HREF=\"manual.html#3.4\">3.4 &ndash; C Closures</A>\n<LI><A HREF=\"manual.html#3.5\">3.5 &ndash; Registry</A>\n<LI><A HREF=\"manual.html#3.6\">3.6 &ndash; Error Handling in C</A>\n<LI><A HREF=\"manual.html#3.7\">3.7 &ndash; Functions and Types</A>\n<LI><A HREF=\"manual.html#3.8\">3.8 &ndash; The Debug Interface</A>\n</UL>\n<P>\n<LI><A HREF=\"manual.html#4\">4 &ndash; The Auxiliary Library</A>\n<UL>\n<LI><A HREF=\"manual.html#4.1\">4.1 &ndash; Functions and Types</A>\n</UL>\n<P>\n<LI><A HREF=\"manual.html#5\">5 &ndash; Standard Libraries</A>\n<UL>\n<LI><A HREF=\"manual.html#5.1\">5.1 &ndash; Basic Functions</A>\n<LI><A HREF=\"manual.html#5.2\">5.2 &ndash; Coroutine Manipulation</A>\n<LI><A HREF=\"manual.html#5.3\">5.3 &ndash; Modules</A>\n<LI><A HREF=\"manual.html#5.4\">5.4 &ndash; String Manipulation</A>\n<UL>\n<LI><A HREF=\"manual.html#5.4.1\">5.4.1 &ndash; Patterns</A>\n</UL>\n<LI><A HREF=\"manual.html#5.5\">5.5 &ndash; Table Manipulation</A>\n<LI><A HREF=\"manual.html#5.6\">5.6 &ndash; Mathematical Functions</A>\n<LI><A HREF=\"manual.html#5.7\">5.7 &ndash; Input and Output Facilities</A>\n<LI><A HREF=\"manual.html#5.8\">5.8 &ndash; Operating System Facilities</A>\n<LI><A HREF=\"manual.html#5.9\">5.9 &ndash; The Debug Library</A>\n</UL>\n<P>\n<LI><A HREF=\"manual.html#6\">6 &ndash; Lua Stand-alone</A>\n<P>\n<LI><A HREF=\"manual.html#7\">7 &ndash; Incompatibilities with the Previous Version</A>\n<UL>\n<LI><A HREF=\"manual.html#7.1\">7.1 &ndash; Changes in the Language</A>\n<LI><A HREF=\"manual.html#7.2\">7.2 &ndash; Changes in the Libraries</A>\n<LI><A HREF=\"manual.html#7.3\">7.3 &ndash; Changes in the API</A>\n</UL>\n<P>\n<LI><A HREF=\"manual.html#8\">8 &ndash; The Complete Syntax of Lua</A>\n</UL>\n\n<H2><A NAME=\"index\">Index</A></H2>\n<TABLE WIDTH=\"100%\">\n<TR VALIGN=\"top\">\n<TD>\n<H3><A NAME=\"functions\">Lua functions</A></H3>\n<A HREF=\"manual.html#pdf-_G\">_G</A><BR>\n<A HREF=\"manual.html#pdf-_VERSION\">_VERSION</A><BR>\n<P>\n\n<A HREF=\"manual.html#pdf-assert\">assert</A><BR>\n<A HREF=\"manual.html#pdf-collectgarbage\">collectgarbage</A><BR>\n<A HREF=\"manual.html#pdf-dofile\">dofile</A><BR>\n<A HREF=\"manual.html#pdf-error\">error</A><BR>\n<A HREF=\"manual.html#pdf-getfenv\">getfenv</A><BR>\n<A HREF=\"manual.html#pdf-getmetatable\">getmetatable</A><BR>\n<A HREF=\"manual.html#pdf-ipairs\">ipairs</A><BR>\n<A HREF=\"manual.html#pdf-load\">load</A><BR>\n<A HREF=\"manual.html#pdf-loadfile\">loadfile</A><BR>\n<A HREF=\"manual.html#pdf-loadstring\">loadstring</A><BR>\n<A HREF=\"manual.html#pdf-module\">module</A><BR>\n<A HREF=\"manual.html#pdf-next\">next</A><BR>\n<A HREF=\"manual.html#pdf-pairs\">pairs</A><BR>\n<A HREF=\"manual.html#pdf-pcall\">pcall</A><BR>\n<A HREF=\"manual.html#pdf-print\">print</A><BR>\n<A HREF=\"manual.html#pdf-rawequal\">rawequal</A><BR>\n<A HREF=\"manual.html#pdf-rawget\">rawget</A><BR>\n<A HREF=\"manual.html#pdf-rawset\">rawset</A><BR>\n<A HREF=\"manual.html#pdf-require\">require</A><BR>\n<A HREF=\"manual.html#pdf-select\">select</A><BR>\n<A HREF=\"manual.html#pdf-setfenv\">setfenv</A><BR>\n<A HREF=\"manual.html#pdf-setmetatable\">setmetatable</A><BR>\n<A HREF=\"manual.html#pdf-tonumber\">tonumber</A><BR>\n<A HREF=\"manual.html#pdf-tostring\">tostring</A><BR>\n<A HREF=\"manual.html#pdf-type\">type</A><BR>\n<A HREF=\"manual.html#pdf-unpack\">unpack</A><BR>\n<A HREF=\"manual.html#pdf-xpcall\">xpcall</A><BR>\n<P>\n\n<A HREF=\"manual.html#pdf-coroutine.create\">coroutine.create</A><BR>\n<A HREF=\"manual.html#pdf-coroutine.resume\">coroutine.resume</A><BR>\n<A HREF=\"manual.html#pdf-coroutine.running\">coroutine.running</A><BR>\n<A HREF=\"manual.html#pdf-coroutine.status\">coroutine.status</A><BR>\n<A HREF=\"manual.html#pdf-coroutine.wrap\">coroutine.wrap</A><BR>\n<A HREF=\"manual.html#pdf-coroutine.yield\">coroutine.yield</A><BR>\n<P>\n\n<A HREF=\"manual.html#pdf-debug.debug\">debug.debug</A><BR>\n<A HREF=\"manual.html#pdf-debug.getfenv\">debug.getfenv</A><BR>\n<A HREF=\"manual.html#pdf-debug.gethook\">debug.gethook</A><BR>\n<A HREF=\"manual.html#pdf-debug.getinfo\">debug.getinfo</A><BR>\n<A HREF=\"manual.html#pdf-debug.getlocal\">debug.getlocal</A><BR>\n<A HREF=\"manual.html#pdf-debug.getmetatable\">debug.getmetatable</A><BR>\n<A HREF=\"manual.html#pdf-debug.getregistry\">debug.getregistry</A><BR>\n<A HREF=\"manual.html#pdf-debug.getupvalue\">debug.getupvalue</A><BR>\n<A HREF=\"manual.html#pdf-debug.setfenv\">debug.setfenv</A><BR>\n<A HREF=\"manual.html#pdf-debug.sethook\">debug.sethook</A><BR>\n<A HREF=\"manual.html#pdf-debug.setlocal\">debug.setlocal</A><BR>\n<A HREF=\"manual.html#pdf-debug.setmetatable\">debug.setmetatable</A><BR>\n<A HREF=\"manual.html#pdf-debug.setupvalue\">debug.setupvalue</A><BR>\n<A HREF=\"manual.html#pdf-debug.traceback\">debug.traceback</A><BR>\n\n</TD>\n<TD>\n<H3>&nbsp;</H3>\n<A HREF=\"manual.html#pdf-file:close\">file:close</A><BR>\n<A HREF=\"manual.html#pdf-file:flush\">file:flush</A><BR>\n<A HREF=\"manual.html#pdf-file:lines\">file:lines</A><BR>\n<A HREF=\"manual.html#pdf-file:read\">file:read</A><BR>\n<A HREF=\"manual.html#pdf-file:seek\">file:seek</A><BR>\n<A HREF=\"manual.html#pdf-file:setvbuf\">file:setvbuf</A><BR>\n<A HREF=\"manual.html#pdf-file:write\">file:write</A><BR>\n<P>\n\n<A HREF=\"manual.html#pdf-io.close\">io.close</A><BR>\n<A HREF=\"manual.html#pdf-io.flush\">io.flush</A><BR>\n<A HREF=\"manual.html#pdf-io.input\">io.input</A><BR>\n<A HREF=\"manual.html#pdf-io.lines\">io.lines</A><BR>\n<A HREF=\"manual.html#pdf-io.open\">io.open</A><BR>\n<A HREF=\"manual.html#pdf-io.output\">io.output</A><BR>\n<A HREF=\"manual.html#pdf-io.popen\">io.popen</A><BR>\n<A HREF=\"manual.html#pdf-io.read\">io.read</A><BR>\n<A HREF=\"manual.html#pdf-io.stderr\">io.stderr</A><BR>\n<A HREF=\"manual.html#pdf-io.stdin\">io.stdin</A><BR>\n<A HREF=\"manual.html#pdf-io.stdout\">io.stdout</A><BR>\n<A HREF=\"manual.html#pdf-io.tmpfile\">io.tmpfile</A><BR>\n<A HREF=\"manual.html#pdf-io.type\">io.type</A><BR>\n<A HREF=\"manual.html#pdf-io.write\">io.write</A><BR>\n<P>\n\n<A HREF=\"manual.html#pdf-math.abs\">math.abs</A><BR>\n<A HREF=\"manual.html#pdf-math.acos\">math.acos</A><BR>\n<A HREF=\"manual.html#pdf-math.asin\">math.asin</A><BR>\n<A HREF=\"manual.html#pdf-math.atan\">math.atan</A><BR>\n<A HREF=\"manual.html#pdf-math.atan2\">math.atan2</A><BR>\n<A HREF=\"manual.html#pdf-math.ceil\">math.ceil</A><BR>\n<A HREF=\"manual.html#pdf-math.cos\">math.cos</A><BR>\n<A HREF=\"manual.html#pdf-math.cosh\">math.cosh</A><BR>\n<A HREF=\"manual.html#pdf-math.deg\">math.deg</A><BR>\n<A HREF=\"manual.html#pdf-math.exp\">math.exp</A><BR>\n<A HREF=\"manual.html#pdf-math.floor\">math.floor</A><BR>\n<A HREF=\"manual.html#pdf-math.fmod\">math.fmod</A><BR>\n<A HREF=\"manual.html#pdf-math.frexp\">math.frexp</A><BR>\n<A HREF=\"manual.html#pdf-math.huge\">math.huge</A><BR>\n<A HREF=\"manual.html#pdf-math.ldexp\">math.ldexp</A><BR>\n<A HREF=\"manual.html#pdf-math.log\">math.log</A><BR>\n<A HREF=\"manual.html#pdf-math.log10\">math.log10</A><BR>\n<A HREF=\"manual.html#pdf-math.max\">math.max</A><BR>\n<A HREF=\"manual.html#pdf-math.min\">math.min</A><BR>\n<A HREF=\"manual.html#pdf-math.modf\">math.modf</A><BR>\n<A HREF=\"manual.html#pdf-math.pi\">math.pi</A><BR>\n<A HREF=\"manual.html#pdf-math.pow\">math.pow</A><BR>\n<A HREF=\"manual.html#pdf-math.rad\">math.rad</A><BR>\n<A HREF=\"manual.html#pdf-math.random\">math.random</A><BR>\n<A HREF=\"manual.html#pdf-math.randomseed\">math.randomseed</A><BR>\n<A HREF=\"manual.html#pdf-math.sin\">math.sin</A><BR>\n<A HREF=\"manual.html#pdf-math.sinh\">math.sinh</A><BR>\n<A HREF=\"manual.html#pdf-math.sqrt\">math.sqrt</A><BR>\n<A HREF=\"manual.html#pdf-math.tan\">math.tan</A><BR>\n<A HREF=\"manual.html#pdf-math.tanh\">math.tanh</A><BR>\n<P>\n\n<A HREF=\"manual.html#pdf-os.clock\">os.clock</A><BR>\n<A HREF=\"manual.html#pdf-os.date\">os.date</A><BR>\n<A HREF=\"manual.html#pdf-os.difftime\">os.difftime</A><BR>\n<A HREF=\"manual.html#pdf-os.execute\">os.execute</A><BR>\n<A HREF=\"manual.html#pdf-os.exit\">os.exit</A><BR>\n<A HREF=\"manual.html#pdf-os.getenv\">os.getenv</A><BR>\n<A HREF=\"manual.html#pdf-os.remove\">os.remove</A><BR>\n<A HREF=\"manual.html#pdf-os.rename\">os.rename</A><BR>\n<A HREF=\"manual.html#pdf-os.setlocale\">os.setlocale</A><BR>\n<A HREF=\"manual.html#pdf-os.time\">os.time</A><BR>\n<A HREF=\"manual.html#pdf-os.tmpname\">os.tmpname</A><BR>\n<P>\n\n<A HREF=\"manual.html#pdf-package.cpath\">package.cpath</A><BR>\n<A HREF=\"manual.html#pdf-package.loaded\">package.loaded</A><BR>\n<A HREF=\"manual.html#pdf-package.loaders\">package.loaders</A><BR>\n<A HREF=\"manual.html#pdf-package.loadlib\">package.loadlib</A><BR>\n<A HREF=\"manual.html#pdf-package.path\">package.path</A><BR>\n<A HREF=\"manual.html#pdf-package.preload\">package.preload</A><BR>\n<A HREF=\"manual.html#pdf-package.seeall\">package.seeall</A><BR>\n<P>\n\n<A HREF=\"manual.html#pdf-string.byte\">string.byte</A><BR>\n<A HREF=\"manual.html#pdf-string.char\">string.char</A><BR>\n<A HREF=\"manual.html#pdf-string.dump\">string.dump</A><BR>\n<A HREF=\"manual.html#pdf-string.find\">string.find</A><BR>\n<A HREF=\"manual.html#pdf-string.format\">string.format</A><BR>\n<A HREF=\"manual.html#pdf-string.gmatch\">string.gmatch</A><BR>\n<A HREF=\"manual.html#pdf-string.gsub\">string.gsub</A><BR>\n<A HREF=\"manual.html#pdf-string.len\">string.len</A><BR>\n<A HREF=\"manual.html#pdf-string.lower\">string.lower</A><BR>\n<A HREF=\"manual.html#pdf-string.match\">string.match</A><BR>\n<A HREF=\"manual.html#pdf-string.rep\">string.rep</A><BR>\n<A HREF=\"manual.html#pdf-string.reverse\">string.reverse</A><BR>\n<A HREF=\"manual.html#pdf-string.sub\">string.sub</A><BR>\n<A HREF=\"manual.html#pdf-string.upper\">string.upper</A><BR>\n<P>\n\n<A HREF=\"manual.html#pdf-table.concat\">table.concat</A><BR>\n<A HREF=\"manual.html#pdf-table.insert\">table.insert</A><BR>\n<A HREF=\"manual.html#pdf-table.maxn\">table.maxn</A><BR>\n<A HREF=\"manual.html#pdf-table.remove\">table.remove</A><BR>\n<A HREF=\"manual.html#pdf-table.sort\">table.sort</A><BR>\n\n</TD>\n<TD>\n<H3>C API</H3>\n<A HREF=\"manual.html#lua_Alloc\">lua_Alloc</A><BR>\n<A HREF=\"manual.html#lua_CFunction\">lua_CFunction</A><BR>\n<A HREF=\"manual.html#lua_Debug\">lua_Debug</A><BR>\n<A HREF=\"manual.html#lua_Hook\">lua_Hook</A><BR>\n<A HREF=\"manual.html#lua_Integer\">lua_Integer</A><BR>\n<A HREF=\"manual.html#lua_Number\">lua_Number</A><BR>\n<A HREF=\"manual.html#lua_Reader\">lua_Reader</A><BR>\n<A HREF=\"manual.html#lua_State\">lua_State</A><BR>\n<A HREF=\"manual.html#lua_Writer\">lua_Writer</A><BR>\n<P>\n\n<A HREF=\"manual.html#lua_atpanic\">lua_atpanic</A><BR>\n<A HREF=\"manual.html#lua_call\">lua_call</A><BR>\n<A HREF=\"manual.html#lua_checkstack\">lua_checkstack</A><BR>\n<A HREF=\"manual.html#lua_close\">lua_close</A><BR>\n<A HREF=\"manual.html#lua_concat\">lua_concat</A><BR>\n<A HREF=\"manual.html#lua_cpcall\">lua_cpcall</A><BR>\n<A HREF=\"manual.html#lua_createtable\">lua_createtable</A><BR>\n<A HREF=\"manual.html#lua_dump\">lua_dump</A><BR>\n<A HREF=\"manual.html#lua_equal\">lua_equal</A><BR>\n<A HREF=\"manual.html#lua_error\">lua_error</A><BR>\n<A HREF=\"manual.html#lua_gc\">lua_gc</A><BR>\n<A HREF=\"manual.html#lua_getallocf\">lua_getallocf</A><BR>\n<A HREF=\"manual.html#lua_getfenv\">lua_getfenv</A><BR>\n<A HREF=\"manual.html#lua_getfield\">lua_getfield</A><BR>\n<A HREF=\"manual.html#lua_getglobal\">lua_getglobal</A><BR>\n<A HREF=\"manual.html#lua_gethook\">lua_gethook</A><BR>\n<A HREF=\"manual.html#lua_gethookcount\">lua_gethookcount</A><BR>\n<A HREF=\"manual.html#lua_gethookmask\">lua_gethookmask</A><BR>\n<A HREF=\"manual.html#lua_getinfo\">lua_getinfo</A><BR>\n<A HREF=\"manual.html#lua_getlocal\">lua_getlocal</A><BR>\n<A HREF=\"manual.html#lua_getmetatable\">lua_getmetatable</A><BR>\n<A HREF=\"manual.html#lua_getstack\">lua_getstack</A><BR>\n<A HREF=\"manual.html#lua_gettable\">lua_gettable</A><BR>\n<A HREF=\"manual.html#lua_gettop\">lua_gettop</A><BR>\n<A HREF=\"manual.html#lua_getupvalue\">lua_getupvalue</A><BR>\n<A HREF=\"manual.html#lua_insert\">lua_insert</A><BR>\n<A HREF=\"manual.html#lua_isboolean\">lua_isboolean</A><BR>\n<A HREF=\"manual.html#lua_iscfunction\">lua_iscfunction</A><BR>\n<A HREF=\"manual.html#lua_isfunction\">lua_isfunction</A><BR>\n<A HREF=\"manual.html#lua_islightuserdata\">lua_islightuserdata</A><BR>\n<A HREF=\"manual.html#lua_isnil\">lua_isnil</A><BR>\n<A HREF=\"manual.html#lua_isnone\">lua_isnone</A><BR>\n<A HREF=\"manual.html#lua_isnoneornil\">lua_isnoneornil</A><BR>\n<A HREF=\"manual.html#lua_isnumber\">lua_isnumber</A><BR>\n<A HREF=\"manual.html#lua_isstring\">lua_isstring</A><BR>\n<A HREF=\"manual.html#lua_istable\">lua_istable</A><BR>\n<A HREF=\"manual.html#lua_isthread\">lua_isthread</A><BR>\n<A HREF=\"manual.html#lua_isuserdata\">lua_isuserdata</A><BR>\n<A HREF=\"manual.html#lua_lessthan\">lua_lessthan</A><BR>\n<A HREF=\"manual.html#lua_load\">lua_load</A><BR>\n<A HREF=\"manual.html#lua_newstate\">lua_newstate</A><BR>\n<A HREF=\"manual.html#lua_newtable\">lua_newtable</A><BR>\n<A HREF=\"manual.html#lua_newthread\">lua_newthread</A><BR>\n<A HREF=\"manual.html#lua_newuserdata\">lua_newuserdata</A><BR>\n<A HREF=\"manual.html#lua_next\">lua_next</A><BR>\n<A HREF=\"manual.html#lua_objlen\">lua_objlen</A><BR>\n<A HREF=\"manual.html#lua_pcall\">lua_pcall</A><BR>\n<A HREF=\"manual.html#lua_pop\">lua_pop</A><BR>\n<A HREF=\"manual.html#lua_pushboolean\">lua_pushboolean</A><BR>\n<A HREF=\"manual.html#lua_pushcclosure\">lua_pushcclosure</A><BR>\n<A HREF=\"manual.html#lua_pushcfunction\">lua_pushcfunction</A><BR>\n<A HREF=\"manual.html#lua_pushfstring\">lua_pushfstring</A><BR>\n<A HREF=\"manual.html#lua_pushinteger\">lua_pushinteger</A><BR>\n<A HREF=\"manual.html#lua_pushlightuserdata\">lua_pushlightuserdata</A><BR>\n<A HREF=\"manual.html#lua_pushliteral\">lua_pushliteral</A><BR>\n<A HREF=\"manual.html#lua_pushlstring\">lua_pushlstring</A><BR>\n<A HREF=\"manual.html#lua_pushnil\">lua_pushnil</A><BR>\n<A HREF=\"manual.html#lua_pushnumber\">lua_pushnumber</A><BR>\n<A HREF=\"manual.html#lua_pushstring\">lua_pushstring</A><BR>\n<A HREF=\"manual.html#lua_pushthread\">lua_pushthread</A><BR>\n<A HREF=\"manual.html#lua_pushvalue\">lua_pushvalue</A><BR>\n<A HREF=\"manual.html#lua_pushvfstring\">lua_pushvfstring</A><BR>\n<A HREF=\"manual.html#lua_rawequal\">lua_rawequal</A><BR>\n<A HREF=\"manual.html#lua_rawget\">lua_rawget</A><BR>\n<A HREF=\"manual.html#lua_rawgeti\">lua_rawgeti</A><BR>\n<A HREF=\"manual.html#lua_rawset\">lua_rawset</A><BR>\n<A HREF=\"manual.html#lua_rawseti\">lua_rawseti</A><BR>\n<A HREF=\"manual.html#lua_register\">lua_register</A><BR>\n<A HREF=\"manual.html#lua_remove\">lua_remove</A><BR>\n<A HREF=\"manual.html#lua_replace\">lua_replace</A><BR>\n<A HREF=\"manual.html#lua_resume\">lua_resume</A><BR>\n<A HREF=\"manual.html#lua_setallocf\">lua_setallocf</A><BR>\n<A HREF=\"manual.html#lua_setfenv\">lua_setfenv</A><BR>\n<A HREF=\"manual.html#lua_setfield\">lua_setfield</A><BR>\n<A HREF=\"manual.html#lua_setglobal\">lua_setglobal</A><BR>\n<A HREF=\"manual.html#lua_sethook\">lua_sethook</A><BR>\n<A HREF=\"manual.html#lua_setlocal\">lua_setlocal</A><BR>\n<A HREF=\"manual.html#lua_setmetatable\">lua_setmetatable</A><BR>\n<A HREF=\"manual.html#lua_settable\">lua_settable</A><BR>\n<A HREF=\"manual.html#lua_settop\">lua_settop</A><BR>\n<A HREF=\"manual.html#lua_setupvalue\">lua_setupvalue</A><BR>\n<A HREF=\"manual.html#lua_status\">lua_status</A><BR>\n<A HREF=\"manual.html#lua_toboolean\">lua_toboolean</A><BR>\n<A HREF=\"manual.html#lua_tocfunction\">lua_tocfunction</A><BR>\n<A HREF=\"manual.html#lua_tointeger\">lua_tointeger</A><BR>\n<A HREF=\"manual.html#lua_tolstring\">lua_tolstring</A><BR>\n<A HREF=\"manual.html#lua_tonumber\">lua_tonumber</A><BR>\n<A HREF=\"manual.html#lua_topointer\">lua_topointer</A><BR>\n<A HREF=\"manual.html#lua_tostring\">lua_tostring</A><BR>\n<A HREF=\"manual.html#lua_tothread\">lua_tothread</A><BR>\n<A HREF=\"manual.html#lua_touserdata\">lua_touserdata</A><BR>\n<A HREF=\"manual.html#lua_type\">lua_type</A><BR>\n<A HREF=\"manual.html#lua_typename\">lua_typename</A><BR>\n<A HREF=\"manual.html#lua_upvalueindex\">lua_upvalueindex</A><BR>\n<A HREF=\"manual.html#lua_xmove\">lua_xmove</A><BR>\n<A HREF=\"manual.html#lua_yield\">lua_yield</A><BR>\n\n</TD>\n<TD>\n<H3>auxiliary library</H3>\n<A HREF=\"manual.html#luaL_Buffer\">luaL_Buffer</A><BR>\n<A HREF=\"manual.html#luaL_Reg\">luaL_Reg</A><BR>\n<P>\n\n<A HREF=\"manual.html#luaL_addchar\">luaL_addchar</A><BR>\n<A HREF=\"manual.html#luaL_addlstring\">luaL_addlstring</A><BR>\n<A HREF=\"manual.html#luaL_addsize\">luaL_addsize</A><BR>\n<A HREF=\"manual.html#luaL_addstring\">luaL_addstring</A><BR>\n<A HREF=\"manual.html#luaL_addvalue\">luaL_addvalue</A><BR>\n<A HREF=\"manual.html#luaL_argcheck\">luaL_argcheck</A><BR>\n<A HREF=\"manual.html#luaL_argerror\">luaL_argerror</A><BR>\n<A HREF=\"manual.html#luaL_buffinit\">luaL_buffinit</A><BR>\n<A HREF=\"manual.html#luaL_callmeta\">luaL_callmeta</A><BR>\n<A HREF=\"manual.html#luaL_checkany\">luaL_checkany</A><BR>\n<A HREF=\"manual.html#luaL_checkint\">luaL_checkint</A><BR>\n<A HREF=\"manual.html#luaL_checkinteger\">luaL_checkinteger</A><BR>\n<A HREF=\"manual.html#luaL_checklong\">luaL_checklong</A><BR>\n<A HREF=\"manual.html#luaL_checklstring\">luaL_checklstring</A><BR>\n<A HREF=\"manual.html#luaL_checknumber\">luaL_checknumber</A><BR>\n<A HREF=\"manual.html#luaL_checkoption\">luaL_checkoption</A><BR>\n<A HREF=\"manual.html#luaL_checkstack\">luaL_checkstack</A><BR>\n<A HREF=\"manual.html#luaL_checkstring\">luaL_checkstring</A><BR>\n<A HREF=\"manual.html#luaL_checktype\">luaL_checktype</A><BR>\n<A HREF=\"manual.html#luaL_checkudata\">luaL_checkudata</A><BR>\n<A HREF=\"manual.html#luaL_dofile\">luaL_dofile</A><BR>\n<A HREF=\"manual.html#luaL_dostring\">luaL_dostring</A><BR>\n<A HREF=\"manual.html#luaL_error\">luaL_error</A><BR>\n<A HREF=\"manual.html#luaL_getmetafield\">luaL_getmetafield</A><BR>\n<A HREF=\"manual.html#luaL_getmetatable\">luaL_getmetatable</A><BR>\n<A HREF=\"manual.html#luaL_gsub\">luaL_gsub</A><BR>\n<A HREF=\"manual.html#luaL_loadbuffer\">luaL_loadbuffer</A><BR>\n<A HREF=\"manual.html#luaL_loadfile\">luaL_loadfile</A><BR>\n<A HREF=\"manual.html#luaL_loadstring\">luaL_loadstring</A><BR>\n<A HREF=\"manual.html#luaL_newmetatable\">luaL_newmetatable</A><BR>\n<A HREF=\"manual.html#luaL_newstate\">luaL_newstate</A><BR>\n<A HREF=\"manual.html#luaL_openlibs\">luaL_openlibs</A><BR>\n<A HREF=\"manual.html#luaL_optint\">luaL_optint</A><BR>\n<A HREF=\"manual.html#luaL_optinteger\">luaL_optinteger</A><BR>\n<A HREF=\"manual.html#luaL_optlong\">luaL_optlong</A><BR>\n<A HREF=\"manual.html#luaL_optlstring\">luaL_optlstring</A><BR>\n<A HREF=\"manual.html#luaL_optnumber\">luaL_optnumber</A><BR>\n<A HREF=\"manual.html#luaL_optstring\">luaL_optstring</A><BR>\n<A HREF=\"manual.html#luaL_prepbuffer\">luaL_prepbuffer</A><BR>\n<A HREF=\"manual.html#luaL_pushresult\">luaL_pushresult</A><BR>\n<A HREF=\"manual.html#luaL_ref\">luaL_ref</A><BR>\n<A HREF=\"manual.html#luaL_register\">luaL_register</A><BR>\n<A HREF=\"manual.html#luaL_typename\">luaL_typename</A><BR>\n<A HREF=\"manual.html#luaL_typerror\">luaL_typerror</A><BR>\n<A HREF=\"manual.html#luaL_unref\">luaL_unref</A><BR>\n<A HREF=\"manual.html#luaL_where\">luaL_where</A><BR>\n\n</TD>\n</TR>\n</TABLE>\n<P>\n\n<HR>\n<SMALL CLASS=\"footer\">\nLast update:\nMon Feb 13 18:53:32 BRST 2012\n</SMALL>\n<!--\nLast change: revised for Lua 5.1.5\n-->\n\n</BODY>\n</HTML>\n"
  },
  {
    "path": "deps/lua/doc/lua.1",
    "content": ".\\\" $Id: lua.man,v 1.11 2006/01/06 16:03:34 lhf Exp $\n.TH LUA 1 \"$Date: 2006/01/06 16:03:34 $\"\n.SH NAME\nlua \\- Lua interpreter\n.SH SYNOPSIS\n.B lua\n[\n.I options\n]\n[\n.I script\n[\n.I args\n]\n]\n.SH DESCRIPTION\n.B lua\nis the stand-alone Lua interpreter.\nIt loads and executes Lua programs,\neither in textual source form or\nin precompiled binary form.\n(Precompiled binaries are output by\n.BR luac ,\nthe Lua compiler.)\n.B lua\ncan be used as a batch interpreter and also interactively.\n.LP\nThe given\n.I options\n(see below)\nare executed and then\nthe Lua program in file\n.I script\nis loaded and executed.\nThe given\n.I args\nare available to\n.I script\nas strings in a global table named\n.BR arg .\nIf these arguments contain spaces or other characters special to the shell,\nthen they should be quoted\n(but note that the quotes will be removed by the shell).\nThe arguments in\n.B arg\nstart at 0,\nwhich contains the string\n.RI ' script '.\nThe index of the last argument is stored in\n.BR arg.n .\nThe arguments given in the command line before\n.IR script ,\nincluding the name of the interpreter,\nare available in negative indices in\n.BR arg .\n.LP\nAt the very start,\nbefore even handling the command line,\n.B lua\nexecutes the contents of the environment variable\n.BR LUA_INIT ,\nif it is defined.\nIf the value of\n.B LUA_INIT\nis of the form\n.RI '@ filename ',\nthen\n.I filename\nis executed.\nOtherwise, the string is assumed to be a Lua statement and is executed.\n.LP\nOptions start with\n.B '\\-'\nand are described below.\nYou can use\n.B \"'\\--'\"\nto signal the end of options.\n.LP\nIf no arguments are given,\nthen\n.B \"\\-v \\-i\"\nis assumed when the standard input is a terminal;\notherwise,\n.B \"\\-\"\nis assumed.\n.LP\nIn interactive mode,\n.B lua\nprompts the user,\nreads lines from the standard input,\nand executes them as they are read.\nIf a line does not contain a complete statement,\nthen a secondary prompt is displayed and\nlines are read until a complete statement is formed or\na syntax error is found.\nSo, one way to interrupt the reading of an incomplete statement is\nto force a syntax error:\nadding a\n.B ';' \nin the middle of a statement is a sure way of forcing a syntax error\n(except inside multiline strings and comments; these must be closed explicitly).\nIf a line starts with\n.BR '=' ,\nthen\n.B lua\ndisplays the values of all the expressions in the remainder of the\nline. The expressions must be separated by commas.\nThe primary prompt is the value of the global variable\n.BR _PROMPT ,\nif this value is a string;\notherwise, the default prompt is used.\nSimilarly, the secondary prompt is the value of the global variable\n.BR _PROMPT2 .\nSo,\nto change the prompts,\nset the corresponding variable to a string of your choice.\nYou can do that after calling the interpreter\nor on the command line\n(but in this case you have to be careful with quotes\nif the prompt string contains a space; otherwise you may confuse the shell.)\nThe default prompts are \"> \" and \">> \".\n.SH OPTIONS\n.TP\n.B \\-\nload and execute the standard input as a file,\nthat is,\nnot interactively,\neven when the standard input is a terminal.\n.TP\n.BI \\-e \" stat\"\nexecute statement\n.IR stat .\nYou need to quote\n.I stat \nif it contains spaces, quotes,\nor other characters special to the shell.\n.TP\n.B \\-i\nenter interactive mode after\n.I script\nis executed.\n.TP\n.BI \\-l \" name\"\ncall\n.BI require(' name ')\nbefore executing\n.IR script .\nTypically used to load libraries.\n.TP\n.B \\-v\nshow version information.\n.SH \"SEE ALSO\"\n.BR luac (1)\n.br\nhttp://www.lua.org/\n.SH DIAGNOSTICS\nError messages should be self explanatory.\n.SH AUTHORS\nR. Ierusalimschy,\nL. H. de Figueiredo,\nand\nW. Celes\n.\\\" EOF\n"
  },
  {
    "path": "deps/lua/doc/lua.css",
    "content": "body {\n\tcolor: #000000 ;\n\tbackground-color: #FFFFFF ;\n\tfont-family: Helvetica, Arial, sans-serif ;\n\ttext-align: justify ;\n\tmargin-right: 30px ;\n\tmargin-left: 30px ;\n}\n\nh1, h2, h3, h4 {\n\tfont-family: Verdana, Geneva, sans-serif ;\n\tfont-weight: normal ;\n\tfont-style: italic ;\n}\n\nh2 {\n\tpadding-top: 0.4em ;\n\tpadding-bottom: 0.4em ;\n\tpadding-left: 30px ;\n\tpadding-right: 30px ;\n\tmargin-left: -30px ;\n\tbackground-color: #E0E0FF ;\n}\n\nh3 {\n\tpadding-left: 0.5em ;\n\tborder-left: solid #E0E0FF 1em ;\n}\n\ntable h3 {\n\tpadding-left: 0px ;\n\tborder-left: none ;\n}\n\na:link {\n\tcolor: #000080 ;\n\tbackground-color: inherit ;\n\ttext-decoration: none ;\n}\n\na:visited {\n\tbackground-color: inherit ;\n\ttext-decoration: none ;\n}\n\na:link:hover, a:visited:hover {\n\tcolor: #000080 ;\n\tbackground-color: #E0E0FF ;\n}\n\na:link:active, a:visited:active {\n\tcolor: #FF0000 ;\n}\n\nhr {\n\tborder: 0 ;\n\theight: 1px ;\n\tcolor: #a0a0a0 ;\n\tbackground-color: #a0a0a0 ;\n}\n\n:target {\n\tbackground-color: #F8F8F8 ;\n\tpadding: 8px ;\n\tborder: solid #a0a0a0 2px ;\n}\n\n.footer {\n\tcolor: gray ;\n\tfont-size: small ;\n}\n\ninput[type=text] {\n\tborder: solid #a0a0a0 2px ;\n\tborder-radius: 2em ;\n\t-moz-border-radius: 2em ;\n\tbackground-image: url('images/search.png') ;\n\tbackground-repeat: no-repeat;\n\tbackground-position: 4px center ;\n\tpadding-left: 20px ;\n\theight: 2em ;\n}\n\n"
  },
  {
    "path": "deps/lua/doc/lua.html",
    "content": "<!-- $Id: lua.man,v 1.11 2006/01/06 16:03:34 lhf Exp $ -->\n<HTML>\n<HEAD>\n<TITLE>LUA man page</TITLE>\n<LINK REL=\"stylesheet\" TYPE=\"text/css\" HREF=\"lua.css\">\n</HEAD>\n\n<BODY BGCOLOR=\"#FFFFFF\">\n\n<H2>NAME</H2>\nlua - Lua interpreter\n<H2>SYNOPSIS</H2>\n<B>lua</B>\n[\n<I>options</I>\n]\n[\n<I>script</I>\n[\n<I>args</I>\n]\n]\n<H2>DESCRIPTION</H2>\n<B>lua</B>\nis the stand-alone Lua interpreter.\nIt loads and executes Lua programs,\neither in textual source form or\nin precompiled binary form.\n(Precompiled binaries are output by\n<B>luac</B>,\nthe Lua compiler.)\n<B>lua</B>\ncan be used as a batch interpreter and also interactively.\n<P>\nThe given\n<I>options</I>\n(see below)\nare executed and then\nthe Lua program in file\n<I>script</I>\nis loaded and executed.\nThe given\n<I>args</I>\nare available to\n<I>script</I>\nas strings in a global table named\n<B>arg</B>.\nIf these arguments contain spaces or other characters special to the shell,\nthen they should be quoted\n(but note that the quotes will be removed by the shell).\nThe arguments in\n<B>arg</B>\nstart at 0,\nwhich contains the string\n'<I>script</I>'.\nThe index of the last argument is stored in\n<B>arg.n</B>.\nThe arguments given in the command line before\n<I>script</I>,\nincluding the name of the interpreter,\nare available in negative indices in\n<B>arg</B>.\n<P>\nAt the very start,\nbefore even handling the command line,\n<B>lua</B>\nexecutes the contents of the environment variable\n<B>LUA_INIT</B>,\nif it is defined.\nIf the value of\n<B>LUA_INIT</B>\nis of the form\n'@<I>filename</I>',\nthen\n<I>filename</I>\nis executed.\nOtherwise, the string is assumed to be a Lua statement and is executed.\n<P>\nOptions start with\n<B>'-'</B>\nand are described below.\nYou can use\n<B>'--'</B>\nto signal the end of options.\n<P>\nIf no arguments are given,\nthen\n<B>\"-v -i\"</B>\nis assumed when the standard input is a terminal;\notherwise,\n<B>\"-\"</B>\nis assumed.\n<P>\nIn interactive mode,\n<B>lua</B>\nprompts the user,\nreads lines from the standard input,\nand executes them as they are read.\nIf a line does not contain a complete statement,\nthen a secondary prompt is displayed and\nlines are read until a complete statement is formed or\na syntax error is found.\nSo, one way to interrupt the reading of an incomplete statement is\nto force a syntax error:\nadding a\n<B>';'</B>\nin the middle of a statement is a sure way of forcing a syntax error\n(except inside multiline strings and comments; these must be closed explicitly).\nIf a line starts with\n<B>'='</B>,\nthen\n<B>lua</B>\ndisplays the values of all the expressions in the remainder of the\nline. The expressions must be separated by commas.\nThe primary prompt is the value of the global variable\n<B>_PROMPT</B>,\nif this value is a string;\notherwise, the default prompt is used.\nSimilarly, the secondary prompt is the value of the global variable\n<B>_PROMPT2</B>.\nSo,\nto change the prompts,\nset the corresponding variable to a string of your choice.\nYou can do that after calling the interpreter\nor on the command line\n(but in this case you have to be careful with quotes\nif the prompt string contains a space; otherwise you may confuse the shell.)\nThe default prompts are \"&gt; \" and \"&gt;&gt; \".\n<H2>OPTIONS</H2>\n<P>\n<B>-</B>\nload and execute the standard input as a file,\nthat is,\nnot interactively,\neven when the standard input is a terminal.\n<P>\n<B>-e </B><I>stat</I>\nexecute statement\n<I>stat</I>.\nYou need to quote\n<I>stat </I>\nif it contains spaces, quotes,\nor other characters special to the shell.\n<P>\n<B>-i</B>\nenter interactive mode after\n<I>script</I>\nis executed.\n<P>\n<B>-l </B><I>name</I>\ncall\n<B>require</B>('<I>name</I>')\nbefore executing\n<I>script</I>.\nTypically used to load libraries.\n<P>\n<B>-v</B>\nshow version information.\n<H2>SEE ALSO</H2>\n<B>luac</B>(1)\n<BR>\n<A HREF=\"http://www.lua.org/\">http://www.lua.org/</A>\n<H2>DIAGNOSTICS</H2>\nError messages should be self explanatory.\n<H2>AUTHORS</H2>\nR. Ierusalimschy,\nL. H. de Figueiredo,\nand\nW. Celes\n<!-- EOF -->\n</BODY>\n</HTML>\n"
  },
  {
    "path": "deps/lua/doc/luac.1",
    "content": ".\\\" $Id: luac.man,v 1.28 2006/01/06 16:03:34 lhf Exp $\n.TH LUAC 1 \"$Date: 2006/01/06 16:03:34 $\"\n.SH NAME\nluac \\- Lua compiler\n.SH SYNOPSIS\n.B luac\n[\n.I options\n] [\n.I filenames\n]\n.SH DESCRIPTION\n.B luac\nis the Lua compiler.\nIt translates programs written in the Lua programming language\ninto binary files that can be later loaded and executed.\n.LP\nThe main advantages of precompiling chunks are:\nfaster loading,\nprotecting source code from accidental user changes,\nand\noff-line syntax checking.\n.LP\nPre-compiling does not imply faster execution\nbecause in Lua chunks are always compiled into bytecodes before being executed.\n.B luac\nsimply allows those bytecodes to be saved in a file for later execution.\n.LP\nPre-compiled chunks are not necessarily smaller than the corresponding source.\nThe main goal in pre-compiling is faster loading.\n.LP\nThe binary files created by\n.B luac\nare portable only among architectures with the same word size and byte order.\n.LP\n.B luac\nproduces a single output file containing the bytecodes\nfor all source files given.\nBy default,\nthe output file is named\n.BR luac.out ,\nbut you can change this with the\n.B \\-o\noption.\n.LP\nIn the command line,\nyou can mix\ntext files containing Lua source and\nbinary files containing precompiled chunks.\nThis is useful to combine several precompiled chunks,\neven from different (but compatible) platforms,\ninto a single precompiled chunk.\n.LP\nYou can use\n.B \"'\\-'\"\nto indicate the standard input as a source file\nand\n.B \"'\\--'\"\nto signal the end of options\n(that is,\nall remaining arguments will be treated as files even if they start with\n.BR \"'\\-'\" ).\n.LP\nThe internal format of the binary files produced by\n.B luac\nis likely to change when a new version of Lua is released.\nSo,\nsave the source files of all Lua programs that you precompile.\n.LP\n.SH OPTIONS\nOptions must be separate.\n.TP\n.B \\-l\nproduce a listing of the compiled bytecode for Lua's virtual machine.\nListing bytecodes is useful to learn about Lua's virtual machine.\nIf no files are given, then\n.B luac\nloads\n.B luac.out\nand lists its contents.\n.TP\n.BI \\-o \" file\"\noutput to\n.IR file ,\ninstead of the default\n.BR luac.out .\n(You can use\n.B \"'\\-'\"\nfor standard output,\nbut not on platforms that open standard output in text mode.)\nThe output file may be a source file because\nall files are loaded before the output file is written.\nBe careful not to overwrite precious files.\n.TP\n.B \\-p\nload files but do not generate any output file.\nUsed mainly for syntax checking and for testing precompiled chunks:\ncorrupted files will probably generate errors when loaded.\nLua always performs a thorough integrity test on precompiled chunks.\nBytecode that passes this test is completely safe,\nin the sense that it will not break the interpreter.\nHowever,\nthere is no guarantee that such code does anything sensible.\n(None can be given, because the halting problem is unsolvable.)\nIf no files are given, then\n.B luac\nloads\n.B luac.out\nand tests its contents.\nNo messages are displayed if the file passes the integrity test.\n.TP\n.B \\-s\nstrip debug information before writing the output file.\nThis saves some space in very large chunks,\nbut if errors occur when running a stripped chunk,\nthen the error messages may not contain the full information they usually do.\nFor instance,\nline numbers and names of local variables are lost.\n.TP\n.B \\-v\nshow version information.\n.SH FILES\n.TP 15\n.B luac.out\ndefault output file\n.SH \"SEE ALSO\"\n.BR lua (1)\n.br\nhttp://www.lua.org/\n.SH DIAGNOSTICS\nError messages should be self explanatory.\n.SH AUTHORS\nL. H. de Figueiredo,\nR. Ierusalimschy and\nW. Celes\n.\\\" EOF\n"
  },
  {
    "path": "deps/lua/doc/luac.html",
    "content": "<!-- $Id: luac.man,v 1.28 2006/01/06 16:03:34 lhf Exp $ -->\n<HTML>\n<HEAD>\n<TITLE>LUAC man page</TITLE>\n<LINK REL=\"stylesheet\" TYPE=\"text/css\" HREF=\"lua.css\">\n</HEAD>\n\n<BODY BGCOLOR=\"#FFFFFF\">\n\n<H2>NAME</H2>\nluac - Lua compiler\n<H2>SYNOPSIS</H2>\n<B>luac</B>\n[\n<I>options</I>\n] [\n<I>filenames</I>\n]\n<H2>DESCRIPTION</H2>\n<B>luac</B>\nis the Lua compiler.\nIt translates programs written in the Lua programming language\ninto binary files that can be later loaded and executed.\n<P>\nThe main advantages of precompiling chunks are:\nfaster loading,\nprotecting source code from accidental user changes,\nand\noff-line syntax checking.\n<P>\nPrecompiling does not imply faster execution\nbecause in Lua chunks are always compiled into bytecodes before being executed.\n<B>luac</B>\nsimply allows those bytecodes to be saved in a file for later execution.\n<P>\nPrecompiled chunks are not necessarily smaller than the corresponding source.\nThe main goal in precompiling is faster loading.\n<P>\nThe binary files created by\n<B>luac</B>\nare portable only among architectures with the same word size and byte order.\n<P>\n<B>luac</B>\nproduces a single output file containing the bytecodes\nfor all source files given.\nBy default,\nthe output file is named\n<B>luac.out</B>,\nbut you can change this with the\n<B>-o</B>\noption.\n<P>\nIn the command line,\nyou can mix\ntext files containing Lua source and\nbinary files containing precompiled chunks.\nThis is useful because several precompiled chunks,\neven from different (but compatible) platforms,\ncan be combined into a single precompiled chunk.\n<P>\nYou can use\n<B>'-'</B>\nto indicate the standard input as a source file\nand\n<B>'--'</B>\nto signal the end of options\n(that is,\nall remaining arguments will be treated as files even if they start with\n<B>'-'</B>).\n<P>\nThe internal format of the binary files produced by\n<B>luac</B>\nis likely to change when a new version of Lua is released.\nSo,\nsave the source files of all Lua programs that you precompile.\n<P>\n<H2>OPTIONS</H2>\nOptions must be separate.\n<P>\n<B>-l</B>\nproduce a listing of the compiled bytecode for Lua's virtual machine.\nListing bytecodes is useful to learn about Lua's virtual machine.\nIf no files are given, then\n<B>luac</B>\nloads\n<B>luac.out</B>\nand lists its contents.\n<P>\n<B>-o </B><I>file</I>\noutput to\n<I>file</I>,\ninstead of the default\n<B>luac.out</B>.\n(You can use\n<B>'-'</B>\nfor standard output,\nbut not on platforms that open standard output in text mode.)\nThe output file may be a source file because\nall files are loaded before the output file is written.\nBe careful not to overwrite precious files.\n<P>\n<B>-p</B>\nload files but do not generate any output file.\nUsed mainly for syntax checking and for testing precompiled chunks:\ncorrupted files will probably generate errors when loaded.\nLua always performs a thorough integrity test on precompiled chunks.\nBytecode that passes this test is completely safe,\nin the sense that it will not break the interpreter.\nHowever,\nthere is no guarantee that such code does anything sensible.\n(None can be given, because the halting problem is unsolvable.)\nIf no files are given, then\n<B>luac</B>\nloads\n<B>luac.out</B>\nand tests its contents.\nNo messages are displayed if the file passes the integrity test.\n<P>\n<B>-s</B>\nstrip debug information before writing the output file.\nThis saves some space in very large chunks,\nbut if errors occur when running a stripped chunk,\nthen the error messages may not contain the full information they usually do.\nFor instance,\nline numbers and names of local variables are lost.\n<P>\n<B>-v</B>\nshow version information.\n<H2>FILES</H2>\n<P>\n<B>luac.out</B>\ndefault output file\n<H2>SEE ALSO</H2>\n<B>lua</B>(1)\n<BR>\n<A HREF=\"http://www.lua.org/\">http://www.lua.org/</A>\n<H2>DIAGNOSTICS</H2>\nError messages should be self explanatory.\n<H2>AUTHORS</H2>\nL. H. de Figueiredo,\nR. Ierusalimschy and\nW. Celes\n<!-- EOF -->\n</BODY>\n</HTML>\n"
  },
  {
    "path": "deps/lua/doc/manual.css",
    "content": "h3 code {\n\tfont-family: inherit ;\n\tfont-size: inherit ;\n}\n\npre, code {\n\tfont-size: 12pt ;\n}\n\nspan.apii {\n\tfloat: right ;\n\tfont-family: inherit ;\n\tfont-style: normal ;\n\tfont-size: small ;\n\tcolor: gray ;\n}\n\np+h1, ul+h1 {\n\tpadding-top: 0.4em ;\n\tpadding-bottom: 0.4em ;\n\tpadding-left: 30px ;\n\tmargin-left: -30px ;\n\tbackground-color: #E0E0FF ;\n}\n"
  },
  {
    "path": "deps/lua/doc/manual.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n<html>\n\n<head>\n<title>Lua 5.1 Reference Manual</title>\n<link rel=\"stylesheet\" type=\"text/css\" href=\"lua.css\">\n<link rel=\"stylesheet\" type=\"text/css\" href=\"manual.css\">\n<META HTTP-EQUIV=\"content-type\" CONTENT=\"text/html; charset=iso-8859-1\">\n</head>\n\n<body>\n\n<hr>\n<h1>\n<a href=\"http://www.lua.org/\"><img src=\"logo.gif\" alt=\"\" border=\"0\"></a>\nLua 5.1 Reference Manual\n</h1>\n\nby Roberto Ierusalimschy, Luiz Henrique de Figueiredo, Waldemar Celes\n<p>\n<small>\nCopyright &copy; 2006&ndash;2012 Lua.org, PUC-Rio.\nFreely available under the terms of the\n<a href=\"http://www.lua.org/license.html\">Lua license</a>.\n</small>\n<hr>\n<p>\n\n<a href=\"contents.html#contents\">contents</A>\n&middot;\n<a href=\"contents.html#index\">index</A>\n&middot;\n<A HREF=\"http://www.lua.org/manual/\">other versions</A>\n\n<!-- ====================================================================== -->\n<p>\n\n<!-- $Id: manual.of,v 1.49.1.2 2012/01/13 20:23:26 roberto Exp $ -->\n\n\n\n\n<h1>1 - <a name=\"1\">Introduction</a></h1>\n\n<p>\nLua is an extension programming language designed to support\ngeneral procedural programming with data description\nfacilities.\nIt also offers good support for object-oriented programming,\nfunctional programming, and data-driven programming.\nLua is intended to be used as a powerful, light-weight\nscripting language for any program that needs one.\nLua is implemented as a library, written in <em>clean</em> C\n(that is, in the common subset of ANSI&nbsp;C and C++).\n\n\n<p>\nBeing an extension language, Lua has no notion of a \"main\" program:\nit only works <em>embedded</em> in a host client,\ncalled the <em>embedding program</em> or simply the <em>host</em>.\nThis host program can invoke functions to execute a piece of Lua code,\ncan write and read Lua variables,\nand can register C&nbsp;functions to be called by Lua code.\nThrough the use of C&nbsp;functions, Lua can be augmented to cope with\na wide range of different domains,\nthus creating customized programming languages sharing a syntactical framework.\nThe Lua distribution includes a sample host program called <code>lua</code>,\nwhich uses the Lua library to offer a complete, stand-alone Lua interpreter.\n\n\n<p>\nLua is free software,\nand is provided as usual with no guarantees,\nas stated in its license.\nThe implementation described in this manual is available\nat Lua's official web site, <code>www.lua.org</code>.\n\n\n<p>\nLike any other reference manual,\nthis document is dry in places.\nFor a discussion of the decisions behind the design of Lua,\nsee the technical papers available at Lua's web site.\nFor a detailed introduction to programming in Lua,\nsee Roberto's book, <em>Programming in Lua (Second Edition)</em>.\n\n\n\n<h1>2 - <a name=\"2\">The Language</a></h1>\n\n<p>\nThis section describes the lexis, the syntax, and the semantics of Lua.\nIn other words,\nthis section describes\nwhich tokens are valid,\nhow they can be combined,\nand what their combinations mean.\n\n\n<p>\nThe language constructs will be explained using the usual extended BNF notation,\nin which\n{<em>a</em>}&nbsp;means&nbsp;0 or more <em>a</em>'s, and\n[<em>a</em>]&nbsp;means an optional <em>a</em>.\nNon-terminals are shown like non-terminal,\nkeywords are shown like <b>kword</b>,\nand other terminal symbols are shown like `<b>=</b>&acute;.\nThe complete syntax of Lua can be found in <a href=\"#8\">&sect;8</a>\nat the end of this manual.\n\n\n\n<h2>2.1 - <a name=\"2.1\">Lexical Conventions</a></h2>\n\n<p>\n<em>Names</em>\n(also called <em>identifiers</em>)\nin Lua can be any string of letters,\ndigits, and underscores,\nnot beginning with a digit.\nThis coincides with the definition of names in most languages.\n(The definition of letter depends on the current locale:\nany character considered alphabetic by the current locale\ncan be used in an identifier.)\nIdentifiers are used to name variables and table fields.\n\n\n<p>\nThe following <em>keywords</em> are reserved\nand cannot be used as names:\n\n\n<pre>\n     and       break     do        else      elseif\n     end       false     for       function  if\n     in        local     nil       not       or\n     repeat    return    then      true      until     while\n</pre>\n\n<p>\nLua is a case-sensitive language:\n<code>and</code> is a reserved word, but <code>And</code> and <code>AND</code>\nare two different, valid names.\nAs a convention, names starting with an underscore followed by\nuppercase letters (such as <a href=\"#pdf-_VERSION\"><code>_VERSION</code></a>)\nare reserved for internal global variables used by Lua.\n\n\n<p>\nThe following strings denote other tokens:\n\n<pre>\n     +     -     *     /     %     ^     #\n     ==    ~=    &lt;=    &gt;=    &lt;     &gt;     =\n     (     )     {     }     [     ]\n     ;     :     ,     .     ..    ...\n</pre>\n\n<p>\n<em>Literal strings</em>\ncan be delimited by matching single or double quotes,\nand can contain the following C-like escape sequences:\n'<code>\\a</code>' (bell),\n'<code>\\b</code>' (backspace),\n'<code>\\f</code>' (form feed),\n'<code>\\n</code>' (newline),\n'<code>\\r</code>' (carriage return),\n'<code>\\t</code>' (horizontal tab),\n'<code>\\v</code>' (vertical tab),\n'<code>\\\\</code>' (backslash),\n'<code>\\\"</code>' (quotation mark [double quote]),\nand '<code>\\'</code>' (apostrophe [single quote]).\nMoreover, a backslash followed by a real newline\nresults in a newline in the string.\nA character in a string can also be specified by its numerical value\nusing the escape sequence <code>\\<em>ddd</em></code>,\nwhere <em>ddd</em> is a sequence of up to three decimal digits.\n(Note that if a numerical escape is to be followed by a digit,\nit must be expressed using exactly three digits.)\nStrings in Lua can contain any 8-bit value, including embedded zeros,\nwhich can be specified as '<code>\\0</code>'.\n\n\n<p>\nLiteral strings can also be defined using a long format\nenclosed by <em>long brackets</em>.\nWe define an <em>opening long bracket of level <em>n</em></em> as an opening\nsquare bracket followed by <em>n</em> equal signs followed by another\nopening square bracket.\nSo, an opening long bracket of level&nbsp;0 is written as <code>[[</code>,\nan opening long bracket of level&nbsp;1 is written as <code>[=[</code>,\nand so on.\nA <em>closing long bracket</em> is defined similarly;\nfor instance, a closing long bracket of level&nbsp;4 is written as <code>]====]</code>.\nA long string starts with an opening long bracket of any level and\nends at the first closing long bracket of the same level.\nLiterals in this bracketed form can run for several lines,\ndo not interpret any escape sequences,\nand ignore long brackets of any other level.\nThey can contain anything except a closing bracket of the proper level.\n\n\n<p>\nFor convenience,\nwhen the opening long bracket is immediately followed by a newline,\nthe newline is not included in the string.\nAs an example, in a system using ASCII\n(in which '<code>a</code>' is coded as&nbsp;97,\nnewline is coded as&nbsp;10, and '<code>1</code>' is coded as&nbsp;49),\nthe five literal strings below denote the same string:\n\n<pre>\n     a = 'alo\\n123\"'\n     a = \"alo\\n123\\\"\"\n     a = '\\97lo\\10\\04923\"'\n     a = [[alo\n     123\"]]\n     a = [==[\n     alo\n     123\"]==]\n</pre>\n\n<p>\nA <em>numerical constant</em> can be written with an optional decimal part\nand an optional decimal exponent.\nLua also accepts integer hexadecimal constants,\nby prefixing them with <code>0x</code>.\nExamples of valid numerical constants are\n\n<pre>\n     3   3.0   3.1416   314.16e-2   0.31416E1   0xff   0x56\n</pre>\n\n<p>\nA <em>comment</em> starts with a double hyphen (<code>--</code>)\nanywhere outside a string.\nIf the text immediately after <code>--</code> is not an opening long bracket,\nthe comment is a <em>short comment</em>,\nwhich runs until the end of the line.\nOtherwise, it is a <em>long comment</em>,\nwhich runs until the corresponding closing long bracket.\nLong comments are frequently used to disable code temporarily.\n\n\n\n\n\n<h2>2.2 - <a name=\"2.2\">Values and Types</a></h2>\n\n<p>\nLua is a <em>dynamically typed language</em>.\nThis means that\nvariables do not have types; only values do.\nThere are no type definitions in the language.\nAll values carry their own type.\n\n\n<p>\nAll values in Lua are <em>first-class values</em>.\nThis means that all values can be stored in variables,\npassed as arguments to other functions, and returned as results.\n\n\n<p>\nThere are eight basic types in Lua:\n<em>nil</em>, <em>boolean</em>, <em>number</em>,\n<em>string</em>, <em>function</em>, <em>userdata</em>,\n<em>thread</em>, and <em>table</em>.\n<em>Nil</em> is the type of the value <b>nil</b>,\nwhose main property is to be different from any other value;\nit usually represents the absence of a useful value.\n<em>Boolean</em> is the type of the values <b>false</b> and <b>true</b>.\nBoth <b>nil</b> and <b>false</b> make a condition false;\nany other value makes it true.\n<em>Number</em> represents real (double-precision floating-point) numbers.\n(It is easy to build Lua interpreters that use other\ninternal representations for numbers,\nsuch as single-precision float or long integers;\nsee file <code>luaconf.h</code>.)\n<em>String</em> represents arrays of characters.\n\nLua is 8-bit clean:\nstrings can contain any 8-bit character,\nincluding embedded zeros ('<code>\\0</code>') (see <a href=\"#2.1\">&sect;2.1</a>).\n\n\n<p>\nLua can call (and manipulate) functions written in Lua and\nfunctions written in C\n(see <a href=\"#2.5.8\">&sect;2.5.8</a>).\n\n\n<p>\nThe type <em>userdata</em> is provided to allow arbitrary C&nbsp;data to\nbe stored in Lua variables.\nThis type corresponds to a block of raw memory\nand has no pre-defined operations in Lua,\nexcept assignment and identity test.\nHowever, by using <em>metatables</em>,\nthe programmer can define operations for userdata values\n(see <a href=\"#2.8\">&sect;2.8</a>).\nUserdata values cannot be created or modified in Lua,\nonly through the C&nbsp;API.\nThis guarantees the integrity of data owned by the host program.\n\n\n<p>\nThe type <em>thread</em> represents independent threads of execution\nand it is used to implement coroutines (see <a href=\"#2.11\">&sect;2.11</a>).\nDo not confuse Lua threads with operating-system threads.\nLua supports coroutines on all systems,\neven those that do not support threads.\n\n\n<p>\nThe type <em>table</em> implements associative arrays,\nthat is, arrays that can be indexed not only with numbers,\nbut with any value (except <b>nil</b>).\nTables can be <em>heterogeneous</em>;\nthat is, they can contain values of all types (except <b>nil</b>).\nTables are the sole data structuring mechanism in Lua;\nthey can be used to represent ordinary arrays,\nsymbol tables, sets, records, graphs, trees, etc.\nTo represent records, Lua uses the field name as an index.\nThe language supports this representation by\nproviding <code>a.name</code> as syntactic sugar for <code>a[\"name\"]</code>.\nThere are several convenient ways to create tables in Lua\n(see <a href=\"#2.5.7\">&sect;2.5.7</a>).\n\n\n<p>\nLike indices,\nthe value of a table field can be of any type (except <b>nil</b>).\nIn particular,\nbecause functions are first-class values,\ntable fields can contain functions.\nThus tables can also carry <em>methods</em> (see <a href=\"#2.5.9\">&sect;2.5.9</a>).\n\n\n<p>\nTables, functions, threads, and (full) userdata values are <em>objects</em>:\nvariables do not actually <em>contain</em> these values,\nonly <em>references</em> to them.\nAssignment, parameter passing, and function returns\nalways manipulate references to such values;\nthese operations do not imply any kind of copy.\n\n\n<p>\nThe library function <a href=\"#pdf-type\"><code>type</code></a> returns a string describing the type\nof a given value.\n\n\n\n<h3>2.2.1 - <a name=\"2.2.1\">Coercion</a></h3>\n\n<p>\nLua provides automatic conversion between\nstring and number values at run time.\nAny arithmetic operation applied to a string tries to convert\nthis string to a number, following the usual conversion rules.\nConversely, whenever a number is used where a string is expected,\nthe number is converted to a string, in a reasonable format.\nFor complete control over how numbers are converted to strings,\nuse the <code>format</code> function from the string library\n(see <a href=\"#pdf-string.format\"><code>string.format</code></a>).\n\n\n\n\n\n\n\n<h2>2.3 - <a name=\"2.3\">Variables</a></h2>\n\n<p>\nVariables are places that store values.\n\nThere are three kinds of variables in Lua:\nglobal variables, local variables, and table fields.\n\n\n<p>\nA single name can denote a global variable or a local variable\n(or a function's formal parameter,\nwhich is a particular kind of local variable):\n\n<pre>\n\tvar ::= Name\n</pre><p>\nName denotes identifiers, as defined in <a href=\"#2.1\">&sect;2.1</a>.\n\n\n<p>\nAny variable is assumed to be global unless explicitly declared\nas a local (see <a href=\"#2.4.7\">&sect;2.4.7</a>).\nLocal variables are <em>lexically scoped</em>:\nlocal variables can be freely accessed by functions\ndefined inside their scope (see <a href=\"#2.6\">&sect;2.6</a>).\n\n\n<p>\nBefore the first assignment to a variable, its value is <b>nil</b>.\n\n\n<p>\nSquare brackets are used to index a table:\n\n<pre>\n\tvar ::= prefixexp `<b>[</b>&acute; exp `<b>]</b>&acute;\n</pre><p>\nThe meaning of accesses to global variables \nand table fields can be changed via metatables.\nAn access to an indexed variable <code>t[i]</code> is equivalent to\na call <code>gettable_event(t,i)</code>.\n(See <a href=\"#2.8\">&sect;2.8</a> for a complete description of the\n<code>gettable_event</code> function.\nThis function is not defined or callable in Lua.\nWe use it here only for explanatory purposes.)\n\n\n<p>\nThe syntax <code>var.Name</code> is just syntactic sugar for\n<code>var[\"Name\"]</code>:\n\n<pre>\n\tvar ::= prefixexp `<b>.</b>&acute; Name\n</pre>\n\n<p>\nAll global variables live as fields in ordinary Lua tables,\ncalled <em>environment tables</em> or simply\n<em>environments</em> (see <a href=\"#2.9\">&sect;2.9</a>).\nEach function has its own reference to an environment,\nso that all global variables in this function\nwill refer to this environment table.\nWhen a function is created,\nit inherits the environment from the function that created it.\nTo get the environment table of a Lua function,\nyou call <a href=\"#pdf-getfenv\"><code>getfenv</code></a>.\nTo replace it,\nyou call <a href=\"#pdf-setfenv\"><code>setfenv</code></a>.\n(You can only manipulate the environment of C&nbsp;functions\nthrough the debug library; (see <a href=\"#5.9\">&sect;5.9</a>).)\n\n\n<p>\nAn access to a global variable <code>x</code>\nis equivalent to <code>_env.x</code>,\nwhich in turn is equivalent to\n\n<pre>\n     gettable_event(_env, \"x\")\n</pre><p>\nwhere <code>_env</code> is the environment of the running function.\n(See <a href=\"#2.8\">&sect;2.8</a> for a complete description of the\n<code>gettable_event</code> function.\nThis function is not defined or callable in Lua.\nSimilarly, the <code>_env</code> variable is not defined in Lua.\nWe use them here only for explanatory purposes.)\n\n\n\n\n\n<h2>2.4 - <a name=\"2.4\">Statements</a></h2>\n\n<p>\nLua supports an almost conventional set of statements,\nsimilar to those in Pascal or C.\nThis set includes\nassignments, control structures, function calls,\nand variable declarations.\n\n\n\n<h3>2.4.1 - <a name=\"2.4.1\">Chunks</a></h3>\n\n<p>\nThe unit of execution of Lua is called a <em>chunk</em>.\nA chunk is simply a sequence of statements,\nwhich are executed sequentially.\nEach statement can be optionally followed by a semicolon:\n\n<pre>\n\tchunk ::= {stat [`<b>;</b>&acute;]}\n</pre><p>\nThere are no empty statements and thus '<code>;;</code>' is not legal.\n\n\n<p>\nLua handles a chunk as the body of an anonymous function \nwith a variable number of arguments\n(see <a href=\"#2.5.9\">&sect;2.5.9</a>).\nAs such, chunks can define local variables,\nreceive arguments, and return values.\n\n\n<p>\nA chunk can be stored in a file or in a string inside the host program.\nTo execute a chunk,\nLua first pre-compiles the chunk into instructions for a virtual machine,\nand then it executes the compiled code\nwith an interpreter for the virtual machine.\n\n\n<p>\nChunks can also be pre-compiled into binary form;\nsee program <code>luac</code> for details.\nPrograms in source and compiled forms are interchangeable;\nLua automatically detects the file type and acts accordingly.\n\n\n\n\n\n\n<h3>2.4.2 - <a name=\"2.4.2\">Blocks</a></h3><p>\nA block is a list of statements;\nsyntactically, a block is the same as a chunk:\n\n<pre>\n\tblock ::= chunk\n</pre>\n\n<p>\nA block can be explicitly delimited to produce a single statement:\n\n<pre>\n\tstat ::= <b>do</b> block <b>end</b>\n</pre><p>\nExplicit blocks are useful\nto control the scope of variable declarations.\nExplicit blocks are also sometimes used to\nadd a <b>return</b> or <b>break</b> statement in the middle\nof another block (see <a href=\"#2.4.4\">&sect;2.4.4</a>).\n\n\n\n\n\n<h3>2.4.3 - <a name=\"2.4.3\">Assignment</a></h3>\n\n<p>\nLua allows multiple assignments.\nTherefore, the syntax for assignment\ndefines a list of variables on the left side\nand a list of expressions on the right side.\nThe elements in both lists are separated by commas:\n\n<pre>\n\tstat ::= varlist `<b>=</b>&acute; explist\n\tvarlist ::= var {`<b>,</b>&acute; var}\n\texplist ::= exp {`<b>,</b>&acute; exp}\n</pre><p>\nExpressions are discussed in <a href=\"#2.5\">&sect;2.5</a>.\n\n\n<p>\nBefore the assignment,\nthe list of values is <em>adjusted</em> to the length of\nthe list of variables.\nIf there are more values than needed,\nthe excess values are thrown away.\nIf there are fewer values than needed,\nthe list is extended with as many  <b>nil</b>'s as needed.\nIf the list of expressions ends with a function call,\nthen all values returned by that call enter the list of values,\nbefore the adjustment\n(except when the call is enclosed in parentheses; see <a href=\"#2.5\">&sect;2.5</a>).\n\n\n<p>\nThe assignment statement first evaluates all its expressions\nand only then are the assignments performed.\nThus the code\n\n<pre>\n     i = 3\n     i, a[i] = i+1, 20\n</pre><p>\nsets <code>a[3]</code> to 20, without affecting <code>a[4]</code>\nbecause the <code>i</code> in <code>a[i]</code> is evaluated (to 3)\nbefore it is assigned&nbsp;4.\nSimilarly, the line\n\n<pre>\n     x, y = y, x\n</pre><p>\nexchanges the values of <code>x</code> and <code>y</code>,\nand\n\n<pre>\n     x, y, z = y, z, x\n</pre><p>\ncyclically permutes the values of <code>x</code>, <code>y</code>, and <code>z</code>.\n\n\n<p>\nThe meaning of assignments to global variables\nand table fields can be changed via metatables.\nAn assignment to an indexed variable <code>t[i] = val</code> is equivalent to\n<code>settable_event(t,i,val)</code>.\n(See <a href=\"#2.8\">&sect;2.8</a> for a complete description of the\n<code>settable_event</code> function.\nThis function is not defined or callable in Lua.\nWe use it here only for explanatory purposes.)\n\n\n<p>\nAn assignment to a global variable <code>x = val</code>\nis equivalent to the assignment\n<code>_env.x = val</code>,\nwhich in turn is equivalent to\n\n<pre>\n     settable_event(_env, \"x\", val)\n</pre><p>\nwhere <code>_env</code> is the environment of the running function.\n(The <code>_env</code> variable is not defined in Lua.\nWe use it here only for explanatory purposes.)\n\n\n\n\n\n<h3>2.4.4 - <a name=\"2.4.4\">Control Structures</a></h3><p>\nThe control structures\n<b>if</b>, <b>while</b>, and <b>repeat</b> have the usual meaning and\nfamiliar syntax:\n\n\n\n\n<pre>\n\tstat ::= <b>while</b> exp <b>do</b> block <b>end</b>\n\tstat ::= <b>repeat</b> block <b>until</b> exp\n\tstat ::= <b>if</b> exp <b>then</b> block {<b>elseif</b> exp <b>then</b> block} [<b>else</b> block] <b>end</b>\n</pre><p>\nLua also has a <b>for</b> statement, in two flavors (see <a href=\"#2.4.5\">&sect;2.4.5</a>).\n\n\n<p>\nThe condition expression of a\ncontrol structure can return any value.\nBoth <b>false</b> and <b>nil</b> are considered false.\nAll values different from <b>nil</b> and <b>false</b> are considered true\n(in particular, the number 0 and the empty string are also true).\n\n\n<p>\nIn the <b>repeat</b>&ndash;<b>until</b> loop,\nthe inner block does not end at the <b>until</b> keyword,\nbut only after the condition.\nSo, the condition can refer to local variables\ndeclared inside the loop block.\n\n\n<p>\nThe <b>return</b> statement is used to return values\nfrom a function or a chunk (which is just a function).\n\nFunctions and chunks can return more than one value,\nand so the syntax for the <b>return</b> statement is\n\n<pre>\n\tstat ::= <b>return</b> [explist]\n</pre>\n\n<p>\nThe <b>break</b> statement is used to terminate the execution of a\n<b>while</b>, <b>repeat</b>, or <b>for</b> loop,\nskipping to the next statement after the loop:\n\n\n<pre>\n\tstat ::= <b>break</b>\n</pre><p>\nA <b>break</b> ends the innermost enclosing loop.\n\n\n<p>\nThe <b>return</b> and <b>break</b>\nstatements can only be written as the <em>last</em> statement of a block.\nIf it is really necessary to <b>return</b> or <b>break</b> in the\nmiddle of a block,\nthen an explicit inner block can be used,\nas in the idioms\n<code>do return end</code> and <code>do break end</code>,\nbecause now <b>return</b> and <b>break</b> are the last statements in\ntheir (inner) blocks.\n\n\n\n\n\n<h3>2.4.5 - <a name=\"2.4.5\">For Statement</a></h3>\n\n<p>\n\nThe <b>for</b> statement has two forms:\none numeric and one generic.\n\n\n<p>\nThe numeric <b>for</b> loop repeats a block of code while a\ncontrol variable runs through an arithmetic progression.\nIt has the following syntax:\n\n<pre>\n\tstat ::= <b>for</b> Name `<b>=</b>&acute; exp `<b>,</b>&acute; exp [`<b>,</b>&acute; exp] <b>do</b> block <b>end</b>\n</pre><p>\nThe <em>block</em> is repeated for <em>name</em> starting at the value of\nthe first <em>exp</em>, until it passes the second <em>exp</em> by steps of the\nthird <em>exp</em>.\nMore precisely, a <b>for</b> statement like\n\n<pre>\n     for v = <em>e1</em>, <em>e2</em>, <em>e3</em> do <em>block</em> end\n</pre><p>\nis equivalent to the code:\n\n<pre>\n     do\n       local <em>var</em>, <em>limit</em>, <em>step</em> = tonumber(<em>e1</em>), tonumber(<em>e2</em>), tonumber(<em>e3</em>)\n       if not (<em>var</em> and <em>limit</em> and <em>step</em>) then error() end\n       while (<em>step</em> &gt; 0 and <em>var</em> &lt;= <em>limit</em>) or (<em>step</em> &lt;= 0 and <em>var</em> &gt;= <em>limit</em>) do\n         local v = <em>var</em>\n         <em>block</em>\n         <em>var</em> = <em>var</em> + <em>step</em>\n       end\n     end\n</pre><p>\nNote the following:\n\n<ul>\n\n<li>\nAll three control expressions are evaluated only once,\nbefore the loop starts.\nThey must all result in numbers.\n</li>\n\n<li>\n<code><em>var</em></code>, <code><em>limit</em></code>, and <code><em>step</em></code> are invisible variables.\nThe names shown here are for explanatory purposes only.\n</li>\n\n<li>\nIf the third expression (the step) is absent,\nthen a step of&nbsp;1 is used.\n</li>\n\n<li>\nYou can use <b>break</b> to exit a <b>for</b> loop.\n</li>\n\n<li>\nThe loop variable <code>v</code> is local to the loop;\nyou cannot use its value after the <b>for</b> ends or is broken.\nIf you need this value,\nassign it to another variable before breaking or exiting the loop.\n</li>\n\n</ul>\n\n<p>\nThe generic <b>for</b> statement works over functions,\ncalled <em>iterators</em>.\nOn each iteration, the iterator function is called to produce a new value,\nstopping when this new value is <b>nil</b>.\nThe generic <b>for</b> loop has the following syntax:\n\n<pre>\n\tstat ::= <b>for</b> namelist <b>in</b> explist <b>do</b> block <b>end</b>\n\tnamelist ::= Name {`<b>,</b>&acute; Name}\n</pre><p>\nA <b>for</b> statement like\n\n<pre>\n     for <em>var_1</em>, &middot;&middot;&middot;, <em>var_n</em> in <em>explist</em> do <em>block</em> end\n</pre><p>\nis equivalent to the code:\n\n<pre>\n     do\n       local <em>f</em>, <em>s</em>, <em>var</em> = <em>explist</em>\n       while true do\n         local <em>var_1</em>, &middot;&middot;&middot;, <em>var_n</em> = <em>f</em>(<em>s</em>, <em>var</em>)\n         <em>var</em> = <em>var_1</em>\n         if <em>var</em> == nil then break end\n         <em>block</em>\n       end\n     end\n</pre><p>\nNote the following:\n\n<ul>\n\n<li>\n<code><em>explist</em></code> is evaluated only once.\nIts results are an <em>iterator</em> function,\na <em>state</em>,\nand an initial value for the first <em>iterator variable</em>.\n</li>\n\n<li>\n<code><em>f</em></code>, <code><em>s</em></code>, and <code><em>var</em></code> are invisible variables.\nThe names are here for explanatory purposes only.\n</li>\n\n<li>\nYou can use <b>break</b> to exit a <b>for</b> loop.\n</li>\n\n<li>\nThe loop variables <code><em>var_i</em></code> are local to the loop;\nyou cannot use their values after the <b>for</b> ends.\nIf you need these values,\nthen assign them to other variables before breaking or exiting the loop.\n</li>\n\n</ul>\n\n\n\n\n<h3>2.4.6 - <a name=\"2.4.6\">Function Calls as Statements</a></h3><p>\nTo allow possible side-effects,\nfunction calls can be executed as statements:\n\n<pre>\n\tstat ::= functioncall\n</pre><p>\nIn this case, all returned values are thrown away.\nFunction calls are explained in <a href=\"#2.5.8\">&sect;2.5.8</a>.\n\n\n\n\n\n<h3>2.4.7 - <a name=\"2.4.7\">Local Declarations</a></h3><p>\nLocal variables can be declared anywhere inside a block.\nThe declaration can include an initial assignment:\n\n<pre>\n\tstat ::= <b>local</b> namelist [`<b>=</b>&acute; explist]\n</pre><p>\nIf present, an initial assignment has the same semantics\nof a multiple assignment (see <a href=\"#2.4.3\">&sect;2.4.3</a>).\nOtherwise, all variables are initialized with <b>nil</b>.\n\n\n<p>\nA chunk is also a block (see <a href=\"#2.4.1\">&sect;2.4.1</a>),\nand so local variables can be declared in a chunk outside any explicit block.\nThe scope of such local variables extends until the end of the chunk.\n\n\n<p>\nThe visibility rules for local variables are explained in <a href=\"#2.6\">&sect;2.6</a>.\n\n\n\n\n\n\n\n<h2>2.5 - <a name=\"2.5\">Expressions</a></h2>\n\n<p>\nThe basic expressions in Lua are the following:\n\n<pre>\n\texp ::= prefixexp\n\texp ::= <b>nil</b> | <b>false</b> | <b>true</b>\n\texp ::= Number\n\texp ::= String\n\texp ::= function\n\texp ::= tableconstructor\n\texp ::= `<b>...</b>&acute;\n\texp ::= exp binop exp\n\texp ::= unop exp\n\tprefixexp ::= var | functioncall | `<b>(</b>&acute; exp `<b>)</b>&acute;\n</pre>\n\n<p>\nNumbers and literal strings are explained in <a href=\"#2.1\">&sect;2.1</a>;\nvariables are explained in <a href=\"#2.3\">&sect;2.3</a>;\nfunction definitions are explained in <a href=\"#2.5.9\">&sect;2.5.9</a>;\nfunction calls are explained in <a href=\"#2.5.8\">&sect;2.5.8</a>;\ntable constructors are explained in <a href=\"#2.5.7\">&sect;2.5.7</a>.\nVararg expressions,\ndenoted by three dots ('<code>...</code>'), can only be used when\ndirectly inside a vararg function;\nthey are explained in <a href=\"#2.5.9\">&sect;2.5.9</a>.\n\n\n<p>\nBinary operators comprise arithmetic operators (see <a href=\"#2.5.1\">&sect;2.5.1</a>),\nrelational operators (see <a href=\"#2.5.2\">&sect;2.5.2</a>), logical operators (see <a href=\"#2.5.3\">&sect;2.5.3</a>),\nand the concatenation operator (see <a href=\"#2.5.4\">&sect;2.5.4</a>).\nUnary operators comprise the unary minus (see <a href=\"#2.5.1\">&sect;2.5.1</a>),\nthe unary <b>not</b> (see <a href=\"#2.5.3\">&sect;2.5.3</a>),\nand the unary <em>length operator</em> (see <a href=\"#2.5.5\">&sect;2.5.5</a>).\n\n\n<p>\nBoth function calls and vararg expressions can result in multiple values.\nIf an expression is used as a statement\n(only possible for function calls (see <a href=\"#2.4.6\">&sect;2.4.6</a>)),\nthen its return list is adjusted to zero elements,\nthus discarding all returned values.\nIf an expression is used as the last (or the only) element\nof a list of expressions,\nthen no adjustment is made\n(unless the call is enclosed in parentheses).\nIn all other contexts,\nLua adjusts the result list to one element,\ndiscarding all values except the first one.\n\n\n<p>\nHere are some examples:\n\n<pre>\n     f()                -- adjusted to 0 results\n     g(f(), x)          -- f() is adjusted to 1 result\n     g(x, f())          -- g gets x plus all results from f()\n     a,b,c = f(), x     -- f() is adjusted to 1 result (c gets nil)\n     a,b = ...          -- a gets the first vararg parameter, b gets\n                        -- the second (both a and b can get nil if there\n                        -- is no corresponding vararg parameter)\n     \n     a,b,c = x, f()     -- f() is adjusted to 2 results\n     a,b,c = f()        -- f() is adjusted to 3 results\n     return f()         -- returns all results from f()\n     return ...         -- returns all received vararg parameters\n     return x,y,f()     -- returns x, y, and all results from f()\n     {f()}              -- creates a list with all results from f()\n     {...}              -- creates a list with all vararg parameters\n     {f(), nil}         -- f() is adjusted to 1 result\n</pre>\n\n<p>\nAny expression enclosed in parentheses always results in only one value.\nThus,\n<code>(f(x,y,z))</code> is always a single value,\neven if <code>f</code> returns several values.\n(The value of <code>(f(x,y,z))</code> is the first value returned by <code>f</code>\nor <b>nil</b> if <code>f</code> does not return any values.)\n\n\n\n<h3>2.5.1 - <a name=\"2.5.1\">Arithmetic Operators</a></h3><p>\nLua supports the usual arithmetic operators:\nthe binary <code>+</code> (addition),\n<code>-</code> (subtraction), <code>*</code> (multiplication),\n<code>/</code> (division), <code>%</code> (modulo), and <code>^</code> (exponentiation);\nand unary <code>-</code> (negation).\nIf the operands are numbers, or strings that can be converted to\nnumbers (see <a href=\"#2.2.1\">&sect;2.2.1</a>),\nthen all operations have the usual meaning.\nExponentiation works for any exponent.\nFor instance, <code>x^(-0.5)</code> computes the inverse of the square root of <code>x</code>.\nModulo is defined as\n\n<pre>\n     a % b == a - math.floor(a/b)*b\n</pre><p>\nThat is, it is the remainder of a division that rounds\nthe quotient towards minus infinity.\n\n\n\n\n\n<h3>2.5.2 - <a name=\"2.5.2\">Relational Operators</a></h3><p>\nThe relational operators in Lua are\n\n<pre>\n     ==    ~=    &lt;     &gt;     &lt;=    &gt;=\n</pre><p>\nThese operators always result in <b>false</b> or <b>true</b>.\n\n\n<p>\nEquality (<code>==</code>) first compares the type of its operands.\nIf the types are different, then the result is <b>false</b>.\nOtherwise, the values of the operands are compared.\nNumbers and strings are compared in the usual way.\nObjects (tables, userdata, threads, and functions)\nare compared by <em>reference</em>:\ntwo objects are considered equal only if they are the <em>same</em> object.\nEvery time you create a new object\n(a table, userdata, thread, or function),\nthis new object is different from any previously existing object.\n\n\n<p>\nYou can change the way that Lua compares tables and userdata \nby using the \"eq\" metamethod (see <a href=\"#2.8\">&sect;2.8</a>).\n\n\n<p>\nThe conversion rules of <a href=\"#2.2.1\">&sect;2.2.1</a>\n<em>do not</em> apply to equality comparisons.\nThus, <code>\"0\"==0</code> evaluates to <b>false</b>,\nand <code>t[0]</code> and <code>t[\"0\"]</code> denote different\nentries in a table.\n\n\n<p>\nThe operator <code>~=</code> is exactly the negation of equality (<code>==</code>).\n\n\n<p>\nThe order operators work as follows.\nIf both arguments are numbers, then they are compared as such.\nOtherwise, if both arguments are strings,\nthen their values are compared according to the current locale.\nOtherwise, Lua tries to call the \"lt\" or the \"le\"\nmetamethod (see <a href=\"#2.8\">&sect;2.8</a>).\nA comparison <code>a &gt; b</code> is translated to <code>b &lt; a</code>\nand <code>a &gt;= b</code> is translated to <code>b &lt;= a</code>.\n\n\n\n\n\n<h3>2.5.3 - <a name=\"2.5.3\">Logical Operators</a></h3><p>\nThe logical operators in Lua are\n<b>and</b>, <b>or</b>, and <b>not</b>.\nLike the control structures (see <a href=\"#2.4.4\">&sect;2.4.4</a>),\nall logical operators consider both <b>false</b> and <b>nil</b> as false\nand anything else as true.\n\n\n<p>\nThe negation operator <b>not</b> always returns <b>false</b> or <b>true</b>.\nThe conjunction operator <b>and</b> returns its first argument\nif this value is <b>false</b> or <b>nil</b>;\notherwise, <b>and</b> returns its second argument.\nThe disjunction operator <b>or</b> returns its first argument\nif this value is different from <b>nil</b> and <b>false</b>;\notherwise, <b>or</b> returns its second argument.\nBoth <b>and</b> and <b>or</b> use short-cut evaluation;\nthat is,\nthe second operand is evaluated only if necessary.\nHere are some examples:\n\n<pre>\n     10 or 20            --&gt; 10\n     10 or error()       --&gt; 10\n     nil or \"a\"          --&gt; \"a\"\n     nil and 10          --&gt; nil\n     false and error()   --&gt; false\n     false and nil       --&gt; false\n     false or nil        --&gt; nil\n     10 and 20           --&gt; 20\n</pre><p>\n(In this manual,\n<code>--&gt;</code> indicates the result of the preceding expression.)\n\n\n\n\n\n<h3>2.5.4 - <a name=\"2.5.4\">Concatenation</a></h3><p>\nThe string concatenation operator in Lua is\ndenoted by two dots ('<code>..</code>').\nIf both operands are strings or numbers, then they are converted to\nstrings according to the rules mentioned in <a href=\"#2.2.1\">&sect;2.2.1</a>.\nOtherwise, the \"concat\" metamethod is called (see <a href=\"#2.8\">&sect;2.8</a>).\n\n\n\n\n\n<h3>2.5.5 - <a name=\"2.5.5\">The Length Operator</a></h3>\n\n<p>\nThe length operator is denoted by the unary operator <code>#</code>.\nThe length of a string is its number of bytes\n(that is, the usual meaning of string length when each\ncharacter is one byte).\n\n\n<p>\nThe length of a table <code>t</code> is defined to be any\ninteger index <code>n</code>\nsuch that <code>t[n]</code> is not <b>nil</b> and <code>t[n+1]</code> is <b>nil</b>;\nmoreover, if <code>t[1]</code> is <b>nil</b>, <code>n</code> can be zero.\nFor a regular array, with non-nil values from 1 to a given <code>n</code>,\nits length is exactly that <code>n</code>,\nthe index of its last value.\nIf the array has \"holes\"\n(that is, <b>nil</b> values between other non-nil values),\nthen <code>#t</code> can be any of the indices that\ndirectly precedes a <b>nil</b> value\n(that is, it may consider any such <b>nil</b> value as the end of\nthe array). \n\n\n\n\n\n<h3>2.5.6 - <a name=\"2.5.6\">Precedence</a></h3><p>\nOperator precedence in Lua follows the table below,\nfrom lower to higher priority:\n\n<pre>\n     or\n     and\n     &lt;     &gt;     &lt;=    &gt;=    ~=    ==\n     ..\n     +     -\n     *     /     %\n     not   #     - (unary)\n     ^\n</pre><p>\nAs usual,\nyou can use parentheses to change the precedences of an expression.\nThe concatenation ('<code>..</code>') and exponentiation ('<code>^</code>')\noperators are right associative.\nAll other binary operators are left associative.\n\n\n\n\n\n<h3>2.5.7 - <a name=\"2.5.7\">Table Constructors</a></h3><p>\nTable constructors are expressions that create tables.\nEvery time a constructor is evaluated, a new table is created.\nA constructor can be used to create an empty table\nor to create a table and initialize some of its fields.\nThe general syntax for constructors is\n\n<pre>\n\ttableconstructor ::= `<b>{</b>&acute; [fieldlist] `<b>}</b>&acute;\n\tfieldlist ::= field {fieldsep field} [fieldsep]\n\tfield ::= `<b>[</b>&acute; exp `<b>]</b>&acute; `<b>=</b>&acute; exp | Name `<b>=</b>&acute; exp | exp\n\tfieldsep ::= `<b>,</b>&acute; | `<b>;</b>&acute;\n</pre>\n\n<p>\nEach field of the form <code>[exp1] = exp2</code> adds to the new table an entry\nwith key <code>exp1</code> and value <code>exp2</code>.\nA field of the form <code>name = exp</code> is equivalent to\n<code>[\"name\"] = exp</code>.\nFinally, fields of the form <code>exp</code> are equivalent to\n<code>[i] = exp</code>, where <code>i</code> are consecutive numerical integers,\nstarting with 1.\nFields in the other formats do not affect this counting.\nFor example,\n\n<pre>\n     a = { [f(1)] = g; \"x\", \"y\"; x = 1, f(x), [30] = 23; 45 }\n</pre><p>\nis equivalent to\n\n<pre>\n     do\n       local t = {}\n       t[f(1)] = g\n       t[1] = \"x\"         -- 1st exp\n       t[2] = \"y\"         -- 2nd exp\n       t.x = 1            -- t[\"x\"] = 1\n       t[3] = f(x)        -- 3rd exp\n       t[30] = 23\n       t[4] = 45          -- 4th exp\n       a = t\n     end\n</pre>\n\n<p>\nIf the last field in the list has the form <code>exp</code>\nand the expression is a function call or a vararg expression,\nthen all values returned by this expression enter the list consecutively\n(see <a href=\"#2.5.8\">&sect;2.5.8</a>).\nTo avoid this,\nenclose the function call or the vararg expression\nin parentheses (see <a href=\"#2.5\">&sect;2.5</a>).\n\n\n<p>\nThe field list can have an optional trailing separator,\nas a convenience for machine-generated code.\n\n\n\n\n\n<h3>2.5.8 - <a name=\"2.5.8\">Function Calls</a></h3><p>\nA function call in Lua has the following syntax:\n\n<pre>\n\tfunctioncall ::= prefixexp args\n</pre><p>\nIn a function call,\nfirst prefixexp and args are evaluated.\nIf the value of prefixexp has type <em>function</em>,\nthen this function is called\nwith the given arguments.\nOtherwise, the prefixexp \"call\" metamethod is called,\nhaving as first parameter the value of prefixexp,\nfollowed by the original call arguments\n(see <a href=\"#2.8\">&sect;2.8</a>).\n\n\n<p>\nThe form\n\n<pre>\n\tfunctioncall ::= prefixexp `<b>:</b>&acute; Name args\n</pre><p>\ncan be used to call \"methods\".\nA call <code>v:name(<em>args</em>)</code>\nis syntactic sugar for <code>v.name(v,<em>args</em>)</code>,\nexcept that <code>v</code> is evaluated only once.\n\n\n<p>\nArguments have the following syntax:\n\n<pre>\n\targs ::= `<b>(</b>&acute; [explist] `<b>)</b>&acute;\n\targs ::= tableconstructor\n\targs ::= String\n</pre><p>\nAll argument expressions are evaluated before the call.\nA call of the form <code>f{<em>fields</em>}</code> is\nsyntactic sugar for <code>f({<em>fields</em>})</code>;\nthat is, the argument list is a single new table.\nA call of the form <code>f'<em>string</em>'</code>\n(or <code>f\"<em>string</em>\"</code> or <code>f[[<em>string</em>]]</code>)\nis syntactic sugar for <code>f('<em>string</em>')</code>;\nthat is, the argument list is a single literal string.\n\n\n<p>\nAs an exception to the free-format syntax of Lua,\nyou cannot put a line break before the '<code>(</code>' in a function call.\nThis restriction avoids some ambiguities in the language.\nIf you write\n\n<pre>\n     a = f\n     (g).x(a)\n</pre><p>\nLua would see that as a single statement, <code>a = f(g).x(a)</code>.\nSo, if you want two statements, you must add a semi-colon between them.\nIf you actually want to call <code>f</code>,\nyou must remove the line break before <code>(g)</code>.\n\n\n<p>\nA call of the form <code>return</code> <em>functioncall</em> is called\na <em>tail call</em>.\nLua implements <em>proper tail calls</em>\n(or <em>proper tail recursion</em>):\nin a tail call,\nthe called function reuses the stack entry of the calling function.\nTherefore, there is no limit on the number of nested tail calls that\na program can execute.\nHowever, a tail call erases any debug information about the\ncalling function.\nNote that a tail call only happens with a particular syntax,\nwhere the <b>return</b> has one single function call as argument;\nthis syntax makes the calling function return exactly\nthe returns of the called function.\nSo, none of the following examples are tail calls:\n\n<pre>\n     return (f(x))        -- results adjusted to 1\n     return 2 * f(x)\n     return x, f(x)       -- additional results\n     f(x); return         -- results discarded\n     return x or f(x)     -- results adjusted to 1\n</pre>\n\n\n\n\n<h3>2.5.9 - <a name=\"2.5.9\">Function Definitions</a></h3>\n\n<p>\nThe syntax for function definition is\n\n<pre>\n\tfunction ::= <b>function</b> funcbody\n\tfuncbody ::= `<b>(</b>&acute; [parlist] `<b>)</b>&acute; block <b>end</b>\n</pre>\n\n<p>\nThe following syntactic sugar simplifies function definitions:\n\n<pre>\n\tstat ::= <b>function</b> funcname funcbody\n\tstat ::= <b>local</b> <b>function</b> Name funcbody\n\tfuncname ::= Name {`<b>.</b>&acute; Name} [`<b>:</b>&acute; Name]\n</pre><p>\nThe statement\n\n<pre>\n     function f () <em>body</em> end\n</pre><p>\ntranslates to\n\n<pre>\n     f = function () <em>body</em> end\n</pre><p>\nThe statement\n\n<pre>\n     function t.a.b.c.f () <em>body</em> end\n</pre><p>\ntranslates to\n\n<pre>\n     t.a.b.c.f = function () <em>body</em> end\n</pre><p>\nThe statement\n\n<pre>\n     local function f () <em>body</em> end\n</pre><p>\ntranslates to\n\n<pre>\n     local f; f = function () <em>body</em> end\n</pre><p>\n<em>not</em> to\n\n<pre>\n     local f = function () <em>body</em> end\n</pre><p>\n(This only makes a difference when the body of the function\ncontains references to <code>f</code>.)\n\n\n<p>\nA function definition is an executable expression,\nwhose value has type <em>function</em>.\nWhen Lua pre-compiles a chunk,\nall its function bodies are pre-compiled too.\nThen, whenever Lua executes the function definition,\nthe function is <em>instantiated</em> (or <em>closed</em>).\nThis function instance (or <em>closure</em>)\nis the final value of the expression.\nDifferent instances of the same function\ncan refer to different  external local variables\nand can have different environment tables.\n\n\n<p>\nParameters act as local variables that are\ninitialized with the argument values:\n\n<pre>\n\tparlist ::= namelist [`<b>,</b>&acute; `<b>...</b>&acute;] | `<b>...</b>&acute;\n</pre><p>\nWhen a function is called,\nthe list of arguments is adjusted to\nthe length of the list of parameters,\nunless the function is a variadic or <em>vararg function</em>,\nwhich is\nindicated by three dots ('<code>...</code>') at the end of its parameter list.\nA vararg function does not adjust its argument list;\ninstead, it collects all extra arguments and supplies them\nto the function through a <em>vararg expression</em>,\nwhich is also written as three dots.\nThe value of this expression is a list of all actual extra arguments,\nsimilar to a function with multiple results.\nIf a vararg expression is used inside another expression\nor in the middle of a list of expressions,\nthen its return list is adjusted to one element.\nIf the expression is used as the last element of a list of expressions,\nthen no adjustment is made\n(unless that last expression is enclosed in parentheses).\n\n\n<p>\nAs an example, consider the following definitions:\n\n<pre>\n     function f(a, b) end\n     function g(a, b, ...) end\n     function r() return 1,2,3 end\n</pre><p>\nThen, we have the following mapping from arguments to parameters and\nto the vararg expression:\n\n<pre>\n     CALL            PARAMETERS\n     \n     f(3)             a=3, b=nil\n     f(3, 4)          a=3, b=4\n     f(3, 4, 5)       a=3, b=4\n     f(r(), 10)       a=1, b=10\n     f(r())           a=1, b=2\n     \n     g(3)             a=3, b=nil, ... --&gt;  (nothing)\n     g(3, 4)          a=3, b=4,   ... --&gt;  (nothing)\n     g(3, 4, 5, 8)    a=3, b=4,   ... --&gt;  5  8\n     g(5, r())        a=5, b=1,   ... --&gt;  2  3\n</pre>\n\n<p>\nResults are returned using the <b>return</b> statement (see <a href=\"#2.4.4\">&sect;2.4.4</a>).\nIf control reaches the end of a function\nwithout encountering a <b>return</b> statement,\nthen the function returns with no results.\n\n\n<p>\nThe <em>colon</em> syntax\nis used for defining <em>methods</em>,\nthat is, functions that have an implicit extra parameter <code>self</code>.\nThus, the statement\n\n<pre>\n     function t.a.b.c:f (<em>params</em>) <em>body</em> end\n</pre><p>\nis syntactic sugar for\n\n<pre>\n     t.a.b.c.f = function (self, <em>params</em>) <em>body</em> end\n</pre>\n\n\n\n\n\n\n<h2>2.6 - <a name=\"2.6\">Visibility Rules</a></h2>\n\n<p>\n\nLua is a lexically scoped language.\nThe scope of variables begins at the first statement <em>after</em>\ntheir declaration and lasts until the end of the innermost block that\nincludes the declaration.\nConsider the following example:\n\n<pre>\n     x = 10                -- global variable\n     do                    -- new block\n       local x = x         -- new 'x', with value 10\n       print(x)            --&gt; 10\n       x = x+1\n       do                  -- another block\n         local x = x+1     -- another 'x'\n         print(x)          --&gt; 12\n       end\n       print(x)            --&gt; 11\n     end\n     print(x)              --&gt; 10  (the global one)\n</pre>\n\n<p>\nNotice that, in a declaration like <code>local x = x</code>,\nthe new <code>x</code> being declared is not in scope yet,\nand so the second <code>x</code> refers to the outside variable.\n\n\n<p>\nBecause of the lexical scoping rules,\nlocal variables can be freely accessed by functions\ndefined inside their scope.\nA local variable used by an inner function is called\nan <em>upvalue</em>, or <em>external local variable</em>,\ninside the inner function.\n\n\n<p>\nNotice that each execution of a <b>local</b> statement\ndefines new local variables.\nConsider the following example:\n\n<pre>\n     a = {}\n     local x = 20\n     for i=1,10 do\n       local y = 0\n       a[i] = function () y=y+1; return x+y end\n     end\n</pre><p>\nThe loop creates ten closures\n(that is, ten instances of the anonymous function).\nEach of these closures uses a different <code>y</code> variable,\nwhile all of them share the same <code>x</code>.\n\n\n\n\n\n<h2>2.7 - <a name=\"2.7\">Error Handling</a></h2>\n\n<p>\nBecause Lua is an embedded extension language,\nall Lua actions start from C&nbsp;code in the host program\ncalling a function from the Lua library (see <a href=\"#lua_pcall\"><code>lua_pcall</code></a>).\nWhenever an error occurs during Lua compilation or execution,\ncontrol returns to C,\nwhich can take appropriate measures\n(such as printing an error message).\n\n\n<p>\nLua code can explicitly generate an error by calling the\n<a href=\"#pdf-error\"><code>error</code></a> function.\nIf you need to catch errors in Lua,\nyou can use the <a href=\"#pdf-pcall\"><code>pcall</code></a> function.\n\n\n\n\n\n<h2>2.8 - <a name=\"2.8\">Metatables</a></h2>\n\n<p>\nEvery value in Lua can have a <em>metatable</em>.\nThis <em>metatable</em> is an ordinary Lua table\nthat defines the behavior of the original value\nunder certain special operations.\nYou can change several aspects of the behavior\nof operations over a value by setting specific fields in its metatable.\nFor instance, when a non-numeric value is the operand of an addition,\nLua checks for a function in the field <code>\"__add\"</code> in its metatable.\nIf it finds one,\nLua calls this function to perform the addition.\n\n\n<p>\nWe call the keys in a metatable <em>events</em>\nand the values <em>metamethods</em>.\nIn the previous example, the event is <code>\"add\"</code> \nand the metamethod is the function that performs the addition.\n\n\n<p>\nYou can query the metatable of any value\nthrough the <a href=\"#pdf-getmetatable\"><code>getmetatable</code></a> function.\n\n\n<p>\nYou can replace the metatable of tables\nthrough the <a href=\"#pdf-setmetatable\"><code>setmetatable</code></a>\nfunction.\nYou cannot change the metatable of other types from Lua\n(except by using the debug library);\nyou must use the C&nbsp;API for that.\n\n\n<p>\nTables and full userdata have individual metatables\n(although multiple tables and userdata can share their metatables).\nValues of all other types share one single metatable per type;\nthat is, there is one single metatable for all numbers,\none for all strings, etc.\n\n\n<p>\nA metatable controls how an object behaves in arithmetic operations,\norder comparisons, concatenation, length operation, and indexing.\nA metatable also can define a function to be called when a userdata\nis garbage collected.\nFor each of these operations Lua associates a specific key\ncalled an <em>event</em>.\nWhen Lua performs one of these operations over a value,\nit checks whether this value has a metatable with the corresponding event.\nIf so, the value associated with that key (the metamethod)\ncontrols how Lua will perform the operation.\n\n\n<p>\nMetatables control the operations listed next.\nEach operation is identified by its corresponding name.\nThe key for each operation is a string with its name prefixed by\ntwo underscores, '<code>__</code>';\nfor instance, the key for operation \"add\" is the\nstring <code>\"__add\"</code>.\nThe semantics of these operations is better explained by a Lua function\ndescribing how the interpreter executes the operation.\n\n\n<p>\nThe code shown here in Lua is only illustrative;\nthe real behavior is hard coded in the interpreter\nand it is much more efficient than this simulation.\nAll functions used in these descriptions\n(<a href=\"#pdf-rawget\"><code>rawget</code></a>, <a href=\"#pdf-tonumber\"><code>tonumber</code></a>, etc.)\nare described in <a href=\"#5.1\">&sect;5.1</a>.\nIn particular, to retrieve the metamethod of a given object,\nwe use the expression\n\n<pre>\n     metatable(obj)[event]\n</pre><p>\nThis should be read as\n\n<pre>\n     rawget(getmetatable(obj) or {}, event)\n</pre><p>\n\nThat is, the access to a metamethod does not invoke other metamethods,\nand the access to objects with no metatables does not fail\n(it simply results in <b>nil</b>).\n\n\n\n<ul>\n\n<li><b>\"add\":</b>\nthe <code>+</code> operation.\n\n\n\n<p>\nThe function <code>getbinhandler</code> below defines how Lua chooses a handler\nfor a binary operation.\nFirst, Lua tries the first operand.\nIf its type does not define a handler for the operation,\nthen Lua tries the second operand.\n\n<pre>\n     function getbinhandler (op1, op2, event)\n       return metatable(op1)[event] or metatable(op2)[event]\n     end\n</pre><p>\nBy using this function,\nthe behavior of the <code>op1 + op2</code> is\n\n<pre>\n     function add_event (op1, op2)\n       local o1, o2 = tonumber(op1), tonumber(op2)\n       if o1 and o2 then  -- both operands are numeric?\n         return o1 + o2   -- '+' here is the primitive 'add'\n       else  -- at least one of the operands is not numeric\n         local h = getbinhandler(op1, op2, \"__add\")\n         if h then\n           -- call the handler with both operands\n           return (h(op1, op2))\n         else  -- no handler available: default behavior\n           error(&middot;&middot;&middot;)\n         end\n       end\n     end\n</pre><p>\n</li>\n\n<li><b>\"sub\":</b>\nthe <code>-</code> operation.\n\nBehavior similar to the \"add\" operation.\n</li>\n\n<li><b>\"mul\":</b>\nthe <code>*</code> operation.\n\nBehavior similar to the \"add\" operation.\n</li>\n\n<li><b>\"div\":</b>\nthe <code>/</code> operation.\n\nBehavior similar to the \"add\" operation.\n</li>\n\n<li><b>\"mod\":</b>\nthe <code>%</code> operation.\n\nBehavior similar to the \"add\" operation,\nwith the operation\n<code>o1 - floor(o1/o2)*o2</code> as the primitive operation.\n</li>\n\n<li><b>\"pow\":</b>\nthe <code>^</code> (exponentiation) operation.\n\nBehavior similar to the \"add\" operation,\nwith the function <code>pow</code> (from the C&nbsp;math library)\nas the primitive operation.\n</li>\n\n<li><b>\"unm\":</b>\nthe unary <code>-</code> operation.\n\n\n<pre>\n     function unm_event (op)\n       local o = tonumber(op)\n       if o then  -- operand is numeric?\n         return -o  -- '-' here is the primitive 'unm'\n       else  -- the operand is not numeric.\n         -- Try to get a handler from the operand\n         local h = metatable(op).__unm\n         if h then\n           -- call the handler with the operand\n           return (h(op))\n         else  -- no handler available: default behavior\n           error(&middot;&middot;&middot;)\n         end\n       end\n     end\n</pre><p>\n</li>\n\n<li><b>\"concat\":</b>\nthe <code>..</code> (concatenation) operation.\n\n\n<pre>\n     function concat_event (op1, op2)\n       if (type(op1) == \"string\" or type(op1) == \"number\") and\n          (type(op2) == \"string\" or type(op2) == \"number\") then\n         return op1 .. op2  -- primitive string concatenation\n       else\n         local h = getbinhandler(op1, op2, \"__concat\")\n         if h then\n           return (h(op1, op2))\n         else\n           error(&middot;&middot;&middot;)\n         end\n       end\n     end\n</pre><p>\n</li>\n\n<li><b>\"len\":</b>\nthe <code>#</code> operation.\n\n\n<pre>\n     function len_event (op)\n       if type(op) == \"string\" then\n         return strlen(op)         -- primitive string length\n       elseif type(op) == \"table\" then\n         return #op                -- primitive table length\n       else\n         local h = metatable(op).__len\n         if h then\n           -- call the handler with the operand\n           return (h(op))\n         else  -- no handler available: default behavior\n           error(&middot;&middot;&middot;)\n         end\n       end\n     end\n</pre><p>\nSee <a href=\"#2.5.5\">&sect;2.5.5</a> for a description of the length of a table.\n</li>\n\n<li><b>\"eq\":</b>\nthe <code>==</code> operation.\n\nThe function <code>getcomphandler</code> defines how Lua chooses a metamethod\nfor comparison operators.\nA metamethod only is selected when both objects\nbeing compared have the same type\nand the same metamethod for the selected operation.\n\n<pre>\n     function getcomphandler (op1, op2, event)\n       if type(op1) ~= type(op2) then return nil end\n       local mm1 = metatable(op1)[event]\n       local mm2 = metatable(op2)[event]\n       if mm1 == mm2 then return mm1 else return nil end\n     end\n</pre><p>\nThe \"eq\" event is defined as follows:\n\n<pre>\n     function eq_event (op1, op2)\n       if type(op1) ~= type(op2) then  -- different types?\n         return false   -- different objects\n       end\n       if op1 == op2 then   -- primitive equal?\n         return true   -- objects are equal\n       end\n       -- try metamethod\n       local h = getcomphandler(op1, op2, \"__eq\")\n       if h then\n         return (h(op1, op2))\n       else\n         return false\n       end\n     end\n</pre><p>\n<code>a ~= b</code> is equivalent to <code>not (a == b)</code>.\n</li>\n\n<li><b>\"lt\":</b>\nthe <code>&lt;</code> operation.\n\n\n<pre>\n     function lt_event (op1, op2)\n       if type(op1) == \"number\" and type(op2) == \"number\" then\n         return op1 &lt; op2   -- numeric comparison\n       elseif type(op1) == \"string\" and type(op2) == \"string\" then\n         return op1 &lt; op2   -- lexicographic comparison\n       else\n         local h = getcomphandler(op1, op2, \"__lt\")\n         if h then\n           return (h(op1, op2))\n         else\n           error(&middot;&middot;&middot;)\n         end\n       end\n     end\n</pre><p>\n<code>a &gt; b</code> is equivalent to <code>b &lt; a</code>.\n</li>\n\n<li><b>\"le\":</b>\nthe <code>&lt;=</code> operation.\n\n\n<pre>\n     function le_event (op1, op2)\n       if type(op1) == \"number\" and type(op2) == \"number\" then\n         return op1 &lt;= op2   -- numeric comparison\n       elseif type(op1) == \"string\" and type(op2) == \"string\" then\n         return op1 &lt;= op2   -- lexicographic comparison\n       else\n         local h = getcomphandler(op1, op2, \"__le\")\n         if h then\n           return (h(op1, op2))\n         else\n           h = getcomphandler(op1, op2, \"__lt\")\n           if h then\n             return not h(op2, op1)\n           else\n             error(&middot;&middot;&middot;)\n           end\n         end\n       end\n     end\n</pre><p>\n<code>a &gt;= b</code> is equivalent to <code>b &lt;= a</code>.\nNote that, in the absence of a \"le\" metamethod,\nLua tries the \"lt\", assuming that <code>a &lt;= b</code> is\nequivalent to <code>not (b &lt; a)</code>.\n</li>\n\n<li><b>\"index\":</b>\nThe indexing access <code>table[key]</code>.\n\n\n<pre>\n     function gettable_event (table, key)\n       local h\n       if type(table) == \"table\" then\n         local v = rawget(table, key)\n         if v ~= nil then return v end\n         h = metatable(table).__index\n         if h == nil then return nil end\n       else\n         h = metatable(table).__index\n         if h == nil then\n           error(&middot;&middot;&middot;)\n         end\n       end\n       if type(h) == \"function\" then\n         return (h(table, key))     -- call the handler\n       else return h[key]           -- or repeat operation on it\n       end\n     end\n</pre><p>\n</li>\n\n<li><b>\"newindex\":</b>\nThe indexing assignment <code>table[key] = value</code>.\n\n\n<pre>\n     function settable_event (table, key, value)\n       local h\n       if type(table) == \"table\" then\n         local v = rawget(table, key)\n         if v ~= nil then rawset(table, key, value); return end\n         h = metatable(table).__newindex\n         if h == nil then rawset(table, key, value); return end\n       else\n         h = metatable(table).__newindex\n         if h == nil then\n           error(&middot;&middot;&middot;)\n         end\n       end\n       if type(h) == \"function\" then\n         h(table, key,value)           -- call the handler\n       else h[key] = value             -- or repeat operation on it\n       end\n     end\n</pre><p>\n</li>\n\n<li><b>\"call\":</b>\ncalled when Lua calls a value.\n\n\n<pre>\n     function function_event (func, ...)\n       if type(func) == \"function\" then\n         return func(...)   -- primitive call\n       else\n         local h = metatable(func).__call\n         if h then\n           return h(func, ...)\n         else\n           error(&middot;&middot;&middot;)\n         end\n       end\n     end\n</pre><p>\n</li>\n\n</ul>\n\n\n\n\n<h2>2.9 - <a name=\"2.9\">Environments</a></h2>\n\n<p>\nBesides metatables,\nobjects of types thread, function, and userdata\nhave another table associated with them,\ncalled their <em>environment</em>.\nLike metatables, environments are regular tables and\nmultiple objects can share the same environment.\n\n\n<p>\nThreads are created sharing the environment of the creating thread.\nUserdata and C&nbsp;functions are created sharing the environment\nof the creating C&nbsp;function.\nNon-nested Lua functions\n(created by <a href=\"#pdf-loadfile\"><code>loadfile</code></a>, <a href=\"#pdf-loadstring\"><code>loadstring</code></a> or <a href=\"#pdf-load\"><code>load</code></a>)\nare created sharing the environment of the creating thread.\nNested Lua functions are created sharing the environment of\nthe creating Lua function.\n\n\n<p>\nEnvironments associated with userdata have no meaning for Lua.\nIt is only a convenience feature for programmers to associate a table to\na userdata.\n\n\n<p>\nEnvironments associated with threads are called\n<em>global environments</em>.\nThey are used as the default environment for threads and\nnon-nested Lua functions created by the thread\nand can be directly accessed by C&nbsp;code (see <a href=\"#3.3\">&sect;3.3</a>).\n\n\n<p>\nThe environment associated with a C&nbsp;function can be directly\naccessed by C&nbsp;code (see <a href=\"#3.3\">&sect;3.3</a>).\nIt is used as the default environment for other C&nbsp;functions\nand userdata created by the function.\n\n\n<p>\nEnvironments associated with Lua functions are used to resolve\nall accesses to global variables within the function (see <a href=\"#2.3\">&sect;2.3</a>).\nThey are used as the default environment for nested Lua functions\ncreated by the function.\n\n\n<p>\nYou can change the environment of a Lua function or the\nrunning thread by calling <a href=\"#pdf-setfenv\"><code>setfenv</code></a>.\nYou can get the environment of a Lua function or the running thread\nby calling <a href=\"#pdf-getfenv\"><code>getfenv</code></a>.\nTo manipulate the environment of other objects\n(userdata, C&nbsp;functions, other threads) you must\nuse the C&nbsp;API.\n\n\n\n\n\n<h2>2.10 - <a name=\"2.10\">Garbage Collection</a></h2>\n\n<p>\nLua performs automatic memory management.\nThis means that\nyou have to worry neither about allocating memory for new objects\nnor about freeing it when the objects are no longer needed.\nLua manages memory automatically by running\na <em>garbage collector</em> from time to time\nto collect all <em>dead objects</em>\n(that is, objects that are no longer accessible from Lua).\nAll memory used by Lua is subject to automatic management:\ntables, userdata, functions, threads, strings, etc.\n\n\n<p>\nLua implements an incremental mark-and-sweep collector.\nIt uses two numbers to control its garbage-collection cycles:\nthe <em>garbage-collector pause</em> and\nthe <em>garbage-collector step multiplier</em>.\nBoth use percentage points as units\n(so that a value of 100 means an internal value of 1).\n\n\n<p>\nThe garbage-collector pause\ncontrols how long the collector waits before starting a new cycle.\nLarger values make the collector less aggressive.\nValues smaller than 100 mean the collector will not wait to\nstart a new cycle.\nA value of 200 means that the collector waits for the total memory in use\nto double before starting a new cycle.\n\n\n<p>\nThe step multiplier\ncontrols the relative speed of the collector relative to\nmemory allocation.\nLarger values make the collector more aggressive but also increase\nthe size of each incremental step.\nValues smaller than 100 make the collector too slow and\ncan result in the collector never finishing a cycle.\nThe default, 200, means that the collector runs at \"twice\"\nthe speed of memory allocation.\n\n\n<p>\nYou can change these numbers by calling <a href=\"#lua_gc\"><code>lua_gc</code></a> in C\nor <a href=\"#pdf-collectgarbage\"><code>collectgarbage</code></a> in Lua.\nWith these functions you can also control \nthe collector directly (e.g., stop and restart it).\n\n\n\n<h3>2.10.1 - <a name=\"2.10.1\">Garbage-Collection Metamethods</a></h3>\n\n<p>\nUsing the C&nbsp;API,\nyou can set garbage-collector metamethods for userdata (see <a href=\"#2.8\">&sect;2.8</a>).\nThese metamethods are also called <em>finalizers</em>.\nFinalizers allow you to coordinate Lua's garbage collection\nwith external resource management\n(such as closing files, network or database connections,\nor freeing your own memory).\n\n\n<p>\nGarbage userdata with a field <code>__gc</code> in their metatables are not\ncollected immediately by the garbage collector.\nInstead, Lua puts them in a list.\nAfter the collection,\nLua does the equivalent of the following function\nfor each userdata in that list:\n\n<pre>\n     function gc_event (udata)\n       local h = metatable(udata).__gc\n       if h then\n         h(udata)\n       end\n     end\n</pre>\n\n<p>\nAt the end of each garbage-collection cycle,\nthe finalizers for userdata are called in <em>reverse</em>\norder of their creation,\namong those collected in that cycle.\nThat is, the first finalizer to be called is the one associated\nwith the userdata created last in the program.\nThe userdata itself is freed only in the next garbage-collection cycle.\n\n\n\n\n\n<h3>2.10.2 - <a name=\"2.10.2\">Weak Tables</a></h3>\n\n<p>\nA <em>weak table</em> is a table whose elements are\n<em>weak references</em>.\nA weak reference is ignored by the garbage collector.\nIn other words,\nif the only references to an object are weak references,\nthen the garbage collector will collect this object.\n\n\n<p>\nA weak table can have weak keys, weak values, or both.\nA table with weak keys allows the collection of its keys,\nbut prevents the collection of its values.\nA table with both weak keys and weak values allows the collection of\nboth keys and values.\nIn any case, if either the key or the value is collected,\nthe whole pair is removed from the table.\nThe weakness of a table is controlled by the\n<code>__mode</code> field of its metatable.\nIf the <code>__mode</code> field is a string containing the character&nbsp;'<code>k</code>',\nthe keys in the table are weak.\nIf <code>__mode</code> contains '<code>v</code>',\nthe values in the table are weak.\n\n\n<p>\nAfter you use a table as a metatable,\nyou should not change the value of its <code>__mode</code> field.\nOtherwise, the weak behavior of the tables controlled by this\nmetatable is undefined.\n\n\n\n\n\n\n\n<h2>2.11 - <a name=\"2.11\">Coroutines</a></h2>\n\n<p>\nLua supports coroutines,\nalso called <em>collaborative multithreading</em>.\nA coroutine in Lua represents an independent thread of execution.\nUnlike threads in multithread systems, however,\na coroutine only suspends its execution by explicitly calling\na yield function.\n\n\n<p>\nYou create a coroutine with a call to <a href=\"#pdf-coroutine.create\"><code>coroutine.create</code></a>.\nIts sole argument is a function\nthat is the main function of the coroutine.\nThe <code>create</code> function only creates a new coroutine and\nreturns a handle to it (an object of type <em>thread</em>);\nit does not start the coroutine execution.\n\n\n<p>\nWhen you first call <a href=\"#pdf-coroutine.resume\"><code>coroutine.resume</code></a>,\npassing as its first argument\na thread returned by <a href=\"#pdf-coroutine.create\"><code>coroutine.create</code></a>,\nthe coroutine starts its execution,\nat the first line of its main function.\nExtra arguments passed to <a href=\"#pdf-coroutine.resume\"><code>coroutine.resume</code></a> are passed on\nto the coroutine main function.\nAfter the coroutine starts running,\nit runs until it terminates or <em>yields</em>.\n\n\n<p>\nA coroutine can terminate its execution in two ways:\nnormally, when its main function returns\n(explicitly or implicitly, after the last instruction);\nand abnormally, if there is an unprotected error.\nIn the first case, <a href=\"#pdf-coroutine.resume\"><code>coroutine.resume</code></a> returns <b>true</b>,\nplus any values returned by the coroutine main function.\nIn case of errors, <a href=\"#pdf-coroutine.resume\"><code>coroutine.resume</code></a> returns <b>false</b>\nplus an error message.\n\n\n<p>\nA coroutine yields by calling <a href=\"#pdf-coroutine.yield\"><code>coroutine.yield</code></a>.\nWhen a coroutine yields,\nthe corresponding <a href=\"#pdf-coroutine.resume\"><code>coroutine.resume</code></a> returns immediately,\neven if the yield happens inside nested function calls\n(that is, not in the main function,\nbut in a function directly or indirectly called by the main function).\nIn the case of a yield, <a href=\"#pdf-coroutine.resume\"><code>coroutine.resume</code></a> also returns <b>true</b>,\nplus any values passed to <a href=\"#pdf-coroutine.yield\"><code>coroutine.yield</code></a>.\nThe next time you resume the same coroutine,\nit continues its execution from the point where it yielded,\nwith the call to <a href=\"#pdf-coroutine.yield\"><code>coroutine.yield</code></a> returning any extra\narguments passed to <a href=\"#pdf-coroutine.resume\"><code>coroutine.resume</code></a>.\n\n\n<p>\nLike <a href=\"#pdf-coroutine.create\"><code>coroutine.create</code></a>,\nthe <a href=\"#pdf-coroutine.wrap\"><code>coroutine.wrap</code></a> function also creates a coroutine,\nbut instead of returning the coroutine itself,\nit returns a function that, when called, resumes the coroutine.\nAny arguments passed to this function\ngo as extra arguments to <a href=\"#pdf-coroutine.resume\"><code>coroutine.resume</code></a>.\n<a href=\"#pdf-coroutine.wrap\"><code>coroutine.wrap</code></a> returns all the values returned by <a href=\"#pdf-coroutine.resume\"><code>coroutine.resume</code></a>,\nexcept the first one (the boolean error code).\nUnlike <a href=\"#pdf-coroutine.resume\"><code>coroutine.resume</code></a>,\n<a href=\"#pdf-coroutine.wrap\"><code>coroutine.wrap</code></a> does not catch errors;\nany error is propagated to the caller.\n\n\n<p>\nAs an example,\nconsider the following code:\n\n<pre>\n     function foo (a)\n       print(\"foo\", a)\n       return coroutine.yield(2*a)\n     end\n     \n     co = coroutine.create(function (a,b)\n           print(\"co-body\", a, b)\n           local r = foo(a+1)\n           print(\"co-body\", r)\n           local r, s = coroutine.yield(a+b, a-b)\n           print(\"co-body\", r, s)\n           return b, \"end\"\n     end)\n            \n     print(\"main\", coroutine.resume(co, 1, 10))\n     print(\"main\", coroutine.resume(co, \"r\"))\n     print(\"main\", coroutine.resume(co, \"x\", \"y\"))\n     print(\"main\", coroutine.resume(co, \"x\", \"y\"))\n</pre><p>\nWhen you run it, it produces the following output:\n\n<pre>\n     co-body 1       10\n     foo     2\n     \n     main    true    4\n     co-body r\n     main    true    11      -9\n     co-body x       y\n     main    true    10      end\n     main    false   cannot resume dead coroutine\n</pre>\n\n\n\n\n<h1>3 - <a name=\"3\">The Application Program Interface</a></h1>\n\n<p>\n\nThis section describes the C&nbsp;API for Lua, that is,\nthe set of C&nbsp;functions available to the host program to communicate\nwith Lua.\nAll API functions and related types and constants\nare declared in the header file <a name=\"pdf-lua.h\"><code>lua.h</code></a>.\n\n\n<p>\nEven when we use the term \"function\",\nany facility in the API may be provided as a macro instead.\nAll such macros use each of their arguments exactly once\n(except for the first argument, which is always a Lua state),\nand so do not generate any hidden side-effects.\n\n\n<p>\nAs in most C&nbsp;libraries,\nthe Lua API functions do not check their arguments for validity or consistency.\nHowever, you can change this behavior by compiling Lua\nwith a proper definition for the macro <a name=\"pdf-luai_apicheck\"><code>luai_apicheck</code></a>,\nin file <code>luaconf.h</code>.\n\n\n\n<h2>3.1 - <a name=\"3.1\">The Stack</a></h2>\n\n<p>\nLua uses a <em>virtual stack</em> to pass values to and from C.\nEach element in this stack represents a Lua value\n(<b>nil</b>, number, string, etc.).\n\n\n<p>\nWhenever Lua calls C, the called function gets a new stack,\nwhich is independent of previous stacks and of stacks of\nC&nbsp;functions that are still active.\nThis stack initially contains any arguments to the C&nbsp;function\nand it is where the C&nbsp;function pushes its results\nto be returned to the caller (see <a href=\"#lua_CFunction\"><code>lua_CFunction</code></a>).\n\n\n<p>\nFor convenience,\nmost query operations in the API do not follow a strict stack discipline.\nInstead, they can refer to any element in the stack\nby using an <em>index</em>:\nA positive index represents an <em>absolute</em> stack position\n(starting at&nbsp;1);\na negative index represents an <em>offset</em> relative to the top of the stack.\nMore specifically, if the stack has <em>n</em> elements,\nthen index&nbsp;1 represents the first element\n(that is, the element that was pushed onto the stack first)\nand\nindex&nbsp;<em>n</em> represents the last element;\nindex&nbsp;-1 also represents the last element\n(that is, the element at the&nbsp;top)\nand index <em>-n</em> represents the first element.\nWe say that an index is <em>valid</em>\nif it lies between&nbsp;1 and the stack top\n(that is, if <code>1 &le; abs(index) &le; top</code>).\n \n\n\n\n\n\n<h2>3.2 - <a name=\"3.2\">Stack Size</a></h2>\n\n<p>\nWhen you interact with Lua API,\nyou are responsible for ensuring consistency.\nIn particular,\n<em>you are responsible for controlling stack overflow</em>.\nYou can use the function <a href=\"#lua_checkstack\"><code>lua_checkstack</code></a>\nto grow the stack size.\n\n\n<p>\nWhenever Lua calls C,\nit ensures that at least <a name=\"pdf-LUA_MINSTACK\"><code>LUA_MINSTACK</code></a> stack positions are available.\n<code>LUA_MINSTACK</code> is defined as 20,\nso that usually you do not have to worry about stack space\nunless your code has loops pushing elements onto the stack.\n\n\n<p>\nMost query functions accept as indices any value inside the\navailable stack space, that is, indices up to the maximum stack size\nyou have set through <a href=\"#lua_checkstack\"><code>lua_checkstack</code></a>.\nSuch indices are called <em>acceptable indices</em>.\nMore formally, we define an <em>acceptable index</em>\nas follows:\n\n<pre>\n     (index &lt; 0 &amp;&amp; abs(index) &lt;= top) ||\n     (index &gt; 0 &amp;&amp; index &lt;= stackspace)\n</pre><p>\nNote that 0 is never an acceptable index.\n\n\n\n\n\n<h2>3.3 - <a name=\"3.3\">Pseudo-Indices</a></h2>\n\n<p>\nUnless otherwise noted,\nany function that accepts valid indices can also be called with\n<em>pseudo-indices</em>,\nwhich represent some Lua values that are accessible to C&nbsp;code\nbut which are not in the stack.\nPseudo-indices are used to access the thread environment,\nthe function environment,\nthe registry,\nand the upvalues of a C&nbsp;function (see <a href=\"#3.4\">&sect;3.4</a>).\n\n\n<p>\nThe thread environment (where global variables live) is\nalways at pseudo-index <a name=\"pdf-LUA_GLOBALSINDEX\"><code>LUA_GLOBALSINDEX</code></a>.\nThe environment of the running C&nbsp;function is always\nat pseudo-index <a name=\"pdf-LUA_ENVIRONINDEX\"><code>LUA_ENVIRONINDEX</code></a>.\n\n\n<p>\nTo access and change the value of global variables,\nyou can use regular table operations over an environment table.\nFor instance, to access the value of a global variable, do\n\n<pre>\n     lua_getfield(L, LUA_GLOBALSINDEX, varname);\n</pre>\n\n\n\n\n<h2>3.4 - <a name=\"3.4\">C Closures</a></h2>\n\n<p>\nWhen a C&nbsp;function is created,\nit is possible to associate some values with it,\nthus creating a <em>C&nbsp;closure</em>;\nthese values are called <em>upvalues</em> and are\naccessible to the function whenever it is called\n(see <a href=\"#lua_pushcclosure\"><code>lua_pushcclosure</code></a>).\n\n\n<p>\nWhenever a C&nbsp;function is called,\nits upvalues are located at specific pseudo-indices.\nThese pseudo-indices are produced by the macro\n<a name=\"lua_upvalueindex\"><code>lua_upvalueindex</code></a>.\nThe first value associated with a function is at position\n<code>lua_upvalueindex(1)</code>, and so on.\nAny access to <code>lua_upvalueindex(<em>n</em>)</code>,\nwhere <em>n</em> is greater than the number of upvalues of the\ncurrent function (but not greater than 256),\nproduces an acceptable (but invalid) index.\n\n\n\n\n\n<h2>3.5 - <a name=\"3.5\">Registry</a></h2>\n\n<p>\nLua provides a <em>registry</em>,\na pre-defined table that can be used by any C&nbsp;code to\nstore whatever Lua value it needs to store.\nThis table is always located at pseudo-index\n<a name=\"pdf-LUA_REGISTRYINDEX\"><code>LUA_REGISTRYINDEX</code></a>.\nAny C&nbsp;library can store data into this table,\nbut it should take care to choose keys different from those used\nby other libraries, to avoid collisions.\nTypically, you should use as key a string containing your library name\nor a light userdata with the address of a C&nbsp;object in your code.\n\n\n<p>\nThe integer keys in the registry are used by the reference mechanism,\nimplemented by the auxiliary library,\nand therefore should not be used for other purposes.\n\n\n\n\n\n<h2>3.6 - <a name=\"3.6\">Error Handling in C</a></h2>\n\n<p>\nInternally, Lua uses the C <code>longjmp</code> facility to handle errors.\n(You can also choose to use exceptions if you use C++;\nsee file <code>luaconf.h</code>.)\nWhen Lua faces any error\n(such as memory allocation errors, type errors, syntax errors,\nand runtime errors)\nit <em>raises</em> an error;\nthat is, it does a long jump.\nA <em>protected environment</em> uses <code>setjmp</code>\nto set a recover point;\nany error jumps to the most recent active recover point.\n\n\n<p>\nMost functions in the API can throw an error,\nfor instance due to a memory allocation error.\nThe documentation for each function indicates whether\nit can throw errors.\n\n\n<p>\nInside a C&nbsp;function you can throw an error by calling <a href=\"#lua_error\"><code>lua_error</code></a>.\n\n\n\n\n\n<h2>3.7 - <a name=\"3.7\">Functions and Types</a></h2>\n\n<p>\nHere we list all functions and types from the C&nbsp;API in\nalphabetical order.\nEach function has an indicator like this:\n<span class=\"apii\">[-o, +p, <em>x</em>]</span>\n\n\n<p>\nThe first field, <code>o</code>,\nis how many elements the function pops from the stack.\nThe second field, <code>p</code>,\nis how many elements the function pushes onto the stack.\n(Any function always pushes its results after popping its arguments.)\nA field in the form <code>x|y</code> means the function can push (or pop)\n<code>x</code> or <code>y</code> elements,\ndepending on the situation;\nan interrogation mark '<code>?</code>' means that\nwe cannot know how many elements the function pops/pushes\nby looking only at its arguments\n(e.g., they may depend on what is on the stack).\nThe third field, <code>x</code>,\ntells whether the function may throw errors:\n'<code>-</code>' means the function never throws any error;\n'<code>m</code>' means the function may throw an error\nonly due to not enough memory;\n'<code>e</code>' means the function may throw other kinds of errors;\n'<code>v</code>' means the function may throw an error on purpose.\n\n\n\n<hr><h3><a name=\"lua_Alloc\"><code>lua_Alloc</code></a></h3>\n<pre>typedef void * (*lua_Alloc) (void *ud,\n                             void *ptr,\n                             size_t osize,\n                             size_t nsize);</pre>\n\n<p>\nThe type of the memory-allocation function used by Lua states.\nThe allocator function must provide a\nfunctionality similar to <code>realloc</code>,\nbut not exactly the same.\nIts arguments are\n<code>ud</code>, an opaque pointer passed to <a href=\"#lua_newstate\"><code>lua_newstate</code></a>;\n<code>ptr</code>, a pointer to the block being allocated/reallocated/freed;\n<code>osize</code>, the original size of the block;\n<code>nsize</code>, the new size of the block.\n<code>ptr</code> is <code>NULL</code> if and only if <code>osize</code> is zero.\nWhen <code>nsize</code> is zero, the allocator must return <code>NULL</code>;\nif <code>osize</code> is not zero,\nit should free the block pointed to by <code>ptr</code>.\nWhen <code>nsize</code> is not zero, the allocator returns <code>NULL</code>\nif and only if it cannot fill the request.\nWhen <code>nsize</code> is not zero and <code>osize</code> is zero,\nthe allocator should behave like <code>malloc</code>.\nWhen <code>nsize</code> and <code>osize</code> are not zero,\nthe allocator behaves like <code>realloc</code>.\nLua assumes that the allocator never fails when\n<code>osize &gt;= nsize</code>.\n\n\n<p>\nHere is a simple implementation for the allocator function.\nIt is used in the auxiliary library by <a href=\"#luaL_newstate\"><code>luaL_newstate</code></a>.\n\n<pre>\n     static void *l_alloc (void *ud, void *ptr, size_t osize,\n                                                size_t nsize) {\n       (void)ud;  (void)osize;  /* not used */\n       if (nsize == 0) {\n         free(ptr);\n         return NULL;\n       }\n       else\n         return realloc(ptr, nsize);\n     }\n</pre><p>\nThis code assumes\nthat <code>free(NULL)</code> has no effect and that\n<code>realloc(NULL, size)</code> is equivalent to <code>malloc(size)</code>.\nANSI&nbsp;C ensures both behaviors.\n\n\n\n\n\n<hr><h3><a name=\"lua_atpanic\"><code>lua_atpanic</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>-</em>]</span>\n<pre>lua_CFunction lua_atpanic (lua_State *L, lua_CFunction panicf);</pre>\n\n<p>\nSets a new panic function and returns the old one.\n\n\n<p>\nIf an error happens outside any protected environment,\nLua calls a <em>panic function</em>\nand then calls <code>exit(EXIT_FAILURE)</code>,\nthus exiting the host application.\nYour panic function can avoid this exit by\nnever returning (e.g., doing a long jump).\n\n\n<p>\nThe panic function can access the error message at the top of the stack.\n\n\n\n\n\n<hr><h3><a name=\"lua_call\"><code>lua_call</code></a></h3><p>\n<span class=\"apii\">[-(nargs + 1), +nresults, <em>e</em>]</span>\n<pre>void lua_call (lua_State *L, int nargs, int nresults);</pre>\n\n<p>\nCalls a function.\n\n\n<p>\nTo call a function you must use the following protocol:\nfirst, the function to be called is pushed onto the stack;\nthen, the arguments to the function are pushed\nin direct order;\nthat is, the first argument is pushed first.\nFinally you call <a href=\"#lua_call\"><code>lua_call</code></a>;\n<code>nargs</code> is the number of arguments that you pushed onto the stack.\nAll arguments and the function value are popped from the stack\nwhen the function is called.\nThe function results are pushed onto the stack when the function returns.\nThe number of results is adjusted to <code>nresults</code>,\nunless <code>nresults</code> is <a name=\"pdf-LUA_MULTRET\"><code>LUA_MULTRET</code></a>.\nIn this case, <em>all</em> results from the function are pushed.\nLua takes care that the returned values fit into the stack space.\nThe function results are pushed onto the stack in direct order\n(the first result is pushed first),\nso that after the call the last result is on the top of the stack.\n\n\n<p>\nAny error inside the called function is propagated upwards\n(with a <code>longjmp</code>).\n\n\n<p>\nThe following example shows how the host program can do the\nequivalent to this Lua code:\n\n<pre>\n     a = f(\"how\", t.x, 14)\n</pre><p>\nHere it is in&nbsp;C:\n\n<pre>\n     lua_getfield(L, LUA_GLOBALSINDEX, \"f\"); /* function to be called */\n     lua_pushstring(L, \"how\");                        /* 1st argument */\n     lua_getfield(L, LUA_GLOBALSINDEX, \"t\");   /* table to be indexed */\n     lua_getfield(L, -1, \"x\");        /* push result of t.x (2nd arg) */\n     lua_remove(L, -2);                  /* remove 't' from the stack */\n     lua_pushinteger(L, 14);                          /* 3rd argument */\n     lua_call(L, 3, 1);     /* call 'f' with 3 arguments and 1 result */\n     lua_setfield(L, LUA_GLOBALSINDEX, \"a\");        /* set global 'a' */\n</pre><p>\nNote that the code above is \"balanced\":\nat its end, the stack is back to its original configuration.\nThis is considered good programming practice.\n\n\n\n\n\n<hr><h3><a name=\"lua_CFunction\"><code>lua_CFunction</code></a></h3>\n<pre>typedef int (*lua_CFunction) (lua_State *L);</pre>\n\n<p>\nType for C&nbsp;functions.\n\n\n<p>\nIn order to communicate properly with Lua,\na C&nbsp;function must use the following protocol,\nwhich defines the way parameters and results are passed:\na C&nbsp;function receives its arguments from Lua in its stack\nin direct order (the first argument is pushed first).\nSo, when the function starts,\n<code>lua_gettop(L)</code> returns the number of arguments received by the function.\nThe first argument (if any) is at index 1\nand its last argument is at index <code>lua_gettop(L)</code>.\nTo return values to Lua, a C&nbsp;function just pushes them onto the stack,\nin direct order (the first result is pushed first),\nand returns the number of results.\nAny other value in the stack below the results will be properly\ndiscarded by Lua.\nLike a Lua function, a C&nbsp;function called by Lua can also return\nmany results.\n\n\n<p>\nAs an example, the following function receives a variable number\nof numerical arguments and returns their average and sum:\n\n<pre>\n     static int foo (lua_State *L) {\n       int n = lua_gettop(L);    /* number of arguments */\n       lua_Number sum = 0;\n       int i;\n       for (i = 1; i &lt;= n; i++) {\n         if (!lua_isnumber(L, i)) {\n           lua_pushstring(L, \"incorrect argument\");\n           lua_error(L);\n         }\n         sum += lua_tonumber(L, i);\n       }\n       lua_pushnumber(L, sum/n);        /* first result */\n       lua_pushnumber(L, sum);         /* second result */\n       return 2;                   /* number of results */\n     }\n</pre>\n\n\n\n\n<hr><h3><a name=\"lua_checkstack\"><code>lua_checkstack</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>m</em>]</span>\n<pre>int lua_checkstack (lua_State *L, int extra);</pre>\n\n<p>\nEnsures that there are at least <code>extra</code> free stack slots in the stack.\nIt returns false if it cannot grow the stack to that size.\nThis function never shrinks the stack;\nif the stack is already larger than the new size,\nit is left unchanged.\n\n\n\n\n\n<hr><h3><a name=\"lua_close\"><code>lua_close</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>-</em>]</span>\n<pre>void lua_close (lua_State *L);</pre>\n\n<p>\nDestroys all objects in the given Lua state\n(calling the corresponding garbage-collection metamethods, if any)\nand frees all dynamic memory used by this state.\nOn several platforms, you may not need to call this function,\nbecause all resources are naturally released when the host program ends.\nOn the other hand, long-running programs,\nsuch as a daemon or a web server,\nmight need to release states as soon as they are not needed,\nto avoid growing too large.\n\n\n\n\n\n<hr><h3><a name=\"lua_concat\"><code>lua_concat</code></a></h3><p>\n<span class=\"apii\">[-n, +1, <em>e</em>]</span>\n<pre>void lua_concat (lua_State *L, int n);</pre>\n\n<p>\nConcatenates the <code>n</code> values at the top of the stack,\npops them, and leaves the result at the top.\nIf <code>n</code>&nbsp;is&nbsp;1, the result is the single value on the stack\n(that is, the function does nothing);\nif <code>n</code> is 0, the result is the empty string.\nConcatenation is performed following the usual semantics of Lua\n(see <a href=\"#2.5.4\">&sect;2.5.4</a>).\n\n\n\n\n\n<hr><h3><a name=\"lua_cpcall\"><code>lua_cpcall</code></a></h3><p>\n<span class=\"apii\">[-0, +(0|1), <em>-</em>]</span>\n<pre>int lua_cpcall (lua_State *L, lua_CFunction func, void *ud);</pre>\n\n<p>\nCalls the C&nbsp;function <code>func</code> in protected mode.\n<code>func</code> starts with only one element in its stack,\na light userdata containing <code>ud</code>.\nIn case of errors,\n<a href=\"#lua_cpcall\"><code>lua_cpcall</code></a> returns the same error codes as <a href=\"#lua_pcall\"><code>lua_pcall</code></a>,\nplus the error object on the top of the stack;\notherwise, it returns zero, and does not change the stack.\nAll values returned by <code>func</code> are discarded.\n\n\n\n\n\n<hr><h3><a name=\"lua_createtable\"><code>lua_createtable</code></a></h3><p>\n<span class=\"apii\">[-0, +1, <em>m</em>]</span>\n<pre>void lua_createtable (lua_State *L, int narr, int nrec);</pre>\n\n<p>\nCreates a new empty table and pushes it onto the stack.\nThe new table has space pre-allocated\nfor <code>narr</code> array elements and <code>nrec</code> non-array elements.\nThis pre-allocation is useful when you know exactly how many elements\nthe table will have.\nOtherwise you can use the function <a href=\"#lua_newtable\"><code>lua_newtable</code></a>.\n\n\n\n\n\n<hr><h3><a name=\"lua_dump\"><code>lua_dump</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>m</em>]</span>\n<pre>int lua_dump (lua_State *L, lua_Writer writer, void *data);</pre>\n\n<p>\nDumps a function as a binary chunk.\nReceives a Lua function on the top of the stack\nand produces a binary chunk that,\nif loaded again,\nresults in a function equivalent to the one dumped.\nAs it produces parts of the chunk,\n<a href=\"#lua_dump\"><code>lua_dump</code></a> calls function <code>writer</code> (see <a href=\"#lua_Writer\"><code>lua_Writer</code></a>)\nwith the given <code>data</code>\nto write them.\n\n\n<p>\nThe value returned is the error code returned by the last\ncall to the writer;\n0&nbsp;means no errors.\n\n\n<p>\nThis function does not pop the Lua function from the stack.\n\n\n\n\n\n<hr><h3><a name=\"lua_equal\"><code>lua_equal</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>e</em>]</span>\n<pre>int lua_equal (lua_State *L, int index1, int index2);</pre>\n\n<p>\nReturns 1 if the two values in acceptable indices <code>index1</code> and\n<code>index2</code> are equal,\nfollowing the semantics of the Lua <code>==</code> operator\n(that is, may call metamethods).\nOtherwise returns&nbsp;0.\nAlso returns&nbsp;0 if any of the indices is non valid.\n\n\n\n\n\n<hr><h3><a name=\"lua_error\"><code>lua_error</code></a></h3><p>\n<span class=\"apii\">[-1, +0, <em>v</em>]</span>\n<pre>int lua_error (lua_State *L);</pre>\n\n<p>\nGenerates a Lua error.\nThe error message (which can actually be a Lua value of any type)\nmust be on the stack top.\nThis function does a long jump,\nand therefore never returns.\n(see <a href=\"#luaL_error\"><code>luaL_error</code></a>).\n\n\n\n\n\n<hr><h3><a name=\"lua_gc\"><code>lua_gc</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>e</em>]</span>\n<pre>int lua_gc (lua_State *L, int what, int data);</pre>\n\n<p>\nControls the garbage collector.\n\n\n<p>\nThis function performs several tasks,\naccording to the value of the parameter <code>what</code>:\n\n<ul>\n\n<li><b><code>LUA_GCSTOP</code>:</b>\nstops the garbage collector.\n</li>\n\n<li><b><code>LUA_GCRESTART</code>:</b>\nrestarts the garbage collector.\n</li>\n\n<li><b><code>LUA_GCCOLLECT</code>:</b>\nperforms a full garbage-collection cycle.\n</li>\n\n<li><b><code>LUA_GCCOUNT</code>:</b>\nreturns the current amount of memory (in Kbytes) in use by Lua.\n</li>\n\n<li><b><code>LUA_GCCOUNTB</code>:</b>\nreturns the remainder of dividing the current amount of bytes of\nmemory in use by Lua by 1024.\n</li>\n\n<li><b><code>LUA_GCSTEP</code>:</b>\nperforms an incremental step of garbage collection.\nThe step \"size\" is controlled by <code>data</code>\n(larger values mean more steps) in a non-specified way.\nIf you want to control the step size\nyou must experimentally tune the value of <code>data</code>.\nThe function returns 1 if the step finished a\ngarbage-collection cycle.\n</li>\n\n<li><b><code>LUA_GCSETPAUSE</code>:</b>\nsets <code>data</code> as the new value\nfor the <em>pause</em> of the collector (see <a href=\"#2.10\">&sect;2.10</a>).\nThe function returns the previous value of the pause.\n</li>\n\n<li><b><code>LUA_GCSETSTEPMUL</code>:</b>\nsets <code>data</code> as the new value for the <em>step multiplier</em> of\nthe collector (see <a href=\"#2.10\">&sect;2.10</a>).\nThe function returns the previous value of the step multiplier.\n</li>\n\n</ul>\n\n\n\n\n<hr><h3><a name=\"lua_getallocf\"><code>lua_getallocf</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>-</em>]</span>\n<pre>lua_Alloc lua_getallocf (lua_State *L, void **ud);</pre>\n\n<p>\nReturns the memory-allocation function of a given state.\nIf <code>ud</code> is not <code>NULL</code>, Lua stores in <code>*ud</code> the\nopaque pointer passed to <a href=\"#lua_newstate\"><code>lua_newstate</code></a>.\n\n\n\n\n\n<hr><h3><a name=\"lua_getfenv\"><code>lua_getfenv</code></a></h3><p>\n<span class=\"apii\">[-0, +1, <em>-</em>]</span>\n<pre>void lua_getfenv (lua_State *L, int index);</pre>\n\n<p>\nPushes onto the stack the environment table of\nthe value at the given index.\n\n\n\n\n\n<hr><h3><a name=\"lua_getfield\"><code>lua_getfield</code></a></h3><p>\n<span class=\"apii\">[-0, +1, <em>e</em>]</span>\n<pre>void lua_getfield (lua_State *L, int index, const char *k);</pre>\n\n<p>\nPushes onto the stack the value <code>t[k]</code>,\nwhere <code>t</code> is the value at the given valid index.\nAs in Lua, this function may trigger a metamethod\nfor the \"index\" event (see <a href=\"#2.8\">&sect;2.8</a>).\n\n\n\n\n\n<hr><h3><a name=\"lua_getglobal\"><code>lua_getglobal</code></a></h3><p>\n<span class=\"apii\">[-0, +1, <em>e</em>]</span>\n<pre>void lua_getglobal (lua_State *L, const char *name);</pre>\n\n<p>\nPushes onto the stack the value of the global <code>name</code>.\nIt is defined as a macro:\n\n<pre>\n     #define lua_getglobal(L,s)  lua_getfield(L, LUA_GLOBALSINDEX, s)\n</pre>\n\n\n\n\n<hr><h3><a name=\"lua_getmetatable\"><code>lua_getmetatable</code></a></h3><p>\n<span class=\"apii\">[-0, +(0|1), <em>-</em>]</span>\n<pre>int lua_getmetatable (lua_State *L, int index);</pre>\n\n<p>\nPushes onto the stack the metatable of the value at the given\nacceptable index.\nIf the index is not valid,\nor if the value does not have a metatable,\nthe function returns&nbsp;0 and pushes nothing on the stack.\n\n\n\n\n\n<hr><h3><a name=\"lua_gettable\"><code>lua_gettable</code></a></h3><p>\n<span class=\"apii\">[-1, +1, <em>e</em>]</span>\n<pre>void lua_gettable (lua_State *L, int index);</pre>\n\n<p>\nPushes onto the stack the value <code>t[k]</code>,\nwhere <code>t</code> is the value at the given valid index\nand <code>k</code> is the value at the top of the stack.\n\n\n<p>\nThis function pops the key from the stack\n(putting the resulting value in its place).\nAs in Lua, this function may trigger a metamethod\nfor the \"index\" event (see <a href=\"#2.8\">&sect;2.8</a>).\n\n\n\n\n\n<hr><h3><a name=\"lua_gettop\"><code>lua_gettop</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>-</em>]</span>\n<pre>int lua_gettop (lua_State *L);</pre>\n\n<p>\nReturns the index of the top element in the stack.\nBecause indices start at&nbsp;1,\nthis result is equal to the number of elements in the stack\n(and so 0&nbsp;means an empty stack).\n\n\n\n\n\n<hr><h3><a name=\"lua_insert\"><code>lua_insert</code></a></h3><p>\n<span class=\"apii\">[-1, +1, <em>-</em>]</span>\n<pre>void lua_insert (lua_State *L, int index);</pre>\n\n<p>\nMoves the top element into the given valid index,\nshifting up the elements above this index to open space.\nCannot be called with a pseudo-index,\nbecause a pseudo-index is not an actual stack position.\n\n\n\n\n\n<hr><h3><a name=\"lua_Integer\"><code>lua_Integer</code></a></h3>\n<pre>typedef ptrdiff_t lua_Integer;</pre>\n\n<p>\nThe type used by the Lua API to represent integral values.\n\n\n<p>\nBy default it is a <code>ptrdiff_t</code>,\nwhich is usually the largest signed integral type the machine handles\n\"comfortably\".\n\n\n\n\n\n<hr><h3><a name=\"lua_isboolean\"><code>lua_isboolean</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>-</em>]</span>\n<pre>int lua_isboolean (lua_State *L, int index);</pre>\n\n<p>\nReturns 1 if the value at the given acceptable index has type boolean,\nand 0&nbsp;otherwise.\n\n\n\n\n\n<hr><h3><a name=\"lua_iscfunction\"><code>lua_iscfunction</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>-</em>]</span>\n<pre>int lua_iscfunction (lua_State *L, int index);</pre>\n\n<p>\nReturns 1 if the value at the given acceptable index is a C&nbsp;function,\nand 0&nbsp;otherwise.\n\n\n\n\n\n<hr><h3><a name=\"lua_isfunction\"><code>lua_isfunction</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>-</em>]</span>\n<pre>int lua_isfunction (lua_State *L, int index);</pre>\n\n<p>\nReturns 1 if the value at the given acceptable index is a function\n(either C or Lua), and 0&nbsp;otherwise.\n\n\n\n\n\n<hr><h3><a name=\"lua_islightuserdata\"><code>lua_islightuserdata</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>-</em>]</span>\n<pre>int lua_islightuserdata (lua_State *L, int index);</pre>\n\n<p>\nReturns 1 if the value at the given acceptable index is a light userdata,\nand 0&nbsp;otherwise.\n\n\n\n\n\n<hr><h3><a name=\"lua_isnil\"><code>lua_isnil</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>-</em>]</span>\n<pre>int lua_isnil (lua_State *L, int index);</pre>\n\n<p>\nReturns 1 if the value at the given acceptable index is <b>nil</b>,\nand 0&nbsp;otherwise.\n\n\n\n\n\n<hr><h3><a name=\"lua_isnone\"><code>lua_isnone</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>-</em>]</span>\n<pre>int lua_isnone (lua_State *L, int index);</pre>\n\n<p>\nReturns 1 if the given acceptable index is not valid\n(that is, it refers to an element outside the current stack),\nand 0&nbsp;otherwise.\n\n\n\n\n\n<hr><h3><a name=\"lua_isnoneornil\"><code>lua_isnoneornil</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>-</em>]</span>\n<pre>int lua_isnoneornil (lua_State *L, int index);</pre>\n\n<p>\nReturns 1 if the given acceptable index is not valid\n(that is, it refers to an element outside the current stack)\nor if the value at this index is <b>nil</b>,\nand 0&nbsp;otherwise.\n\n\n\n\n\n<hr><h3><a name=\"lua_isnumber\"><code>lua_isnumber</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>-</em>]</span>\n<pre>int lua_isnumber (lua_State *L, int index);</pre>\n\n<p>\nReturns 1 if the value at the given acceptable index is a number\nor a string convertible to a number,\nand 0&nbsp;otherwise.\n\n\n\n\n\n<hr><h3><a name=\"lua_isstring\"><code>lua_isstring</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>-</em>]</span>\n<pre>int lua_isstring (lua_State *L, int index);</pre>\n\n<p>\nReturns 1 if the value at the given acceptable index is a string\nor a number (which is always convertible to a string),\nand 0&nbsp;otherwise.\n\n\n\n\n\n<hr><h3><a name=\"lua_istable\"><code>lua_istable</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>-</em>]</span>\n<pre>int lua_istable (lua_State *L, int index);</pre>\n\n<p>\nReturns 1 if the value at the given acceptable index is a table,\nand 0&nbsp;otherwise.\n\n\n\n\n\n<hr><h3><a name=\"lua_isthread\"><code>lua_isthread</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>-</em>]</span>\n<pre>int lua_isthread (lua_State *L, int index);</pre>\n\n<p>\nReturns 1 if the value at the given acceptable index is a thread,\nand 0&nbsp;otherwise.\n\n\n\n\n\n<hr><h3><a name=\"lua_isuserdata\"><code>lua_isuserdata</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>-</em>]</span>\n<pre>int lua_isuserdata (lua_State *L, int index);</pre>\n\n<p>\nReturns 1 if the value at the given acceptable index is a userdata\n(either full or light), and 0&nbsp;otherwise.\n\n\n\n\n\n<hr><h3><a name=\"lua_lessthan\"><code>lua_lessthan</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>e</em>]</span>\n<pre>int lua_lessthan (lua_State *L, int index1, int index2);</pre>\n\n<p>\nReturns 1 if the value at acceptable index <code>index1</code> is smaller\nthan the value at acceptable index <code>index2</code>,\nfollowing the semantics of the Lua <code>&lt;</code> operator\n(that is, may call metamethods).\nOtherwise returns&nbsp;0.\nAlso returns&nbsp;0 if any of the indices is non valid.\n\n\n\n\n\n<hr><h3><a name=\"lua_load\"><code>lua_load</code></a></h3><p>\n<span class=\"apii\">[-0, +1, <em>-</em>]</span>\n<pre>int lua_load (lua_State *L,\n              lua_Reader reader,\n              void *data,\n              const char *chunkname);</pre>\n\n<p>\nLoads a Lua chunk.\nIf there are no errors,\n<a href=\"#lua_load\"><code>lua_load</code></a> pushes the compiled chunk as a Lua\nfunction on top of the stack.\nOtherwise, it pushes an error message.\nThe return values of <a href=\"#lua_load\"><code>lua_load</code></a> are:\n\n<ul>\n\n<li><b>0:</b> no errors;</li>\n\n<li><b><a name=\"pdf-LUA_ERRSYNTAX\"><code>LUA_ERRSYNTAX</code></a>:</b>\nsyntax error during pre-compilation;</li>\n\n<li><b><a href=\"#pdf-LUA_ERRMEM\"><code>LUA_ERRMEM</code></a>:</b>\nmemory allocation error.</li>\n\n</ul>\n\n<p>\nThis function only loads a chunk;\nit does not run it.\n\n\n<p>\n<a href=\"#lua_load\"><code>lua_load</code></a> automatically detects whether the chunk is text or binary,\nand loads it accordingly (see program <code>luac</code>).\n\n\n<p>\nThe <a href=\"#lua_load\"><code>lua_load</code></a> function uses a user-supplied <code>reader</code> function\nto read the chunk (see <a href=\"#lua_Reader\"><code>lua_Reader</code></a>).\nThe <code>data</code> argument is an opaque value passed to the reader function.\n\n\n<p>\nThe <code>chunkname</code> argument gives a name to the chunk,\nwhich is used for error messages and in debug information (see <a href=\"#3.8\">&sect;3.8</a>).\n\n\n\n\n\n<hr><h3><a name=\"lua_newstate\"><code>lua_newstate</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>-</em>]</span>\n<pre>lua_State *lua_newstate (lua_Alloc f, void *ud);</pre>\n\n<p>\nCreates a new, independent state.\nReturns <code>NULL</code> if cannot create the state\n(due to lack of memory).\nThe argument <code>f</code> is the allocator function;\nLua does all memory allocation for this state through this function.\nThe second argument, <code>ud</code>, is an opaque pointer that Lua\nsimply passes to the allocator in every call.\n\n\n\n\n\n<hr><h3><a name=\"lua_newtable\"><code>lua_newtable</code></a></h3><p>\n<span class=\"apii\">[-0, +1, <em>m</em>]</span>\n<pre>void lua_newtable (lua_State *L);</pre>\n\n<p>\nCreates a new empty table and pushes it onto the stack.\nIt is equivalent to <code>lua_createtable(L, 0, 0)</code>.\n\n\n\n\n\n<hr><h3><a name=\"lua_newthread\"><code>lua_newthread</code></a></h3><p>\n<span class=\"apii\">[-0, +1, <em>m</em>]</span>\n<pre>lua_State *lua_newthread (lua_State *L);</pre>\n\n<p>\nCreates a new thread, pushes it on the stack,\nand returns a pointer to a <a href=\"#lua_State\"><code>lua_State</code></a> that represents this new thread.\nThe new state returned by this function shares with the original state\nall global objects (such as tables),\nbut has an independent execution stack.\n\n\n<p>\nThere is no explicit function to close or to destroy a thread.\nThreads are subject to garbage collection,\nlike any Lua object.\n\n\n\n\n\n<hr><h3><a name=\"lua_newuserdata\"><code>lua_newuserdata</code></a></h3><p>\n<span class=\"apii\">[-0, +1, <em>m</em>]</span>\n<pre>void *lua_newuserdata (lua_State *L, size_t size);</pre>\n\n<p>\nThis function allocates a new block of memory with the given size,\npushes onto the stack a new full userdata with the block address,\nand returns this address.\n\n\n<p>\nUserdata represent C&nbsp;values in Lua.\nA <em>full userdata</em> represents a block of memory.\nIt is an object (like a table):\nyou must create it, it can have its own metatable,\nand you can detect when it is being collected.\nA full userdata is only equal to itself (under raw equality).\n\n\n<p>\nWhen Lua collects a full userdata with a <code>gc</code> metamethod,\nLua calls the metamethod and marks the userdata as finalized.\nWhen this userdata is collected again then\nLua frees its corresponding memory.\n\n\n\n\n\n<hr><h3><a name=\"lua_next\"><code>lua_next</code></a></h3><p>\n<span class=\"apii\">[-1, +(2|0), <em>e</em>]</span>\n<pre>int lua_next (lua_State *L, int index);</pre>\n\n<p>\nPops a key from the stack,\nand pushes a key-value pair from the table at the given index\n(the \"next\" pair after the given key).\nIf there are no more elements in the table,\nthen <a href=\"#lua_next\"><code>lua_next</code></a> returns 0 (and pushes nothing).\n\n\n<p>\nA typical traversal looks like this:\n\n<pre>\n     /* table is in the stack at index 't' */\n     lua_pushnil(L);  /* first key */\n     while (lua_next(L, t) != 0) {\n       /* uses 'key' (at index -2) and 'value' (at index -1) */\n       printf(\"%s - %s\\n\",\n              lua_typename(L, lua_type(L, -2)),\n              lua_typename(L, lua_type(L, -1)));\n       /* removes 'value'; keeps 'key' for next iteration */\n       lua_pop(L, 1);\n     }\n</pre>\n\n<p>\nWhile traversing a table,\ndo not call <a href=\"#lua_tolstring\"><code>lua_tolstring</code></a> directly on a key,\nunless you know that the key is actually a string.\nRecall that <a href=\"#lua_tolstring\"><code>lua_tolstring</code></a> <em>changes</em>\nthe value at the given index;\nthis confuses the next call to <a href=\"#lua_next\"><code>lua_next</code></a>.\n\n\n\n\n\n<hr><h3><a name=\"lua_Number\"><code>lua_Number</code></a></h3>\n<pre>typedef double lua_Number;</pre>\n\n<p>\nThe type of numbers in Lua.\nBy default, it is double, but that can be changed in <code>luaconf.h</code>.\n\n\n<p>\nThrough the configuration file you can change\nLua to operate with another type for numbers (e.g., float or long).\n\n\n\n\n\n<hr><h3><a name=\"lua_objlen\"><code>lua_objlen</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>-</em>]</span>\n<pre>size_t lua_objlen (lua_State *L, int index);</pre>\n\n<p>\nReturns the \"length\" of the value at the given acceptable index:\nfor strings, this is the string length;\nfor tables, this is the result of the length operator ('<code>#</code>');\nfor userdata, this is the size of the block of memory allocated\nfor the userdata;\nfor other values, it is&nbsp;0.\n\n\n\n\n\n<hr><h3><a name=\"lua_pcall\"><code>lua_pcall</code></a></h3><p>\n<span class=\"apii\">[-(nargs + 1), +(nresults|1), <em>-</em>]</span>\n<pre>int lua_pcall (lua_State *L, int nargs, int nresults, int errfunc);</pre>\n\n<p>\nCalls a function in protected mode.\n\n\n<p>\nBoth <code>nargs</code> and <code>nresults</code> have the same meaning as\nin <a href=\"#lua_call\"><code>lua_call</code></a>.\nIf there are no errors during the call,\n<a href=\"#lua_pcall\"><code>lua_pcall</code></a> behaves exactly like <a href=\"#lua_call\"><code>lua_call</code></a>.\nHowever, if there is any error,\n<a href=\"#lua_pcall\"><code>lua_pcall</code></a> catches it,\npushes a single value on the stack (the error message),\nand returns an error code.\nLike <a href=\"#lua_call\"><code>lua_call</code></a>,\n<a href=\"#lua_pcall\"><code>lua_pcall</code></a> always removes the function\nand its arguments from the stack.\n\n\n<p>\nIf <code>errfunc</code> is 0,\nthen the error message returned on the stack\nis exactly the original error message.\nOtherwise, <code>errfunc</code> is the stack index of an\n<em>error handler function</em>.\n(In the current implementation, this index cannot be a pseudo-index.)\nIn case of runtime errors,\nthis function will be called with the error message\nand its return value will be the message returned on the stack by <a href=\"#lua_pcall\"><code>lua_pcall</code></a>.\n\n\n<p>\nTypically, the error handler function is used to add more debug\ninformation to the error message, such as a stack traceback.\nSuch information cannot be gathered after the return of <a href=\"#lua_pcall\"><code>lua_pcall</code></a>,\nsince by then the stack has unwound.\n\n\n<p>\nThe <a href=\"#lua_pcall\"><code>lua_pcall</code></a> function returns 0 in case of success\nor one of the following error codes\n(defined in <code>lua.h</code>):\n\n<ul>\n\n<li><b><a name=\"pdf-LUA_ERRRUN\"><code>LUA_ERRRUN</code></a>:</b>\na runtime error.\n</li>\n\n<li><b><a name=\"pdf-LUA_ERRMEM\"><code>LUA_ERRMEM</code></a>:</b>\nmemory allocation error.\nFor such errors, Lua does not call the error handler function.\n</li>\n\n<li><b><a name=\"pdf-LUA_ERRERR\"><code>LUA_ERRERR</code></a>:</b>\nerror while running the error handler function.\n</li>\n\n</ul>\n\n\n\n\n<hr><h3><a name=\"lua_pop\"><code>lua_pop</code></a></h3><p>\n<span class=\"apii\">[-n, +0, <em>-</em>]</span>\n<pre>void lua_pop (lua_State *L, int n);</pre>\n\n<p>\nPops <code>n</code> elements from the stack.\n\n\n\n\n\n<hr><h3><a name=\"lua_pushboolean\"><code>lua_pushboolean</code></a></h3><p>\n<span class=\"apii\">[-0, +1, <em>-</em>]</span>\n<pre>void lua_pushboolean (lua_State *L, int b);</pre>\n\n<p>\nPushes a boolean value with value <code>b</code> onto the stack.\n\n\n\n\n\n<hr><h3><a name=\"lua_pushcclosure\"><code>lua_pushcclosure</code></a></h3><p>\n<span class=\"apii\">[-n, +1, <em>m</em>]</span>\n<pre>void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n);</pre>\n\n<p>\nPushes a new C&nbsp;closure onto the stack.\n\n\n<p>\nWhen a C&nbsp;function is created,\nit is possible to associate some values with it,\nthus creating a C&nbsp;closure (see <a href=\"#3.4\">&sect;3.4</a>);\nthese values are then accessible to the function whenever it is called.\nTo associate values with a C&nbsp;function,\nfirst these values should be pushed onto the stack\n(when there are multiple values, the first value is pushed first).\nThen <a href=\"#lua_pushcclosure\"><code>lua_pushcclosure</code></a>\nis called to create and push the C&nbsp;function onto the stack,\nwith the argument <code>n</code> telling how many values should be\nassociated with the function.\n<a href=\"#lua_pushcclosure\"><code>lua_pushcclosure</code></a> also pops these values from the stack.\n\n\n<p>\nThe maximum value for <code>n</code> is 255.\n\n\n\n\n\n<hr><h3><a name=\"lua_pushcfunction\"><code>lua_pushcfunction</code></a></h3><p>\n<span class=\"apii\">[-0, +1, <em>m</em>]</span>\n<pre>void lua_pushcfunction (lua_State *L, lua_CFunction f);</pre>\n\n<p>\nPushes a C&nbsp;function onto the stack.\nThis function receives a pointer to a C function\nand pushes onto the stack a Lua value of type <code>function</code> that,\nwhen called, invokes the corresponding C&nbsp;function.\n\n\n<p>\nAny function to be registered in Lua must\nfollow the correct protocol to receive its parameters\nand return its results (see <a href=\"#lua_CFunction\"><code>lua_CFunction</code></a>).\n\n\n<p>\n<code>lua_pushcfunction</code> is defined as a macro:\n\n<pre>\n     #define lua_pushcfunction(L,f)  lua_pushcclosure(L,f,0)\n</pre>\n\n\n\n\n<hr><h3><a name=\"lua_pushfstring\"><code>lua_pushfstring</code></a></h3><p>\n<span class=\"apii\">[-0, +1, <em>m</em>]</span>\n<pre>const char *lua_pushfstring (lua_State *L, const char *fmt, ...);</pre>\n\n<p>\nPushes onto the stack a formatted string\nand returns a pointer to this string.\nIt is similar to the C&nbsp;function <code>sprintf</code>,\nbut has some important differences:\n\n<ul>\n\n<li>\nYou do not have to allocate space for the result:\nthe result is a Lua string and Lua takes care of memory allocation\n(and deallocation, through garbage collection).\n</li>\n\n<li>\nThe conversion specifiers are quite restricted.\nThere are no flags, widths, or precisions.\nThe conversion specifiers can only be\n'<code>%%</code>' (inserts a '<code>%</code>' in the string),\n'<code>%s</code>' (inserts a zero-terminated string, with no size restrictions),\n'<code>%f</code>' (inserts a <a href=\"#lua_Number\"><code>lua_Number</code></a>),\n'<code>%p</code>' (inserts a pointer as a hexadecimal numeral),\n'<code>%d</code>' (inserts an <code>int</code>), and\n'<code>%c</code>' (inserts an <code>int</code> as a character).\n</li>\n\n</ul>\n\n\n\n\n<hr><h3><a name=\"lua_pushinteger\"><code>lua_pushinteger</code></a></h3><p>\n<span class=\"apii\">[-0, +1, <em>-</em>]</span>\n<pre>void lua_pushinteger (lua_State *L, lua_Integer n);</pre>\n\n<p>\nPushes a number with value <code>n</code> onto the stack.\n\n\n\n\n\n<hr><h3><a name=\"lua_pushlightuserdata\"><code>lua_pushlightuserdata</code></a></h3><p>\n<span class=\"apii\">[-0, +1, <em>-</em>]</span>\n<pre>void lua_pushlightuserdata (lua_State *L, void *p);</pre>\n\n<p>\nPushes a light userdata onto the stack.\n\n\n<p>\nUserdata represent C&nbsp;values in Lua.\nA <em>light userdata</em> represents a pointer.\nIt is a value (like a number):\nyou do not create it, it has no individual metatable,\nand it is not collected (as it was never created).\nA light userdata is equal to \"any\"\nlight userdata with the same C&nbsp;address.\n\n\n\n\n\n<hr><h3><a name=\"lua_pushliteral\"><code>lua_pushliteral</code></a></h3><p>\n<span class=\"apii\">[-0, +1, <em>m</em>]</span>\n<pre>void lua_pushliteral (lua_State *L, const char *s);</pre>\n\n<p>\nThis macro is equivalent to <a href=\"#lua_pushlstring\"><code>lua_pushlstring</code></a>,\nbut can be used only when <code>s</code> is a literal string.\nIn these cases, it automatically provides the string length.\n\n\n\n\n\n<hr><h3><a name=\"lua_pushlstring\"><code>lua_pushlstring</code></a></h3><p>\n<span class=\"apii\">[-0, +1, <em>m</em>]</span>\n<pre>void lua_pushlstring (lua_State *L, const char *s, size_t len);</pre>\n\n<p>\nPushes the string pointed to by <code>s</code> with size <code>len</code>\nonto the stack.\nLua makes (or reuses) an internal copy of the given string,\nso the memory at <code>s</code> can be freed or reused immediately after\nthe function returns.\nThe string can contain embedded zeros.\n\n\n\n\n\n<hr><h3><a name=\"lua_pushnil\"><code>lua_pushnil</code></a></h3><p>\n<span class=\"apii\">[-0, +1, <em>-</em>]</span>\n<pre>void lua_pushnil (lua_State *L);</pre>\n\n<p>\nPushes a nil value onto the stack.\n\n\n\n\n\n<hr><h3><a name=\"lua_pushnumber\"><code>lua_pushnumber</code></a></h3><p>\n<span class=\"apii\">[-0, +1, <em>-</em>]</span>\n<pre>void lua_pushnumber (lua_State *L, lua_Number n);</pre>\n\n<p>\nPushes a number with value <code>n</code> onto the stack.\n\n\n\n\n\n<hr><h3><a name=\"lua_pushstring\"><code>lua_pushstring</code></a></h3><p>\n<span class=\"apii\">[-0, +1, <em>m</em>]</span>\n<pre>void lua_pushstring (lua_State *L, const char *s);</pre>\n\n<p>\nPushes the zero-terminated string pointed to by <code>s</code>\nonto the stack.\nLua makes (or reuses) an internal copy of the given string,\nso the memory at <code>s</code> can be freed or reused immediately after\nthe function returns.\nThe string cannot contain embedded zeros;\nit is assumed to end at the first zero.\n\n\n\n\n\n<hr><h3><a name=\"lua_pushthread\"><code>lua_pushthread</code></a></h3><p>\n<span class=\"apii\">[-0, +1, <em>-</em>]</span>\n<pre>int lua_pushthread (lua_State *L);</pre>\n\n<p>\nPushes the thread represented by <code>L</code> onto the stack.\nReturns 1 if this thread is the main thread of its state.\n\n\n\n\n\n<hr><h3><a name=\"lua_pushvalue\"><code>lua_pushvalue</code></a></h3><p>\n<span class=\"apii\">[-0, +1, <em>-</em>]</span>\n<pre>void lua_pushvalue (lua_State *L, int index);</pre>\n\n<p>\nPushes a copy of the element at the given valid index\nonto the stack.\n\n\n\n\n\n<hr><h3><a name=\"lua_pushvfstring\"><code>lua_pushvfstring</code></a></h3><p>\n<span class=\"apii\">[-0, +1, <em>m</em>]</span>\n<pre>const char *lua_pushvfstring (lua_State *L,\n                              const char *fmt,\n                              va_list argp);</pre>\n\n<p>\nEquivalent to <a href=\"#lua_pushfstring\"><code>lua_pushfstring</code></a>, except that it receives a <code>va_list</code>\ninstead of a variable number of arguments.\n\n\n\n\n\n<hr><h3><a name=\"lua_rawequal\"><code>lua_rawequal</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>-</em>]</span>\n<pre>int lua_rawequal (lua_State *L, int index1, int index2);</pre>\n\n<p>\nReturns 1 if the two values in acceptable indices <code>index1</code> and\n<code>index2</code> are primitively equal\n(that is, without calling metamethods).\nOtherwise returns&nbsp;0.\nAlso returns&nbsp;0 if any of the indices are non valid.\n\n\n\n\n\n<hr><h3><a name=\"lua_rawget\"><code>lua_rawget</code></a></h3><p>\n<span class=\"apii\">[-1, +1, <em>-</em>]</span>\n<pre>void lua_rawget (lua_State *L, int index);</pre>\n\n<p>\nSimilar to <a href=\"#lua_gettable\"><code>lua_gettable</code></a>, but does a raw access\n(i.e., without metamethods).\n\n\n\n\n\n<hr><h3><a name=\"lua_rawgeti\"><code>lua_rawgeti</code></a></h3><p>\n<span class=\"apii\">[-0, +1, <em>-</em>]</span>\n<pre>void lua_rawgeti (lua_State *L, int index, int n);</pre>\n\n<p>\nPushes onto the stack the value <code>t[n]</code>,\nwhere <code>t</code> is the value at the given valid index.\nThe access is raw;\nthat is, it does not invoke metamethods.\n\n\n\n\n\n<hr><h3><a name=\"lua_rawset\"><code>lua_rawset</code></a></h3><p>\n<span class=\"apii\">[-2, +0, <em>m</em>]</span>\n<pre>void lua_rawset (lua_State *L, int index);</pre>\n\n<p>\nSimilar to <a href=\"#lua_settable\"><code>lua_settable</code></a>, but does a raw assignment\n(i.e., without metamethods).\n\n\n\n\n\n<hr><h3><a name=\"lua_rawseti\"><code>lua_rawseti</code></a></h3><p>\n<span class=\"apii\">[-1, +0, <em>m</em>]</span>\n<pre>void lua_rawseti (lua_State *L, int index, int n);</pre>\n\n<p>\nDoes the equivalent of <code>t[n] = v</code>,\nwhere <code>t</code> is the value at the given valid index\nand <code>v</code> is the value at the top of the stack.\n\n\n<p>\nThis function pops the value from the stack.\nThe assignment is raw;\nthat is, it does not invoke metamethods.\n\n\n\n\n\n<hr><h3><a name=\"lua_Reader\"><code>lua_Reader</code></a></h3>\n<pre>typedef const char * (*lua_Reader) (lua_State *L,\n                                    void *data,\n                                    size_t *size);</pre>\n\n<p>\nThe reader function used by <a href=\"#lua_load\"><code>lua_load</code></a>.\nEvery time it needs another piece of the chunk,\n<a href=\"#lua_load\"><code>lua_load</code></a> calls the reader,\npassing along its <code>data</code> parameter.\nThe reader must return a pointer to a block of memory\nwith a new piece of the chunk\nand set <code>size</code> to the block size.\nThe block must exist until the reader function is called again.\nTo signal the end of the chunk,\nthe reader must return <code>NULL</code> or set <code>size</code> to zero.\nThe reader function may return pieces of any size greater than zero.\n\n\n\n\n\n<hr><h3><a name=\"lua_register\"><code>lua_register</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>e</em>]</span>\n<pre>void lua_register (lua_State *L,\n                   const char *name,\n                   lua_CFunction f);</pre>\n\n<p>\nSets the C function <code>f</code> as the new value of global <code>name</code>.\nIt is defined as a macro:\n\n<pre>\n     #define lua_register(L,n,f) \\\n            (lua_pushcfunction(L, f), lua_setglobal(L, n))\n</pre>\n\n\n\n\n<hr><h3><a name=\"lua_remove\"><code>lua_remove</code></a></h3><p>\n<span class=\"apii\">[-1, +0, <em>-</em>]</span>\n<pre>void lua_remove (lua_State *L, int index);</pre>\n\n<p>\nRemoves the element at the given valid index,\nshifting down the elements above this index to fill the gap.\nCannot be called with a pseudo-index,\nbecause a pseudo-index is not an actual stack position.\n\n\n\n\n\n<hr><h3><a name=\"lua_replace\"><code>lua_replace</code></a></h3><p>\n<span class=\"apii\">[-1, +0, <em>-</em>]</span>\n<pre>void lua_replace (lua_State *L, int index);</pre>\n\n<p>\nMoves the top element into the given position (and pops it),\nwithout shifting any element\n(therefore replacing the value at the given position).\n\n\n\n\n\n<hr><h3><a name=\"lua_resume\"><code>lua_resume</code></a></h3><p>\n<span class=\"apii\">[-?, +?, <em>-</em>]</span>\n<pre>int lua_resume (lua_State *L, int narg);</pre>\n\n<p>\nStarts and resumes a coroutine in a given thread.\n\n\n<p>\nTo start a coroutine, you first create a new thread\n(see <a href=\"#lua_newthread\"><code>lua_newthread</code></a>);\nthen you push onto its stack the main function plus any arguments;\nthen you call <a href=\"#lua_resume\"><code>lua_resume</code></a>,\nwith <code>narg</code> being the number of arguments.\nThis call returns when the coroutine suspends or finishes its execution.\nWhen it returns, the stack contains all values passed to <a href=\"#lua_yield\"><code>lua_yield</code></a>,\nor all values returned by the body function.\n<a href=\"#lua_resume\"><code>lua_resume</code></a> returns\n<a href=\"#pdf-LUA_YIELD\"><code>LUA_YIELD</code></a> if the coroutine yields,\n0 if the coroutine finishes its execution\nwithout errors,\nor an error code in case of errors (see <a href=\"#lua_pcall\"><code>lua_pcall</code></a>).\nIn case of errors,\nthe stack is not unwound,\nso you can use the debug API over it.\nThe error message is on the top of the stack.\nTo restart a coroutine, you put on its stack only the values to\nbe passed as results from <code>yield</code>,\nand then call <a href=\"#lua_resume\"><code>lua_resume</code></a>.\n\n\n\n\n\n<hr><h3><a name=\"lua_setallocf\"><code>lua_setallocf</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>-</em>]</span>\n<pre>void lua_setallocf (lua_State *L, lua_Alloc f, void *ud);</pre>\n\n<p>\nChanges the allocator function of a given state to <code>f</code>\nwith user data <code>ud</code>.\n\n\n\n\n\n<hr><h3><a name=\"lua_setfenv\"><code>lua_setfenv</code></a></h3><p>\n<span class=\"apii\">[-1, +0, <em>-</em>]</span>\n<pre>int lua_setfenv (lua_State *L, int index);</pre>\n\n<p>\nPops a table from the stack and sets it as\nthe new environment for the value at the given index.\nIf the value at the given index is\nneither a function nor a thread nor a userdata,\n<a href=\"#lua_setfenv\"><code>lua_setfenv</code></a> returns 0.\nOtherwise it returns 1.\n\n\n\n\n\n<hr><h3><a name=\"lua_setfield\"><code>lua_setfield</code></a></h3><p>\n<span class=\"apii\">[-1, +0, <em>e</em>]</span>\n<pre>void lua_setfield (lua_State *L, int index, const char *k);</pre>\n\n<p>\nDoes the equivalent to <code>t[k] = v</code>,\nwhere <code>t</code> is the value at the given valid index\nand <code>v</code> is the value at the top of the stack.\n\n\n<p>\nThis function pops the value from the stack.\nAs in Lua, this function may trigger a metamethod\nfor the \"newindex\" event (see <a href=\"#2.8\">&sect;2.8</a>).\n\n\n\n\n\n<hr><h3><a name=\"lua_setglobal\"><code>lua_setglobal</code></a></h3><p>\n<span class=\"apii\">[-1, +0, <em>e</em>]</span>\n<pre>void lua_setglobal (lua_State *L, const char *name);</pre>\n\n<p>\nPops a value from the stack and\nsets it as the new value of global <code>name</code>.\nIt is defined as a macro:\n\n<pre>\n     #define lua_setglobal(L,s)   lua_setfield(L, LUA_GLOBALSINDEX, s)\n</pre>\n\n\n\n\n<hr><h3><a name=\"lua_setmetatable\"><code>lua_setmetatable</code></a></h3><p>\n<span class=\"apii\">[-1, +0, <em>-</em>]</span>\n<pre>int lua_setmetatable (lua_State *L, int index);</pre>\n\n<p>\nPops a table from the stack and\nsets it as the new metatable for the value at the given\nacceptable index.\n\n\n\n\n\n<hr><h3><a name=\"lua_settable\"><code>lua_settable</code></a></h3><p>\n<span class=\"apii\">[-2, +0, <em>e</em>]</span>\n<pre>void lua_settable (lua_State *L, int index);</pre>\n\n<p>\nDoes the equivalent to <code>t[k] = v</code>,\nwhere <code>t</code> is the value at the given valid index,\n<code>v</code> is the value at the top of the stack,\nand <code>k</code> is the value just below the top.\n\n\n<p>\nThis function pops both the key and the value from the stack.\nAs in Lua, this function may trigger a metamethod\nfor the \"newindex\" event (see <a href=\"#2.8\">&sect;2.8</a>).\n\n\n\n\n\n<hr><h3><a name=\"lua_settop\"><code>lua_settop</code></a></h3><p>\n<span class=\"apii\">[-?, +?, <em>-</em>]</span>\n<pre>void lua_settop (lua_State *L, int index);</pre>\n\n<p>\nAccepts any acceptable index, or&nbsp;0,\nand sets the stack top to this index.\nIf the new top is larger than the old one,\nthen the new elements are filled with <b>nil</b>.\nIf <code>index</code> is&nbsp;0, then all stack elements are removed.\n\n\n\n\n\n<hr><h3><a name=\"lua_State\"><code>lua_State</code></a></h3>\n<pre>typedef struct lua_State lua_State;</pre>\n\n<p>\nOpaque structure that keeps the whole state of a Lua interpreter.\nThe Lua library is fully reentrant:\nit has no global variables.\nAll information about a state is kept in this structure.\n\n\n<p>\nA pointer to this state must be passed as the first argument to\nevery function in the library, except to <a href=\"#lua_newstate\"><code>lua_newstate</code></a>,\nwhich creates a Lua state from scratch.\n\n\n\n\n\n<hr><h3><a name=\"lua_status\"><code>lua_status</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>-</em>]</span>\n<pre>int lua_status (lua_State *L);</pre>\n\n<p>\nReturns the status of the thread <code>L</code>.\n\n\n<p>\nThe status can be 0 for a normal thread,\nan error code if the thread finished its execution with an error,\nor <a name=\"pdf-LUA_YIELD\"><code>LUA_YIELD</code></a> if the thread is suspended.\n\n\n\n\n\n<hr><h3><a name=\"lua_toboolean\"><code>lua_toboolean</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>-</em>]</span>\n<pre>int lua_toboolean (lua_State *L, int index);</pre>\n\n<p>\nConverts the Lua value at the given acceptable index to a C&nbsp;boolean\nvalue (0&nbsp;or&nbsp;1).\nLike all tests in Lua,\n<a href=\"#lua_toboolean\"><code>lua_toboolean</code></a> returns 1 for any Lua value\ndifferent from <b>false</b> and <b>nil</b>;\notherwise it returns 0.\nIt also returns 0 when called with a non-valid index.\n(If you want to accept only actual boolean values,\nuse <a href=\"#lua_isboolean\"><code>lua_isboolean</code></a> to test the value's type.)\n\n\n\n\n\n<hr><h3><a name=\"lua_tocfunction\"><code>lua_tocfunction</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>-</em>]</span>\n<pre>lua_CFunction lua_tocfunction (lua_State *L, int index);</pre>\n\n<p>\nConverts a value at the given acceptable index to a C&nbsp;function.\nThat value must be a C&nbsp;function;\notherwise, returns <code>NULL</code>.\n\n\n\n\n\n<hr><h3><a name=\"lua_tointeger\"><code>lua_tointeger</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>-</em>]</span>\n<pre>lua_Integer lua_tointeger (lua_State *L, int index);</pre>\n\n<p>\nConverts the Lua value at the given acceptable index\nto the signed integral type <a href=\"#lua_Integer\"><code>lua_Integer</code></a>.\nThe Lua value must be a number or a string convertible to a number\n(see <a href=\"#2.2.1\">&sect;2.2.1</a>);\notherwise, <a href=\"#lua_tointeger\"><code>lua_tointeger</code></a> returns&nbsp;0.\n\n\n<p>\nIf the number is not an integer,\nit is truncated in some non-specified way.\n\n\n\n\n\n<hr><h3><a name=\"lua_tolstring\"><code>lua_tolstring</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>m</em>]</span>\n<pre>const char *lua_tolstring (lua_State *L, int index, size_t *len);</pre>\n\n<p>\nConverts the Lua value at the given acceptable index to a C&nbsp;string.\nIf <code>len</code> is not <code>NULL</code>,\nit also sets <code>*len</code> with the string length.\nThe Lua value must be a string or a number;\notherwise, the function returns <code>NULL</code>.\nIf the value is a number,\nthen <a href=\"#lua_tolstring\"><code>lua_tolstring</code></a> also\n<em>changes the actual value in the stack to a string</em>.\n(This change confuses <a href=\"#lua_next\"><code>lua_next</code></a>\nwhen <a href=\"#lua_tolstring\"><code>lua_tolstring</code></a> is applied to keys during a table traversal.)\n\n\n<p>\n<a href=\"#lua_tolstring\"><code>lua_tolstring</code></a> returns a fully aligned pointer\nto a string inside the Lua state.\nThis string always has a zero ('<code>\\0</code>')\nafter its last character (as in&nbsp;C),\nbut can contain other zeros in its body.\nBecause Lua has garbage collection,\nthere is no guarantee that the pointer returned by <a href=\"#lua_tolstring\"><code>lua_tolstring</code></a>\nwill be valid after the corresponding value is removed from the stack.\n\n\n\n\n\n<hr><h3><a name=\"lua_tonumber\"><code>lua_tonumber</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>-</em>]</span>\n<pre>lua_Number lua_tonumber (lua_State *L, int index);</pre>\n\n<p>\nConverts the Lua value at the given acceptable index\nto the C&nbsp;type <a href=\"#lua_Number\"><code>lua_Number</code></a> (see <a href=\"#lua_Number\"><code>lua_Number</code></a>).\nThe Lua value must be a number or a string convertible to a number\n(see <a href=\"#2.2.1\">&sect;2.2.1</a>);\notherwise, <a href=\"#lua_tonumber\"><code>lua_tonumber</code></a> returns&nbsp;0.\n\n\n\n\n\n<hr><h3><a name=\"lua_topointer\"><code>lua_topointer</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>-</em>]</span>\n<pre>const void *lua_topointer (lua_State *L, int index);</pre>\n\n<p>\nConverts the value at the given acceptable index to a generic\nC&nbsp;pointer (<code>void*</code>).\nThe value can be a userdata, a table, a thread, or a function;\notherwise, <a href=\"#lua_topointer\"><code>lua_topointer</code></a> returns <code>NULL</code>.\nDifferent objects will give different pointers.\nThere is no way to convert the pointer back to its original value.\n\n\n<p>\nTypically this function is used only for debug information.\n\n\n\n\n\n<hr><h3><a name=\"lua_tostring\"><code>lua_tostring</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>m</em>]</span>\n<pre>const char *lua_tostring (lua_State *L, int index);</pre>\n\n<p>\nEquivalent to <a href=\"#lua_tolstring\"><code>lua_tolstring</code></a> with <code>len</code> equal to <code>NULL</code>.\n\n\n\n\n\n<hr><h3><a name=\"lua_tothread\"><code>lua_tothread</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>-</em>]</span>\n<pre>lua_State *lua_tothread (lua_State *L, int index);</pre>\n\n<p>\nConverts the value at the given acceptable index to a Lua thread\n(represented as <code>lua_State*</code>).\nThis value must be a thread;\notherwise, the function returns <code>NULL</code>.\n\n\n\n\n\n<hr><h3><a name=\"lua_touserdata\"><code>lua_touserdata</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>-</em>]</span>\n<pre>void *lua_touserdata (lua_State *L, int index);</pre>\n\n<p>\nIf the value at the given acceptable index is a full userdata,\nreturns its block address.\nIf the value is a light userdata,\nreturns its pointer.\nOtherwise, returns <code>NULL</code>.\n\n\n\n\n\n<hr><h3><a name=\"lua_type\"><code>lua_type</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>-</em>]</span>\n<pre>int lua_type (lua_State *L, int index);</pre>\n\n<p>\nReturns the type of the value in the given acceptable index,\nor <code>LUA_TNONE</code> for a non-valid index\n(that is, an index to an \"empty\" stack position).\nThe types returned by <a href=\"#lua_type\"><code>lua_type</code></a> are coded by the following constants\ndefined in <code>lua.h</code>:\n<code>LUA_TNIL</code>,\n<code>LUA_TNUMBER</code>,\n<code>LUA_TBOOLEAN</code>,\n<code>LUA_TSTRING</code>,\n<code>LUA_TTABLE</code>,\n<code>LUA_TFUNCTION</code>,\n<code>LUA_TUSERDATA</code>,\n<code>LUA_TTHREAD</code>,\nand\n<code>LUA_TLIGHTUSERDATA</code>.\n\n\n\n\n\n<hr><h3><a name=\"lua_typename\"><code>lua_typename</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>-</em>]</span>\n<pre>const char *lua_typename  (lua_State *L, int tp);</pre>\n\n<p>\nReturns the name of the type encoded by the value <code>tp</code>,\nwhich must be one the values returned by <a href=\"#lua_type\"><code>lua_type</code></a>.\n\n\n\n\n\n<hr><h3><a name=\"lua_Writer\"><code>lua_Writer</code></a></h3>\n<pre>typedef int (*lua_Writer) (lua_State *L,\n                           const void* p,\n                           size_t sz,\n                           void* ud);</pre>\n\n<p>\nThe type of the writer function used by <a href=\"#lua_dump\"><code>lua_dump</code></a>.\nEvery time it produces another piece of chunk,\n<a href=\"#lua_dump\"><code>lua_dump</code></a> calls the writer,\npassing along the buffer to be written (<code>p</code>),\nits size (<code>sz</code>),\nand the <code>data</code> parameter supplied to <a href=\"#lua_dump\"><code>lua_dump</code></a>.\n\n\n<p>\nThe writer returns an error code:\n0&nbsp;means no errors;\nany other value means an error and stops <a href=\"#lua_dump\"><code>lua_dump</code></a> from\ncalling the writer again.\n\n\n\n\n\n<hr><h3><a name=\"lua_xmove\"><code>lua_xmove</code></a></h3><p>\n<span class=\"apii\">[-?, +?, <em>-</em>]</span>\n<pre>void lua_xmove (lua_State *from, lua_State *to, int n);</pre>\n\n<p>\nExchange values between different threads of the <em>same</em> global state.\n\n\n<p>\nThis function pops <code>n</code> values from the stack <code>from</code>,\nand pushes them onto the stack <code>to</code>.\n\n\n\n\n\n<hr><h3><a name=\"lua_yield\"><code>lua_yield</code></a></h3><p>\n<span class=\"apii\">[-?, +?, <em>-</em>]</span>\n<pre>int lua_yield  (lua_State *L, int nresults);</pre>\n\n<p>\nYields a coroutine.\n\n\n<p>\nThis function should only be called as the\nreturn expression of a C&nbsp;function, as follows:\n\n<pre>\n     return lua_yield (L, nresults);\n</pre><p>\nWhen a C&nbsp;function calls <a href=\"#lua_yield\"><code>lua_yield</code></a> in that way,\nthe running coroutine suspends its execution,\nand the call to <a href=\"#lua_resume\"><code>lua_resume</code></a> that started this coroutine returns.\nThe parameter <code>nresults</code> is the number of values from the stack\nthat are passed as results to <a href=\"#lua_resume\"><code>lua_resume</code></a>.\n\n\n\n\n\n\n\n<h2>3.8 - <a name=\"3.8\">The Debug Interface</a></h2>\n\n<p>\nLua has no built-in debugging facilities.\nInstead, it offers a special interface\nby means of functions and <em>hooks</em>.\nThis interface allows the construction of different\nkinds of debuggers, profilers, and other tools\nthat need \"inside information\" from the interpreter.\n\n\n\n<hr><h3><a name=\"lua_Debug\"><code>lua_Debug</code></a></h3>\n<pre>typedef struct lua_Debug {\n  int event;\n  const char *name;           /* (n) */\n  const char *namewhat;       /* (n) */\n  const char *what;           /* (S) */\n  const char *source;         /* (S) */\n  int currentline;            /* (l) */\n  int nups;                   /* (u) number of upvalues */\n  int linedefined;            /* (S) */\n  int lastlinedefined;        /* (S) */\n  char short_src[LUA_IDSIZE]; /* (S) */\n  /* private part */\n  <em>other fields</em>\n} lua_Debug;</pre>\n\n<p>\nA structure used to carry different pieces of\ninformation about an active function.\n<a href=\"#lua_getstack\"><code>lua_getstack</code></a> fills only the private part\nof this structure, for later use.\nTo fill the other fields of <a href=\"#lua_Debug\"><code>lua_Debug</code></a> with useful information,\ncall <a href=\"#lua_getinfo\"><code>lua_getinfo</code></a>.\n\n\n<p>\nThe fields of <a href=\"#lua_Debug\"><code>lua_Debug</code></a> have the following meaning:\n\n<ul>\n\n<li><b><code>source</code>:</b>\nIf the function was defined in a string,\nthen <code>source</code> is that string.\nIf the function was defined in a file,\nthen <code>source</code> starts with a '<code>@</code>' followed by the file name.\n</li>\n\n<li><b><code>short_src</code>:</b>\na \"printable\" version of <code>source</code>, to be used in error messages.\n</li>\n\n<li><b><code>linedefined</code>:</b>\nthe line number where the definition of the function starts.\n</li>\n\n<li><b><code>lastlinedefined</code>:</b>\nthe line number where the definition of the function ends.\n</li>\n\n<li><b><code>what</code>:</b>\nthe string <code>\"Lua\"</code> if the function is a Lua function,\n<code>\"C\"</code> if it is a C&nbsp;function,\n<code>\"main\"</code> if it is the main part of a chunk,\nand <code>\"tail\"</code> if it was a function that did a tail call.\nIn the latter case,\nLua has no other information about the function.\n</li>\n\n<li><b><code>currentline</code>:</b>\nthe current line where the given function is executing.\nWhen no line information is available,\n<code>currentline</code> is set to -1.\n</li>\n\n<li><b><code>name</code>:</b>\na reasonable name for the given function.\nBecause functions in Lua are first-class values,\nthey do not have a fixed name:\nsome functions can be the value of multiple global variables,\nwhile others can be stored only in a table field.\nThe <code>lua_getinfo</code> function checks how the function was\ncalled to find a suitable name.\nIf it cannot find a name,\nthen <code>name</code> is set to <code>NULL</code>.\n</li>\n\n<li><b><code>namewhat</code>:</b>\nexplains the <code>name</code> field.\nThe value of <code>namewhat</code> can be\n<code>\"global\"</code>, <code>\"local\"</code>, <code>\"method\"</code>,\n<code>\"field\"</code>, <code>\"upvalue\"</code>, or <code>\"\"</code> (the empty string),\naccording to how the function was called.\n(Lua uses the empty string when no other option seems to apply.)\n</li>\n\n<li><b><code>nups</code>:</b>\nthe number of upvalues of the function.\n</li>\n\n</ul>\n\n\n\n\n<hr><h3><a name=\"lua_gethook\"><code>lua_gethook</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>-</em>]</span>\n<pre>lua_Hook lua_gethook (lua_State *L);</pre>\n\n<p>\nReturns the current hook function.\n\n\n\n\n\n<hr><h3><a name=\"lua_gethookcount\"><code>lua_gethookcount</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>-</em>]</span>\n<pre>int lua_gethookcount (lua_State *L);</pre>\n\n<p>\nReturns the current hook count.\n\n\n\n\n\n<hr><h3><a name=\"lua_gethookmask\"><code>lua_gethookmask</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>-</em>]</span>\n<pre>int lua_gethookmask (lua_State *L);</pre>\n\n<p>\nReturns the current hook mask.\n\n\n\n\n\n<hr><h3><a name=\"lua_getinfo\"><code>lua_getinfo</code></a></h3><p>\n<span class=\"apii\">[-(0|1), +(0|1|2), <em>m</em>]</span>\n<pre>int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar);</pre>\n\n<p>\nReturns information about a specific function or function invocation.\n\n\n<p>\nTo get information about a function invocation,\nthe parameter <code>ar</code> must be a valid activation record that was\nfilled by a previous call to <a href=\"#lua_getstack\"><code>lua_getstack</code></a> or\ngiven as argument to a hook (see <a href=\"#lua_Hook\"><code>lua_Hook</code></a>).\n\n\n<p>\nTo get information about a function you push it onto the stack\nand start the <code>what</code> string with the character '<code>&gt;</code>'.\n(In that case,\n<code>lua_getinfo</code> pops the function in the top of the stack.)\nFor instance, to know in which line a function <code>f</code> was defined,\nyou can write the following code:\n\n<pre>\n     lua_Debug ar;\n     lua_getfield(L, LUA_GLOBALSINDEX, \"f\");  /* get global 'f' */\n     lua_getinfo(L, \"&gt;S\", &amp;ar);\n     printf(\"%d\\n\", ar.linedefined);\n</pre>\n\n<p>\nEach character in the string <code>what</code>\nselects some fields of the structure <code>ar</code> to be filled or\na value to be pushed on the stack:\n\n<ul>\n\n<li><b>'<code>n</code>':</b> fills in the field <code>name</code> and <code>namewhat</code>;\n</li>\n\n<li><b>'<code>S</code>':</b>\nfills in the fields <code>source</code>, <code>short_src</code>,\n<code>linedefined</code>, <code>lastlinedefined</code>, and <code>what</code>;\n</li>\n\n<li><b>'<code>l</code>':</b> fills in the field <code>currentline</code>;\n</li>\n\n<li><b>'<code>u</code>':</b> fills in the field <code>nups</code>;\n</li>\n\n<li><b>'<code>f</code>':</b>\npushes onto the stack the function that is\nrunning at the given level;\n</li>\n\n<li><b>'<code>L</code>':</b>\npushes onto the stack a table whose indices are the\nnumbers of the lines that are valid on the function.\n(A <em>valid line</em> is a line with some associated code,\nthat is, a line where you can put a break point.\nNon-valid lines include empty lines and comments.)\n</li>\n\n</ul>\n\n<p>\nThis function returns 0 on error\n(for instance, an invalid option in <code>what</code>).\n\n\n\n\n\n<hr><h3><a name=\"lua_getlocal\"><code>lua_getlocal</code></a></h3><p>\n<span class=\"apii\">[-0, +(0|1), <em>-</em>]</span>\n<pre>const char *lua_getlocal (lua_State *L, lua_Debug *ar, int n);</pre>\n\n<p>\nGets information about a local variable of a given activation record.\nThe parameter <code>ar</code> must be a valid activation record that was\nfilled by a previous call to <a href=\"#lua_getstack\"><code>lua_getstack</code></a> or\ngiven as argument to a hook (see <a href=\"#lua_Hook\"><code>lua_Hook</code></a>).\nThe index <code>n</code> selects which local variable to inspect\n(1 is the first parameter or active local variable, and so on,\nuntil the last active local variable).\n<a href=\"#lua_getlocal\"><code>lua_getlocal</code></a> pushes the variable's value onto the stack\nand returns its name.\n\n\n<p>\nVariable names starting with '<code>(</code>' (open parentheses)\nrepresent internal variables\n(loop control variables, temporaries, and C&nbsp;function locals).\n\n\n<p>\nReturns <code>NULL</code> (and pushes nothing)\nwhen the index is greater than\nthe number of active local variables.\n\n\n\n\n\n<hr><h3><a name=\"lua_getstack\"><code>lua_getstack</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>-</em>]</span>\n<pre>int lua_getstack (lua_State *L, int level, lua_Debug *ar);</pre>\n\n<p>\nGet information about the interpreter runtime stack.\n\n\n<p>\nThis function fills parts of a <a href=\"#lua_Debug\"><code>lua_Debug</code></a> structure with\nan identification of the <em>activation record</em>\nof the function executing at a given level.\nLevel&nbsp;0 is the current running function,\nwhereas level <em>n+1</em> is the function that has called level <em>n</em>.\nWhen there are no errors, <a href=\"#lua_getstack\"><code>lua_getstack</code></a> returns 1;\nwhen called with a level greater than the stack depth,\nit returns 0.\n\n\n\n\n\n<hr><h3><a name=\"lua_getupvalue\"><code>lua_getupvalue</code></a></h3><p>\n<span class=\"apii\">[-0, +(0|1), <em>-</em>]</span>\n<pre>const char *lua_getupvalue (lua_State *L, int funcindex, int n);</pre>\n\n<p>\nGets information about a closure's upvalue.\n(For Lua functions,\nupvalues are the external local variables that the function uses,\nand that are consequently included in its closure.)\n<a href=\"#lua_getupvalue\"><code>lua_getupvalue</code></a> gets the index <code>n</code> of an upvalue,\npushes the upvalue's value onto the stack,\nand returns its name.\n<code>funcindex</code> points to the closure in the stack.\n(Upvalues have no particular order,\nas they are active through the whole function.\nSo, they are numbered in an arbitrary order.)\n\n\n<p>\nReturns <code>NULL</code> (and pushes nothing)\nwhen the index is greater than the number of upvalues.\nFor C&nbsp;functions, this function uses the empty string <code>\"\"</code>\nas a name for all upvalues.\n\n\n\n\n\n<hr><h3><a name=\"lua_Hook\"><code>lua_Hook</code></a></h3>\n<pre>typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar);</pre>\n\n<p>\nType for debugging hook functions.\n\n\n<p>\nWhenever a hook is called, its <code>ar</code> argument has its field\n<code>event</code> set to the specific event that triggered the hook.\nLua identifies these events with the following constants:\n<a name=\"pdf-LUA_HOOKCALL\"><code>LUA_HOOKCALL</code></a>, <a name=\"pdf-LUA_HOOKRET\"><code>LUA_HOOKRET</code></a>,\n<a name=\"pdf-LUA_HOOKTAILRET\"><code>LUA_HOOKTAILRET</code></a>, <a name=\"pdf-LUA_HOOKLINE\"><code>LUA_HOOKLINE</code></a>,\nand <a name=\"pdf-LUA_HOOKCOUNT\"><code>LUA_HOOKCOUNT</code></a>.\nMoreover, for line events, the field <code>currentline</code> is also set.\nTo get the value of any other field in <code>ar</code>,\nthe hook must call <a href=\"#lua_getinfo\"><code>lua_getinfo</code></a>.\nFor return events, <code>event</code> can be <code>LUA_HOOKRET</code>,\nthe normal value, or <code>LUA_HOOKTAILRET</code>.\nIn the latter case, Lua is simulating a return from\na function that did a tail call;\nin this case, it is useless to call <a href=\"#lua_getinfo\"><code>lua_getinfo</code></a>.\n\n\n<p>\nWhile Lua is running a hook, it disables other calls to hooks.\nTherefore, if a hook calls back Lua to execute a function or a chunk,\nthis execution occurs without any calls to hooks.\n\n\n\n\n\n<hr><h3><a name=\"lua_sethook\"><code>lua_sethook</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>-</em>]</span>\n<pre>int lua_sethook (lua_State *L, lua_Hook f, int mask, int count);</pre>\n\n<p>\nSets the debugging hook function.\n\n\n<p>\nArgument <code>f</code> is the hook function.\n<code>mask</code> specifies on which events the hook will be called:\nit is formed by a bitwise or of the constants\n<a name=\"pdf-LUA_MASKCALL\"><code>LUA_MASKCALL</code></a>,\n<a name=\"pdf-LUA_MASKRET\"><code>LUA_MASKRET</code></a>,\n<a name=\"pdf-LUA_MASKLINE\"><code>LUA_MASKLINE</code></a>,\nand <a name=\"pdf-LUA_MASKCOUNT\"><code>LUA_MASKCOUNT</code></a>.\nThe <code>count</code> argument is only meaningful when the mask\nincludes <code>LUA_MASKCOUNT</code>.\nFor each event, the hook is called as explained below:\n\n<ul>\n\n<li><b>The call hook:</b> is called when the interpreter calls a function.\nThe hook is called just after Lua enters the new function,\nbefore the function gets its arguments.\n</li>\n\n<li><b>The return hook:</b> is called when the interpreter returns from a function.\nThe hook is called just before Lua leaves the function.\nYou have no access to the values to be returned by the function.\n</li>\n\n<li><b>The line hook:</b> is called when the interpreter is about to\nstart the execution of a new line of code,\nor when it jumps back in the code (even to the same line).\n(This event only happens while Lua is executing a Lua function.)\n</li>\n\n<li><b>The count hook:</b> is called after the interpreter executes every\n<code>count</code> instructions.\n(This event only happens while Lua is executing a Lua function.)\n</li>\n\n</ul>\n\n<p>\nA hook is disabled by setting <code>mask</code> to zero.\n\n\n\n\n\n<hr><h3><a name=\"lua_setlocal\"><code>lua_setlocal</code></a></h3><p>\n<span class=\"apii\">[-(0|1), +0, <em>-</em>]</span>\n<pre>const char *lua_setlocal (lua_State *L, lua_Debug *ar, int n);</pre>\n\n<p>\nSets the value of a local variable of a given activation record.\nParameters <code>ar</code> and <code>n</code> are as in <a href=\"#lua_getlocal\"><code>lua_getlocal</code></a>\n(see <a href=\"#lua_getlocal\"><code>lua_getlocal</code></a>).\n<a href=\"#lua_setlocal\"><code>lua_setlocal</code></a> assigns the value at the top of the stack\nto the variable and returns its name.\nIt also pops the value from the stack.\n\n\n<p>\nReturns <code>NULL</code> (and pops nothing)\nwhen the index is greater than\nthe number of active local variables.\n\n\n\n\n\n<hr><h3><a name=\"lua_setupvalue\"><code>lua_setupvalue</code></a></h3><p>\n<span class=\"apii\">[-(0|1), +0, <em>-</em>]</span>\n<pre>const char *lua_setupvalue (lua_State *L, int funcindex, int n);</pre>\n\n<p>\nSets the value of a closure's upvalue.\nIt assigns the value at the top of the stack\nto the upvalue and returns its name.\nIt also pops the value from the stack.\nParameters <code>funcindex</code> and <code>n</code> are as in the <a href=\"#lua_getupvalue\"><code>lua_getupvalue</code></a>\n(see <a href=\"#lua_getupvalue\"><code>lua_getupvalue</code></a>).\n\n\n<p>\nReturns <code>NULL</code> (and pops nothing)\nwhen the index is greater than the number of upvalues.\n\n\n\n\n\n\n\n<h1>4 - <a name=\"4\">The Auxiliary Library</a></h1>\n\n<p>\n\nThe <em>auxiliary library</em> provides several convenient functions\nto interface C with Lua.\nWhile the basic API provides the primitive functions for all \ninteractions between C and Lua,\nthe auxiliary library provides higher-level functions for some\ncommon tasks.\n\n\n<p>\nAll functions from the auxiliary library\nare defined in header file <code>lauxlib.h</code> and\nhave a prefix <code>luaL_</code>.\n\n\n<p>\nAll functions in the auxiliary library are built on\ntop of the basic API,\nand so they provide nothing that cannot be done with this API.\n\n\n<p>\nSeveral functions in the auxiliary library are used to\ncheck C&nbsp;function arguments.\nTheir names are always <code>luaL_check*</code> or <code>luaL_opt*</code>.\nAll of these functions throw an error if the check is not satisfied.\nBecause the error message is formatted for arguments\n(e.g., \"<code>bad argument #1</code>\"),\nyou should not use these functions for other stack values.\n\n\n\n<h2>4.1 - <a name=\"4.1\">Functions and Types</a></h2>\n\n<p>\nHere we list all functions and types from the auxiliary library\nin alphabetical order.\n\n\n\n<hr><h3><a name=\"luaL_addchar\"><code>luaL_addchar</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>m</em>]</span>\n<pre>void luaL_addchar (luaL_Buffer *B, char c);</pre>\n\n<p>\nAdds the character <code>c</code> to the buffer <code>B</code>\n(see <a href=\"#luaL_Buffer\"><code>luaL_Buffer</code></a>).\n\n\n\n\n\n<hr><h3><a name=\"luaL_addlstring\"><code>luaL_addlstring</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>m</em>]</span>\n<pre>void luaL_addlstring (luaL_Buffer *B, const char *s, size_t l);</pre>\n\n<p>\nAdds the string pointed to by <code>s</code> with length <code>l</code> to\nthe buffer <code>B</code>\n(see <a href=\"#luaL_Buffer\"><code>luaL_Buffer</code></a>).\nThe string may contain embedded zeros.\n\n\n\n\n\n<hr><h3><a name=\"luaL_addsize\"><code>luaL_addsize</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>m</em>]</span>\n<pre>void luaL_addsize (luaL_Buffer *B, size_t n);</pre>\n\n<p>\nAdds to the buffer <code>B</code> (see <a href=\"#luaL_Buffer\"><code>luaL_Buffer</code></a>)\na string of length <code>n</code> previously copied to the\nbuffer area (see <a href=\"#luaL_prepbuffer\"><code>luaL_prepbuffer</code></a>).\n\n\n\n\n\n<hr><h3><a name=\"luaL_addstring\"><code>luaL_addstring</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>m</em>]</span>\n<pre>void luaL_addstring (luaL_Buffer *B, const char *s);</pre>\n\n<p>\nAdds the zero-terminated string pointed to by <code>s</code>\nto the buffer <code>B</code>\n(see <a href=\"#luaL_Buffer\"><code>luaL_Buffer</code></a>).\nThe string may not contain embedded zeros.\n\n\n\n\n\n<hr><h3><a name=\"luaL_addvalue\"><code>luaL_addvalue</code></a></h3><p>\n<span class=\"apii\">[-1, +0, <em>m</em>]</span>\n<pre>void luaL_addvalue (luaL_Buffer *B);</pre>\n\n<p>\nAdds the value at the top of the stack\nto the buffer <code>B</code>\n(see <a href=\"#luaL_Buffer\"><code>luaL_Buffer</code></a>).\nPops the value.\n\n\n<p>\nThis is the only function on string buffers that can (and must)\nbe called with an extra element on the stack,\nwhich is the value to be added to the buffer.\n\n\n\n\n\n<hr><h3><a name=\"luaL_argcheck\"><code>luaL_argcheck</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>v</em>]</span>\n<pre>void luaL_argcheck (lua_State *L,\n                    int cond,\n                    int narg,\n                    const char *extramsg);</pre>\n\n<p>\nChecks whether <code>cond</code> is true.\nIf not, raises an error with the following message,\nwhere <code>func</code> is retrieved from the call stack:\n\n<pre>\n     bad argument #&lt;narg&gt; to &lt;func&gt; (&lt;extramsg&gt;)\n</pre>\n\n\n\n\n<hr><h3><a name=\"luaL_argerror\"><code>luaL_argerror</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>v</em>]</span>\n<pre>int luaL_argerror (lua_State *L, int narg, const char *extramsg);</pre>\n\n<p>\nRaises an error with the following message,\nwhere <code>func</code> is retrieved from the call stack:\n\n<pre>\n     bad argument #&lt;narg&gt; to &lt;func&gt; (&lt;extramsg&gt;)\n</pre>\n\n<p>\nThis function never returns,\nbut it is an idiom to use it in C&nbsp;functions\nas <code>return luaL_argerror(<em>args</em>)</code>.\n\n\n\n\n\n<hr><h3><a name=\"luaL_Buffer\"><code>luaL_Buffer</code></a></h3>\n<pre>typedef struct luaL_Buffer luaL_Buffer;</pre>\n\n<p>\nType for a <em>string buffer</em>.\n\n\n<p>\nA string buffer allows C&nbsp;code to build Lua strings piecemeal.\nIts pattern of use is as follows:\n\n<ul>\n\n<li>First you declare a variable <code>b</code> of type <a href=\"#luaL_Buffer\"><code>luaL_Buffer</code></a>.</li>\n\n<li>Then you initialize it with a call <code>luaL_buffinit(L, &amp;b)</code>.</li>\n\n<li>\nThen you add string pieces to the buffer calling any of\nthe <code>luaL_add*</code> functions.\n</li>\n\n<li>\nYou finish by calling <code>luaL_pushresult(&amp;b)</code>.\nThis call leaves the final string on the top of the stack.\n</li>\n\n</ul>\n\n<p>\nDuring its normal operation,\na string buffer uses a variable number of stack slots.\nSo, while using a buffer, you cannot assume that you know where\nthe top of the stack is.\nYou can use the stack between successive calls to buffer operations\nas long as that use is balanced;\nthat is,\nwhen you call a buffer operation,\nthe stack is at the same level\nit was immediately after the previous buffer operation.\n(The only exception to this rule is <a href=\"#luaL_addvalue\"><code>luaL_addvalue</code></a>.)\nAfter calling <a href=\"#luaL_pushresult\"><code>luaL_pushresult</code></a> the stack is back to its\nlevel when the buffer was initialized,\nplus the final string on its top.\n\n\n\n\n\n<hr><h3><a name=\"luaL_buffinit\"><code>luaL_buffinit</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>-</em>]</span>\n<pre>void luaL_buffinit (lua_State *L, luaL_Buffer *B);</pre>\n\n<p>\nInitializes a buffer <code>B</code>.\nThis function does not allocate any space;\nthe buffer must be declared as a variable\n(see <a href=\"#luaL_Buffer\"><code>luaL_Buffer</code></a>).\n\n\n\n\n\n<hr><h3><a name=\"luaL_callmeta\"><code>luaL_callmeta</code></a></h3><p>\n<span class=\"apii\">[-0, +(0|1), <em>e</em>]</span>\n<pre>int luaL_callmeta (lua_State *L, int obj, const char *e);</pre>\n\n<p>\nCalls a metamethod.\n\n\n<p>\nIf the object at index <code>obj</code> has a metatable and this\nmetatable has a field <code>e</code>,\nthis function calls this field and passes the object as its only argument.\nIn this case this function returns 1 and pushes onto the\nstack the value returned by the call.\nIf there is no metatable or no metamethod,\nthis function returns 0 (without pushing any value on the stack).\n\n\n\n\n\n<hr><h3><a name=\"luaL_checkany\"><code>luaL_checkany</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>v</em>]</span>\n<pre>void luaL_checkany (lua_State *L, int narg);</pre>\n\n<p>\nChecks whether the function has an argument\nof any type (including <b>nil</b>) at position <code>narg</code>.\n\n\n\n\n\n<hr><h3><a name=\"luaL_checkint\"><code>luaL_checkint</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>v</em>]</span>\n<pre>int luaL_checkint (lua_State *L, int narg);</pre>\n\n<p>\nChecks whether the function argument <code>narg</code> is a number\nand returns this number cast to an <code>int</code>.\n\n\n\n\n\n<hr><h3><a name=\"luaL_checkinteger\"><code>luaL_checkinteger</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>v</em>]</span>\n<pre>lua_Integer luaL_checkinteger (lua_State *L, int narg);</pre>\n\n<p>\nChecks whether the function argument <code>narg</code> is a number\nand returns this number cast to a <a href=\"#lua_Integer\"><code>lua_Integer</code></a>.\n\n\n\n\n\n<hr><h3><a name=\"luaL_checklong\"><code>luaL_checklong</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>v</em>]</span>\n<pre>long luaL_checklong (lua_State *L, int narg);</pre>\n\n<p>\nChecks whether the function argument <code>narg</code> is a number\nand returns this number cast to a <code>long</code>.\n\n\n\n\n\n<hr><h3><a name=\"luaL_checklstring\"><code>luaL_checklstring</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>v</em>]</span>\n<pre>const char *luaL_checklstring (lua_State *L, int narg, size_t *l);</pre>\n\n<p>\nChecks whether the function argument <code>narg</code> is a string\nand returns this string;\nif <code>l</code> is not <code>NULL</code> fills <code>*l</code>\nwith the string's length.\n\n\n<p>\nThis function uses <a href=\"#lua_tolstring\"><code>lua_tolstring</code></a> to get its result,\nso all conversions and caveats of that function apply here.\n\n\n\n\n\n<hr><h3><a name=\"luaL_checknumber\"><code>luaL_checknumber</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>v</em>]</span>\n<pre>lua_Number luaL_checknumber (lua_State *L, int narg);</pre>\n\n<p>\nChecks whether the function argument <code>narg</code> is a number\nand returns this number.\n\n\n\n\n\n<hr><h3><a name=\"luaL_checkoption\"><code>luaL_checkoption</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>v</em>]</span>\n<pre>int luaL_checkoption (lua_State *L,\n                      int narg,\n                      const char *def,\n                      const char *const lst[]);</pre>\n\n<p>\nChecks whether the function argument <code>narg</code> is a string and\nsearches for this string in the array <code>lst</code>\n(which must be NULL-terminated).\nReturns the index in the array where the string was found.\nRaises an error if the argument is not a string or\nif the string cannot be found.\n\n\n<p>\nIf <code>def</code> is not <code>NULL</code>,\nthe function uses <code>def</code> as a default value when\nthere is no argument <code>narg</code> or if this argument is <b>nil</b>.\n\n\n<p>\nThis is a useful function for mapping strings to C&nbsp;enums.\n(The usual convention in Lua libraries is\nto use strings instead of numbers to select options.)\n\n\n\n\n\n<hr><h3><a name=\"luaL_checkstack\"><code>luaL_checkstack</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>v</em>]</span>\n<pre>void luaL_checkstack (lua_State *L, int sz, const char *msg);</pre>\n\n<p>\nGrows the stack size to <code>top + sz</code> elements,\nraising an error if the stack cannot grow to that size.\n<code>msg</code> is an additional text to go into the error message.\n\n\n\n\n\n<hr><h3><a name=\"luaL_checkstring\"><code>luaL_checkstring</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>v</em>]</span>\n<pre>const char *luaL_checkstring (lua_State *L, int narg);</pre>\n\n<p>\nChecks whether the function argument <code>narg</code> is a string\nand returns this string.\n\n\n<p>\nThis function uses <a href=\"#lua_tolstring\"><code>lua_tolstring</code></a> to get its result,\nso all conversions and caveats of that function apply here.\n\n\n\n\n\n<hr><h3><a name=\"luaL_checktype\"><code>luaL_checktype</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>v</em>]</span>\n<pre>void luaL_checktype (lua_State *L, int narg, int t);</pre>\n\n<p>\nChecks whether the function argument <code>narg</code> has type <code>t</code>.\nSee <a href=\"#lua_type\"><code>lua_type</code></a> for the encoding of types for <code>t</code>.\n\n\n\n\n\n<hr><h3><a name=\"luaL_checkudata\"><code>luaL_checkudata</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>v</em>]</span>\n<pre>void *luaL_checkudata (lua_State *L, int narg, const char *tname);</pre>\n\n<p>\nChecks whether the function argument <code>narg</code> is a userdata\nof the type <code>tname</code> (see <a href=\"#luaL_newmetatable\"><code>luaL_newmetatable</code></a>).\n\n\n\n\n\n<hr><h3><a name=\"luaL_dofile\"><code>luaL_dofile</code></a></h3><p>\n<span class=\"apii\">[-0, +?, <em>m</em>]</span>\n<pre>int luaL_dofile (lua_State *L, const char *filename);</pre>\n\n<p>\nLoads and runs the given file.\nIt is defined as the following macro:\n\n<pre>\n     (luaL_loadfile(L, filename) || lua_pcall(L, 0, LUA_MULTRET, 0))\n</pre><p>\nIt returns 0 if there are no errors\nor 1 in case of errors.\n\n\n\n\n\n<hr><h3><a name=\"luaL_dostring\"><code>luaL_dostring</code></a></h3><p>\n<span class=\"apii\">[-0, +?, <em>m</em>]</span>\n<pre>int luaL_dostring (lua_State *L, const char *str);</pre>\n\n<p>\nLoads and runs the given string.\nIt is defined as the following macro:\n\n<pre>\n     (luaL_loadstring(L, str) || lua_pcall(L, 0, LUA_MULTRET, 0))\n</pre><p>\nIt returns 0 if there are no errors\nor 1 in case of errors.\n\n\n\n\n\n<hr><h3><a name=\"luaL_error\"><code>luaL_error</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>v</em>]</span>\n<pre>int luaL_error (lua_State *L, const char *fmt, ...);</pre>\n\n<p>\nRaises an error.\nThe error message format is given by <code>fmt</code>\nplus any extra arguments,\nfollowing the same rules of <a href=\"#lua_pushfstring\"><code>lua_pushfstring</code></a>.\nIt also adds at the beginning of the message the file name and\nthe line number where the error occurred,\nif this information is available.\n\n\n<p>\nThis function never returns,\nbut it is an idiom to use it in C&nbsp;functions\nas <code>return luaL_error(<em>args</em>)</code>.\n\n\n\n\n\n<hr><h3><a name=\"luaL_getmetafield\"><code>luaL_getmetafield</code></a></h3><p>\n<span class=\"apii\">[-0, +(0|1), <em>m</em>]</span>\n<pre>int luaL_getmetafield (lua_State *L, int obj, const char *e);</pre>\n\n<p>\nPushes onto the stack the field <code>e</code> from the metatable\nof the object at index <code>obj</code>.\nIf the object does not have a metatable,\nor if the metatable does not have this field,\nreturns 0 and pushes nothing.\n\n\n\n\n\n<hr><h3><a name=\"luaL_getmetatable\"><code>luaL_getmetatable</code></a></h3><p>\n<span class=\"apii\">[-0, +1, <em>-</em>]</span>\n<pre>void luaL_getmetatable (lua_State *L, const char *tname);</pre>\n\n<p>\nPushes onto the stack the metatable associated with name <code>tname</code>\nin the registry (see <a href=\"#luaL_newmetatable\"><code>luaL_newmetatable</code></a>).\n\n\n\n\n\n<hr><h3><a name=\"luaL_gsub\"><code>luaL_gsub</code></a></h3><p>\n<span class=\"apii\">[-0, +1, <em>m</em>]</span>\n<pre>const char *luaL_gsub (lua_State *L,\n                       const char *s,\n                       const char *p,\n                       const char *r);</pre>\n\n<p>\nCreates a copy of string <code>s</code> by replacing\nany occurrence of the string <code>p</code>\nwith the string <code>r</code>.\nPushes the resulting string on the stack and returns it.\n\n\n\n\n\n<hr><h3><a name=\"luaL_loadbuffer\"><code>luaL_loadbuffer</code></a></h3><p>\n<span class=\"apii\">[-0, +1, <em>m</em>]</span>\n<pre>int luaL_loadbuffer (lua_State *L,\n                     const char *buff,\n                     size_t sz,\n                     const char *name);</pre>\n\n<p>\nLoads a buffer as a Lua chunk.\nThis function uses <a href=\"#lua_load\"><code>lua_load</code></a> to load the chunk in the\nbuffer pointed to by <code>buff</code> with size <code>sz</code>.\n\n\n<p>\nThis function returns the same results as <a href=\"#lua_load\"><code>lua_load</code></a>.\n<code>name</code> is the chunk name,\nused for debug information and error messages.\n\n\n\n\n\n<hr><h3><a name=\"luaL_loadfile\"><code>luaL_loadfile</code></a></h3><p>\n<span class=\"apii\">[-0, +1, <em>m</em>]</span>\n<pre>int luaL_loadfile (lua_State *L, const char *filename);</pre>\n\n<p>\nLoads a file as a Lua chunk.\nThis function uses <a href=\"#lua_load\"><code>lua_load</code></a> to load the chunk in the file\nnamed <code>filename</code>.\nIf <code>filename</code> is <code>NULL</code>,\nthen it loads from the standard input.\nThe first line in the file is ignored if it starts with a <code>#</code>.\n\n\n<p>\nThis function returns the same results as <a href=\"#lua_load\"><code>lua_load</code></a>,\nbut it has an extra error code <a name=\"pdf-LUA_ERRFILE\"><code>LUA_ERRFILE</code></a>\nif it cannot open/read the file.\n\n\n<p>\nAs <a href=\"#lua_load\"><code>lua_load</code></a>, this function only loads the chunk;\nit does not run it.\n\n\n\n\n\n<hr><h3><a name=\"luaL_loadstring\"><code>luaL_loadstring</code></a></h3><p>\n<span class=\"apii\">[-0, +1, <em>m</em>]</span>\n<pre>int luaL_loadstring (lua_State *L, const char *s);</pre>\n\n<p>\nLoads a string as a Lua chunk.\nThis function uses <a href=\"#lua_load\"><code>lua_load</code></a> to load the chunk in\nthe zero-terminated string <code>s</code>.\n\n\n<p>\nThis function returns the same results as <a href=\"#lua_load\"><code>lua_load</code></a>.\n\n\n<p>\nAlso as <a href=\"#lua_load\"><code>lua_load</code></a>, this function only loads the chunk;\nit does not run it.\n\n\n\n\n\n<hr><h3><a name=\"luaL_newmetatable\"><code>luaL_newmetatable</code></a></h3><p>\n<span class=\"apii\">[-0, +1, <em>m</em>]</span>\n<pre>int luaL_newmetatable (lua_State *L, const char *tname);</pre>\n\n<p>\nIf the registry already has the key <code>tname</code>,\nreturns 0.\nOtherwise,\ncreates a new table to be used as a metatable for userdata,\nadds it to the registry with key <code>tname</code>,\nand returns 1.\n\n\n<p>\nIn both cases pushes onto the stack the final value associated\nwith <code>tname</code> in the registry.\n\n\n\n\n\n<hr><h3><a name=\"luaL_newstate\"><code>luaL_newstate</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>-</em>]</span>\n<pre>lua_State *luaL_newstate (void);</pre>\n\n<p>\nCreates a new Lua state.\nIt calls <a href=\"#lua_newstate\"><code>lua_newstate</code></a> with an\nallocator based on the standard&nbsp;C <code>realloc</code> function\nand then sets a panic function (see <a href=\"#lua_atpanic\"><code>lua_atpanic</code></a>) that prints\nan error message to the standard error output in case of fatal\nerrors.\n\n\n<p>\nReturns the new state,\nor <code>NULL</code> if there is a memory allocation error.\n\n\n\n\n\n<hr><h3><a name=\"luaL_openlibs\"><code>luaL_openlibs</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>m</em>]</span>\n<pre>void luaL_openlibs (lua_State *L);</pre>\n\n<p>\nOpens all standard Lua libraries into the given state.\n\n\n\n\n\n<hr><h3><a name=\"luaL_optint\"><code>luaL_optint</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>v</em>]</span>\n<pre>int luaL_optint (lua_State *L, int narg, int d);</pre>\n\n<p>\nIf the function argument <code>narg</code> is a number,\nreturns this number cast to an <code>int</code>.\nIf this argument is absent or is <b>nil</b>,\nreturns <code>d</code>.\nOtherwise, raises an error.\n\n\n\n\n\n<hr><h3><a name=\"luaL_optinteger\"><code>luaL_optinteger</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>v</em>]</span>\n<pre>lua_Integer luaL_optinteger (lua_State *L,\n                             int narg,\n                             lua_Integer d);</pre>\n\n<p>\nIf the function argument <code>narg</code> is a number,\nreturns this number cast to a <a href=\"#lua_Integer\"><code>lua_Integer</code></a>.\nIf this argument is absent or is <b>nil</b>,\nreturns <code>d</code>.\nOtherwise, raises an error.\n\n\n\n\n\n<hr><h3><a name=\"luaL_optlong\"><code>luaL_optlong</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>v</em>]</span>\n<pre>long luaL_optlong (lua_State *L, int narg, long d);</pre>\n\n<p>\nIf the function argument <code>narg</code> is a number,\nreturns this number cast to a <code>long</code>.\nIf this argument is absent or is <b>nil</b>,\nreturns <code>d</code>.\nOtherwise, raises an error.\n\n\n\n\n\n<hr><h3><a name=\"luaL_optlstring\"><code>luaL_optlstring</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>v</em>]</span>\n<pre>const char *luaL_optlstring (lua_State *L,\n                             int narg,\n                             const char *d,\n                             size_t *l);</pre>\n\n<p>\nIf the function argument <code>narg</code> is a string,\nreturns this string.\nIf this argument is absent or is <b>nil</b>,\nreturns <code>d</code>.\nOtherwise, raises an error.\n\n\n<p>\nIf <code>l</code> is not <code>NULL</code>,\nfills the position <code>*l</code> with the results's length.\n\n\n\n\n\n<hr><h3><a name=\"luaL_optnumber\"><code>luaL_optnumber</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>v</em>]</span>\n<pre>lua_Number luaL_optnumber (lua_State *L, int narg, lua_Number d);</pre>\n\n<p>\nIf the function argument <code>narg</code> is a number,\nreturns this number.\nIf this argument is absent or is <b>nil</b>,\nreturns <code>d</code>.\nOtherwise, raises an error.\n\n\n\n\n\n<hr><h3><a name=\"luaL_optstring\"><code>luaL_optstring</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>v</em>]</span>\n<pre>const char *luaL_optstring (lua_State *L,\n                            int narg,\n                            const char *d);</pre>\n\n<p>\nIf the function argument <code>narg</code> is a string,\nreturns this string.\nIf this argument is absent or is <b>nil</b>,\nreturns <code>d</code>.\nOtherwise, raises an error.\n\n\n\n\n\n<hr><h3><a name=\"luaL_prepbuffer\"><code>luaL_prepbuffer</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>-</em>]</span>\n<pre>char *luaL_prepbuffer (luaL_Buffer *B);</pre>\n\n<p>\nReturns an address to a space of size <a name=\"pdf-LUAL_BUFFERSIZE\"><code>LUAL_BUFFERSIZE</code></a>\nwhere you can copy a string to be added to buffer <code>B</code>\n(see <a href=\"#luaL_Buffer\"><code>luaL_Buffer</code></a>).\nAfter copying the string into this space you must call\n<a href=\"#luaL_addsize\"><code>luaL_addsize</code></a> with the size of the string to actually add \nit to the buffer.\n\n\n\n\n\n<hr><h3><a name=\"luaL_pushresult\"><code>luaL_pushresult</code></a></h3><p>\n<span class=\"apii\">[-?, +1, <em>m</em>]</span>\n<pre>void luaL_pushresult (luaL_Buffer *B);</pre>\n\n<p>\nFinishes the use of buffer <code>B</code> leaving the final string on\nthe top of the stack.\n\n\n\n\n\n<hr><h3><a name=\"luaL_ref\"><code>luaL_ref</code></a></h3><p>\n<span class=\"apii\">[-1, +0, <em>m</em>]</span>\n<pre>int luaL_ref (lua_State *L, int t);</pre>\n\n<p>\nCreates and returns a <em>reference</em>,\nin the table at index <code>t</code>,\nfor the object at the top of the stack (and pops the object).\n\n\n<p>\nA reference is a unique integer key.\nAs long as you do not manually add integer keys into table <code>t</code>,\n<a href=\"#luaL_ref\"><code>luaL_ref</code></a> ensures the uniqueness of the key it returns.\nYou can retrieve an object referred by reference <code>r</code>\nby calling <code>lua_rawgeti(L, t, r)</code>.\nFunction <a href=\"#luaL_unref\"><code>luaL_unref</code></a> frees a reference and its associated object.\n\n\n<p>\nIf the object at the top of the stack is <b>nil</b>,\n<a href=\"#luaL_ref\"><code>luaL_ref</code></a> returns the constant <a name=\"pdf-LUA_REFNIL\"><code>LUA_REFNIL</code></a>.\nThe constant <a name=\"pdf-LUA_NOREF\"><code>LUA_NOREF</code></a> is guaranteed to be different\nfrom any reference returned by <a href=\"#luaL_ref\"><code>luaL_ref</code></a>.\n\n\n\n\n\n<hr><h3><a name=\"luaL_Reg\"><code>luaL_Reg</code></a></h3>\n<pre>typedef struct luaL_Reg {\n  const char *name;\n  lua_CFunction func;\n} luaL_Reg;</pre>\n\n<p>\nType for arrays of functions to be registered by\n<a href=\"#luaL_register\"><code>luaL_register</code></a>.\n<code>name</code> is the function name and <code>func</code> is a pointer to\nthe function.\nAny array of <a href=\"#luaL_Reg\"><code>luaL_Reg</code></a> must end with an sentinel entry\nin which both <code>name</code> and <code>func</code> are <code>NULL</code>.\n\n\n\n\n\n<hr><h3><a name=\"luaL_register\"><code>luaL_register</code></a></h3><p>\n<span class=\"apii\">[-(0|1), +1, <em>m</em>]</span>\n<pre>void luaL_register (lua_State *L,\n                    const char *libname,\n                    const luaL_Reg *l);</pre>\n\n<p>\nOpens a library.\n\n\n<p>\nWhen called with <code>libname</code> equal to <code>NULL</code>,\nit simply registers all functions in the list <code>l</code>\n(see <a href=\"#luaL_Reg\"><code>luaL_Reg</code></a>) into the table on the top of the stack.\n\n\n<p>\nWhen called with a non-null <code>libname</code>,\n<code>luaL_register</code> creates a new table <code>t</code>,\nsets it as the value of the global variable <code>libname</code>,\nsets it as the value of <code>package.loaded[libname]</code>,\nand registers on it all functions in the list <code>l</code>.\nIf there is a table in <code>package.loaded[libname]</code> or in\nvariable <code>libname</code>,\nreuses this table instead of creating a new one.\n\n\n<p>\nIn any case the function leaves the table\non the top of the stack.\n\n\n\n\n\n<hr><h3><a name=\"luaL_typename\"><code>luaL_typename</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>-</em>]</span>\n<pre>const char *luaL_typename (lua_State *L, int index);</pre>\n\n<p>\nReturns the name of the type of the value at the given index.\n\n\n\n\n\n<hr><h3><a name=\"luaL_typerror\"><code>luaL_typerror</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>v</em>]</span>\n<pre>int luaL_typerror (lua_State *L, int narg, const char *tname);</pre>\n\n<p>\nGenerates an error with a message like the following:\n\n<pre>\n     <em>location</em>: bad argument <em>narg</em> to '<em>func</em>' (<em>tname</em> expected, got <em>rt</em>)\n</pre><p>\nwhere <code><em>location</em></code> is produced by <a href=\"#luaL_where\"><code>luaL_where</code></a>,\n<code><em>func</em></code> is the name of the current function,\nand <code><em>rt</em></code> is the type name of the actual argument.\n\n\n\n\n\n<hr><h3><a name=\"luaL_unref\"><code>luaL_unref</code></a></h3><p>\n<span class=\"apii\">[-0, +0, <em>-</em>]</span>\n<pre>void luaL_unref (lua_State *L, int t, int ref);</pre>\n\n<p>\nReleases reference <code>ref</code> from the table at index <code>t</code>\n(see <a href=\"#luaL_ref\"><code>luaL_ref</code></a>).\nThe entry is removed from the table,\nso that the referred object can be collected.\nThe reference <code>ref</code> is also freed to be used again.\n\n\n<p>\nIf <code>ref</code> is <a href=\"#pdf-LUA_NOREF\"><code>LUA_NOREF</code></a> or <a href=\"#pdf-LUA_REFNIL\"><code>LUA_REFNIL</code></a>,\n<a href=\"#luaL_unref\"><code>luaL_unref</code></a> does nothing.\n\n\n\n\n\n<hr><h3><a name=\"luaL_where\"><code>luaL_where</code></a></h3><p>\n<span class=\"apii\">[-0, +1, <em>m</em>]</span>\n<pre>void luaL_where (lua_State *L, int lvl);</pre>\n\n<p>\nPushes onto the stack a string identifying the current position\nof the control at level <code>lvl</code> in the call stack.\nTypically this string has the following format:\n\n<pre>\n     <em>chunkname</em>:<em>currentline</em>:\n</pre><p>\nLevel&nbsp;0 is the running function,\nlevel&nbsp;1 is the function that called the running function,\netc.\n\n\n<p>\nThis function is used to build a prefix for error messages.\n\n\n\n\n\n\n\n<h1>5 - <a name=\"5\">Standard Libraries</a></h1>\n\n<p>\nThe standard Lua libraries provide useful functions\nthat are implemented directly through the C&nbsp;API.\nSome of these functions provide essential services to the language\n(e.g., <a href=\"#pdf-type\"><code>type</code></a> and <a href=\"#pdf-getmetatable\"><code>getmetatable</code></a>);\nothers provide access to \"outside\" services (e.g., I/O);\nand others could be implemented in Lua itself,\nbut are quite useful or have critical performance requirements that\ndeserve an implementation in C (e.g., <a href=\"#pdf-table.sort\"><code>table.sort</code></a>).\n\n\n<p>\nAll libraries are implemented through the official C&nbsp;API\nand are provided as separate C&nbsp;modules.\nCurrently, Lua has the following standard libraries:\n\n<ul>\n\n<li>basic library, which includes the coroutine sub-library;</li>\n\n<li>package library;</li>\n\n<li>string manipulation;</li>\n\n<li>table manipulation;</li>\n\n<li>mathematical functions (sin, log, etc.);</li>\n\n<li>input and output;</li>\n\n<li>operating system facilities;</li>\n\n<li>debug facilities.</li>\n\n</ul><p>\nExcept for the basic and package libraries,\neach library provides all its functions as fields of a global table\nor as methods of its objects.\n\n\n<p>\nTo have access to these libraries,\nthe C&nbsp;host program should call the <a href=\"#luaL_openlibs\"><code>luaL_openlibs</code></a> function,\nwhich opens all standard libraries.\nAlternatively,\nit can open them individually by calling\n<a name=\"pdf-luaopen_base\"><code>luaopen_base</code></a> (for the basic library),\n<a name=\"pdf-luaopen_package\"><code>luaopen_package</code></a> (for the package library),\n<a name=\"pdf-luaopen_string\"><code>luaopen_string</code></a> (for the string library),\n<a name=\"pdf-luaopen_table\"><code>luaopen_table</code></a> (for the table library),\n<a name=\"pdf-luaopen_math\"><code>luaopen_math</code></a> (for the mathematical library),\n<a name=\"pdf-luaopen_io\"><code>luaopen_io</code></a> (for the I/O library),\n<a name=\"pdf-luaopen_os\"><code>luaopen_os</code></a> (for the Operating System library),\nand <a name=\"pdf-luaopen_debug\"><code>luaopen_debug</code></a> (for the debug library).\nThese functions are declared in <a name=\"pdf-lualib.h\"><code>lualib.h</code></a>\nand should not be called directly:\nyou must call them like any other Lua C&nbsp;function,\ne.g., by using <a href=\"#lua_call\"><code>lua_call</code></a>.\n\n\n\n<h2>5.1 - <a name=\"5.1\">Basic Functions</a></h2>\n\n<p>\nThe basic library provides some core functions to Lua.\nIf you do not include this library in your application,\nyou should check carefully whether you need to provide \nimplementations for some of its facilities.\n\n\n<p>\n<hr><h3><a name=\"pdf-assert\"><code>assert (v [, message])</code></a></h3>\nIssues an  error when\nthe value of its argument <code>v</code> is false (i.e., <b>nil</b> or <b>false</b>);\notherwise, returns all its arguments.\n<code>message</code> is an error message;\nwhen absent, it defaults to \"assertion failed!\"\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-collectgarbage\"><code>collectgarbage ([opt [, arg]])</code></a></h3>\n\n\n<p>\nThis function is a generic interface to the garbage collector.\nIt performs different functions according to its first argument, <code>opt</code>:\n\n<ul>\n\n<li><b>\"collect\":</b>\nperforms a full garbage-collection cycle.\nThis is the default option.\n</li>\n\n<li><b>\"stop\":</b>\nstops the garbage collector.\n</li>\n\n<li><b>\"restart\":</b>\nrestarts the garbage collector.\n</li>\n\n<li><b>\"count\":</b>\nreturns the total memory in use by Lua (in Kbytes).\n</li>\n\n<li><b>\"step\":</b>\nperforms a garbage-collection step.\nThe step \"size\" is controlled by <code>arg</code>\n(larger values mean more steps) in a non-specified way.\nIf you want to control the step size\nyou must experimentally tune the value of <code>arg</code>.\nReturns <b>true</b> if the step finished a collection cycle.\n</li>\n\n<li><b>\"setpause\":</b>\nsets <code>arg</code> as the new value for the <em>pause</em> of\nthe collector (see <a href=\"#2.10\">&sect;2.10</a>).\nReturns the previous value for <em>pause</em>.\n</li>\n\n<li><b>\"setstepmul\":</b>\nsets <code>arg</code> as the new value for the <em>step multiplier</em> of\nthe collector (see <a href=\"#2.10\">&sect;2.10</a>).\nReturns the previous value for <em>step</em>.\n</li>\n\n</ul>\n\n\n\n<p>\n<hr><h3><a name=\"pdf-dofile\"><code>dofile ([filename])</code></a></h3>\nOpens the named file and executes its contents as a Lua chunk.\nWhen called without arguments,\n<code>dofile</code> executes the contents of the standard input (<code>stdin</code>).\nReturns all values returned by the chunk.\nIn case of errors, <code>dofile</code> propagates the error\nto its caller (that is, <code>dofile</code> does not run in protected mode).\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-error\"><code>error (message [, level])</code></a></h3>\nTerminates the last protected function called\nand returns <code>message</code> as the error message.\nFunction <code>error</code> never returns.\n\n\n<p>\nUsually, <code>error</code> adds some information about the error position\nat the beginning of the message.\nThe <code>level</code> argument specifies how to get the error position.\nWith level&nbsp;1 (the default), the error position is where the\n<code>error</code> function was called.\nLevel&nbsp;2 points the error to where the function\nthat called <code>error</code> was called; and so on.\nPassing a level&nbsp;0 avoids the addition of error position information\nto the message.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-_G\"><code>_G</code></a></h3>\nA global variable (not a function) that\nholds the global environment (that is, <code>_G._G = _G</code>).\nLua itself does not use this variable;\nchanging its value does not affect any environment,\nnor vice-versa.\n(Use <a href=\"#pdf-setfenv\"><code>setfenv</code></a> to change environments.)\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-getfenv\"><code>getfenv ([f])</code></a></h3>\nReturns the current environment in use by the function.\n<code>f</code> can be a Lua function or a number\nthat specifies the function at that stack level:\nLevel&nbsp;1 is the function calling <code>getfenv</code>.\nIf the given function is not a Lua function,\nor if <code>f</code> is 0,\n<code>getfenv</code> returns the global environment.\nThe default for <code>f</code> is 1.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-getmetatable\"><code>getmetatable (object)</code></a></h3>\n\n\n<p>\nIf <code>object</code> does not have a metatable, returns <b>nil</b>.\nOtherwise,\nif the object's metatable has a <code>\"__metatable\"</code> field,\nreturns the associated value.\nOtherwise, returns the metatable of the given object.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-ipairs\"><code>ipairs (t)</code></a></h3>\n\n\n<p>\nReturns three values: an iterator function, the table <code>t</code>, and 0,\nso that the construction\n\n<pre>\n     for i,v in ipairs(t) do <em>body</em> end\n</pre><p>\nwill iterate over the pairs (<code>1,t[1]</code>), (<code>2,t[2]</code>), &middot;&middot;&middot;,\nup to the first integer key absent from the table.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-load\"><code>load (func [, chunkname])</code></a></h3>\n\n\n<p>\nLoads a chunk using function <code>func</code> to get its pieces.\nEach call to <code>func</code> must return a string that concatenates\nwith previous results.\nA return of an empty string, <b>nil</b>, or no value signals the end of the chunk.\n\n\n<p>\nIf there are no errors, \nreturns the compiled chunk as a function;\notherwise, returns <b>nil</b> plus the error message.\nThe environment of the returned function is the global environment.\n\n\n<p>\n<code>chunkname</code> is used as the chunk name for error messages\nand debug information.\nWhen absent,\nit defaults to \"<code>=(load)</code>\".\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-loadfile\"><code>loadfile ([filename])</code></a></h3>\n\n\n<p>\nSimilar to <a href=\"#pdf-load\"><code>load</code></a>,\nbut gets the chunk from file <code>filename</code>\nor from the standard input,\nif no file name is given.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-loadstring\"><code>loadstring (string [, chunkname])</code></a></h3>\n\n\n<p>\nSimilar to <a href=\"#pdf-load\"><code>load</code></a>,\nbut gets the chunk from the given string.\n\n\n<p>\nTo load and run a given string, use the idiom\n\n<pre>\n     assert(loadstring(s))()\n</pre>\n\n<p>\nWhen absent,\n<code>chunkname</code> defaults to the given string.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-next\"><code>next (table [, index])</code></a></h3>\n\n\n<p>\nAllows a program to traverse all fields of a table.\nIts first argument is a table and its second argument\nis an index in this table.\n<code>next</code> returns the next index of the table\nand its associated value.\nWhen called with <b>nil</b> as its second argument,\n<code>next</code> returns an initial index\nand its associated value.\nWhen called with the last index,\nor with <b>nil</b> in an empty table,\n<code>next</code> returns <b>nil</b>.\nIf the second argument is absent, then it is interpreted as <b>nil</b>.\nIn particular,\nyou can use <code>next(t)</code> to check whether a table is empty.\n\n\n<p>\nThe order in which the indices are enumerated is not specified,\n<em>even for numeric indices</em>.\n(To traverse a table in numeric order,\nuse a numerical <b>for</b> or the <a href=\"#pdf-ipairs\"><code>ipairs</code></a> function.)\n\n\n<p>\nThe behavior of <code>next</code> is <em>undefined</em> if,\nduring the traversal,\nyou assign any value to a non-existent field in the table.\nYou may however modify existing fields.\nIn particular, you may clear existing fields.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-pairs\"><code>pairs (t)</code></a></h3>\n\n\n<p>\nReturns three values: the <a href=\"#pdf-next\"><code>next</code></a> function, the table <code>t</code>, and <b>nil</b>,\nso that the construction\n\n<pre>\n     for k,v in pairs(t) do <em>body</em> end\n</pre><p>\nwill iterate over all key&ndash;value pairs of table <code>t</code>.\n\n\n<p>\nSee function <a href=\"#pdf-next\"><code>next</code></a> for the caveats of modifying\nthe table during its traversal.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-pcall\"><code>pcall (f, arg1, &middot;&middot;&middot;)</code></a></h3>\n\n\n<p>\nCalls function <code>f</code> with\nthe given arguments in <em>protected mode</em>.\nThis means that any error inside&nbsp;<code>f</code> is not propagated;\ninstead, <code>pcall</code> catches the error\nand returns a status code.\nIts first result is the status code (a boolean),\nwhich is true if the call succeeds without errors.\nIn such case, <code>pcall</code> also returns all results from the call,\nafter this first result.\nIn case of any error, <code>pcall</code> returns <b>false</b> plus the error message.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-print\"><code>print (&middot;&middot;&middot;)</code></a></h3>\nReceives any number of arguments,\nand prints their values to <code>stdout</code>,\nusing the <a href=\"#pdf-tostring\"><code>tostring</code></a> function to convert them to strings.\n<code>print</code> is not intended for formatted output,\nbut only as a quick way to show a value,\ntypically for debugging.\nFor formatted output, use <a href=\"#pdf-string.format\"><code>string.format</code></a>.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-rawequal\"><code>rawequal (v1, v2)</code></a></h3>\nChecks whether <code>v1</code> is equal to <code>v2</code>,\nwithout invoking any metamethod.\nReturns a boolean.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-rawget\"><code>rawget (table, index)</code></a></h3>\nGets the real value of <code>table[index]</code>,\nwithout invoking any metamethod.\n<code>table</code> must be a table;\n<code>index</code> may be any value.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-rawset\"><code>rawset (table, index, value)</code></a></h3>\nSets the real value of <code>table[index]</code> to <code>value</code>,\nwithout invoking any metamethod.\n<code>table</code> must be a table,\n<code>index</code> any value different from <b>nil</b>,\nand <code>value</code> any Lua value.\n\n\n<p>\nThis function returns <code>table</code>.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-select\"><code>select (index, &middot;&middot;&middot;)</code></a></h3>\n\n\n<p>\nIf <code>index</code> is a number,\nreturns all arguments after argument number <code>index</code>.\nOtherwise, <code>index</code> must be the string <code>\"#\"</code>,\nand <code>select</code> returns the total number of extra arguments it received.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-setfenv\"><code>setfenv (f, table)</code></a></h3>\n\n\n<p>\nSets the environment to be used by the given function.\n<code>f</code> can be a Lua function or a number\nthat specifies the function at that stack level:\nLevel&nbsp;1 is the function calling <code>setfenv</code>.\n<code>setfenv</code> returns the given function.\n\n\n<p>\nAs a special case, when <code>f</code> is 0 <code>setfenv</code> changes\nthe environment of the running thread.\nIn this case, <code>setfenv</code> returns no values.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-setmetatable\"><code>setmetatable (table, metatable)</code></a></h3>\n\n\n<p>\nSets the metatable for the given table.\n(You cannot change the metatable of other types from Lua, only from&nbsp;C.)\nIf <code>metatable</code> is <b>nil</b>,\nremoves the metatable of the given table.\nIf the original metatable has a <code>\"__metatable\"</code> field,\nraises an error.\n\n\n<p>\nThis function returns <code>table</code>.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-tonumber\"><code>tonumber (e [, base])</code></a></h3>\nTries to convert its argument to a number.\nIf the argument is already a number or a string convertible\nto a number, then <code>tonumber</code> returns this number;\notherwise, it returns <b>nil</b>.\n\n\n<p>\nAn optional argument specifies the base to interpret the numeral.\nThe base may be any integer between 2 and 36, inclusive.\nIn bases above&nbsp;10, the letter '<code>A</code>' (in either upper or lower case)\nrepresents&nbsp;10, '<code>B</code>' represents&nbsp;11, and so forth,\nwith '<code>Z</code>' representing 35.\nIn base 10 (the default), the number can have a decimal part,\nas well as an optional exponent part (see <a href=\"#2.1\">&sect;2.1</a>).\nIn other bases, only unsigned integers are accepted.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-tostring\"><code>tostring (e)</code></a></h3>\nReceives an argument of any type and\nconverts it to a string in a reasonable format.\nFor complete control of how numbers are converted,\nuse <a href=\"#pdf-string.format\"><code>string.format</code></a>.\n\n\n<p>\nIf the metatable of <code>e</code> has a <code>\"__tostring\"</code> field,\nthen <code>tostring</code> calls the corresponding value\nwith <code>e</code> as argument,\nand uses the result of the call as its result.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-type\"><code>type (v)</code></a></h3>\nReturns the type of its only argument, coded as a string.\nThe possible results of this function are\n\"<code>nil</code>\" (a string, not the value <b>nil</b>),\n\"<code>number</code>\",\n\"<code>string</code>\",\n\"<code>boolean</code>\",\n\"<code>table</code>\",\n\"<code>function</code>\",\n\"<code>thread</code>\",\nand \"<code>userdata</code>\".\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-unpack\"><code>unpack (list [, i [, j]])</code></a></h3>\nReturns the elements from the given table.\nThis function is equivalent to\n\n<pre>\n     return list[i], list[i+1], &middot;&middot;&middot;, list[j]\n</pre><p>\nexcept that the above code can be written only for a fixed number\nof elements.\nBy default, <code>i</code> is&nbsp;1 and <code>j</code> is the length of the list,\nas defined by the length operator (see <a href=\"#2.5.5\">&sect;2.5.5</a>).\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-_VERSION\"><code>_VERSION</code></a></h3>\nA global variable (not a function) that\nholds a string containing the current interpreter version.\nThe current contents of this variable is \"<code>Lua 5.1</code>\".\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-xpcall\"><code>xpcall (f, err)</code></a></h3>\n\n\n<p>\nThis function is similar to <a href=\"#pdf-pcall\"><code>pcall</code></a>,\nexcept that you can set a new error handler.\n\n\n<p>\n<code>xpcall</code> calls function <code>f</code> in protected mode,\nusing <code>err</code> as the error handler.\nAny error inside <code>f</code> is not propagated;\ninstead, <code>xpcall</code> catches the error,\ncalls the <code>err</code> function with the original error object,\nand returns a status code.\nIts first result is the status code (a boolean),\nwhich is true if the call succeeds without errors.\nIn this case, <code>xpcall</code> also returns all results from the call,\nafter this first result.\nIn case of any error,\n<code>xpcall</code> returns <b>false</b> plus the result from <code>err</code>.\n\n\n\n\n\n\n\n<h2>5.2 - <a name=\"5.2\">Coroutine Manipulation</a></h2>\n\n<p>\nThe operations related to coroutines comprise a sub-library of\nthe basic library and come inside the table <a name=\"pdf-coroutine\"><code>coroutine</code></a>.\nSee <a href=\"#2.11\">&sect;2.11</a> for a general description of coroutines.\n\n\n<p>\n<hr><h3><a name=\"pdf-coroutine.create\"><code>coroutine.create (f)</code></a></h3>\n\n\n<p>\nCreates a new coroutine, with body <code>f</code>.\n<code>f</code> must be a Lua function.\nReturns this new coroutine,\nan object with type <code>\"thread\"</code>.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-coroutine.resume\"><code>coroutine.resume (co [, val1, &middot;&middot;&middot;])</code></a></h3>\n\n\n<p>\nStarts or continues the execution of coroutine <code>co</code>.\nThe first time you resume a coroutine,\nit starts running its body.\nThe values <code>val1</code>, &middot;&middot;&middot; are passed\nas the arguments to the body function.\nIf the coroutine has yielded,\n<code>resume</code> restarts it;\nthe values <code>val1</code>, &middot;&middot;&middot; are passed\nas the results from the yield.\n\n\n<p>\nIf the coroutine runs without any errors,\n<code>resume</code> returns <b>true</b> plus any values passed to <code>yield</code>\n(if the coroutine yields) or any values returned by the body function\n(if the coroutine terminates).\nIf there is any error,\n<code>resume</code> returns <b>false</b> plus the error message.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-coroutine.running\"><code>coroutine.running ()</code></a></h3>\n\n\n<p>\nReturns the running coroutine,\nor <b>nil</b> when called by the main thread.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-coroutine.status\"><code>coroutine.status (co)</code></a></h3>\n\n\n<p>\nReturns the status of coroutine <code>co</code>, as a string:\n<code>\"running\"</code>,\nif the coroutine is running (that is, it called <code>status</code>);\n<code>\"suspended\"</code>, if the coroutine is suspended in a call to <code>yield</code>,\nor if it has not started running yet;\n<code>\"normal\"</code> if the coroutine is active but not running\n(that is, it has resumed another coroutine);\nand <code>\"dead\"</code> if the coroutine has finished its body function,\nor if it has stopped with an error.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-coroutine.wrap\"><code>coroutine.wrap (f)</code></a></h3>\n\n\n<p>\nCreates a new coroutine, with body <code>f</code>.\n<code>f</code> must be a Lua function.\nReturns a function that resumes the coroutine each time it is called.\nAny arguments passed to the function behave as the\nextra arguments to <code>resume</code>.\nReturns the same values returned by <code>resume</code>,\nexcept the first boolean.\nIn case of error, propagates the error.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-coroutine.yield\"><code>coroutine.yield (&middot;&middot;&middot;)</code></a></h3>\n\n\n<p>\nSuspends the execution of the calling coroutine.\nThe coroutine cannot be running a C&nbsp;function,\na metamethod, or an iterator.\nAny arguments to <code>yield</code> are passed as extra results to <code>resume</code>.\n\n\n\n\n\n\n\n<h2>5.3 - <a name=\"5.3\">Modules</a></h2>\n\n<p>\nThe package library provides basic\nfacilities for loading and building modules in Lua.\nIt exports two of its functions directly in the global environment:\n<a href=\"#pdf-require\"><code>require</code></a> and <a href=\"#pdf-module\"><code>module</code></a>.\nEverything else is exported in a table <a name=\"pdf-package\"><code>package</code></a>.\n\n\n<p>\n<hr><h3><a name=\"pdf-module\"><code>module (name [, &middot;&middot;&middot;])</code></a></h3>\n\n\n<p>\nCreates a module.\nIf there is a table in <code>package.loaded[name]</code>,\nthis table is the module.\nOtherwise, if there is a global table <code>t</code> with the given name,\nthis table is the module.\nOtherwise creates a new table <code>t</code> and\nsets it as the value of the global <code>name</code> and\nthe value of <code>package.loaded[name]</code>.\nThis function also initializes <code>t._NAME</code> with the given name,\n<code>t._M</code> with the module (<code>t</code> itself),\nand <code>t._PACKAGE</code> with the package name\n(the full module name minus last component; see below).\nFinally, <code>module</code> sets <code>t</code> as the new environment\nof the current function and the new value of <code>package.loaded[name]</code>,\nso that <a href=\"#pdf-require\"><code>require</code></a> returns <code>t</code>.\n\n\n<p>\nIf <code>name</code> is a compound name\n(that is, one with components separated by dots),\n<code>module</code> creates (or reuses, if they already exist)\ntables for each component.\nFor instance, if <code>name</code> is <code>a.b.c</code>,\nthen <code>module</code> stores the module table in field <code>c</code> of\nfield <code>b</code> of global <code>a</code>.\n\n\n<p>\nThis function can receive optional <em>options</em> after\nthe module name,\nwhere each option is a function to be applied over the module.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-require\"><code>require (modname)</code></a></h3>\n\n\n<p>\nLoads the given module.\nThe function starts by looking into the <a href=\"#pdf-package.loaded\"><code>package.loaded</code></a> table\nto determine whether <code>modname</code> is already loaded.\nIf it is, then <code>require</code> returns the value stored\nat <code>package.loaded[modname]</code>.\nOtherwise, it tries to find a <em>loader</em> for the module.\n\n\n<p>\nTo find a loader,\n<code>require</code> is guided by the <a href=\"#pdf-package.loaders\"><code>package.loaders</code></a> array.\nBy changing this array,\nwe can change how <code>require</code> looks for a module.\nThe following explanation is based on the default configuration\nfor <a href=\"#pdf-package.loaders\"><code>package.loaders</code></a>.\n\n\n<p>\nFirst <code>require</code> queries <code>package.preload[modname]</code>.\nIf it has a value,\nthis value (which should be a function) is the loader.\nOtherwise <code>require</code> searches for a Lua loader using the\npath stored in <a href=\"#pdf-package.path\"><code>package.path</code></a>.\nIf that also fails, it searches for a C&nbsp;loader using the\npath stored in <a href=\"#pdf-package.cpath\"><code>package.cpath</code></a>.\nIf that also fails,\nit tries an <em>all-in-one</em> loader (see <a href=\"#pdf-package.loaders\"><code>package.loaders</code></a>).\n\n\n<p>\nOnce a loader is found,\n<code>require</code> calls the loader with a single argument, <code>modname</code>.\nIf the loader returns any value,\n<code>require</code> assigns the returned value to <code>package.loaded[modname]</code>.\nIf the loader returns no value and\nhas not assigned any value to <code>package.loaded[modname]</code>,\nthen <code>require</code> assigns <b>true</b> to this entry.\nIn any case, <code>require</code> returns the\nfinal value of <code>package.loaded[modname]</code>.\n\n\n<p>\nIf there is any error loading or running the module,\nor if it cannot find any loader for the module,\nthen <code>require</code> signals an error. \n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-package.cpath\"><code>package.cpath</code></a></h3>\n\n\n<p>\nThe path used by <a href=\"#pdf-require\"><code>require</code></a> to search for a C&nbsp;loader.\n\n\n<p>\nLua initializes the C&nbsp;path <a href=\"#pdf-package.cpath\"><code>package.cpath</code></a> in the same way\nit initializes the Lua path <a href=\"#pdf-package.path\"><code>package.path</code></a>,\nusing the environment variable <a name=\"pdf-LUA_CPATH\"><code>LUA_CPATH</code></a>\nor a default path defined in <code>luaconf.h</code>.\n\n\n\n\n<p>\n\n<hr><h3><a name=\"pdf-package.loaded\"><code>package.loaded</code></a></h3>\n\n\n<p>\nA table used by <a href=\"#pdf-require\"><code>require</code></a> to control which\nmodules are already loaded.\nWhen you require a module <code>modname</code> and\n<code>package.loaded[modname]</code> is not false,\n<a href=\"#pdf-require\"><code>require</code></a> simply returns the value stored there.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-package.loaders\"><code>package.loaders</code></a></h3>\n\n\n<p>\nA table used by <a href=\"#pdf-require\"><code>require</code></a> to control how to load modules.\n\n\n<p>\nEach entry in this table is a <em>searcher function</em>.\nWhen looking for a module,\n<a href=\"#pdf-require\"><code>require</code></a> calls each of these searchers in ascending order,\nwith the module name (the argument given to <a href=\"#pdf-require\"><code>require</code></a>) as its\nsole parameter.\nThe function can return another function (the module <em>loader</em>)\nor a string explaining why it did not find that module\n(or <b>nil</b> if it has nothing to say).\nLua initializes this table with four functions.\n\n\n<p>\nThe first searcher simply looks for a loader in the\n<a href=\"#pdf-package.preload\"><code>package.preload</code></a> table.\n\n\n<p>\nThe second searcher looks for a loader as a Lua library,\nusing the path stored at <a href=\"#pdf-package.path\"><code>package.path</code></a>.\nA path is a sequence of <em>templates</em> separated by semicolons.\nFor each template,\nthe searcher will change each interrogation\nmark in the template by <code>filename</code>,\nwhich is the module name with each dot replaced by a\n\"directory separator\" (such as \"<code>/</code>\" in Unix);\nthen it will try to open the resulting file name.\nSo, for instance, if the Lua path is the string\n\n<pre>\n     \"./?.lua;./?.lc;/usr/local/?/init.lua\"\n</pre><p>\nthe search for a Lua file for module <code>foo</code>\nwill try to open the files\n<code>./foo.lua</code>, <code>./foo.lc</code>, and\n<code>/usr/local/foo/init.lua</code>, in that order.\n\n\n<p>\nThe third searcher looks for a loader as a C&nbsp;library,\nusing the path given by the variable <a href=\"#pdf-package.cpath\"><code>package.cpath</code></a>.\nFor instance,\nif the C&nbsp;path is the string\n\n<pre>\n     \"./?.so;./?.dll;/usr/local/?/init.so\"\n</pre><p>\nthe searcher for module <code>foo</code>\nwill try to open the files <code>./foo.so</code>, <code>./foo.dll</code>,\nand <code>/usr/local/foo/init.so</code>, in that order.\nOnce it finds a C&nbsp;library,\nthis searcher first uses a dynamic link facility to link the\napplication with the library.\nThen it tries to find a C&nbsp;function inside the library to\nbe used as the loader.\nThe name of this C&nbsp;function is the string \"<code>luaopen_</code>\"\nconcatenated with a copy of the module name where each dot\nis replaced by an underscore.\nMoreover, if the module name has a hyphen,\nits prefix up to (and including) the first hyphen is removed.\nFor instance, if the module name is <code>a.v1-b.c</code>,\nthe function name will be <code>luaopen_b_c</code>.\n\n\n<p>\nThe fourth searcher tries an <em>all-in-one loader</em>.\nIt searches the C&nbsp;path for a library for\nthe root name of the given module.\nFor instance, when requiring <code>a.b.c</code>,\nit will search for a C&nbsp;library for <code>a</code>.\nIf found, it looks into it for an open function for\nthe submodule;\nin our example, that would be <code>luaopen_a_b_c</code>.\nWith this facility, a package can pack several C&nbsp;submodules\ninto one single library,\nwith each submodule keeping its original open function.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-package.loadlib\"><code>package.loadlib (libname, funcname)</code></a></h3>\n\n\n<p>\nDynamically links the host program with the C&nbsp;library <code>libname</code>.\nInside this library, looks for a function <code>funcname</code>\nand returns this function as a C&nbsp;function.\n(So, <code>funcname</code> must follow the protocol (see <a href=\"#lua_CFunction\"><code>lua_CFunction</code></a>)).\n\n\n<p>\nThis is a low-level function.\nIt completely bypasses the package and module system.\nUnlike <a href=\"#pdf-require\"><code>require</code></a>,\nit does not perform any path searching and\ndoes not automatically adds extensions.\n<code>libname</code> must be the complete file name of the C&nbsp;library,\nincluding if necessary a path and extension.\n<code>funcname</code> must be the exact name exported by the C&nbsp;library\n(which may depend on the C&nbsp;compiler and linker used).\n\n\n<p>\nThis function is not supported by ANSI C.\nAs such, it is only available on some platforms\n(Windows, Linux, Mac OS X, Solaris, BSD,\nplus other Unix systems that support the <code>dlfcn</code> standard).\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-package.path\"><code>package.path</code></a></h3>\n\n\n<p>\nThe path used by <a href=\"#pdf-require\"><code>require</code></a> to search for a Lua loader.\n\n\n<p>\nAt start-up, Lua initializes this variable with\nthe value of the environment variable <a name=\"pdf-LUA_PATH\"><code>LUA_PATH</code></a> or\nwith a default path defined in <code>luaconf.h</code>,\nif the environment variable is not defined.\nAny \"<code>;;</code>\" in the value of the environment variable\nis replaced by the default path.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-package.preload\"><code>package.preload</code></a></h3>\n\n\n<p>\nA table to store loaders for specific modules\n(see <a href=\"#pdf-require\"><code>require</code></a>).\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-package.seeall\"><code>package.seeall (module)</code></a></h3>\n\n\n<p>\nSets a metatable for <code>module</code> with\nits <code>__index</code> field referring to the global environment,\nso that this module inherits values\nfrom the global environment.\nTo be used as an option to function <a href=\"#pdf-module\"><code>module</code></a>.\n\n\n\n\n\n\n\n<h2>5.4 - <a name=\"5.4\">String Manipulation</a></h2>\n\n<p>\nThis library provides generic functions for string manipulation,\nsuch as finding and extracting substrings, and pattern matching.\nWhen indexing a string in Lua, the first character is at position&nbsp;1\n(not at&nbsp;0, as in C).\nIndices are allowed to be negative and are interpreted as indexing backwards,\nfrom the end of the string.\nThus, the last character is at position -1, and so on.\n\n\n<p>\nThe string library provides all its functions inside the table\n<a name=\"pdf-string\"><code>string</code></a>.\nIt also sets a metatable for strings\nwhere the <code>__index</code> field points to the <code>string</code> table.\nTherefore, you can use the string functions in object-oriented style.\nFor instance, <code>string.byte(s, i)</code>\ncan be written as <code>s:byte(i)</code>.\n\n\n<p>\nThe string library assumes one-byte character encodings.\n\n\n<p>\n<hr><h3><a name=\"pdf-string.byte\"><code>string.byte (s [, i [, j]])</code></a></h3>\nReturns the internal numerical codes of the characters <code>s[i]</code>,\n<code>s[i+1]</code>, &middot;&middot;&middot;, <code>s[j]</code>.\nThe default value for <code>i</code> is&nbsp;1;\nthe default value for <code>j</code> is&nbsp;<code>i</code>.\n\n\n<p>\nNote that numerical codes are not necessarily portable across platforms.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-string.char\"><code>string.char (&middot;&middot;&middot;)</code></a></h3>\nReceives zero or more integers.\nReturns a string with length equal to the number of arguments,\nin which each character has the internal numerical code equal\nto its corresponding argument.\n\n\n<p>\nNote that numerical codes are not necessarily portable across platforms.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-string.dump\"><code>string.dump (function)</code></a></h3>\n\n\n<p>\nReturns a string containing a binary representation of the given function,\nso that a later <a href=\"#pdf-loadstring\"><code>loadstring</code></a> on this string returns\na copy of the function.\n<code>function</code> must be a Lua function without upvalues.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-string.find\"><code>string.find (s, pattern [, init [, plain]])</code></a></h3>\nLooks for the first match of\n<code>pattern</code> in the string <code>s</code>.\nIf it finds a match, then <code>find</code> returns the indices of&nbsp;<code>s</code>\nwhere this occurrence starts and ends;\notherwise, it returns <b>nil</b>.\nA third, optional numerical argument <code>init</code> specifies\nwhere to start the search;\nits default value is&nbsp;1 and can be negative.\nA value of <b>true</b> as a fourth, optional argument <code>plain</code>\nturns off the pattern matching facilities,\nso the function does a plain \"find substring\" operation,\nwith no characters in <code>pattern</code> being considered \"magic\".\nNote that if <code>plain</code> is given, then <code>init</code> must be given as well.\n\n\n<p>\nIf the pattern has captures,\nthen in a successful match\nthe captured values are also returned,\nafter the two indices.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-string.format\"><code>string.format (formatstring, &middot;&middot;&middot;)</code></a></h3>\nReturns a formatted version of its variable number of arguments\nfollowing the description given in its first argument (which must be a string).\nThe format string follows the same rules as the <code>printf</code> family of\nstandard C&nbsp;functions.\nThe only differences are that the options/modifiers\n<code>*</code>, <code>l</code>, <code>L</code>, <code>n</code>, <code>p</code>,\nand <code>h</code> are not supported\nand that there is an extra option, <code>q</code>.\nThe <code>q</code> option formats a string in a form suitable to be safely read\nback by the Lua interpreter:\nthe string is written between double quotes,\nand all double quotes, newlines, embedded zeros,\nand backslashes in the string\nare correctly escaped when written.\nFor instance, the call\n\n<pre>\n     string.format('%q', 'a string with \"quotes\" and \\n new line')\n</pre><p>\nwill produce the string:\n\n<pre>\n     \"a string with \\\"quotes\\\" and \\\n      new line\"\n</pre>\n\n<p>\nThe options <code>c</code>, <code>d</code>, <code>E</code>, <code>e</code>, <code>f</code>,\n<code>g</code>, <code>G</code>, <code>i</code>, <code>o</code>, <code>u</code>, <code>X</code>, and <code>x</code> all\nexpect a number as argument,\nwhereas <code>q</code> and <code>s</code> expect a string.\n\n\n<p>\nThis function does not accept string values\ncontaining embedded zeros,\nexcept as arguments to the <code>q</code> option.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-string.gmatch\"><code>string.gmatch (s, pattern)</code></a></h3>\nReturns an iterator function that,\neach time it is called,\nreturns the next captures from <code>pattern</code> over string <code>s</code>.\nIf <code>pattern</code> specifies no captures,\nthen the whole match is produced in each call.\n\n\n<p>\nAs an example, the following loop\n\n<pre>\n     s = \"hello world from Lua\"\n     for w in string.gmatch(s, \"%a+\") do\n       print(w)\n     end\n</pre><p>\nwill iterate over all the words from string <code>s</code>,\nprinting one per line.\nThe next example collects all pairs <code>key=value</code> from the\ngiven string into a table:\n\n<pre>\n     t = {}\n     s = \"from=world, to=Lua\"\n     for k, v in string.gmatch(s, \"(%w+)=(%w+)\") do\n       t[k] = v\n     end\n</pre>\n\n<p>\nFor this function, a '<code>^</code>' at the start of a pattern does not\nwork as an anchor, as this would prevent the iteration.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-string.gsub\"><code>string.gsub (s, pattern, repl [, n])</code></a></h3>\nReturns a copy of <code>s</code>\nin which all (or the first <code>n</code>, if given)\noccurrences of the <code>pattern</code> have been\nreplaced by a replacement string specified by <code>repl</code>,\nwhich can be a string, a table, or a function.\n<code>gsub</code> also returns, as its second value,\nthe total number of matches that occurred.\n\n\n<p>\nIf <code>repl</code> is a string, then its value is used for replacement.\nThe character&nbsp;<code>%</code> works as an escape character:\nany sequence in <code>repl</code> of the form <code>%<em>n</em></code>,\nwith <em>n</em> between 1 and 9,\nstands for the value of the <em>n</em>-th captured substring (see below).\nThe sequence <code>%0</code> stands for the whole match.\nThe sequence <code>%%</code> stands for a single&nbsp;<code>%</code>.\n\n\n<p>\nIf <code>repl</code> is a table, then the table is queried for every match,\nusing the first capture as the key;\nif the pattern specifies no captures,\nthen the whole match is used as the key.\n\n\n<p>\nIf <code>repl</code> is a function, then this function is called every time a\nmatch occurs, with all captured substrings passed as arguments,\nin order;\nif the pattern specifies no captures,\nthen the whole match is passed as a sole argument.\n\n\n<p>\nIf the value returned by the table query or by the function call\nis a string or a number,\nthen it is used as the replacement string;\notherwise, if it is <b>false</b> or <b>nil</b>,\nthen there is no replacement\n(that is, the original match is kept in the string).\n\n\n<p>\nHere are some examples:\n\n<pre>\n     x = string.gsub(\"hello world\", \"(%w+)\", \"%1 %1\")\n     --&gt; x=\"hello hello world world\"\n     \n     x = string.gsub(\"hello world\", \"%w+\", \"%0 %0\", 1)\n     --&gt; x=\"hello hello world\"\n     \n     x = string.gsub(\"hello world from Lua\", \"(%w+)%s*(%w+)\", \"%2 %1\")\n     --&gt; x=\"world hello Lua from\"\n     \n     x = string.gsub(\"home = $HOME, user = $USER\", \"%$(%w+)\", os.getenv)\n     --&gt; x=\"home = /home/roberto, user = roberto\"\n     \n     x = string.gsub(\"4+5 = $return 4+5$\", \"%$(.-)%$\", function (s)\n           return loadstring(s)()\n         end)\n     --&gt; x=\"4+5 = 9\"\n     \n     local t = {name=\"lua\", version=\"5.1\"}\n     x = string.gsub(\"$name-$version.tar.gz\", \"%$(%w+)\", t)\n     --&gt; x=\"lua-5.1.tar.gz\"\n</pre>\n\n\n\n<p>\n<hr><h3><a name=\"pdf-string.len\"><code>string.len (s)</code></a></h3>\nReceives a string and returns its length.\nThe empty string <code>\"\"</code> has length 0.\nEmbedded zeros are counted,\nso <code>\"a\\000bc\\000\"</code> has length 5.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-string.lower\"><code>string.lower (s)</code></a></h3>\nReceives a string and returns a copy of this string with all\nuppercase letters changed to lowercase.\nAll other characters are left unchanged.\nThe definition of what an uppercase letter is depends on the current locale.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-string.match\"><code>string.match (s, pattern [, init])</code></a></h3>\nLooks for the first <em>match</em> of\n<code>pattern</code> in the string <code>s</code>.\nIf it finds one, then <code>match</code> returns\nthe captures from the pattern;\notherwise it returns <b>nil</b>.\nIf <code>pattern</code> specifies no captures,\nthen the whole match is returned.\nA third, optional numerical argument <code>init</code> specifies\nwhere to start the search;\nits default value is&nbsp;1 and can be negative.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-string.rep\"><code>string.rep (s, n)</code></a></h3>\nReturns a string that is the concatenation of <code>n</code> copies of\nthe string <code>s</code>.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-string.reverse\"><code>string.reverse (s)</code></a></h3>\nReturns a string that is the string <code>s</code> reversed.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-string.sub\"><code>string.sub (s, i [, j])</code></a></h3>\nReturns the substring of <code>s</code> that\nstarts at <code>i</code>  and continues until <code>j</code>;\n<code>i</code> and <code>j</code> can be negative.\nIf <code>j</code> is absent, then it is assumed to be equal to -1\n(which is the same as the string length).\nIn particular,\nthe call <code>string.sub(s,1,j)</code> returns a prefix of <code>s</code>\nwith length <code>j</code>,\nand <code>string.sub(s, -i)</code> returns a suffix of <code>s</code>\nwith length <code>i</code>.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-string.upper\"><code>string.upper (s)</code></a></h3>\nReceives a string and returns a copy of this string with all\nlowercase letters changed to uppercase.\nAll other characters are left unchanged.\nThe definition of what a lowercase letter is depends on the current locale.\n\n\n\n<h3>5.4.1 - <a name=\"5.4.1\">Patterns</a></h3>\n\n\n<h4>Character Class:</h4><p>\nA <em>character class</em> is used to represent a set of characters.\nThe following combinations are allowed in describing a character class:\n\n<ul>\n\n<li><b><em>x</em>:</b>\n(where <em>x</em> is not one of the <em>magic characters</em>\n<code>^$()%.[]*+-?</code>)\nrepresents the character <em>x</em> itself.\n</li>\n\n<li><b><code>.</code>:</b> (a dot) represents all characters.</li>\n\n<li><b><code>%a</code>:</b> represents all letters.</li>\n\n<li><b><code>%c</code>:</b> represents all control characters.</li>\n\n<li><b><code>%d</code>:</b> represents all digits.</li>\n\n<li><b><code>%l</code>:</b> represents all lowercase letters.</li>\n\n<li><b><code>%p</code>:</b> represents all punctuation characters.</li>\n\n<li><b><code>%s</code>:</b> represents all space characters.</li>\n\n<li><b><code>%u</code>:</b> represents all uppercase letters.</li>\n\n<li><b><code>%w</code>:</b> represents all alphanumeric characters.</li>\n\n<li><b><code>%x</code>:</b> represents all hexadecimal digits.</li>\n\n<li><b><code>%z</code>:</b> represents the character with representation 0.</li>\n\n<li><b><code>%<em>x</em></code>:</b> (where <em>x</em> is any non-alphanumeric character)\nrepresents the character <em>x</em>.\nThis is the standard way to escape the magic characters.\nAny punctuation character (even the non magic)\ncan be preceded by a '<code>%</code>'\nwhen used to represent itself in a pattern.\n</li>\n\n<li><b><code>[<em>set</em>]</code>:</b>\nrepresents the class which is the union of all\ncharacters in <em>set</em>.\nA range of characters can be specified by\nseparating the end characters of the range with a '<code>-</code>'.\nAll classes <code>%</code><em>x</em> described above can also be used as\ncomponents in <em>set</em>.\nAll other characters in <em>set</em> represent themselves.\nFor example, <code>[%w_]</code> (or <code>[_%w]</code>)\nrepresents all alphanumeric characters plus the underscore,\n<code>[0-7]</code> represents the octal digits,\nand <code>[0-7%l%-]</code> represents the octal digits plus\nthe lowercase letters plus the '<code>-</code>' character.\n\n\n<p>\nThe interaction between ranges and classes is not defined.\nTherefore, patterns like <code>[%a-z]</code> or <code>[a-%%]</code>\nhave no meaning.\n</li>\n\n<li><b><code>[^<em>set</em>]</code>:</b>\nrepresents the complement of <em>set</em>,\nwhere <em>set</em> is interpreted as above.\n</li>\n\n</ul><p>\nFor all classes represented by single letters (<code>%a</code>, <code>%c</code>, etc.),\nthe corresponding uppercase letter represents the complement of the class.\nFor instance, <code>%S</code> represents all non-space characters.\n\n\n<p>\nThe definitions of letter, space, and other character groups\ndepend on the current locale.\nIn particular, the class <code>[a-z]</code> may not be equivalent to <code>%l</code>.\n\n\n\n\n\n<h4>Pattern Item:</h4><p>\nA <em>pattern item</em> can be\n\n<ul>\n\n<li>\na single character class,\nwhich matches any single character in the class;\n</li>\n\n<li>\na single character class followed by '<code>*</code>',\nwhich matches 0 or more repetitions of characters in the class.\nThese repetition items will always match the longest possible sequence;\n</li>\n\n<li>\na single character class followed by '<code>+</code>',\nwhich matches 1 or more repetitions of characters in the class.\nThese repetition items will always match the longest possible sequence;\n</li>\n\n<li>\na single character class followed by '<code>-</code>',\nwhich also matches 0 or more repetitions of characters in the class.\nUnlike '<code>*</code>',\nthese repetition items will always match the <em>shortest</em> possible sequence;\n</li>\n\n<li>\na single character class followed by '<code>?</code>',\nwhich matches 0 or 1 occurrence of a character in the class;\n</li>\n\n<li>\n<code>%<em>n</em></code>, for <em>n</em> between 1 and 9;\nsuch item matches a substring equal to the <em>n</em>-th captured string\n(see below);\n</li>\n\n<li>\n<code>%b<em>xy</em></code>, where <em>x</em> and <em>y</em> are two distinct characters;\nsuch item matches strings that start with&nbsp;<em>x</em>, end with&nbsp;<em>y</em>,\nand where the <em>x</em> and <em>y</em> are <em>balanced</em>.\nThis means that, if one reads the string from left to right,\ncounting <em>+1</em> for an <em>x</em> and <em>-1</em> for a <em>y</em>,\nthe ending <em>y</em> is the first <em>y</em> where the count reaches 0.\nFor instance, the item <code>%b()</code> matches expressions with\nbalanced parentheses.\n</li>\n\n</ul>\n\n\n\n\n<h4>Pattern:</h4><p>\nA <em>pattern</em> is a sequence of pattern items.\nA '<code>^</code>' at the beginning of a pattern anchors the match at the\nbeginning of the subject string.\nA '<code>$</code>' at the end of a pattern anchors the match at the\nend of the subject string.\nAt other positions,\n'<code>^</code>' and '<code>$</code>' have no special meaning and represent themselves.\n\n\n\n\n\n<h4>Captures:</h4><p>\nA pattern can contain sub-patterns enclosed in parentheses;\nthey describe <em>captures</em>.\nWhen a match succeeds, the substrings of the subject string\nthat match captures are stored (<em>captured</em>) for future use.\nCaptures are numbered according to their left parentheses.\nFor instance, in the pattern <code>\"(a*(.)%w(%s*))\"</code>,\nthe part of the string matching <code>\"a*(.)%w(%s*)\"</code> is\nstored as the first capture (and therefore has number&nbsp;1);\nthe character matching \"<code>.</code>\" is captured with number&nbsp;2,\nand the part matching \"<code>%s*</code>\" has number&nbsp;3.\n\n\n<p>\nAs a special case, the empty capture <code>()</code> captures\nthe current string position (a number).\nFor instance, if we apply the pattern <code>\"()aa()\"</code> on the\nstring <code>\"flaaap\"</code>, there will be two captures: 3&nbsp;and&nbsp;5.\n\n\n<p>\nA pattern cannot contain embedded zeros.  Use <code>%z</code> instead.\n\n\n\n\n\n\n\n\n\n\n\n<h2>5.5 - <a name=\"5.5\">Table Manipulation</a></h2><p>\nThis library provides generic functions for table manipulation.\nIt provides all its functions inside the table <a name=\"pdf-table\"><code>table</code></a>.\n\n\n<p>\nMost functions in the table library assume that the table\nrepresents an array or a list.\nFor these functions, when we talk about the \"length\" of a table\nwe mean the result of the length operator.\n\n\n<p>\n<hr><h3><a name=\"pdf-table.concat\"><code>table.concat (table [, sep [, i [, j]]])</code></a></h3>\nGiven an array where all elements are strings or numbers,\nreturns <code>table[i]..sep..table[i+1] &middot;&middot;&middot; sep..table[j]</code>.\nThe default value for <code>sep</code> is the empty string,\nthe default for <code>i</code> is 1,\nand the default for <code>j</code> is the length of the table.\nIf <code>i</code> is greater than <code>j</code>, returns the empty string.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-table.insert\"><code>table.insert (table, [pos,] value)</code></a></h3>\n\n\n<p>\nInserts element <code>value</code> at position <code>pos</code> in <code>table</code>,\nshifting up other elements to open space, if necessary.\nThe default value for <code>pos</code> is <code>n+1</code>,\nwhere <code>n</code> is the length of the table (see <a href=\"#2.5.5\">&sect;2.5.5</a>),\nso that a call <code>table.insert(t,x)</code> inserts <code>x</code> at the end\nof table <code>t</code>.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-table.maxn\"><code>table.maxn (table)</code></a></h3>\n\n\n<p>\nReturns the largest positive numerical index of the given table,\nor zero if the table has no positive numerical indices.\n(To do its job this function does a linear traversal of\nthe whole table.) \n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-table.remove\"><code>table.remove (table [, pos])</code></a></h3>\n\n\n<p>\nRemoves from <code>table</code> the element at position <code>pos</code>,\nshifting down other elements to close the space, if necessary.\nReturns the value of the removed element.\nThe default value for <code>pos</code> is <code>n</code>,\nwhere <code>n</code> is the length of the table,\nso that a call <code>table.remove(t)</code> removes the last element\nof table <code>t</code>.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-table.sort\"><code>table.sort (table [, comp])</code></a></h3>\nSorts table elements in a given order, <em>in-place</em>,\nfrom <code>table[1]</code> to <code>table[n]</code>,\nwhere <code>n</code> is the length of the table.\nIf <code>comp</code> is given,\nthen it must be a function that receives two table elements,\nand returns true\nwhen the first is less than the second\n(so that <code>not comp(a[i+1],a[i])</code> will be true after the sort).\nIf <code>comp</code> is not given,\nthen the standard Lua operator <code>&lt;</code> is used instead.\n\n\n<p>\nThe sort algorithm is not stable;\nthat is, elements considered equal by the given order\nmay have their relative positions changed by the sort.\n\n\n\n\n\n\n\n<h2>5.6 - <a name=\"5.6\">Mathematical Functions</a></h2>\n\n<p>\nThis library is an interface to the standard C&nbsp;math library.\nIt provides all its functions inside the table <a name=\"pdf-math\"><code>math</code></a>.\n\n\n<p>\n<hr><h3><a name=\"pdf-math.abs\"><code>math.abs (x)</code></a></h3>\n\n\n<p>\nReturns the absolute value of <code>x</code>.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-math.acos\"><code>math.acos (x)</code></a></h3>\n\n\n<p>\nReturns the arc cosine of <code>x</code> (in radians).\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-math.asin\"><code>math.asin (x)</code></a></h3>\n\n\n<p>\nReturns the arc sine of <code>x</code> (in radians).\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-math.atan\"><code>math.atan (x)</code></a></h3>\n\n\n<p>\nReturns the arc tangent of <code>x</code> (in radians).\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-math.atan2\"><code>math.atan2 (y, x)</code></a></h3>\n\n\n<p>\nReturns the arc tangent of <code>y/x</code> (in radians),\nbut uses the signs of both parameters to find the\nquadrant of the result.\n(It also handles correctly the case of <code>x</code> being zero.)\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-math.ceil\"><code>math.ceil (x)</code></a></h3>\n\n\n<p>\nReturns the smallest integer larger than or equal to <code>x</code>.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-math.cos\"><code>math.cos (x)</code></a></h3>\n\n\n<p>\nReturns the cosine of <code>x</code> (assumed to be in radians).\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-math.cosh\"><code>math.cosh (x)</code></a></h3>\n\n\n<p>\nReturns the hyperbolic cosine of <code>x</code>.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-math.deg\"><code>math.deg (x)</code></a></h3>\n\n\n<p>\nReturns the angle <code>x</code> (given in radians) in degrees.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-math.exp\"><code>math.exp (x)</code></a></h3>\n\n\n<p>\nReturns the value <em>e<sup>x</sup></em>.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-math.floor\"><code>math.floor (x)</code></a></h3>\n\n\n<p>\nReturns the largest integer smaller than or equal to <code>x</code>.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-math.fmod\"><code>math.fmod (x, y)</code></a></h3>\n\n\n<p>\nReturns the remainder of the division of <code>x</code> by <code>y</code>\nthat rounds the quotient towards zero.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-math.frexp\"><code>math.frexp (x)</code></a></h3>\n\n\n<p>\nReturns <code>m</code> and <code>e</code> such that <em>x = m2<sup>e</sup></em>,\n<code>e</code> is an integer and the absolute value of <code>m</code> is\nin the range <em>[0.5, 1)</em>\n(or zero when <code>x</code> is zero).\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-math.huge\"><code>math.huge</code></a></h3>\n\n\n<p>\nThe value <code>HUGE_VAL</code>,\na value larger than or equal to any other numerical value.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-math.ldexp\"><code>math.ldexp (m, e)</code></a></h3>\n\n\n<p>\nReturns <em>m2<sup>e</sup></em> (<code>e</code> should be an integer).\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-math.log\"><code>math.log (x)</code></a></h3>\n\n\n<p>\nReturns the natural logarithm of <code>x</code>.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-math.log10\"><code>math.log10 (x)</code></a></h3>\n\n\n<p>\nReturns the base-10 logarithm of <code>x</code>.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-math.max\"><code>math.max (x, &middot;&middot;&middot;)</code></a></h3>\n\n\n<p>\nReturns the maximum value among its arguments.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-math.min\"><code>math.min (x, &middot;&middot;&middot;)</code></a></h3>\n\n\n<p>\nReturns the minimum value among its arguments.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-math.modf\"><code>math.modf (x)</code></a></h3>\n\n\n<p>\nReturns two numbers,\nthe integral part of <code>x</code> and the fractional part of <code>x</code>.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-math.pi\"><code>math.pi</code></a></h3>\n\n\n<p>\nThe value of <em>pi</em>.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-math.pow\"><code>math.pow (x, y)</code></a></h3>\n\n\n<p>\nReturns <em>x<sup>y</sup></em>.\n(You can also use the expression <code>x^y</code> to compute this value.)\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-math.rad\"><code>math.rad (x)</code></a></h3>\n\n\n<p>\nReturns the angle <code>x</code> (given in degrees) in radians.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-math.random\"><code>math.random ([m [, n]])</code></a></h3>\n\n\n<p>\nThis function is an interface to the simple\npseudo-random generator function <code>rand</code> provided by ANSI&nbsp;C.\n(No guarantees can be given for its statistical properties.)\n\n\n<p>\nWhen called without arguments,\nreturns a uniform pseudo-random real number\nin the range <em>[0,1)</em>.  \nWhen called with an integer number <code>m</code>,\n<code>math.random</code> returns\na uniform pseudo-random integer in the range <em>[1, m]</em>.\nWhen called with two integer numbers <code>m</code> and <code>n</code>,\n<code>math.random</code> returns a uniform pseudo-random\ninteger in the range <em>[m, n]</em>.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-math.randomseed\"><code>math.randomseed (x)</code></a></h3>\n\n\n<p>\nSets <code>x</code> as the \"seed\"\nfor the pseudo-random generator:\nequal seeds produce equal sequences of numbers.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-math.sin\"><code>math.sin (x)</code></a></h3>\n\n\n<p>\nReturns the sine of <code>x</code> (assumed to be in radians).\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-math.sinh\"><code>math.sinh (x)</code></a></h3>\n\n\n<p>\nReturns the hyperbolic sine of <code>x</code>.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-math.sqrt\"><code>math.sqrt (x)</code></a></h3>\n\n\n<p>\nReturns the square root of <code>x</code>.\n(You can also use the expression <code>x^0.5</code> to compute this value.)\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-math.tan\"><code>math.tan (x)</code></a></h3>\n\n\n<p>\nReturns the tangent of <code>x</code> (assumed to be in radians).\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-math.tanh\"><code>math.tanh (x)</code></a></h3>\n\n\n<p>\nReturns the hyperbolic tangent of <code>x</code>.\n\n\n\n\n\n\n\n<h2>5.7 - <a name=\"5.7\">Input and Output Facilities</a></h2>\n\n<p>\nThe I/O library provides two different styles for file manipulation.\nThe first one uses implicit file descriptors;\nthat is, there are operations to set a default input file and a\ndefault output file,\nand all input/output operations are over these default files.\nThe second style uses explicit file descriptors.\n\n\n<p>\nWhen using implicit file descriptors,\nall operations are supplied by table <a name=\"pdf-io\"><code>io</code></a>.\nWhen using explicit file descriptors,\nthe operation <a href=\"#pdf-io.open\"><code>io.open</code></a> returns a file descriptor\nand then all operations are supplied as methods of the file descriptor.\n\n\n<p>\nThe table <code>io</code> also provides\nthree predefined file descriptors with their usual meanings from C:\n<a name=\"pdf-io.stdin\"><code>io.stdin</code></a>, <a name=\"pdf-io.stdout\"><code>io.stdout</code></a>, and <a name=\"pdf-io.stderr\"><code>io.stderr</code></a>.\nThe I/O library never closes these files.\n\n\n<p>\nUnless otherwise stated,\nall I/O functions return <b>nil</b> on failure\n(plus an error message as a second result and\na system-dependent error code as a third result)\nand some value different from <b>nil</b> on success.\n\n\n<p>\n<hr><h3><a name=\"pdf-io.close\"><code>io.close ([file])</code></a></h3>\n\n\n<p>\nEquivalent to <code>file:close()</code>.\nWithout a <code>file</code>, closes the default output file.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-io.flush\"><code>io.flush ()</code></a></h3>\n\n\n<p>\nEquivalent to <code>file:flush</code> over the default output file.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-io.input\"><code>io.input ([file])</code></a></h3>\n\n\n<p>\nWhen called with a file name, it opens the named file (in text mode),\nand sets its handle as the default input file.\nWhen called with a file handle,\nit simply sets this file handle as the default input file.\nWhen called without parameters,\nit returns the current default input file.\n\n\n<p>\nIn case of errors this function raises the error,\ninstead of returning an error code.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-io.lines\"><code>io.lines ([filename])</code></a></h3>\n\n\n<p>\nOpens the given file name in read mode\nand returns an iterator function that,\neach time it is called,\nreturns a new line from the file.\nTherefore, the construction\n\n<pre>\n     for line in io.lines(filename) do <em>body</em> end\n</pre><p>\nwill iterate over all lines of the file.\nWhen the iterator function detects the end of file,\nit returns <b>nil</b> (to finish the loop) and automatically closes the file.\n\n\n<p>\nThe call <code>io.lines()</code> (with no file name) is equivalent\nto <code>io.input():lines()</code>;\nthat is, it iterates over the lines of the default input file.\nIn this case it does not close the file when the loop ends.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-io.open\"><code>io.open (filename [, mode])</code></a></h3>\n\n\n<p>\nThis function opens a file,\nin the mode specified in the string <code>mode</code>.\nIt returns a new file handle,\nor, in case of errors, <b>nil</b> plus an error message.\n\n\n<p>\nThe <code>mode</code> string can be any of the following:\n\n<ul>\n<li><b>\"r\":</b> read mode (the default);</li>\n<li><b>\"w\":</b> write mode;</li>\n<li><b>\"a\":</b> append mode;</li>\n<li><b>\"r+\":</b> update mode, all previous data is preserved;</li>\n<li><b>\"w+\":</b> update mode, all previous data is erased;</li>\n<li><b>\"a+\":</b> append update mode, previous data is preserved,\n  writing is only allowed at the end of file.</li>\n</ul><p>\nThe <code>mode</code> string can also have a '<code>b</code>' at the end,\nwhich is needed in some systems to open the file in binary mode.\nThis string is exactly what is used in the\nstandard&nbsp;C function <code>fopen</code>.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-io.output\"><code>io.output ([file])</code></a></h3>\n\n\n<p>\nSimilar to <a href=\"#pdf-io.input\"><code>io.input</code></a>, but operates over the default output file.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-io.popen\"><code>io.popen (prog [, mode])</code></a></h3>\n\n\n<p>\nStarts program <code>prog</code> in a separated process and returns\na file handle that you can use to read data from this program\n(if <code>mode</code> is <code>\"r\"</code>, the default)\nor to write data to this program\n(if <code>mode</code> is <code>\"w\"</code>).\n\n\n<p>\nThis function is system dependent and is not available\non all platforms.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-io.read\"><code>io.read (&middot;&middot;&middot;)</code></a></h3>\n\n\n<p>\nEquivalent to <code>io.input():read</code>.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-io.tmpfile\"><code>io.tmpfile ()</code></a></h3>\n\n\n<p>\nReturns a handle for a temporary file.\nThis file is opened in update mode\nand it is automatically removed when the program ends.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-io.type\"><code>io.type (obj)</code></a></h3>\n\n\n<p>\nChecks whether <code>obj</code> is a valid file handle.\nReturns the string <code>\"file\"</code> if <code>obj</code> is an open file handle,\n<code>\"closed file\"</code> if <code>obj</code> is a closed file handle,\nor <b>nil</b> if <code>obj</code> is not a file handle.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-io.write\"><code>io.write (&middot;&middot;&middot;)</code></a></h3>\n\n\n<p>\nEquivalent to <code>io.output():write</code>.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-file:close\"><code>file:close ()</code></a></h3>\n\n\n<p>\nCloses <code>file</code>.\nNote that files are automatically closed when\ntheir handles are garbage collected,\nbut that takes an unpredictable amount of time to happen.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-file:flush\"><code>file:flush ()</code></a></h3>\n\n\n<p>\nSaves any written data to <code>file</code>.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-file:lines\"><code>file:lines ()</code></a></h3>\n\n\n<p>\nReturns an iterator function that,\neach time it is called,\nreturns a new line from the file.\nTherefore, the construction\n\n<pre>\n     for line in file:lines() do <em>body</em> end\n</pre><p>\nwill iterate over all lines of the file.\n(Unlike <a href=\"#pdf-io.lines\"><code>io.lines</code></a>, this function does not close the file\nwhen the loop ends.)\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-file:read\"><code>file:read (&middot;&middot;&middot;)</code></a></h3>\n\n\n<p>\nReads the file <code>file</code>,\naccording to the given formats, which specify what to read.\nFor each format,\nthe function returns a string (or a number) with the characters read,\nor <b>nil</b> if it cannot read data with the specified format.\nWhen called without formats,\nit uses a default format that reads the entire next line\n(see below).\n\n\n<p>\nThe available formats are\n\n<ul>\n\n<li><b>\"*n\":</b>\nreads a number;\nthis is the only format that returns a number instead of a string.\n</li>\n\n<li><b>\"*a\":</b>\nreads the whole file, starting at the current position.\nOn end of file, it returns the empty string.\n</li>\n\n<li><b>\"*l\":</b>\nreads the next line (skipping the end of line),\nreturning <b>nil</b> on end of file.\nThis is the default format.\n</li>\n\n<li><b><em>number</em>:</b>\nreads a string with up to this number of characters,\nreturning <b>nil</b> on end of file.\nIf number is zero,\nit reads nothing and returns an empty string,\nor <b>nil</b> on end of file.\n</li>\n\n</ul>\n\n\n\n<p>\n<hr><h3><a name=\"pdf-file:seek\"><code>file:seek ([whence] [, offset])</code></a></h3>\n\n\n<p>\nSets and gets the file position,\nmeasured from the beginning of the file,\nto the position given by <code>offset</code> plus a base\nspecified by the string <code>whence</code>, as follows:\n\n<ul>\n<li><b>\"set\":</b> base is position 0 (beginning of the file);</li>\n<li><b>\"cur\":</b> base is current position;</li>\n<li><b>\"end\":</b> base is end of file;</li>\n</ul><p>\nIn case of success, function <code>seek</code> returns the final file position,\nmeasured in bytes from the beginning of the file.\nIf this function fails, it returns <b>nil</b>,\nplus a string describing the error.\n\n\n<p>\nThe default value for <code>whence</code> is <code>\"cur\"</code>,\nand for <code>offset</code> is 0.\nTherefore, the call <code>file:seek()</code> returns the current\nfile position, without changing it;\nthe call <code>file:seek(\"set\")</code> sets the position to the\nbeginning of the file (and returns 0);\nand the call <code>file:seek(\"end\")</code> sets the position to the\nend of the file, and returns its size.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-file:setvbuf\"><code>file:setvbuf (mode [, size])</code></a></h3>\n\n\n<p>\nSets the buffering mode for an output file.\nThere are three available modes:\n\n<ul>\n\n<li><b>\"no\":</b>\nno buffering; the result of any output operation appears immediately.\n</li>\n\n<li><b>\"full\":</b>\nfull buffering; output operation is performed only\nwhen the buffer is full (or when you explicitly <code>flush</code> the file\n(see <a href=\"#pdf-io.flush\"><code>io.flush</code></a>)).\n</li>\n\n<li><b>\"line\":</b>\nline buffering; output is buffered until a newline is output\nor there is any input from some special files\n(such as a terminal device).\n</li>\n\n</ul><p>\nFor the last two cases, <code>size</code>\nspecifies the size of the buffer, in bytes.\nThe default is an appropriate size.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-file:write\"><code>file:write (&middot;&middot;&middot;)</code></a></h3>\n\n\n<p>\nWrites the value of each of its arguments to\nthe <code>file</code>.\nThe arguments must be strings or numbers.\nTo write other values,\nuse <a href=\"#pdf-tostring\"><code>tostring</code></a> or <a href=\"#pdf-string.format\"><code>string.format</code></a> before <code>write</code>.\n\n\n\n\n\n\n\n<h2>5.8 - <a name=\"5.8\">Operating System Facilities</a></h2>\n\n<p>\nThis library is implemented through table <a name=\"pdf-os\"><code>os</code></a>.\n\n\n<p>\n<hr><h3><a name=\"pdf-os.clock\"><code>os.clock ()</code></a></h3>\n\n\n<p>\nReturns an approximation of the amount in seconds of CPU time\nused by the program.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-os.date\"><code>os.date ([format [, time]])</code></a></h3>\n\n\n<p>\nReturns a string or a table containing date and time,\nformatted according to the given string <code>format</code>.\n\n\n<p>\nIf the <code>time</code> argument is present,\nthis is the time to be formatted\n(see the <a href=\"#pdf-os.time\"><code>os.time</code></a> function for a description of this value).\nOtherwise, <code>date</code> formats the current time.\n\n\n<p>\nIf <code>format</code> starts with '<code>!</code>',\nthen the date is formatted in Coordinated Universal Time.\nAfter this optional character,\nif <code>format</code> is the string \"<code>*t</code>\",\nthen <code>date</code> returns a table with the following fields:\n<code>year</code> (four digits), <code>month</code> (1--12), <code>day</code> (1--31),\n<code>hour</code> (0--23), <code>min</code> (0--59), <code>sec</code> (0--61),\n<code>wday</code> (weekday, Sunday is&nbsp;1),\n<code>yday</code> (day of the year),\nand <code>isdst</code> (daylight saving flag, a boolean).\n\n\n<p>\nIf <code>format</code> is not \"<code>*t</code>\",\nthen <code>date</code> returns the date as a string,\nformatted according to the same rules as the C&nbsp;function <code>strftime</code>.\n\n\n<p>\nWhen called without arguments,\n<code>date</code> returns a reasonable date and time representation that depends on\nthe host system and on the current locale\n(that is, <code>os.date()</code> is equivalent to <code>os.date(\"%c\")</code>).\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-os.difftime\"><code>os.difftime (t2, t1)</code></a></h3>\n\n\n<p>\nReturns the number of seconds from time <code>t1</code> to time <code>t2</code>.\nIn POSIX, Windows, and some other systems,\nthis value is exactly <code>t2</code><em>-</em><code>t1</code>.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-os.execute\"><code>os.execute ([command])</code></a></h3>\n\n\n<p>\nThis function is equivalent to the C&nbsp;function <code>system</code>.\nIt passes <code>command</code> to be executed by an operating system shell.\nIt returns a status code, which is system-dependent.\nIf <code>command</code> is absent, then it returns nonzero if a shell is available\nand zero otherwise.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-os.exit\"><code>os.exit ([code])</code></a></h3>\n\n\n<p>\nCalls the C&nbsp;function <code>exit</code>,\nwith an optional <code>code</code>,\nto terminate the host program.\nThe default value for <code>code</code> is the success code.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-os.getenv\"><code>os.getenv (varname)</code></a></h3>\n\n\n<p>\nReturns the value of the process environment variable <code>varname</code>,\nor <b>nil</b> if the variable is not defined.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-os.remove\"><code>os.remove (filename)</code></a></h3>\n\n\n<p>\nDeletes the file or directory with the given name.\nDirectories must be empty to be removed.\nIf this function fails, it returns <b>nil</b>,\nplus a string describing the error.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-os.rename\"><code>os.rename (oldname, newname)</code></a></h3>\n\n\n<p>\nRenames file or directory named <code>oldname</code> to <code>newname</code>.\nIf this function fails, it returns <b>nil</b>,\nplus a string describing the error.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-os.setlocale\"><code>os.setlocale (locale [, category])</code></a></h3>\n\n\n<p>\nSets the current locale of the program.\n<code>locale</code> is a string specifying a locale;\n<code>category</code> is an optional string describing which category to change:\n<code>\"all\"</code>, <code>\"collate\"</code>, <code>\"ctype\"</code>,\n<code>\"monetary\"</code>, <code>\"numeric\"</code>, or <code>\"time\"</code>;\nthe default category is <code>\"all\"</code>.\nThe function returns the name of the new locale,\nor <b>nil</b> if the request cannot be honored.\n\n\n<p>\nIf <code>locale</code> is the empty string,\nthe current locale is set to an implementation-defined native locale.\nIf <code>locale</code> is the string \"<code>C</code>\",\nthe current locale is set to the standard C locale.\n\n\n<p>\nWhen called with <b>nil</b> as the first argument,\nthis function only returns the name of the current locale\nfor the given category.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-os.time\"><code>os.time ([table])</code></a></h3>\n\n\n<p>\nReturns the current time when called without arguments,\nor a time representing the date and time specified by the given table.\nThis table must have fields <code>year</code>, <code>month</code>, and <code>day</code>,\nand may have fields <code>hour</code>, <code>min</code>, <code>sec</code>, and <code>isdst</code>\n(for a description of these fields, see the <a href=\"#pdf-os.date\"><code>os.date</code></a> function).\n\n\n<p>\nThe returned value is a number, whose meaning depends on your system.\nIn POSIX, Windows, and some other systems, this number counts the number\nof seconds since some given start time (the \"epoch\").\nIn other systems, the meaning is not specified,\nand the number returned by <code>time</code> can be used only as an argument to\n<code>date</code> and <code>difftime</code>.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-os.tmpname\"><code>os.tmpname ()</code></a></h3>\n\n\n<p>\nReturns a string with a file name that can\nbe used for a temporary file.\nThe file must be explicitly opened before its use\nand explicitly removed when no longer needed.\n\n\n<p>\nOn some systems (POSIX),\nthis function also creates a file with that name,\nto avoid security risks.\n(Someone else might create the file with wrong permissions\nin the time between getting the name and creating the file.)\nYou still have to open the file to use it\nand to remove it (even if you do not use it).\n\n\n<p>\nWhen possible,\nyou may prefer to use <a href=\"#pdf-io.tmpfile\"><code>io.tmpfile</code></a>,\nwhich automatically removes the file when the program ends.\n\n\n\n\n\n\n\n<h2>5.9 - <a name=\"5.9\">The Debug Library</a></h2>\n\n<p>\nThis library provides\nthe functionality of the debug interface to Lua programs.\nYou should exert care when using this library.\nThe functions provided here should be used exclusively for debugging\nand similar tasks, such as profiling.\nPlease resist the temptation to use them as a\nusual programming tool:\nthey can be very slow.\nMoreover, several of these functions\nviolate some assumptions about Lua code\n(e.g., that variables local to a function\ncannot be accessed from outside or\nthat userdata metatables cannot be changed by Lua code)\nand therefore can compromise otherwise secure code.\n\n\n<p>\nAll functions in this library are provided\ninside the <a name=\"pdf-debug\"><code>debug</code></a> table.\nAll functions that operate over a thread\nhave an optional first argument which is the\nthread to operate over.\nThe default is always the current thread.\n\n\n<p>\n<hr><h3><a name=\"pdf-debug.debug\"><code>debug.debug ()</code></a></h3>\n\n\n<p>\nEnters an interactive mode with the user,\nrunning each string that the user enters.\nUsing simple commands and other debug facilities,\nthe user can inspect global and local variables,\nchange their values, evaluate expressions, and so on.\nA line containing only the word <code>cont</code> finishes this function,\nso that the caller continues its execution.\n\n\n<p>\nNote that commands for <code>debug.debug</code> are not lexically nested\nwithin any function, and so have no direct access to local variables.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-debug.getfenv\"><code>debug.getfenv (o)</code></a></h3>\nReturns the environment of object <code>o</code>.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-debug.gethook\"><code>debug.gethook ([thread])</code></a></h3>\n\n\n<p>\nReturns the current hook settings of the thread, as three values:\nthe current hook function, the current hook mask,\nand the current hook count\n(as set by the <a href=\"#pdf-debug.sethook\"><code>debug.sethook</code></a> function).\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-debug.getinfo\"><code>debug.getinfo ([thread,] function [, what])</code></a></h3>\n\n\n<p>\nReturns a table with information about a function.\nYou can give the function directly,\nor you can give a number as the value of <code>function</code>,\nwhich means the function running at level <code>function</code> of the call stack\nof the given thread:\nlevel&nbsp;0 is the current function (<code>getinfo</code> itself);\nlevel&nbsp;1 is the function that called <code>getinfo</code>;\nand so on.\nIf <code>function</code> is a number larger than the number of active functions,\nthen <code>getinfo</code> returns <b>nil</b>.\n\n\n<p>\nThe returned table can contain all the fields returned by <a href=\"#lua_getinfo\"><code>lua_getinfo</code></a>,\nwith the string <code>what</code> describing which fields to fill in.\nThe default for <code>what</code> is to get all information available,\nexcept the table of valid lines.\nIf present,\nthe option '<code>f</code>'\nadds a field named <code>func</code> with the function itself.\nIf present,\nthe option '<code>L</code>'\nadds a field named <code>activelines</code> with the table of\nvalid lines.\n\n\n<p>\nFor instance, the expression <code>debug.getinfo(1,\"n\").name</code> returns\na table with a name for the current function,\nif a reasonable name can be found,\nand the expression <code>debug.getinfo(print)</code>\nreturns a table with all available information\nabout the <a href=\"#pdf-print\"><code>print</code></a> function.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-debug.getlocal\"><code>debug.getlocal ([thread,] level, local)</code></a></h3>\n\n\n<p>\nThis function returns the name and the value of the local variable\nwith index <code>local</code> of the function at level <code>level</code> of the stack.\n(The first parameter or local variable has index&nbsp;1, and so on,\nuntil the last active local variable.)\nThe function returns <b>nil</b> if there is no local\nvariable with the given index,\nand raises an error when called with a <code>level</code> out of range.\n(You can call <a href=\"#pdf-debug.getinfo\"><code>debug.getinfo</code></a> to check whether the level is valid.)\n\n\n<p>\nVariable names starting with '<code>(</code>' (open parentheses)\nrepresent internal variables\n(loop control variables, temporaries, and C&nbsp;function locals).\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-debug.getmetatable\"><code>debug.getmetatable (object)</code></a></h3>\n\n\n<p>\nReturns the metatable of the given <code>object</code>\nor <b>nil</b> if it does not have a metatable.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-debug.getregistry\"><code>debug.getregistry ()</code></a></h3>\n\n\n<p>\nReturns the registry table (see <a href=\"#3.5\">&sect;3.5</a>).\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-debug.getupvalue\"><code>debug.getupvalue (func, up)</code></a></h3>\n\n\n<p>\nThis function returns the name and the value of the upvalue\nwith index <code>up</code> of the function <code>func</code>.\nThe function returns <b>nil</b> if there is no upvalue with the given index.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-debug.setfenv\"><code>debug.setfenv (object, table)</code></a></h3>\n\n\n<p>\nSets the environment of the given <code>object</code> to the given <code>table</code>.\nReturns <code>object</code>.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-debug.sethook\"><code>debug.sethook ([thread,] hook, mask [, count])</code></a></h3>\n\n\n<p>\nSets the given function as a hook.\nThe string <code>mask</code> and the number <code>count</code> describe\nwhen the hook will be called.\nThe string mask may have the following characters,\nwith the given meaning:\n\n<ul>\n<li><b><code>\"c\"</code>:</b> the hook is called every time Lua calls a function;</li>\n<li><b><code>\"r\"</code>:</b> the hook is called every time Lua returns from a function;</li>\n<li><b><code>\"l\"</code>:</b> the hook is called every time Lua enters a new line of code.</li>\n</ul><p>\nWith a <code>count</code> different from zero,\nthe hook is called after every <code>count</code> instructions.\n\n\n<p>\nWhen called without arguments,\n<a href=\"#pdf-debug.sethook\"><code>debug.sethook</code></a> turns off the hook.\n\n\n<p>\nWhen the hook is called, its first parameter is a string\ndescribing the event that has triggered its call:\n<code>\"call\"</code>, <code>\"return\"</code> (or <code>\"tail return\"</code>,\nwhen simulating a return from a tail call),\n<code>\"line\"</code>, and <code>\"count\"</code>.\nFor line events,\nthe hook also gets the new line number as its second parameter.\nInside a hook,\nyou can call <code>getinfo</code> with level&nbsp;2 to get more information about\nthe running function\n(level&nbsp;0 is the <code>getinfo</code> function,\nand level&nbsp;1 is the hook function),\nunless the event is <code>\"tail return\"</code>.\nIn this case, Lua is only simulating the return,\nand a call to <code>getinfo</code> will return invalid data.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-debug.setlocal\"><code>debug.setlocal ([thread,] level, local, value)</code></a></h3>\n\n\n<p>\nThis function assigns the value <code>value</code> to the local variable\nwith index <code>local</code> of the function at level <code>level</code> of the stack.\nThe function returns <b>nil</b> if there is no local\nvariable with the given index,\nand raises an error when called with a <code>level</code> out of range.\n(You can call <code>getinfo</code> to check whether the level is valid.)\nOtherwise, it returns the name of the local variable.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-debug.setmetatable\"><code>debug.setmetatable (object, table)</code></a></h3>\n\n\n<p>\nSets the metatable for the given <code>object</code> to the given <code>table</code>\n(which can be <b>nil</b>).\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-debug.setupvalue\"><code>debug.setupvalue (func, up, value)</code></a></h3>\n\n\n<p>\nThis function assigns the value <code>value</code> to the upvalue\nwith index <code>up</code> of the function <code>func</code>.\nThe function returns <b>nil</b> if there is no upvalue\nwith the given index.\nOtherwise, it returns the name of the upvalue.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-debug.traceback\"><code>debug.traceback ([thread,] [message [, level]])</code></a></h3>\n\n\n<p>\nReturns a string with a traceback of the call stack.\nAn optional <code>message</code> string is appended\nat the beginning of the traceback.\nAn optional <code>level</code> number tells at which level\nto start the traceback\n(default is 1, the function calling <code>traceback</code>).\n\n\n\n\n\n\n\n<h1>6 - <a name=\"6\">Lua Stand-alone</a></h1>\n\n<p>\nAlthough Lua has been designed as an extension language,\nto be embedded in a host C&nbsp;program,\nit is also frequently used as a stand-alone language.\nAn interpreter for Lua as a stand-alone language,\ncalled simply <code>lua</code>,\nis provided with the standard distribution.\nThe stand-alone interpreter includes\nall standard libraries, including the debug library.\nIts usage is:\n\n<pre>\n     lua [options] [script [args]]\n</pre><p>\nThe options are:\n\n<ul>\n<li><b><code>-e <em>stat</em></code>:</b> executes string <em>stat</em>;</li>\n<li><b><code>-l <em>mod</em></code>:</b> \"requires\" <em>mod</em>;</li>\n<li><b><code>-i</code>:</b> enters interactive mode after running <em>script</em>;</li>\n<li><b><code>-v</code>:</b> prints version information;</li>\n<li><b><code>--</code>:</b> stops handling options;</li>\n<li><b><code>-</code>:</b> executes <code>stdin</code> as a file and stops handling options.</li>\n</ul><p>\nAfter handling its options, <code>lua</code> runs the given <em>script</em>,\npassing to it the given <em>args</em> as string arguments.\nWhen called without arguments,\n<code>lua</code> behaves as <code>lua -v -i</code>\nwhen the standard input (<code>stdin</code>) is a terminal,\nand as <code>lua -</code> otherwise.\n\n\n<p>\nBefore running any argument,\nthe interpreter checks for an environment variable <a name=\"pdf-LUA_INIT\"><code>LUA_INIT</code></a>.\nIf its format is <code>@<em>filename</em></code>,\nthen <code>lua</code> executes the file.\nOtherwise, <code>lua</code> executes the string itself.\n\n\n<p>\nAll options are handled in order, except <code>-i</code>.\nFor instance, an invocation like\n\n<pre>\n     $ lua -e'a=1' -e 'print(a)' script.lua\n</pre><p>\nwill first set <code>a</code> to 1, then print the value of <code>a</code> (which is '<code>1</code>'),\nand finally run the file <code>script.lua</code> with no arguments.\n(Here <code>$</code> is the shell prompt. Your prompt may be different.)\n\n\n<p>\nBefore starting to run the script,\n<code>lua</code> collects all arguments in the command line\nin a global table called <code>arg</code>.\nThe script name is stored at index 0,\nthe first argument after the script name goes to index 1,\nand so on.\nAny arguments before the script name\n(that is, the interpreter name plus the options)\ngo to negative indices.\nFor instance, in the call\n\n<pre>\n     $ lua -la b.lua t1 t2\n</pre><p>\nthe interpreter first runs the file <code>a.lua</code>,\nthen creates a table\n\n<pre>\n     arg = { [-2] = \"lua\", [-1] = \"-la\",\n             [0] = \"b.lua\",\n             [1] = \"t1\", [2] = \"t2\" }\n</pre><p>\nand finally runs the file <code>b.lua</code>.\nThe script is called with <code>arg[1]</code>, <code>arg[2]</code>, &middot;&middot;&middot;\nas arguments;\nit can also access these arguments with the vararg expression '<code>...</code>'.\n\n\n<p>\nIn interactive mode,\nif you write an incomplete statement,\nthe interpreter waits for its completion\nby issuing a different prompt.\n\n\n<p>\nIf the global variable <a name=\"pdf-_PROMPT\"><code>_PROMPT</code></a> contains a string,\nthen its value is used as the prompt.\nSimilarly, if the global variable <a name=\"pdf-_PROMPT2\"><code>_PROMPT2</code></a> contains a string,\nits value is used as the secondary prompt\n(issued during incomplete statements).\nTherefore, both prompts can be changed directly on the command line\nor in any Lua programs by assigning to <code>_PROMPT</code>.\nSee the next example:\n\n<pre>\n     $ lua -e\"_PROMPT='myprompt&gt; '\" -i\n</pre><p>\n(The outer pair of quotes is for the shell,\nthe inner pair is for Lua.)\nNote the use of <code>-i</code> to enter interactive mode;\notherwise,\nthe program would just end silently\nright after the assignment to <code>_PROMPT</code>.\n\n\n<p>\nTo allow the use of Lua as a\nscript interpreter in Unix systems,\nthe stand-alone interpreter skips\nthe first line of a chunk if it starts with <code>#</code>.\nTherefore, Lua scripts can be made into executable programs\nby using <code>chmod +x</code> and the&nbsp;<code>#!</code> form,\nas in\n\n<pre>\n     #!/usr/local/bin/lua\n</pre><p>\n(Of course,\nthe location of the Lua interpreter may be different in your machine.\nIf <code>lua</code> is in your <code>PATH</code>,\nthen \n\n<pre>\n     #!/usr/bin/env lua\n</pre><p>\nis a more portable solution.) \n\n\n\n<h1>7 - <a name=\"7\">Incompatibilities with the Previous Version</a></h1>\n\n<p>\nHere we list the incompatibilities that you may find when moving a program\nfrom Lua&nbsp;5.0 to Lua&nbsp;5.1.\nYou can avoid most of the incompatibilities compiling Lua with\nappropriate options (see file <code>luaconf.h</code>).\nHowever,\nall these compatibility options will be removed in the next version of Lua.\n\n\n\n<h2>7.1 - <a name=\"7.1\">Changes in the Language</a></h2>\n<ul>\n\n<li>\nThe vararg system changed from the pseudo-argument <code>arg</code> with a\ntable with the extra arguments to the vararg expression.\n(See compile-time option <code>LUA_COMPAT_VARARG</code> in <code>luaconf.h</code>.)\n</li>\n\n<li>\nThere was a subtle change in the scope of the implicit\nvariables of the <b>for</b> statement and for the <b>repeat</b> statement.\n</li>\n\n<li>\nThe long string/long comment syntax (<code>[[<em>string</em>]]</code>)\ndoes not allow nesting.\nYou can use the new syntax (<code>[=[<em>string</em>]=]</code>) in these cases.\n(See compile-time option <code>LUA_COMPAT_LSTR</code> in <code>luaconf.h</code>.)\n</li>\n\n</ul>\n\n\n\n\n<h2>7.2 - <a name=\"7.2\">Changes in the Libraries</a></h2>\n<ul>\n\n<li>\nFunction <code>string.gfind</code> was renamed <a href=\"#pdf-string.gmatch\"><code>string.gmatch</code></a>.\n(See compile-time option <code>LUA_COMPAT_GFIND</code> in <code>luaconf.h</code>.)\n</li>\n\n<li>\nWhen <a href=\"#pdf-string.gsub\"><code>string.gsub</code></a> is called with a function as its\nthird argument,\nwhenever this function returns <b>nil</b> or <b>false</b> the\nreplacement string is the whole match,\ninstead of the empty string.\n</li>\n\n<li>\nFunction <code>table.setn</code> was deprecated.\nFunction <code>table.getn</code> corresponds\nto the new length operator (<code>#</code>);\nuse the operator instead of the function.\n(See compile-time option <code>LUA_COMPAT_GETN</code> in <code>luaconf.h</code>.)\n</li>\n\n<li>\nFunction <code>loadlib</code> was renamed <a href=\"#pdf-package.loadlib\"><code>package.loadlib</code></a>.\n(See compile-time option <code>LUA_COMPAT_LOADLIB</code> in <code>luaconf.h</code>.)\n</li>\n\n<li>\nFunction <code>math.mod</code> was renamed <a href=\"#pdf-math.fmod\"><code>math.fmod</code></a>.\n(See compile-time option <code>LUA_COMPAT_MOD</code> in <code>luaconf.h</code>.)\n</li>\n\n<li>\nFunctions <code>table.foreach</code> and <code>table.foreachi</code> are deprecated.\nYou can use a for loop with <code>pairs</code> or <code>ipairs</code> instead.\n</li>\n\n<li>\nThere were substantial changes in function <a href=\"#pdf-require\"><code>require</code></a> due to\nthe new module system.\nHowever, the new behavior is mostly compatible with the old,\nbut <code>require</code> gets the path from <a href=\"#pdf-package.path\"><code>package.path</code></a> instead\nof from <code>LUA_PATH</code>.\n</li>\n\n<li>\nFunction <a href=\"#pdf-collectgarbage\"><code>collectgarbage</code></a> has different arguments.\nFunction <code>gcinfo</code> is deprecated;\nuse <code>collectgarbage(\"count\")</code> instead.\n</li>\n\n</ul>\n\n\n\n\n<h2>7.3 - <a name=\"7.3\">Changes in the API</a></h2>\n<ul>\n\n<li>\nThe <code>luaopen_*</code> functions (to open libraries)\ncannot be called directly,\nlike a regular C function.\nThey must be called through Lua,\nlike a Lua function.\n</li>\n\n<li>\nFunction <code>lua_open</code> was replaced by <a href=\"#lua_newstate\"><code>lua_newstate</code></a> to\nallow the user to set a memory-allocation function.\nYou can use <a href=\"#luaL_newstate\"><code>luaL_newstate</code></a> from the standard library to\ncreate a state with a standard allocation function\n(based on <code>realloc</code>).\n</li>\n\n<li>\nFunctions <code>luaL_getn</code> and <code>luaL_setn</code>\n(from the auxiliary library) are deprecated.\nUse <a href=\"#lua_objlen\"><code>lua_objlen</code></a> instead of <code>luaL_getn</code>\nand nothing instead of <code>luaL_setn</code>.\n</li>\n\n<li>\nFunction <code>luaL_openlib</code> was replaced by <a href=\"#luaL_register\"><code>luaL_register</code></a>.\n</li>\n\n<li>\nFunction <code>luaL_checkudata</code> now throws an error when the given value\nis not a userdata of the expected type.\n(In Lua&nbsp;5.0 it returned <code>NULL</code>.)\n</li>\n\n</ul>\n\n\n\n\n<h1>8 - <a name=\"8\">The Complete Syntax of Lua</a></h1>\n\n<p>\nHere is the complete syntax of Lua in extended BNF.\n(It does not describe operator precedences.)\n\n\n\n\n<pre>\n\n\tchunk ::= {stat [`<b>;</b>&acute;]} [laststat [`<b>;</b>&acute;]]\n\n\tblock ::= chunk\n\n\tstat ::=  varlist `<b>=</b>&acute; explist | \n\t\t functioncall | \n\t\t <b>do</b> block <b>end</b> | \n\t\t <b>while</b> exp <b>do</b> block <b>end</b> | \n\t\t <b>repeat</b> block <b>until</b> exp | \n\t\t <b>if</b> exp <b>then</b> block {<b>elseif</b> exp <b>then</b> block} [<b>else</b> block] <b>end</b> | \n\t\t <b>for</b> Name `<b>=</b>&acute; exp `<b>,</b>&acute; exp [`<b>,</b>&acute; exp] <b>do</b> block <b>end</b> | \n\t\t <b>for</b> namelist <b>in</b> explist <b>do</b> block <b>end</b> | \n\t\t <b>function</b> funcname funcbody | \n\t\t <b>local</b> <b>function</b> Name funcbody | \n\t\t <b>local</b> namelist [`<b>=</b>&acute; explist] \n\n\tlaststat ::= <b>return</b> [explist] | <b>break</b>\n\n\tfuncname ::= Name {`<b>.</b>&acute; Name} [`<b>:</b>&acute; Name]\n\n\tvarlist ::= var {`<b>,</b>&acute; var}\n\n\tvar ::=  Name | prefixexp `<b>[</b>&acute; exp `<b>]</b>&acute; | prefixexp `<b>.</b>&acute; Name \n\n\tnamelist ::= Name {`<b>,</b>&acute; Name}\n\n\texplist ::= {exp `<b>,</b>&acute;} exp\n\n\texp ::=  <b>nil</b> | <b>false</b> | <b>true</b> | Number | String | `<b>...</b>&acute; | function | \n\t\t prefixexp | tableconstructor | exp binop exp | unop exp \n\n\tprefixexp ::= var | functioncall | `<b>(</b>&acute; exp `<b>)</b>&acute;\n\n\tfunctioncall ::=  prefixexp args | prefixexp `<b>:</b>&acute; Name args \n\n\targs ::=  `<b>(</b>&acute; [explist] `<b>)</b>&acute; | tableconstructor | String \n\n\tfunction ::= <b>function</b> funcbody\n\n\tfuncbody ::= `<b>(</b>&acute; [parlist] `<b>)</b>&acute; block <b>end</b>\n\n\tparlist ::= namelist [`<b>,</b>&acute; `<b>...</b>&acute;] | `<b>...</b>&acute;\n\n\ttableconstructor ::= `<b>{</b>&acute; [fieldlist] `<b>}</b>&acute;\n\n\tfieldlist ::= field {fieldsep field} [fieldsep]\n\n\tfield ::= `<b>[</b>&acute; exp `<b>]</b>&acute; `<b>=</b>&acute; exp | Name `<b>=</b>&acute; exp | exp\n\n\tfieldsep ::= `<b>,</b>&acute; | `<b>;</b>&acute;\n\n\tbinop ::= `<b>+</b>&acute; | `<b>-</b>&acute; | `<b>*</b>&acute; | `<b>/</b>&acute; | `<b>^</b>&acute; | `<b>%</b>&acute; | `<b>..</b>&acute; | \n\t\t `<b>&lt;</b>&acute; | `<b>&lt;=</b>&acute; | `<b>&gt;</b>&acute; | `<b>&gt;=</b>&acute; | `<b>==</b>&acute; | `<b>~=</b>&acute; | \n\t\t <b>and</b> | <b>or</b>\n\n\tunop ::= `<b>-</b>&acute; | <b>not</b> | `<b>#</b>&acute;\n\n</pre>\n\n<p>\n\n\n\n\n\n\n\n<HR>\n<SMALL CLASS=\"footer\">\nLast update:\nMon Feb 13 18:54:19 BRST 2012\n</SMALL>\n<!--\nLast change: revised for Lua 5.1.5\n-->\n\n</body></html>\n\n"
  },
  {
    "path": "deps/lua/doc/readme.html",
    "content": "<HTML>\n<HEAD>\n<TITLE>Lua documentation</TITLE>\n<LINK REL=\"stylesheet\" TYPE=\"text/css\" HREF=\"lua.css\">\n</HEAD>\n\n<BODY>\n\n<HR>\n<H1>\n<A HREF=\"http://www.lua.org/\"><IMG SRC=\"logo.gif\" ALT=\"Lua\" BORDER=0></A>\nDocumentation\n</H1>\n\nThis is the documentation included in the source distribution of Lua 5.1.5.\n\n<UL>\n<LI><A HREF=\"contents.html\">Reference manual</A>\n<LI><A HREF=\"lua.html\">lua man page</A>\n<LI><A HREF=\"luac.html\">luac man page</A>\n<LI><A HREF=\"../README\">lua/README</A>\n<LI><A HREF=\"../etc/README\">lua/etc/README</A>\n<LI><A HREF=\"../test/README\">lua/test/README</A>\n</UL>\n\nLua's\n<A HREF=\"http://www.lua.org/\">official web site</A>\ncontains updated documentation,\nespecially the\n<A HREF=\"http://www.lua.org/manual/5.1/\">reference manual</A>.\n<P>\n\n<HR>\n<SMALL>\nLast update:\nFri Feb  3 09:44:42 BRST 2012\n</SMALL>\n\n</BODY>\n</HTML>\n"
  },
  {
    "path": "deps/lua/etc/Makefile",
    "content": "# makefile for Lua etc\n\nTOP= ..\nLIB= $(TOP)/src\nINC= $(TOP)/src\nBIN= $(TOP)/src\nSRC= $(TOP)/src\nTST= $(TOP)/test\n\nCC= gcc\nCFLAGS= -O2 -Wall -I$(INC) $(MYCFLAGS)\nMYCFLAGS= \nMYLDFLAGS= -Wl,-E\nMYLIBS= -lm\n#MYLIBS= -lm -Wl,-E -ldl -lreadline -lhistory -lncurses\nRM= rm -f\n\ndefault:\n\t@echo 'Please choose a target: min noparser one strict clean'\n\nmin:\tmin.c\n\t$(CC) $(CFLAGS) $@.c -L$(LIB) -llua $(MYLIBS)\n\techo 'print\"Hello there!\"' | ./a.out\n\nnoparser: noparser.o\n\t$(CC) noparser.o $(SRC)/lua.o -L$(LIB) -llua $(MYLIBS)\n\t$(BIN)/luac $(TST)/hello.lua\n\t-./a.out luac.out\n\t-./a.out -e'a=1'\n\none:\n\t$(CC) $(CFLAGS) all.c $(MYLIBS)\n\t./a.out $(TST)/hello.lua\n\nstrict:\n\t-$(BIN)/lua -e 'print(a);b=2'\n\t-$(BIN)/lua -lstrict -e 'print(a)'\n\t-$(BIN)/lua -e 'function f() b=2 end f()'\n\t-$(BIN)/lua -lstrict -e 'function f() b=2 end f()'\n\nclean:\n\t$(RM) a.out core core.* *.o luac.out\n\n.PHONY:\tdefault min noparser one strict clean\n"
  },
  {
    "path": "deps/lua/etc/README",
    "content": "This directory contains some useful files and code.\nUnlike the code in ../src, everything here is in the public domain.\n\nIf any of the makes fail, you're probably not using the same libraries\nused to build Lua. Set MYLIBS in Makefile accordingly.\n\nall.c\n\tFull Lua interpreter in a single file.\n\tDo \"make one\" for a demo.\n\nlua.hpp\n\tLua header files for C++ using 'extern \"C\"'.\n\nlua.ico\n\tA Lua icon for Windows (and web sites: save as favicon.ico).\n\tDrawn by hand by Markus Gritsch <gritsch@iue.tuwien.ac.at>.\n\nlua.pc\n\tpkg-config data for Lua\n\nluavs.bat\n\tScript to build Lua under \"Visual Studio .NET Command Prompt\".\n\tRun it from the toplevel as etc\\luavs.bat.\n\nmin.c\n\tA minimal Lua interpreter.\n\tGood for learning and for starting your own.\n\tDo \"make min\" for a demo.\n\nnoparser.c\n\tLinking with noparser.o avoids loading the parsing modules in lualib.a.\n\tDo \"make noparser\" for a demo.\n\nstrict.lua\n\tTraps uses of undeclared global variables.\n\tDo \"make strict\" for a demo.\n\n"
  },
  {
    "path": "deps/lua/etc/all.c",
    "content": "/*\n* all.c -- Lua core, libraries and interpreter in a single file\n*/\n\n#define luaall_c\n\n#include \"lapi.c\"\n#include \"lcode.c\"\n#include \"ldebug.c\"\n#include \"ldo.c\"\n#include \"ldump.c\"\n#include \"lfunc.c\"\n#include \"lgc.c\"\n#include \"llex.c\"\n#include \"lmem.c\"\n#include \"lobject.c\"\n#include \"lopcodes.c\"\n#include \"lparser.c\"\n#include \"lstate.c\"\n#include \"lstring.c\"\n#include \"ltable.c\"\n#include \"ltm.c\"\n#include \"lundump.c\"\n#include \"lvm.c\"\n#include \"lzio.c\"\n\n#include \"lauxlib.c\"\n#include \"lbaselib.c\"\n#include \"ldblib.c\"\n#include \"liolib.c\"\n#include \"linit.c\"\n#include \"lmathlib.c\"\n#include \"loadlib.c\"\n#include \"loslib.c\"\n#include \"lstrlib.c\"\n#include \"ltablib.c\"\n\n#include \"lua.c\"\n"
  },
  {
    "path": "deps/lua/etc/lua.hpp",
    "content": "// lua.hpp\n// Lua header files for C++\n// <<extern \"C\">> not supplied automatically because Lua also compiles as C++\n\nextern \"C\" {\n#include \"lua.h\"\n#include \"lualib.h\"\n#include \"lauxlib.h\"\n}\n"
  },
  {
    "path": "deps/lua/etc/lua.pc",
    "content": "# lua.pc -- pkg-config data for Lua\n\n# vars from install Makefile\n\n# grep '^V=' ../Makefile\nV= 5.1\n# grep '^R=' ../Makefile\nR= 5.1.5\n\n# grep '^INSTALL_.*=' ../Makefile | sed 's/INSTALL_TOP/prefix/'\nprefix= /usr/local\nINSTALL_BIN= ${prefix}/bin\nINSTALL_INC= ${prefix}/include\nINSTALL_LIB= ${prefix}/lib\nINSTALL_MAN= ${prefix}/man/man1\nINSTALL_LMOD= ${prefix}/share/lua/${V}\nINSTALL_CMOD= ${prefix}/lib/lua/${V}\n\n# canonical vars\nexec_prefix=${prefix}\nlibdir=${exec_prefix}/lib\nincludedir=${prefix}/include\n\nName: Lua\nDescription: An Extensible Extension Language\nVersion: ${R}\nRequires: \nLibs: -L${libdir} -llua -lm\nCflags: -I${includedir}\n\n# (end of lua.pc)\n"
  },
  {
    "path": "deps/lua/etc/luavs.bat",
    "content": "@rem Script to build Lua under \"Visual Studio .NET Command Prompt\".\r\n@rem Do not run from this directory; run it from the toplevel: etc\\luavs.bat .\r\n@rem It creates lua51.dll, lua51.lib, lua.exe, and luac.exe in src.\r\n@rem (contributed by David Manura and Mike Pall)\r\n\r\n@setlocal\r\n@set MYCOMPILE=cl /nologo /MD /O2 /W3 /c /D_CRT_SECURE_NO_DEPRECATE\r\n@set MYLINK=link /nologo\r\n@set MYMT=mt /nologo\r\n\r\ncd src\r\n%MYCOMPILE% /DLUA_BUILD_AS_DLL l*.c\r\ndel lua.obj luac.obj\r\n%MYLINK% /DLL /out:lua51.dll l*.obj\r\nif exist lua51.dll.manifest^\r\n  %MYMT% -manifest lua51.dll.manifest -outputresource:lua51.dll;2\r\n%MYCOMPILE% /DLUA_BUILD_AS_DLL lua.c\r\n%MYLINK% /out:lua.exe lua.obj lua51.lib\r\nif exist lua.exe.manifest^\r\n  %MYMT% -manifest lua.exe.manifest -outputresource:lua.exe\r\n%MYCOMPILE% l*.c print.c\r\ndel lua.obj linit.obj lbaselib.obj ldblib.obj liolib.obj lmathlib.obj^\r\n    loslib.obj ltablib.obj lstrlib.obj loadlib.obj\r\n%MYLINK% /out:luac.exe *.obj\r\nif exist luac.exe.manifest^\r\n  %MYMT% -manifest luac.exe.manifest -outputresource:luac.exe\r\ndel *.obj *.manifest\r\ncd ..\r\n"
  },
  {
    "path": "deps/lua/etc/min.c",
    "content": "/*\n* min.c -- a minimal Lua interpreter\n* loads stdin only with minimal error handling.\n* no interaction, and no standard library, only a \"print\" function.\n*/\n\n#include <stdio.h>\n\n#include \"lua.h\"\n#include \"lauxlib.h\"\n\nstatic int print(lua_State *L)\n{\n int n=lua_gettop(L);\n int i;\n for (i=1; i<=n; i++)\n {\n  if (i>1) printf(\"\\t\");\n  if (lua_isstring(L,i))\n   printf(\"%s\",lua_tostring(L,i));\n  else if (lua_isnil(L,i))\n   printf(\"%s\",\"nil\");\n  else if (lua_isboolean(L,i))\n   printf(\"%s\",lua_toboolean(L,i) ? \"true\" : \"false\");\n  else\n   printf(\"%s:%p\",luaL_typename(L,i),lua_topointer(L,i));\n }\n printf(\"\\n\");\n return 0;\n}\n\nint main(void)\n{\n lua_State *L=lua_open();\n lua_register(L,\"print\",print);\n if (luaL_dofile(L,NULL)!=0) fprintf(stderr,\"%s\\n\",lua_tostring(L,-1));\n lua_close(L);\n return 0;\n}\n"
  },
  {
    "path": "deps/lua/etc/noparser.c",
    "content": "/*\n* The code below can be used to make a Lua core that does not contain the\n* parsing modules (lcode, llex, lparser), which represent 35% of the total core.\n* You'll only be able to load binary files and strings, precompiled with luac.\n* (Of course, you'll have to build luac with the original parsing modules!)\n*\n* To use this module, simply compile it (\"make noparser\" does that) and list\n* its object file before the Lua libraries. The linker should then not load\n* the parsing modules. To try it, do \"make luab\".\n*\n* If you also want to avoid the dump module (ldump.o), define NODUMP.\n* #define NODUMP\n*/\n\n#define LUA_CORE\n\n#include \"llex.h\"\n#include \"lparser.h\"\n#include \"lzio.h\"\n\nLUAI_FUNC void luaX_init (lua_State *L) {\n  UNUSED(L);\n}\n\nLUAI_FUNC Proto *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, const char *name) {\n  UNUSED(z);\n  UNUSED(buff);\n  UNUSED(name);\n  lua_pushliteral(L,\"parser not loaded\");\n  lua_error(L);\n  return NULL;\n}\n\n#ifdef NODUMP\n#include \"lundump.h\"\n\nLUAI_FUNC int luaU_dump (lua_State* L, const Proto* f, lua_Writer w, void* data, int strip) {\n  UNUSED(f);\n  UNUSED(w);\n  UNUSED(data);\n  UNUSED(strip);\n#if 1\n  UNUSED(L);\n  return 0;\n#else\n  lua_pushliteral(L,\"dumper not loaded\");\n  lua_error(L);\n#endif\n}\n#endif\n"
  },
  {
    "path": "deps/lua/etc/strict.lua",
    "content": "--\n-- strict.lua\n-- checks uses of undeclared global variables\n-- All global variables must be 'declared' through a regular assignment\n-- (even assigning nil will do) in a main chunk before being used\n-- anywhere or assigned to inside a function.\n--\n\nlocal getinfo, error, rawset, rawget = debug.getinfo, error, rawset, rawget\n\nlocal mt = getmetatable(_G)\nif mt == nil then\n  mt = {}\n  setmetatable(_G, mt)\nend\n\nmt.__declared = {}\n\nlocal function what ()\n  local d = getinfo(3, \"S\")\n  return d and d.what or \"C\"\nend\n\nmt.__newindex = function (t, n, v)\n  if not mt.__declared[n] then\n    local w = what()\n    if w ~= \"main\" and w ~= \"C\" then\n      error(\"assign to undeclared variable '\"..n..\"'\", 2)\n    end\n    mt.__declared[n] = true\n  end\n  rawset(t, n, v)\nend\n  \nmt.__index = function (t, n)\n  if not mt.__declared[n] and what() ~= \"C\" then\n    error(\"variable '\"..n..\"' is not declared\", 2)\n  end\n  return rawget(t, n)\nend\n\n"
  },
  {
    "path": "deps/lua/src/Makefile",
    "content": "# makefile for building Lua\n# see ../INSTALL for installation instructions\n# see ../Makefile and luaconf.h for further customization\n\n# == CHANGE THE SETTINGS BELOW TO SUIT YOUR ENVIRONMENT =======================\n\n# Your platform. See PLATS for possible values.\nPLAT= none\n\nCC?= gcc\nCFLAGS= -O2 -Wall $(MYCFLAGS)\nAR= ar rcu\nRANLIB= ranlib\nRM= rm -f\nLIBS= -lm $(MYLIBS)\n\nMYCFLAGS=\nMYLDFLAGS=\nMYLIBS=\n\n# == END OF USER SETTINGS. NO NEED TO CHANGE ANYTHING BELOW THIS LINE =========\n\nPLATS= aix ansi bsd freebsd generic linux macosx mingw posix solaris\n\nLUA_A=\tliblua.a\nCORE_O=\tlapi.o lcode.o ldebug.o ldo.o ldump.o lfunc.o lgc.o llex.o lmem.o \\\n\tlobject.o lopcodes.o lparser.o lstate.o lstring.o ltable.o ltm.o  \\\n\tlundump.o lvm.o lzio.o strbuf.o\nLIB_O=\tlauxlib.o lbaselib.o ldblib.o liolib.o lmathlib.o loslib.o ltablib.o \\\n\tlstrlib.o loadlib.o linit.o lua_cjson.o lua_struct.o lua_cmsgpack.o\n\nLUA_T=\tlua\nLUA_O=\tlua.o\n\nLUAC_T=\tluac\nLUAC_O=\tluac.o print.o\n\nALL_O= $(CORE_O) $(LIB_O) $(LUA_O) $(LUAC_O)\nALL_T= $(LUA_A) $(LUA_T) $(LUAC_T)\nALL_A= $(LUA_A)\n\ndefault: $(PLAT)\n\nall:\t$(ALL_T)\n\no:\t$(ALL_O)\n\na:\t$(ALL_A)\n\n$(LUA_A): $(CORE_O) $(LIB_O)\n\t$(AR) $@ $(CORE_O) $(LIB_O)\t# DLL needs all object files\n\t$(RANLIB) $@\n\n$(LUA_T): $(LUA_O) $(LUA_A)\n\t$(CC) -o $@ $(MYLDFLAGS) $(LUA_O) $(LUA_A) $(LIBS)\n\n$(LUAC_T): $(LUAC_O) $(LUA_A)\n\t$(CC) -o $@ $(MYLDFLAGS) $(LUAC_O) $(LUA_A) $(LIBS)\n\nclean:\n\t$(RM) $(ALL_T) $(ALL_O)\n\ndepend:\n\t@$(CC) $(CFLAGS) -MM l*.c print.c\n\necho:\n\t@echo \"PLAT = $(PLAT)\"\n\t@echo \"CC = $(CC)\"\n\t@echo \"CFLAGS = $(CFLAGS)\"\n\t@echo \"AR = $(AR)\"\n\t@echo \"RANLIB = $(RANLIB)\"\n\t@echo \"RM = $(RM)\"\n\t@echo \"MYCFLAGS = $(MYCFLAGS)\"\n\t@echo \"MYLDFLAGS = $(MYLDFLAGS)\"\n\t@echo \"MYLIBS = $(MYLIBS)\"\n\n# convenience targets for popular platforms\n\nnone:\n\t@echo \"Please choose a platform:\"\n\t@echo \"   $(PLATS)\"\n\naix:\n\t$(MAKE) all CC=\"xlc\" CFLAGS=\"-O2 -DLUA_USE_POSIX -DLUA_USE_DLOPEN\" MYLIBS=\"-ldl\" MYLDFLAGS=\"-brtl -bexpall\"\n\nansi:\n\t$(MAKE) all MYCFLAGS=-DLUA_ANSI\n\nbsd:\n\t$(MAKE) all MYCFLAGS=\"-DLUA_USE_POSIX -DLUA_USE_DLOPEN\" MYLIBS=\"-Wl,-E\"\n\nfreebsd:\n\t$(MAKE) all MYCFLAGS=\"-DLUA_USE_LINUX\" MYLIBS=\"-Wl,-E -lreadline\"\n\ngeneric:\n\t$(MAKE) all MYCFLAGS=\n\nlinux:\n\t$(MAKE) all MYCFLAGS=-DLUA_USE_LINUX MYLIBS=\"-Wl,-E -ldl -lreadline -lhistory -lncurses\"\n\nmacosx:\n\t$(MAKE) all MYCFLAGS=-DLUA_USE_LINUX MYLIBS=\"-lreadline\"\n# use this on Mac OS X 10.3-\n#\t$(MAKE) all MYCFLAGS=-DLUA_USE_MACOSX\n\nmingw:\n\t$(MAKE) \"LUA_A=lua51.dll\" \"LUA_T=lua.exe\" \\\n\t\"AR=$(CC) -shared -o\" \"RANLIB=strip --strip-unneeded\" \\\n\t\"MYCFLAGS=-DLUA_BUILD_AS_DLL\" \"MYLIBS=\" \"MYLDFLAGS=-s\" lua.exe\n\t$(MAKE) \"LUAC_T=luac.exe\" luac.exe\n\nposix:\n\t$(MAKE) all MYCFLAGS=-DLUA_USE_POSIX\n\nsolaris:\n\t$(MAKE) all MYCFLAGS=\"-DLUA_USE_POSIX -DLUA_USE_DLOPEN\" MYLIBS=\"-ldl\"\n\n# list targets that do not create files (but not all makes understand .PHONY)\n.PHONY: all $(PLATS) default o a clean depend echo none\n\n# DO NOT DELETE\n\nlapi.o: lapi.c lua.h luaconf.h lapi.h lobject.h llimits.h ldebug.h \\\n  lstate.h ltm.h lzio.h lmem.h ldo.h lfunc.h lgc.h lstring.h ltable.h \\\n  lundump.h lvm.h\nlauxlib.o: lauxlib.c lua.h luaconf.h lauxlib.h\nlbaselib.o: lbaselib.c lua.h luaconf.h lauxlib.h lualib.h\nlcode.o: lcode.c lua.h luaconf.h lcode.h llex.h lobject.h llimits.h \\\n  lzio.h lmem.h lopcodes.h lparser.h ldebug.h lstate.h ltm.h ldo.h lgc.h \\\n  ltable.h\nldblib.o: ldblib.c lua.h luaconf.h lauxlib.h lualib.h\nldebug.o: ldebug.c lua.h luaconf.h lapi.h lobject.h llimits.h lcode.h \\\n  llex.h lzio.h lmem.h lopcodes.h lparser.h ldebug.h lstate.h ltm.h ldo.h \\\n  lfunc.h lstring.h lgc.h ltable.h lvm.h\nldo.o: ldo.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h ltm.h \\\n  lzio.h lmem.h ldo.h lfunc.h lgc.h lopcodes.h lparser.h lstring.h \\\n  ltable.h lundump.h lvm.h\nldump.o: ldump.c lua.h luaconf.h lobject.h llimits.h lstate.h ltm.h \\\n  lzio.h lmem.h lundump.h\nlfunc.o: lfunc.c lua.h luaconf.h lfunc.h lobject.h llimits.h lgc.h lmem.h \\\n  lstate.h ltm.h lzio.h\nlgc.o: lgc.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h ltm.h \\\n  lzio.h lmem.h ldo.h lfunc.h lgc.h lstring.h ltable.h\nlinit.o: linit.c lua.h luaconf.h lualib.h lauxlib.h\nliolib.o: liolib.c lua.h luaconf.h lauxlib.h lualib.h\nllex.o: llex.c lua.h luaconf.h ldo.h lobject.h llimits.h lstate.h ltm.h \\\n  lzio.h lmem.h llex.h lparser.h lstring.h lgc.h ltable.h\nlmathlib.o: lmathlib.c lua.h luaconf.h lauxlib.h lualib.h\nlmem.o: lmem.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h \\\n  ltm.h lzio.h lmem.h ldo.h\nloadlib.o: loadlib.c lua.h luaconf.h lauxlib.h lualib.h\nlobject.o: lobject.c lua.h luaconf.h ldo.h lobject.h llimits.h lstate.h \\\n  ltm.h lzio.h lmem.h lstring.h lgc.h lvm.h\nlopcodes.o: lopcodes.c lopcodes.h llimits.h lua.h luaconf.h\nloslib.o: loslib.c lua.h luaconf.h lauxlib.h lualib.h\nlparser.o: lparser.c lua.h luaconf.h lcode.h llex.h lobject.h llimits.h \\\n  lzio.h lmem.h lopcodes.h lparser.h ldebug.h lstate.h ltm.h ldo.h \\\n  lfunc.h lstring.h lgc.h ltable.h\nlstate.o: lstate.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h \\\n  ltm.h lzio.h lmem.h ldo.h lfunc.h lgc.h llex.h lstring.h ltable.h\nlstring.o: lstring.c lua.h luaconf.h lmem.h llimits.h lobject.h lstate.h \\\n  ltm.h lzio.h lstring.h lgc.h\nlstrlib.o: lstrlib.c lua.h luaconf.h lauxlib.h lualib.h\nltable.o: ltable.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h \\\n  ltm.h lzio.h lmem.h ldo.h lgc.h ltable.h\nltablib.o: ltablib.c lua.h luaconf.h lauxlib.h lualib.h\nltm.o: ltm.c lua.h luaconf.h lobject.h llimits.h lstate.h ltm.h lzio.h \\\n  lmem.h lstring.h lgc.h ltable.h\nlua.o: lua.c lua.h luaconf.h lauxlib.h lualib.h\nluac.o: luac.c lua.h luaconf.h lauxlib.h ldo.h lobject.h llimits.h \\\n  lstate.h ltm.h lzio.h lmem.h lfunc.h lopcodes.h lstring.h lgc.h \\\n  lundump.h\nlundump.o: lundump.c lua.h luaconf.h ldebug.h lstate.h lobject.h \\\n  llimits.h ltm.h lzio.h lmem.h ldo.h lfunc.h lstring.h lgc.h lundump.h\nlvm.o: lvm.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h ltm.h \\\n  lzio.h lmem.h ldo.h lfunc.h lgc.h lopcodes.h lstring.h ltable.h lvm.h\nlzio.o: lzio.c lua.h luaconf.h llimits.h lmem.h lstate.h lobject.h ltm.h \\\n  lzio.h\nprint.o: print.c ldebug.h lstate.h lua.h luaconf.h lobject.h llimits.h \\\n  ltm.h lzio.h lmem.h lopcodes.h lundump.h\n\n# (end of Makefile)\n"
  },
  {
    "path": "deps/lua/src/lapi.c",
    "content": "/*\n** $Id: lapi.c,v 2.55.1.5 2008/07/04 18:41:18 roberto Exp $\n** Lua API\n** See Copyright Notice in lua.h\n*/\n\n\n#include <assert.h>\n#include <math.h>\n#include <stdarg.h>\n#include <string.h>\n\n#define lapi_c\n#define LUA_CORE\n\n#include \"lua.h\"\n\n#include \"lapi.h\"\n#include \"ldebug.h\"\n#include \"ldo.h\"\n#include \"lfunc.h\"\n#include \"lgc.h\"\n#include \"lmem.h\"\n#include \"lobject.h\"\n#include \"lstate.h\"\n#include \"lstring.h\"\n#include \"ltable.h\"\n#include \"ltm.h\"\n#include \"lundump.h\"\n#include \"lvm.h\"\n\n\n\nconst char lua_ident[] =\n  \"$Lua: \" LUA_RELEASE \" \" LUA_COPYRIGHT \" $\\n\"\n  \"$Authors: \" LUA_AUTHORS \" $\\n\"\n  \"$URL: www.lua.org $\\n\";\n\n\n\n#define api_checknelems(L, n)\tapi_check(L, (n) <= (L->top - L->base))\n\n#define api_checkvalidindex(L, i)\tapi_check(L, (i) != luaO_nilobject)\n\n#define api_incr_top(L)   {api_check(L, L->top < L->ci->top); L->top++;}\n\n\n\nstatic TValue *index2adr (lua_State *L, int idx) {\n  if (idx > 0) {\n    TValue *o = L->base + (idx - 1);\n    api_check(L, idx <= L->ci->top - L->base);\n    if (o >= L->top) return cast(TValue *, luaO_nilobject);\n    else return o;\n  }\n  else if (idx > LUA_REGISTRYINDEX) {\n    api_check(L, idx != 0 && -idx <= L->top - L->base);\n    return L->top + idx;\n  }\n  else switch (idx) {  /* pseudo-indices */\n    case LUA_REGISTRYINDEX: return registry(L);\n    case LUA_ENVIRONINDEX: {\n      Closure *func = curr_func(L);\n      sethvalue(L, &L->env, func->c.env);\n      return &L->env;\n    }\n    case LUA_GLOBALSINDEX: return gt(L);\n    default: {\n      Closure *func = curr_func(L);\n      idx = LUA_GLOBALSINDEX - idx;\n      return (idx <= func->c.nupvalues)\n                ? &func->c.upvalue[idx-1]\n                : cast(TValue *, luaO_nilobject);\n    }\n  }\n}\n\n\nstatic Table *getcurrenv (lua_State *L) {\n  if (L->ci == L->base_ci)  /* no enclosing function? */\n    return hvalue(gt(L));  /* use global table as environment */\n  else {\n    Closure *func = curr_func(L);\n    return func->c.env;\n  }\n}\n\n\nvoid luaA_pushobject (lua_State *L, const TValue *o) {\n  setobj2s(L, L->top, o);\n  api_incr_top(L);\n}\n\n\nLUA_API int lua_checkstack (lua_State *L, int size) {\n  int res = 1;\n  lua_lock(L);\n  if (size > LUAI_MAXCSTACK || (L->top - L->base + size) > LUAI_MAXCSTACK)\n    res = 0;  /* stack overflow */\n  else if (size > 0) {\n    luaD_checkstack(L, size);\n    if (L->ci->top < L->top + size)\n      L->ci->top = L->top + size;\n  }\n  lua_unlock(L);\n  return res;\n}\n\n\nLUA_API void lua_xmove (lua_State *from, lua_State *to, int n) {\n  int i;\n  if (from == to) return;\n  lua_lock(to);\n  api_checknelems(from, n);\n  api_check(from, G(from) == G(to));\n  api_check(from, to->ci->top - to->top >= n);\n  from->top -= n;\n  for (i = 0; i < n; i++) {\n    setobj2s(to, to->top++, from->top + i);\n  }\n  lua_unlock(to);\n}\n\n\nLUA_API void lua_setlevel (lua_State *from, lua_State *to) {\n  to->nCcalls = from->nCcalls;\n}\n\n\nLUA_API lua_CFunction lua_atpanic (lua_State *L, lua_CFunction panicf) {\n  lua_CFunction old;\n  lua_lock(L);\n  old = G(L)->panic;\n  G(L)->panic = panicf;\n  lua_unlock(L);\n  return old;\n}\n\n\nLUA_API lua_State *lua_newthread (lua_State *L) {\n  lua_State *L1;\n  lua_lock(L);\n  luaC_checkGC(L);\n  L1 = luaE_newthread(L);\n  setthvalue(L, L->top, L1);\n  api_incr_top(L);\n  lua_unlock(L);\n  luai_userstatethread(L, L1);\n  return L1;\n}\n\n\n\n/*\n** basic stack manipulation\n*/\n\n\nLUA_API int lua_gettop (lua_State *L) {\n  return cast_int(L->top - L->base);\n}\n\n\nLUA_API void lua_settop (lua_State *L, int idx) {\n  lua_lock(L);\n  if (idx >= 0) {\n    api_check(L, idx <= L->stack_last - L->base);\n    while (L->top < L->base + idx)\n      setnilvalue(L->top++);\n    L->top = L->base + idx;\n  }\n  else {\n    api_check(L, -(idx+1) <= (L->top - L->base));\n    L->top += idx+1;  /* `subtract' index (index is negative) */\n  }\n  lua_unlock(L);\n}\n\n\nLUA_API void lua_remove (lua_State *L, int idx) {\n  StkId p;\n  lua_lock(L);\n  p = index2adr(L, idx);\n  api_checkvalidindex(L, p);\n  while (++p < L->top) setobjs2s(L, p-1, p);\n  L->top--;\n  lua_unlock(L);\n}\n\n\nLUA_API void lua_insert (lua_State *L, int idx) {\n  StkId p;\n  StkId q;\n  lua_lock(L);\n  p = index2adr(L, idx);\n  api_checkvalidindex(L, p);\n  for (q = L->top; q>p; q--) setobjs2s(L, q, q-1);\n  setobjs2s(L, p, L->top);\n  lua_unlock(L);\n}\n\n\nLUA_API void lua_replace (lua_State *L, int idx) {\n  StkId o;\n  lua_lock(L);\n  /* explicit test for incompatible code */\n  if (idx == LUA_ENVIRONINDEX && L->ci == L->base_ci)\n    luaG_runerror(L, \"no calling environment\");\n  api_checknelems(L, 1);\n  o = index2adr(L, idx);\n  api_checkvalidindex(L, o);\n  if (idx == LUA_ENVIRONINDEX) {\n    Closure *func = curr_func(L);\n    api_check(L, ttistable(L->top - 1)); \n    func->c.env = hvalue(L->top - 1);\n    luaC_barrier(L, func, L->top - 1);\n  }\n  else {\n    setobj(L, o, L->top - 1);\n    if (idx < LUA_GLOBALSINDEX)  /* function upvalue? */\n      luaC_barrier(L, curr_func(L), L->top - 1);\n  }\n  L->top--;\n  lua_unlock(L);\n}\n\n\nLUA_API void lua_pushvalue (lua_State *L, int idx) {\n  lua_lock(L);\n  setobj2s(L, L->top, index2adr(L, idx));\n  api_incr_top(L);\n  lua_unlock(L);\n}\n\n\n\n/*\n** access functions (stack -> C)\n*/\n\n\nLUA_API int lua_type (lua_State *L, int idx) {\n  StkId o = index2adr(L, idx);\n  return (o == luaO_nilobject) ? LUA_TNONE : ttype(o);\n}\n\n\nLUA_API const char *lua_typename (lua_State *L, int t) {\n  UNUSED(L);\n  return (t == LUA_TNONE) ? \"no value\" : luaT_typenames[t];\n}\n\n\nLUA_API int lua_iscfunction (lua_State *L, int idx) {\n  StkId o = index2adr(L, idx);\n  return iscfunction(o);\n}\n\n\nLUA_API int lua_isnumber (lua_State *L, int idx) {\n  TValue n;\n  const TValue *o = index2adr(L, idx);\n  return tonumber(o, &n);\n}\n\n\nLUA_API int lua_isstring (lua_State *L, int idx) {\n  int t = lua_type(L, idx);\n  return (t == LUA_TSTRING || t == LUA_TNUMBER);\n}\n\n\nLUA_API int lua_isuserdata (lua_State *L, int idx) {\n  const TValue *o = index2adr(L, idx);\n  return (ttisuserdata(o) || ttislightuserdata(o));\n}\n\n\nLUA_API int lua_rawequal (lua_State *L, int index1, int index2) {\n  StkId o1 = index2adr(L, index1);\n  StkId o2 = index2adr(L, index2);\n  return (o1 == luaO_nilobject || o2 == luaO_nilobject) ? 0\n         : luaO_rawequalObj(o1, o2);\n}\n\n\nLUA_API int lua_equal (lua_State *L, int index1, int index2) {\n  StkId o1, o2;\n  int i;\n  lua_lock(L);  /* may call tag method */\n  o1 = index2adr(L, index1);\n  o2 = index2adr(L, index2);\n  i = (o1 == luaO_nilobject || o2 == luaO_nilobject) ? 0 : equalobj(L, o1, o2);\n  lua_unlock(L);\n  return i;\n}\n\n\nLUA_API int lua_lessthan (lua_State *L, int index1, int index2) {\n  StkId o1, o2;\n  int i;\n  lua_lock(L);  /* may call tag method */\n  o1 = index2adr(L, index1);\n  o2 = index2adr(L, index2);\n  i = (o1 == luaO_nilobject || o2 == luaO_nilobject) ? 0\n       : luaV_lessthan(L, o1, o2);\n  lua_unlock(L);\n  return i;\n}\n\n\n\nLUA_API lua_Number lua_tonumber (lua_State *L, int idx) {\n  TValue n;\n  const TValue *o = index2adr(L, idx);\n  if (tonumber(o, &n))\n    return nvalue(o);\n  else\n    return 0;\n}\n\n\nLUA_API lua_Integer lua_tointeger (lua_State *L, int idx) {\n  TValue n;\n  const TValue *o = index2adr(L, idx);\n  if (tonumber(o, &n)) {\n    lua_Integer res;\n    lua_Number num = nvalue(o);\n    lua_number2integer(res, num);\n    return res;\n  }\n  else\n    return 0;\n}\n\n\nLUA_API int lua_toboolean (lua_State *L, int idx) {\n  const TValue *o = index2adr(L, idx);\n  return !l_isfalse(o);\n}\n\n\nLUA_API const char *lua_tolstring (lua_State *L, int idx, size_t *len) {\n  StkId o = index2adr(L, idx);\n  if (!ttisstring(o)) {\n    lua_lock(L);  /* `luaV_tostring' may create a new string */\n    if (!luaV_tostring(L, o)) {  /* conversion failed? */\n      if (len != NULL) *len = 0;\n      lua_unlock(L);\n      return NULL;\n    }\n    luaC_checkGC(L);\n    o = index2adr(L, idx);  /* previous call may reallocate the stack */\n    lua_unlock(L);\n  }\n  if (len != NULL) *len = tsvalue(o)->len;\n  return svalue(o);\n}\n\n\nLUA_API size_t lua_objlen (lua_State *L, int idx) {\n  StkId o = index2adr(L, idx);\n  switch (ttype(o)) {\n    case LUA_TSTRING: return tsvalue(o)->len;\n    case LUA_TUSERDATA: return uvalue(o)->len;\n    case LUA_TTABLE: return luaH_getn(hvalue(o));\n    case LUA_TNUMBER: {\n      size_t l;\n      lua_lock(L);  /* `luaV_tostring' may create a new string */\n      l = (luaV_tostring(L, o) ? tsvalue(o)->len : 0);\n      lua_unlock(L);\n      return l;\n    }\n    default: return 0;\n  }\n}\n\n\nLUA_API lua_CFunction lua_tocfunction (lua_State *L, int idx) {\n  StkId o = index2adr(L, idx);\n  return (!iscfunction(o)) ? NULL : clvalue(o)->c.f;\n}\n\n\nLUA_API void *lua_touserdata (lua_State *L, int idx) {\n  StkId o = index2adr(L, idx);\n  switch (ttype(o)) {\n    case LUA_TUSERDATA: return (rawuvalue(o) + 1);\n    case LUA_TLIGHTUSERDATA: return pvalue(o);\n    default: return NULL;\n  }\n}\n\n\nLUA_API lua_State *lua_tothread (lua_State *L, int idx) {\n  StkId o = index2adr(L, idx);\n  return (!ttisthread(o)) ? NULL : thvalue(o);\n}\n\n\nLUA_API const void *lua_topointer (lua_State *L, int idx) {\n  StkId o = index2adr(L, idx);\n  switch (ttype(o)) {\n    case LUA_TTABLE: return hvalue(o);\n    case LUA_TFUNCTION: return clvalue(o);\n    case LUA_TTHREAD: return thvalue(o);\n    case LUA_TUSERDATA:\n    case LUA_TLIGHTUSERDATA:\n      return lua_touserdata(L, idx);\n    default: return NULL;\n  }\n}\n\n\n\n/*\n** push functions (C -> stack)\n*/\n\n\nLUA_API void lua_pushnil (lua_State *L) {\n  lua_lock(L);\n  setnilvalue(L->top);\n  api_incr_top(L);\n  lua_unlock(L);\n}\n\n\nLUA_API void lua_pushnumber (lua_State *L, lua_Number n) {\n  lua_lock(L);\n  setnvalue(L->top, n);\n  api_incr_top(L);\n  lua_unlock(L);\n}\n\n\nLUA_API void lua_pushinteger (lua_State *L, lua_Integer n) {\n  lua_lock(L);\n  setnvalue(L->top, cast_num(n));\n  api_incr_top(L);\n  lua_unlock(L);\n}\n\n\nLUA_API void lua_pushlstring (lua_State *L, const char *s, size_t len) {\n  lua_lock(L);\n  luaC_checkGC(L);\n  setsvalue2s(L, L->top, luaS_newlstr(L, s, len));\n  api_incr_top(L);\n  lua_unlock(L);\n}\n\n\nLUA_API void lua_pushstring (lua_State *L, const char *s) {\n  if (s == NULL)\n    lua_pushnil(L);\n  else\n    lua_pushlstring(L, s, strlen(s));\n}\n\n\nLUA_API const char *lua_pushvfstring (lua_State *L, const char *fmt,\n                                      va_list argp) {\n  const char *ret;\n  lua_lock(L);\n  luaC_checkGC(L);\n  ret = luaO_pushvfstring(L, fmt, argp);\n  lua_unlock(L);\n  return ret;\n}\n\n\nLUA_API const char *lua_pushfstring (lua_State *L, const char *fmt, ...) {\n  const char *ret;\n  va_list argp;\n  lua_lock(L);\n  luaC_checkGC(L);\n  va_start(argp, fmt);\n  ret = luaO_pushvfstring(L, fmt, argp);\n  va_end(argp);\n  lua_unlock(L);\n  return ret;\n}\n\n\nLUA_API void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n) {\n  Closure *cl;\n  lua_lock(L);\n  luaC_checkGC(L);\n  api_checknelems(L, n);\n  cl = luaF_newCclosure(L, n, getcurrenv(L));\n  cl->c.f = fn;\n  L->top -= n;\n  while (n--)\n    setobj2n(L, &cl->c.upvalue[n], L->top+n);\n  setclvalue(L, L->top, cl);\n  lua_assert(iswhite(obj2gco(cl)));\n  api_incr_top(L);\n  lua_unlock(L);\n}\n\n\nLUA_API void lua_pushboolean (lua_State *L, int b) {\n  lua_lock(L);\n  setbvalue(L->top, (b != 0));  /* ensure that true is 1 */\n  api_incr_top(L);\n  lua_unlock(L);\n}\n\n\nLUA_API void lua_pushlightuserdata (lua_State *L, void *p) {\n  lua_lock(L);\n  setpvalue(L->top, p);\n  api_incr_top(L);\n  lua_unlock(L);\n}\n\n\nLUA_API int lua_pushthread (lua_State *L) {\n  lua_lock(L);\n  setthvalue(L, L->top, L);\n  api_incr_top(L);\n  lua_unlock(L);\n  return (G(L)->mainthread == L);\n}\n\n\n\n/*\n** get functions (Lua -> stack)\n*/\n\n\nLUA_API void lua_gettable (lua_State *L, int idx) {\n  StkId t;\n  lua_lock(L);\n  t = index2adr(L, idx);\n  api_checkvalidindex(L, t);\n  luaV_gettable(L, t, L->top - 1, L->top - 1);\n  lua_unlock(L);\n}\n\n\nLUA_API void lua_getfield (lua_State *L, int idx, const char *k) {\n  StkId t;\n  TValue key;\n  lua_lock(L);\n  t = index2adr(L, idx);\n  api_checkvalidindex(L, t);\n  setsvalue(L, &key, luaS_new(L, k));\n  luaV_gettable(L, t, &key, L->top);\n  api_incr_top(L);\n  lua_unlock(L);\n}\n\n\nLUA_API void lua_rawget (lua_State *L, int idx) {\n  StkId t;\n  lua_lock(L);\n  t = index2adr(L, idx);\n  api_check(L, ttistable(t));\n  setobj2s(L, L->top - 1, luaH_get(hvalue(t), L->top - 1));\n  lua_unlock(L);\n}\n\n\nLUA_API void lua_rawgeti (lua_State *L, int idx, int n) {\n  StkId o;\n  lua_lock(L);\n  o = index2adr(L, idx);\n  api_check(L, ttistable(o));\n  setobj2s(L, L->top, luaH_getnum(hvalue(o), n));\n  api_incr_top(L);\n  lua_unlock(L);\n}\n\n\nLUA_API void lua_createtable (lua_State *L, int narray, int nrec) {\n  lua_lock(L);\n  luaC_checkGC(L);\n  sethvalue(L, L->top, luaH_new(L, narray, nrec));\n  api_incr_top(L);\n  lua_unlock(L);\n}\n\n\nLUA_API int lua_getmetatable (lua_State *L, int objindex) {\n  const TValue *obj;\n  Table *mt = NULL;\n  int res;\n  lua_lock(L);\n  obj = index2adr(L, objindex);\n  switch (ttype(obj)) {\n    case LUA_TTABLE:\n      mt = hvalue(obj)->metatable;\n      break;\n    case LUA_TUSERDATA:\n      mt = uvalue(obj)->metatable;\n      break;\n    default:\n      mt = G(L)->mt[ttype(obj)];\n      break;\n  }\n  if (mt == NULL)\n    res = 0;\n  else {\n    sethvalue(L, L->top, mt);\n    api_incr_top(L);\n    res = 1;\n  }\n  lua_unlock(L);\n  return res;\n}\n\n\nLUA_API void lua_getfenv (lua_State *L, int idx) {\n  StkId o;\n  lua_lock(L);\n  o = index2adr(L, idx);\n  api_checkvalidindex(L, o);\n  switch (ttype(o)) {\n    case LUA_TFUNCTION:\n      sethvalue(L, L->top, clvalue(o)->c.env);\n      break;\n    case LUA_TUSERDATA:\n      sethvalue(L, L->top, uvalue(o)->env);\n      break;\n    case LUA_TTHREAD:\n      setobj2s(L, L->top,  gt(thvalue(o)));\n      break;\n    default:\n      setnilvalue(L->top);\n      break;\n  }\n  api_incr_top(L);\n  lua_unlock(L);\n}\n\n\n/*\n** set functions (stack -> Lua)\n*/\n\n\nLUA_API void lua_settable (lua_State *L, int idx) {\n  StkId t;\n  lua_lock(L);\n  api_checknelems(L, 2);\n  t = index2adr(L, idx);\n  api_checkvalidindex(L, t);\n  luaV_settable(L, t, L->top - 2, L->top - 1);\n  L->top -= 2;  /* pop index and value */\n  lua_unlock(L);\n}\n\n\nLUA_API void lua_setfield (lua_State *L, int idx, const char *k) {\n  StkId t;\n  TValue key;\n  lua_lock(L);\n  api_checknelems(L, 1);\n  t = index2adr(L, idx);\n  api_checkvalidindex(L, t);\n  setsvalue(L, &key, luaS_new(L, k));\n  luaV_settable(L, t, &key, L->top - 1);\n  L->top--;  /* pop value */\n  lua_unlock(L);\n}\n\n\nLUA_API void lua_rawset (lua_State *L, int idx) {\n  StkId t;\n  lua_lock(L);\n  api_checknelems(L, 2);\n  t = index2adr(L, idx);\n  api_check(L, ttistable(t));\n  setobj2t(L, luaH_set(L, hvalue(t), L->top-2), L->top-1);\n  luaC_barriert(L, hvalue(t), L->top-1);\n  L->top -= 2;\n  lua_unlock(L);\n}\n\n\nLUA_API void lua_rawseti (lua_State *L, int idx, int n) {\n  StkId o;\n  lua_lock(L);\n  api_checknelems(L, 1);\n  o = index2adr(L, idx);\n  api_check(L, ttistable(o));\n  setobj2t(L, luaH_setnum(L, hvalue(o), n), L->top-1);\n  luaC_barriert(L, hvalue(o), L->top-1);\n  L->top--;\n  lua_unlock(L);\n}\n\n\nLUA_API int lua_setmetatable (lua_State *L, int objindex) {\n  TValue *obj;\n  Table *mt;\n  lua_lock(L);\n  api_checknelems(L, 1);\n  obj = index2adr(L, objindex);\n  api_checkvalidindex(L, obj);\n  if (ttisnil(L->top - 1))\n    mt = NULL;\n  else {\n    api_check(L, ttistable(L->top - 1));\n    mt = hvalue(L->top - 1);\n  }\n  switch (ttype(obj)) {\n    case LUA_TTABLE: {\n      hvalue(obj)->metatable = mt;\n      if (mt)\n        luaC_objbarriert(L, hvalue(obj), mt);\n      break;\n    }\n    case LUA_TUSERDATA: {\n      uvalue(obj)->metatable = mt;\n      if (mt)\n        luaC_objbarrier(L, rawuvalue(obj), mt);\n      break;\n    }\n    default: {\n      G(L)->mt[ttype(obj)] = mt;\n      break;\n    }\n  }\n  L->top--;\n  lua_unlock(L);\n  return 1;\n}\n\n\nLUA_API int lua_setfenv (lua_State *L, int idx) {\n  StkId o;\n  int res = 1;\n  lua_lock(L);\n  api_checknelems(L, 1);\n  o = index2adr(L, idx);\n  api_checkvalidindex(L, o);\n  api_check(L, ttistable(L->top - 1));\n  switch (ttype(o)) {\n    case LUA_TFUNCTION:\n      clvalue(o)->c.env = hvalue(L->top - 1);\n      break;\n    case LUA_TUSERDATA:\n      uvalue(o)->env = hvalue(L->top - 1);\n      break;\n    case LUA_TTHREAD:\n      sethvalue(L, gt(thvalue(o)), hvalue(L->top - 1));\n      break;\n    default:\n      res = 0;\n      break;\n  }\n  if (res) luaC_objbarrier(L, gcvalue(o), hvalue(L->top - 1));\n  L->top--;\n  lua_unlock(L);\n  return res;\n}\n\n\n/*\n** `load' and `call' functions (run Lua code)\n*/\n\n\n#define adjustresults(L,nres) \\\n    { if (nres == LUA_MULTRET && L->top >= L->ci->top) L->ci->top = L->top; }\n\n\n#define checkresults(L,na,nr) \\\n     api_check(L, (nr) == LUA_MULTRET || (L->ci->top - L->top >= (nr) - (na)))\n\t\n\nLUA_API void lua_call (lua_State *L, int nargs, int nresults) {\n  StkId func;\n  lua_lock(L);\n  api_checknelems(L, nargs+1);\n  checkresults(L, nargs, nresults);\n  func = L->top - (nargs+1);\n  luaD_call(L, func, nresults);\n  adjustresults(L, nresults);\n  lua_unlock(L);\n}\n\n\n\n/*\n** Execute a protected call.\n*/\nstruct CallS {  /* data to `f_call' */\n  StkId func;\n  int nresults;\n};\n\n\nstatic void f_call (lua_State *L, void *ud) {\n  struct CallS *c = cast(struct CallS *, ud);\n  luaD_call(L, c->func, c->nresults);\n}\n\n\n\nLUA_API int lua_pcall (lua_State *L, int nargs, int nresults, int errfunc) {\n  struct CallS c;\n  int status;\n  ptrdiff_t func;\n  lua_lock(L);\n  api_checknelems(L, nargs+1);\n  checkresults(L, nargs, nresults);\n  if (errfunc == 0)\n    func = 0;\n  else {\n    StkId o = index2adr(L, errfunc);\n    api_checkvalidindex(L, o);\n    func = savestack(L, o);\n  }\n  c.func = L->top - (nargs+1);  /* function to be called */\n  c.nresults = nresults;\n  status = luaD_pcall(L, f_call, &c, savestack(L, c.func), func);\n  adjustresults(L, nresults);\n  lua_unlock(L);\n  return status;\n}\n\n\n/*\n** Execute a protected C call.\n*/\nstruct CCallS {  /* data to `f_Ccall' */\n  lua_CFunction func;\n  void *ud;\n};\n\n\nstatic void f_Ccall (lua_State *L, void *ud) {\n  struct CCallS *c = cast(struct CCallS *, ud);\n  Closure *cl;\n  cl = luaF_newCclosure(L, 0, getcurrenv(L));\n  cl->c.f = c->func;\n  setclvalue(L, L->top, cl);  /* push function */\n  api_incr_top(L);\n  setpvalue(L->top, c->ud);  /* push only argument */\n  api_incr_top(L);\n  luaD_call(L, L->top - 2, 0);\n}\n\n\nLUA_API int lua_cpcall (lua_State *L, lua_CFunction func, void *ud) {\n  struct CCallS c;\n  int status;\n  lua_lock(L);\n  c.func = func;\n  c.ud = ud;\n  status = luaD_pcall(L, f_Ccall, &c, savestack(L, L->top), 0);\n  lua_unlock(L);\n  return status;\n}\n\n\nLUA_API int lua_load (lua_State *L, lua_Reader reader, void *data,\n                      const char *chunkname) {\n  ZIO z;\n  int status;\n  lua_lock(L);\n  if (!chunkname) chunkname = \"?\";\n  luaZ_init(L, &z, reader, data);\n  status = luaD_protectedparser(L, &z, chunkname);\n  lua_unlock(L);\n  return status;\n}\n\n\nLUA_API int lua_dump (lua_State *L, lua_Writer writer, void *data) {\n  int status;\n  TValue *o;\n  lua_lock(L);\n  api_checknelems(L, 1);\n  o = L->top - 1;\n  if (isLfunction(o))\n    status = luaU_dump(L, clvalue(o)->l.p, writer, data, 0);\n  else\n    status = 1;\n  lua_unlock(L);\n  return status;\n}\n\n\nLUA_API int  lua_status (lua_State *L) {\n  return L->status;\n}\n\n\n/*\n** Garbage-collection function\n*/\n\nLUA_API int lua_gc (lua_State *L, int what, int data) {\n  int res = 0;\n  global_State *g;\n  lua_lock(L);\n  g = G(L);\n  switch (what) {\n    case LUA_GCSTOP: {\n      g->GCthreshold = MAX_LUMEM;\n      break;\n    }\n    case LUA_GCRESTART: {\n      g->GCthreshold = g->totalbytes;\n      break;\n    }\n    case LUA_GCCOLLECT: {\n      luaC_fullgc(L);\n      break;\n    }\n    case LUA_GCCOUNT: {\n      /* GC values are expressed in Kbytes: #bytes/2^10 */\n      res = cast_int(g->totalbytes >> 10);\n      break;\n    }\n    case LUA_GCCOUNTB: {\n      res = cast_int(g->totalbytes & 0x3ff);\n      break;\n    }\n    case LUA_GCSTEP: {\n      lu_mem a = (cast(lu_mem, data) << 10);\n      if (a <= g->totalbytes)\n        g->GCthreshold = g->totalbytes - a;\n      else\n        g->GCthreshold = 0;\n      while (g->GCthreshold <= g->totalbytes) {\n        luaC_step(L);\n        if (g->gcstate == GCSpause) {  /* end of cycle? */\n          res = 1;  /* signal it */\n          break;\n        }\n      }\n      break;\n    }\n    case LUA_GCSETPAUSE: {\n      res = g->gcpause;\n      g->gcpause = data;\n      break;\n    }\n    case LUA_GCSETSTEPMUL: {\n      res = g->gcstepmul;\n      g->gcstepmul = data;\n      break;\n    }\n    default: res = -1;  /* invalid option */\n  }\n  lua_unlock(L);\n  return res;\n}\n\n\n\n/*\n** miscellaneous functions\n*/\n\n\nLUA_API int lua_error (lua_State *L) {\n  lua_lock(L);\n  api_checknelems(L, 1);\n  luaG_errormsg(L);\n  lua_unlock(L);\n  return 0;  /* to avoid warnings */\n}\n\n\nLUA_API int lua_next (lua_State *L, int idx) {\n  StkId t;\n  int more;\n  lua_lock(L);\n  t = index2adr(L, idx);\n  api_check(L, ttistable(t));\n  more = luaH_next(L, hvalue(t), L->top - 1);\n  if (more) {\n    api_incr_top(L);\n  }\n  else  /* no more elements */\n    L->top -= 1;  /* remove key */\n  lua_unlock(L);\n  return more;\n}\n\n\nLUA_API void lua_concat (lua_State *L, int n) {\n  lua_lock(L);\n  api_checknelems(L, n);\n  if (n >= 2) {\n    luaC_checkGC(L);\n    luaV_concat(L, n, cast_int(L->top - L->base) - 1);\n    L->top -= (n-1);\n  }\n  else if (n == 0) {  /* push empty string */\n    setsvalue2s(L, L->top, luaS_newlstr(L, \"\", 0));\n    api_incr_top(L);\n  }\n  /* else n == 1; nothing to do */\n  lua_unlock(L);\n}\n\n\nLUA_API lua_Alloc lua_getallocf (lua_State *L, void **ud) {\n  lua_Alloc f;\n  lua_lock(L);\n  if (ud) *ud = G(L)->ud;\n  f = G(L)->frealloc;\n  lua_unlock(L);\n  return f;\n}\n\n\nLUA_API void lua_setallocf (lua_State *L, lua_Alloc f, void *ud) {\n  lua_lock(L);\n  G(L)->ud = ud;\n  G(L)->frealloc = f;\n  lua_unlock(L);\n}\n\n\nLUA_API void *lua_newuserdata (lua_State *L, size_t size) {\n  Udata *u;\n  lua_lock(L);\n  luaC_checkGC(L);\n  u = luaS_newudata(L, size, getcurrenv(L));\n  setuvalue(L, L->top, u);\n  api_incr_top(L);\n  lua_unlock(L);\n  return u + 1;\n}\n\n\n\n\nstatic const char *aux_upvalue (StkId fi, int n, TValue **val) {\n  Closure *f;\n  if (!ttisfunction(fi)) return NULL;\n  f = clvalue(fi);\n  if (f->c.isC) {\n    if (!(1 <= n && n <= f->c.nupvalues)) return NULL;\n    *val = &f->c.upvalue[n-1];\n    return \"\";\n  }\n  else {\n    Proto *p = f->l.p;\n    if (!(1 <= n && n <= p->sizeupvalues)) return NULL;\n    *val = f->l.upvals[n-1]->v;\n    return getstr(p->upvalues[n-1]);\n  }\n}\n\n\nLUA_API const char *lua_getupvalue (lua_State *L, int funcindex, int n) {\n  const char *name;\n  TValue *val;\n  lua_lock(L);\n  name = aux_upvalue(index2adr(L, funcindex), n, &val);\n  if (name) {\n    setobj2s(L, L->top, val);\n    api_incr_top(L);\n  }\n  lua_unlock(L);\n  return name;\n}\n\n\nLUA_API const char *lua_setupvalue (lua_State *L, int funcindex, int n) {\n  const char *name;\n  TValue *val;\n  StkId fi;\n  lua_lock(L);\n  fi = index2adr(L, funcindex);\n  api_checknelems(L, 1);\n  name = aux_upvalue(fi, n, &val);\n  if (name) {\n    L->top--;\n    setobj(L, val, L->top);\n    luaC_barrier(L, clvalue(fi), L->top);\n  }\n  lua_unlock(L);\n  return name;\n}\n\n"
  },
  {
    "path": "deps/lua/src/lapi.h",
    "content": "/*\n** $Id: lapi.h,v 2.2.1.1 2007/12/27 13:02:25 roberto Exp $\n** Auxiliary functions from Lua API\n** See Copyright Notice in lua.h\n*/\n\n#ifndef lapi_h\n#define lapi_h\n\n\n#include \"lobject.h\"\n\n\nLUAI_FUNC void luaA_pushobject (lua_State *L, const TValue *o);\n\n#endif\n"
  },
  {
    "path": "deps/lua/src/lauxlib.c",
    "content": "/*\n** $Id: lauxlib.c,v 1.159.1.3 2008/01/21 13:20:51 roberto Exp $\n** Auxiliary functions for building Lua libraries\n** See Copyright Notice in lua.h\n*/\n\n\n#include <ctype.h>\n#include <errno.h>\n#include <stdarg.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n\n/* This file uses only the official API of Lua.\n** Any function declared here could be written as an application function.\n*/\n\n#define lauxlib_c\n#define LUA_LIB\n\n#include \"lua.h\"\n\n#include \"lauxlib.h\"\n\n\n#define FREELIST_REF\t0\t/* free list of references */\n\n\n/* convert a stack index to positive */\n#define abs_index(L, i)\t\t((i) > 0 || (i) <= LUA_REGISTRYINDEX ? (i) : \\\n\t\t\t\t\tlua_gettop(L) + (i) + 1)\n\n\n/*\n** {======================================================\n** Error-report functions\n** =======================================================\n*/\n\n\nLUALIB_API int luaL_argerror (lua_State *L, int narg, const char *extramsg) {\n  lua_Debug ar;\n  if (!lua_getstack(L, 0, &ar))  /* no stack frame? */\n    return luaL_error(L, \"bad argument #%d (%s)\", narg, extramsg);\n  lua_getinfo(L, \"n\", &ar);\n  if (strcmp(ar.namewhat, \"method\") == 0) {\n    narg--;  /* do not count `self' */\n    if (narg == 0)  /* error is in the self argument itself? */\n      return luaL_error(L, \"calling \" LUA_QS \" on bad self (%s)\",\n                           ar.name, extramsg);\n  }\n  if (ar.name == NULL)\n    ar.name = \"?\";\n  return luaL_error(L, \"bad argument #%d to \" LUA_QS \" (%s)\",\n                        narg, ar.name, extramsg);\n}\n\n\nLUALIB_API int luaL_typerror (lua_State *L, int narg, const char *tname) {\n  const char *msg = lua_pushfstring(L, \"%s expected, got %s\",\n                                    tname, luaL_typename(L, narg));\n  return luaL_argerror(L, narg, msg);\n}\n\n\nstatic void tag_error (lua_State *L, int narg, int tag) {\n  luaL_typerror(L, narg, lua_typename(L, tag));\n}\n\n\nLUALIB_API void luaL_where (lua_State *L, int level) {\n  lua_Debug ar;\n  if (lua_getstack(L, level, &ar)) {  /* check function at level */\n    lua_getinfo(L, \"Sl\", &ar);  /* get info about it */\n    if (ar.currentline > 0) {  /* is there info? */\n      lua_pushfstring(L, \"%s:%d: \", ar.short_src, ar.currentline);\n      return;\n    }\n  }\n  lua_pushliteral(L, \"\");  /* else, no information available... */\n}\n\n\nLUALIB_API int luaL_error (lua_State *L, const char *fmt, ...) {\n  va_list argp;\n  va_start(argp, fmt);\n  luaL_where(L, 1);\n  lua_pushvfstring(L, fmt, argp);\n  va_end(argp);\n  lua_concat(L, 2);\n  return lua_error(L);\n}\n\n/* }====================================================== */\n\n\nLUALIB_API int luaL_checkoption (lua_State *L, int narg, const char *def,\n                                 const char *const lst[]) {\n  const char *name = (def) ? luaL_optstring(L, narg, def) :\n                             luaL_checkstring(L, narg);\n  int i;\n  for (i=0; lst[i]; i++)\n    if (strcmp(lst[i], name) == 0)\n      return i;\n  return luaL_argerror(L, narg,\n                       lua_pushfstring(L, \"invalid option \" LUA_QS, name));\n}\n\n\nLUALIB_API int luaL_newmetatable (lua_State *L, const char *tname) {\n  lua_getfield(L, LUA_REGISTRYINDEX, tname);  /* get registry.name */\n  if (!lua_isnil(L, -1))  /* name already in use? */\n    return 0;  /* leave previous value on top, but return 0 */\n  lua_pop(L, 1);\n  lua_newtable(L);  /* create metatable */\n  lua_pushvalue(L, -1);\n  lua_setfield(L, LUA_REGISTRYINDEX, tname);  /* registry.name = metatable */\n  return 1;\n}\n\n\nLUALIB_API void *luaL_checkudata (lua_State *L, int ud, const char *tname) {\n  void *p = lua_touserdata(L, ud);\n  if (p != NULL) {  /* value is a userdata? */\n    if (lua_getmetatable(L, ud)) {  /* does it have a metatable? */\n      lua_getfield(L, LUA_REGISTRYINDEX, tname);  /* get correct metatable */\n      if (lua_rawequal(L, -1, -2)) {  /* does it have the correct mt? */\n        lua_pop(L, 2);  /* remove both metatables */\n        return p;\n      }\n    }\n  }\n  luaL_typerror(L, ud, tname);  /* else error */\n  return NULL;  /* to avoid warnings */\n}\n\n\nLUALIB_API void luaL_checkstack (lua_State *L, int space, const char *mes) {\n  if (!lua_checkstack(L, space))\n    luaL_error(L, \"stack overflow (%s)\", mes);\n}\n\n\nLUALIB_API void luaL_checktype (lua_State *L, int narg, int t) {\n  if (lua_type(L, narg) != t)\n    tag_error(L, narg, t);\n}\n\n\nLUALIB_API void luaL_checkany (lua_State *L, int narg) {\n  if (lua_type(L, narg) == LUA_TNONE)\n    luaL_argerror(L, narg, \"value expected\");\n}\n\n\nLUALIB_API const char *luaL_checklstring (lua_State *L, int narg, size_t *len) {\n  const char *s = lua_tolstring(L, narg, len);\n  if (!s) tag_error(L, narg, LUA_TSTRING);\n  return s;\n}\n\n\nLUALIB_API const char *luaL_optlstring (lua_State *L, int narg,\n                                        const char *def, size_t *len) {\n  if (lua_isnoneornil(L, narg)) {\n    if (len)\n      *len = (def ? strlen(def) : 0);\n    return def;\n  }\n  else return luaL_checklstring(L, narg, len);\n}\n\n\nLUALIB_API lua_Number luaL_checknumber (lua_State *L, int narg) {\n  lua_Number d = lua_tonumber(L, narg);\n  if (d == 0 && !lua_isnumber(L, narg))  /* avoid extra test when d is not 0 */\n    tag_error(L, narg, LUA_TNUMBER);\n  return d;\n}\n\n\nLUALIB_API lua_Number luaL_optnumber (lua_State *L, int narg, lua_Number def) {\n  return luaL_opt(L, luaL_checknumber, narg, def);\n}\n\n\nLUALIB_API lua_Integer luaL_checkinteger (lua_State *L, int narg) {\n  lua_Integer d = lua_tointeger(L, narg);\n  if (d == 0 && !lua_isnumber(L, narg))  /* avoid extra test when d is not 0 */\n    tag_error(L, narg, LUA_TNUMBER);\n  return d;\n}\n\n\nLUALIB_API lua_Integer luaL_optinteger (lua_State *L, int narg,\n                                                      lua_Integer def) {\n  return luaL_opt(L, luaL_checkinteger, narg, def);\n}\n\n\nLUALIB_API int luaL_getmetafield (lua_State *L, int obj, const char *event) {\n  if (!lua_getmetatable(L, obj))  /* no metatable? */\n    return 0;\n  lua_pushstring(L, event);\n  lua_rawget(L, -2);\n  if (lua_isnil(L, -1)) {\n    lua_pop(L, 2);  /* remove metatable and metafield */\n    return 0;\n  }\n  else {\n    lua_remove(L, -2);  /* remove only metatable */\n    return 1;\n  }\n}\n\n\nLUALIB_API int luaL_callmeta (lua_State *L, int obj, const char *event) {\n  obj = abs_index(L, obj);\n  if (!luaL_getmetafield(L, obj, event))  /* no metafield? */\n    return 0;\n  lua_pushvalue(L, obj);\n  lua_call(L, 1, 1);\n  return 1;\n}\n\n\nLUALIB_API void (luaL_register) (lua_State *L, const char *libname,\n                                const luaL_Reg *l) {\n  luaI_openlib(L, libname, l, 0);\n}\n\n\nstatic int libsize (const luaL_Reg *l) {\n  int size = 0;\n  for (; l->name; l++) size++;\n  return size;\n}\n\n\nLUALIB_API void luaI_openlib (lua_State *L, const char *libname,\n                              const luaL_Reg *l, int nup) {\n  if (libname) {\n    int size = libsize(l);\n    /* check whether lib already exists */\n    luaL_findtable(L, LUA_REGISTRYINDEX, \"_LOADED\", 1);\n    lua_getfield(L, -1, libname);  /* get _LOADED[libname] */\n    if (!lua_istable(L, -1)) {  /* not found? */\n      lua_pop(L, 1);  /* remove previous result */\n      /* try global variable (and create one if it does not exist) */\n      if (luaL_findtable(L, LUA_GLOBALSINDEX, libname, size) != NULL)\n        luaL_error(L, \"name conflict for module \" LUA_QS, libname);\n      lua_pushvalue(L, -1);\n      lua_setfield(L, -3, libname);  /* _LOADED[libname] = new table */\n    }\n    lua_remove(L, -2);  /* remove _LOADED table */\n    lua_insert(L, -(nup+1));  /* move library table to below upvalues */\n  }\n  for (; l->name; l++) {\n    int i;\n    for (i=0; i<nup; i++)  /* copy upvalues to the top */\n      lua_pushvalue(L, -nup);\n    lua_pushcclosure(L, l->func, nup);\n    lua_setfield(L, -(nup+2), l->name);\n  }\n  lua_pop(L, nup);  /* remove upvalues */\n}\n\n\n\n/*\n** {======================================================\n** getn-setn: size for arrays\n** =======================================================\n*/\n\n#if defined(LUA_COMPAT_GETN)\n\nstatic int checkint (lua_State *L, int topop) {\n  int n = (lua_type(L, -1) == LUA_TNUMBER) ? lua_tointeger(L, -1) : -1;\n  lua_pop(L, topop);\n  return n;\n}\n\n\nstatic void getsizes (lua_State *L) {\n  lua_getfield(L, LUA_REGISTRYINDEX, \"LUA_SIZES\");\n  if (lua_isnil(L, -1)) {  /* no `size' table? */\n    lua_pop(L, 1);  /* remove nil */\n    lua_newtable(L);  /* create it */\n    lua_pushvalue(L, -1);  /* `size' will be its own metatable */\n    lua_setmetatable(L, -2);\n    lua_pushliteral(L, \"kv\");\n    lua_setfield(L, -2, \"__mode\");  /* metatable(N).__mode = \"kv\" */\n    lua_pushvalue(L, -1);\n    lua_setfield(L, LUA_REGISTRYINDEX, \"LUA_SIZES\");  /* store in register */\n  }\n}\n\n\nLUALIB_API void luaL_setn (lua_State *L, int t, int n) {\n  t = abs_index(L, t);\n  lua_pushliteral(L, \"n\");\n  lua_rawget(L, t);\n  if (checkint(L, 1) >= 0) {  /* is there a numeric field `n'? */\n    lua_pushliteral(L, \"n\");  /* use it */\n    lua_pushinteger(L, n);\n    lua_rawset(L, t);\n  }\n  else {  /* use `sizes' */\n    getsizes(L);\n    lua_pushvalue(L, t);\n    lua_pushinteger(L, n);\n    lua_rawset(L, -3);  /* sizes[t] = n */\n    lua_pop(L, 1);  /* remove `sizes' */\n  }\n}\n\n\nLUALIB_API int luaL_getn (lua_State *L, int t) {\n  int n;\n  t = abs_index(L, t);\n  lua_pushliteral(L, \"n\");  /* try t.n */\n  lua_rawget(L, t);\n  if ((n = checkint(L, 1)) >= 0) return n;\n  getsizes(L);  /* else try sizes[t] */\n  lua_pushvalue(L, t);\n  lua_rawget(L, -2);\n  if ((n = checkint(L, 2)) >= 0) return n;\n  return (int)lua_objlen(L, t);\n}\n\n#endif\n\n/* }====================================================== */\n\n\n\nLUALIB_API const char *luaL_gsub (lua_State *L, const char *s, const char *p,\n                                                               const char *r) {\n  const char *wild;\n  size_t l = strlen(p);\n  luaL_Buffer b;\n  luaL_buffinit(L, &b);\n  while ((wild = strstr(s, p)) != NULL) {\n    luaL_addlstring(&b, s, wild - s);  /* push prefix */\n    luaL_addstring(&b, r);  /* push replacement in place of pattern */\n    s = wild + l;  /* continue after `p' */\n  }\n  luaL_addstring(&b, s);  /* push last suffix */\n  luaL_pushresult(&b);\n  return lua_tostring(L, -1);\n}\n\n\nLUALIB_API const char *luaL_findtable (lua_State *L, int idx,\n                                       const char *fname, int szhint) {\n  const char *e;\n  lua_pushvalue(L, idx);\n  do {\n    e = strchr(fname, '.');\n    if (e == NULL) e = fname + strlen(fname);\n    lua_pushlstring(L, fname, e - fname);\n    lua_rawget(L, -2);\n    if (lua_isnil(L, -1)) {  /* no such field? */\n      lua_pop(L, 1);  /* remove this nil */\n      lua_createtable(L, 0, (*e == '.' ? 1 : szhint)); /* new table for field */\n      lua_pushlstring(L, fname, e - fname);\n      lua_pushvalue(L, -2);\n      lua_settable(L, -4);  /* set new table into field */\n    }\n    else if (!lua_istable(L, -1)) {  /* field has a non-table value? */\n      lua_pop(L, 2);  /* remove table and value */\n      return fname;  /* return problematic part of the name */\n    }\n    lua_remove(L, -2);  /* remove previous table */\n    fname = e + 1;\n  } while (*e == '.');\n  return NULL;\n}\n\n\n\n/*\n** {======================================================\n** Generic Buffer manipulation\n** =======================================================\n*/\n\n\n#define bufflen(B)\t((B)->p - (B)->buffer)\n#define bufffree(B)\t((size_t)(LUAL_BUFFERSIZE - bufflen(B)))\n\n#define LIMIT\t(LUA_MINSTACK/2)\n\n\nstatic int emptybuffer (luaL_Buffer *B) {\n  size_t l = bufflen(B);\n  if (l == 0) return 0;  /* put nothing on stack */\n  else {\n    lua_pushlstring(B->L, B->buffer, l);\n    B->p = B->buffer;\n    B->lvl++;\n    return 1;\n  }\n}\n\n\nstatic void adjuststack (luaL_Buffer *B) {\n  if (B->lvl > 1) {\n    lua_State *L = B->L;\n    int toget = 1;  /* number of levels to concat */\n    size_t toplen = lua_strlen(L, -1);\n    do {\n      size_t l = lua_strlen(L, -(toget+1));\n      if (B->lvl - toget + 1 >= LIMIT || toplen > l) {\n        toplen += l;\n        toget++;\n      }\n      else break;\n    } while (toget < B->lvl);\n    lua_concat(L, toget);\n    B->lvl = B->lvl - toget + 1;\n  }\n}\n\n\nLUALIB_API char *luaL_prepbuffer (luaL_Buffer *B) {\n  if (emptybuffer(B))\n    adjuststack(B);\n  return B->buffer;\n}\n\n\nLUALIB_API void luaL_addlstring (luaL_Buffer *B, const char *s, size_t l) {\n  while (l--)\n    luaL_addchar(B, *s++);\n}\n\n\nLUALIB_API void luaL_addstring (luaL_Buffer *B, const char *s) {\n  luaL_addlstring(B, s, strlen(s));\n}\n\n\nLUALIB_API void luaL_pushresult (luaL_Buffer *B) {\n  emptybuffer(B);\n  lua_concat(B->L, B->lvl);\n  B->lvl = 1;\n}\n\n\nLUALIB_API void luaL_addvalue (luaL_Buffer *B) {\n  lua_State *L = B->L;\n  size_t vl;\n  const char *s = lua_tolstring(L, -1, &vl);\n  if (vl <= bufffree(B)) {  /* fit into buffer? */\n    memcpy(B->p, s, vl);  /* put it there */\n    B->p += vl;\n    lua_pop(L, 1);  /* remove from stack */\n  }\n  else {\n    if (emptybuffer(B))\n      lua_insert(L, -2);  /* put buffer before new value */\n    B->lvl++;  /* add new value into B stack */\n    adjuststack(B);\n  }\n}\n\n\nLUALIB_API void luaL_buffinit (lua_State *L, luaL_Buffer *B) {\n  B->L = L;\n  B->p = B->buffer;\n  B->lvl = 0;\n}\n\n/* }====================================================== */\n\n\nLUALIB_API int luaL_ref (lua_State *L, int t) {\n  int ref;\n  t = abs_index(L, t);\n  if (lua_isnil(L, -1)) {\n    lua_pop(L, 1);  /* remove from stack */\n    return LUA_REFNIL;  /* `nil' has a unique fixed reference */\n  }\n  lua_rawgeti(L, t, FREELIST_REF);  /* get first free element */\n  ref = (int)lua_tointeger(L, -1);  /* ref = t[FREELIST_REF] */\n  lua_pop(L, 1);  /* remove it from stack */\n  if (ref != 0) {  /* any free element? */\n    lua_rawgeti(L, t, ref);  /* remove it from list */\n    lua_rawseti(L, t, FREELIST_REF);  /* (t[FREELIST_REF] = t[ref]) */\n  }\n  else {  /* no free elements */\n    ref = (int)lua_objlen(L, t);\n    ref++;  /* create new reference */\n  }\n  lua_rawseti(L, t, ref);\n  return ref;\n}\n\n\nLUALIB_API void luaL_unref (lua_State *L, int t, int ref) {\n  if (ref >= 0) {\n    t = abs_index(L, t);\n    lua_rawgeti(L, t, FREELIST_REF);\n    lua_rawseti(L, t, ref);  /* t[ref] = t[FREELIST_REF] */\n    lua_pushinteger(L, ref);\n    lua_rawseti(L, t, FREELIST_REF);  /* t[FREELIST_REF] = ref */\n  }\n}\n\n\n\n/*\n** {======================================================\n** Load functions\n** =======================================================\n*/\n\ntypedef struct LoadF {\n  int extraline;\n  FILE *f;\n  char buff[LUAL_BUFFERSIZE];\n} LoadF;\n\n\nstatic const char *getF (lua_State *L, void *ud, size_t *size) {\n  LoadF *lf = (LoadF *)ud;\n  (void)L;\n  if (lf->extraline) {\n    lf->extraline = 0;\n    *size = 1;\n    return \"\\n\";\n  }\n  if (feof(lf->f)) return NULL;\n  *size = fread(lf->buff, 1, sizeof(lf->buff), lf->f);\n  return (*size > 0) ? lf->buff : NULL;\n}\n\n\nstatic int errfile (lua_State *L, const char *what, int fnameindex) {\n  const char *serr = strerror(errno);\n  const char *filename = lua_tostring(L, fnameindex) + 1;\n  lua_pushfstring(L, \"cannot %s %s: %s\", what, filename, serr);\n  lua_remove(L, fnameindex);\n  return LUA_ERRFILE;\n}\n\n\nLUALIB_API int luaL_loadfile (lua_State *L, const char *filename) {\n  LoadF lf;\n  int status, readstatus;\n  int c;\n  int fnameindex = lua_gettop(L) + 1;  /* index of filename on the stack */\n  lf.extraline = 0;\n  if (filename == NULL) {\n    lua_pushliteral(L, \"=stdin\");\n    lf.f = stdin;\n  }\n  else {\n    lua_pushfstring(L, \"@%s\", filename);\n    lf.f = fopen(filename, \"r\");\n    if (lf.f == NULL) return errfile(L, \"open\", fnameindex);\n  }\n  c = getc(lf.f);\n  if (c == '#') {  /* Unix exec. file? */\n    lf.extraline = 1;\n    while ((c = getc(lf.f)) != EOF && c != '\\n') ;  /* skip first line */\n    if (c == '\\n') c = getc(lf.f);\n  }\n  if (c == LUA_SIGNATURE[0] && filename) {  /* binary file? */\n    lf.f = freopen(filename, \"rb\", lf.f);  /* reopen in binary mode */\n    if (lf.f == NULL) return errfile(L, \"reopen\", fnameindex);\n    /* skip eventual `#!...' */\n   while ((c = getc(lf.f)) != EOF && c != LUA_SIGNATURE[0]) ;\n    lf.extraline = 0;\n  }\n  ungetc(c, lf.f);\n  status = lua_load(L, getF, &lf, lua_tostring(L, -1));\n  readstatus = ferror(lf.f);\n  if (filename) fclose(lf.f);  /* close file (even in case of errors) */\n  if (readstatus) {\n    lua_settop(L, fnameindex);  /* ignore results from `lua_load' */\n    return errfile(L, \"read\", fnameindex);\n  }\n  lua_remove(L, fnameindex);\n  return status;\n}\n\n\ntypedef struct LoadS {\n  const char *s;\n  size_t size;\n} LoadS;\n\n\nstatic const char *getS (lua_State *L, void *ud, size_t *size) {\n  LoadS *ls = (LoadS *)ud;\n  (void)L;\n  if (ls->size == 0) return NULL;\n  *size = ls->size;\n  ls->size = 0;\n  return ls->s;\n}\n\n\nLUALIB_API int luaL_loadbuffer (lua_State *L, const char *buff, size_t size,\n                                const char *name) {\n  LoadS ls;\n  ls.s = buff;\n  ls.size = size;\n  return lua_load(L, getS, &ls, name);\n}\n\n\nLUALIB_API int (luaL_loadstring) (lua_State *L, const char *s) {\n  return luaL_loadbuffer(L, s, strlen(s), s);\n}\n\n\n\n/* }====================================================== */\n\n\nstatic void *l_alloc (void *ud, void *ptr, size_t osize, size_t nsize) {\n  (void)ud;\n  (void)osize;\n  if (nsize == 0) {\n    free(ptr);\n    return NULL;\n  }\n  else\n    return realloc(ptr, nsize);\n}\n\n\nstatic int panic (lua_State *L) {\n  (void)L;  /* to avoid warnings */\n  fprintf(stderr, \"PANIC: unprotected error in call to Lua API (%s)\\n\",\n                   lua_tostring(L, -1));\n  return 0;\n}\n\n\nLUALIB_API lua_State *luaL_newstate (void) {\n  lua_State *L = lua_newstate(l_alloc, NULL);\n  if (L) lua_atpanic(L, &panic);\n  return L;\n}\n\n"
  },
  {
    "path": "deps/lua/src/lauxlib.h",
    "content": "/*\n** $Id: lauxlib.h,v 1.88.1.1 2007/12/27 13:02:25 roberto Exp $\n** Auxiliary functions for building Lua libraries\n** See Copyright Notice in lua.h\n*/\n\n\n#ifndef lauxlib_h\n#define lauxlib_h\n\n\n#include <stddef.h>\n#include <stdio.h>\n\n#include \"lua.h\"\n\n\n#if defined(LUA_COMPAT_GETN)\nLUALIB_API int (luaL_getn) (lua_State *L, int t);\nLUALIB_API void (luaL_setn) (lua_State *L, int t, int n);\n#else\n#define luaL_getn(L,i)          ((int)lua_objlen(L, i))\n#define luaL_setn(L,i,j)        ((void)0)  /* no op! */\n#endif\n\n#if defined(LUA_COMPAT_OPENLIB)\n#define luaI_openlib\tluaL_openlib\n#endif\n\n\n/* extra error code for `luaL_load' */\n#define LUA_ERRFILE     (LUA_ERRERR+1)\n\n\ntypedef struct luaL_Reg {\n  const char *name;\n  lua_CFunction func;\n} luaL_Reg;\n\n\n\nLUALIB_API void (luaI_openlib) (lua_State *L, const char *libname,\n                                const luaL_Reg *l, int nup);\nLUALIB_API void (luaL_register) (lua_State *L, const char *libname,\n                                const luaL_Reg *l);\nLUALIB_API int (luaL_getmetafield) (lua_State *L, int obj, const char *e);\nLUALIB_API int (luaL_callmeta) (lua_State *L, int obj, const char *e);\nLUALIB_API int (luaL_typerror) (lua_State *L, int narg, const char *tname);\nLUALIB_API int (luaL_argerror) (lua_State *L, int numarg, const char *extramsg);\nLUALIB_API const char *(luaL_checklstring) (lua_State *L, int numArg,\n                                                          size_t *l);\nLUALIB_API const char *(luaL_optlstring) (lua_State *L, int numArg,\n                                          const char *def, size_t *l);\nLUALIB_API lua_Number (luaL_checknumber) (lua_State *L, int numArg);\nLUALIB_API lua_Number (luaL_optnumber) (lua_State *L, int nArg, lua_Number def);\n\nLUALIB_API lua_Integer (luaL_checkinteger) (lua_State *L, int numArg);\nLUALIB_API lua_Integer (luaL_optinteger) (lua_State *L, int nArg,\n                                          lua_Integer def);\n\nLUALIB_API void (luaL_checkstack) (lua_State *L, int sz, const char *msg);\nLUALIB_API void (luaL_checktype) (lua_State *L, int narg, int t);\nLUALIB_API void (luaL_checkany) (lua_State *L, int narg);\n\nLUALIB_API int   (luaL_newmetatable) (lua_State *L, const char *tname);\nLUALIB_API void *(luaL_checkudata) (lua_State *L, int ud, const char *tname);\n\nLUALIB_API void (luaL_where) (lua_State *L, int lvl);\nLUALIB_API int (luaL_error) (lua_State *L, const char *fmt, ...);\n\nLUALIB_API int (luaL_checkoption) (lua_State *L, int narg, const char *def,\n                                   const char *const lst[]);\n\nLUALIB_API int (luaL_ref) (lua_State *L, int t);\nLUALIB_API void (luaL_unref) (lua_State *L, int t, int ref);\n\nLUALIB_API int (luaL_loadfile) (lua_State *L, const char *filename);\nLUALIB_API int (luaL_loadbuffer) (lua_State *L, const char *buff, size_t sz,\n                                  const char *name);\nLUALIB_API int (luaL_loadstring) (lua_State *L, const char *s);\n\nLUALIB_API lua_State *(luaL_newstate) (void);\n\n\nLUALIB_API const char *(luaL_gsub) (lua_State *L, const char *s, const char *p,\n                                                  const char *r);\n\nLUALIB_API const char *(luaL_findtable) (lua_State *L, int idx,\n                                         const char *fname, int szhint);\n\n\n\n\n/*\n** ===============================================================\n** some useful macros\n** ===============================================================\n*/\n\n#define luaL_argcheck(L, cond,numarg,extramsg)\t\\\n\t\t((void)((cond) || luaL_argerror(L, (numarg), (extramsg))))\n#define luaL_checkstring(L,n)\t(luaL_checklstring(L, (n), NULL))\n#define luaL_optstring(L,n,d)\t(luaL_optlstring(L, (n), (d), NULL))\n#define luaL_checkint(L,n)\t((int)luaL_checkinteger(L, (n)))\n#define luaL_optint(L,n,d)\t((int)luaL_optinteger(L, (n), (d)))\n#define luaL_checklong(L,n)\t((long)luaL_checkinteger(L, (n)))\n#define luaL_optlong(L,n,d)\t((long)luaL_optinteger(L, (n), (d)))\n\n#define luaL_typename(L,i)\tlua_typename(L, lua_type(L,(i)))\n\n#define luaL_dofile(L, fn) \\\n\t(luaL_loadfile(L, fn) || lua_pcall(L, 0, LUA_MULTRET, 0))\n\n#define luaL_dostring(L, s) \\\n\t(luaL_loadstring(L, s) || lua_pcall(L, 0, LUA_MULTRET, 0))\n\n#define luaL_getmetatable(L,n)\t(lua_getfield(L, LUA_REGISTRYINDEX, (n)))\n\n#define luaL_opt(L,f,n,d)\t(lua_isnoneornil(L,(n)) ? (d) : f(L,(n)))\n\n/*\n** {======================================================\n** Generic Buffer manipulation\n** =======================================================\n*/\n\n\n\ntypedef struct luaL_Buffer {\n  char *p;\t\t\t/* current position in buffer */\n  int lvl;  /* number of strings in the stack (level) */\n  lua_State *L;\n  char buffer[LUAL_BUFFERSIZE];\n} luaL_Buffer;\n\n#define luaL_addchar(B,c) \\\n  ((void)((B)->p < ((B)->buffer+LUAL_BUFFERSIZE) || luaL_prepbuffer(B)), \\\n   (*(B)->p++ = (char)(c)))\n\n/* compatibility only */\n#define luaL_putchar(B,c)\tluaL_addchar(B,c)\n\n#define luaL_addsize(B,n)\t((B)->p += (n))\n\nLUALIB_API void (luaL_buffinit) (lua_State *L, luaL_Buffer *B);\nLUALIB_API char *(luaL_prepbuffer) (luaL_Buffer *B);\nLUALIB_API void (luaL_addlstring) (luaL_Buffer *B, const char *s, size_t l);\nLUALIB_API void (luaL_addstring) (luaL_Buffer *B, const char *s);\nLUALIB_API void (luaL_addvalue) (luaL_Buffer *B);\nLUALIB_API void (luaL_pushresult) (luaL_Buffer *B);\n\n\n/* }====================================================== */\n\n\n/* compatibility with ref system */\n\n/* pre-defined references */\n#define LUA_NOREF       (-2)\n#define LUA_REFNIL      (-1)\n\n#define lua_ref(L,lock) ((lock) ? luaL_ref(L, LUA_REGISTRYINDEX) : \\\n      (lua_pushstring(L, \"unlocked references are obsolete\"), lua_error(L), 0))\n\n#define lua_unref(L,ref)        luaL_unref(L, LUA_REGISTRYINDEX, (ref))\n\n#define lua_getref(L,ref)       lua_rawgeti(L, LUA_REGISTRYINDEX, (ref))\n\n\n#define luaL_reg\tluaL_Reg\n\n#endif\n\n\n"
  },
  {
    "path": "deps/lua/src/lbaselib.c",
    "content": "/*\n** $Id: lbaselib.c,v 1.191.1.6 2008/02/14 16:46:22 roberto Exp $\n** Basic library\n** See Copyright Notice in lua.h\n*/\n\n\n\n#include <ctype.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#define lbaselib_c\n#define LUA_LIB\n\n#include \"lua.h\"\n\n#include \"lauxlib.h\"\n#include \"lualib.h\"\n\n\n\n\n/*\n** If your system does not support `stdout', you can just remove this function.\n** If you need, you can define your own `print' function, following this\n** model but changing `fputs' to put the strings at a proper place\n** (a console window or a log file, for instance).\n*/\nstatic int luaB_print (lua_State *L) {\n  int n = lua_gettop(L);  /* number of arguments */\n  int i;\n  lua_getglobal(L, \"tostring\");\n  for (i=1; i<=n; i++) {\n    const char *s;\n    lua_pushvalue(L, -1);  /* function to be called */\n    lua_pushvalue(L, i);   /* value to print */\n    lua_call(L, 1, 1);\n    s = lua_tostring(L, -1);  /* get result */\n    if (s == NULL)\n      return luaL_error(L, LUA_QL(\"tostring\") \" must return a string to \"\n                           LUA_QL(\"print\"));\n    if (i>1) fputs(\"\\t\", stdout);\n    fputs(s, stdout);\n    lua_pop(L, 1);  /* pop result */\n  }\n  fputs(\"\\n\", stdout);\n  return 0;\n}\n\n\nstatic int luaB_tonumber (lua_State *L) {\n  int base = luaL_optint(L, 2, 10);\n  if (base == 10) {  /* standard conversion */\n    luaL_checkany(L, 1);\n    if (lua_isnumber(L, 1)) {\n      lua_pushnumber(L, lua_tonumber(L, 1));\n      return 1;\n    }\n  }\n  else {\n    const char *s1 = luaL_checkstring(L, 1);\n    char *s2;\n    unsigned long n;\n    luaL_argcheck(L, 2 <= base && base <= 36, 2, \"base out of range\");\n    n = strtoul(s1, &s2, base);\n    if (s1 != s2) {  /* at least one valid digit? */\n      while (isspace((unsigned char)(*s2))) s2++;  /* skip trailing spaces */\n      if (*s2 == '\\0') {  /* no invalid trailing characters? */\n        lua_pushnumber(L, (lua_Number)n);\n        return 1;\n      }\n    }\n  }\n  lua_pushnil(L);  /* else not a number */\n  return 1;\n}\n\n\nstatic int luaB_error (lua_State *L) {\n  int level = luaL_optint(L, 2, 1);\n  lua_settop(L, 1);\n  if (lua_isstring(L, 1) && level > 0) {  /* add extra information? */\n    luaL_where(L, level);\n    lua_pushvalue(L, 1);\n    lua_concat(L, 2);\n  }\n  return lua_error(L);\n}\n\n\nstatic int luaB_getmetatable (lua_State *L) {\n  luaL_checkany(L, 1);\n  if (!lua_getmetatable(L, 1)) {\n    lua_pushnil(L);\n    return 1;  /* no metatable */\n  }\n  luaL_getmetafield(L, 1, \"__metatable\");\n  return 1;  /* returns either __metatable field (if present) or metatable */\n}\n\n\nstatic int luaB_setmetatable (lua_State *L) {\n  int t = lua_type(L, 2);\n  luaL_checktype(L, 1, LUA_TTABLE);\n  luaL_argcheck(L, t == LUA_TNIL || t == LUA_TTABLE, 2,\n                    \"nil or table expected\");\n  if (luaL_getmetafield(L, 1, \"__metatable\"))\n    luaL_error(L, \"cannot change a protected metatable\");\n  lua_settop(L, 2);\n  lua_setmetatable(L, 1);\n  return 1;\n}\n\n\nstatic void getfunc (lua_State *L, int opt) {\n  if (lua_isfunction(L, 1)) lua_pushvalue(L, 1);\n  else {\n    lua_Debug ar;\n    int level = opt ? luaL_optint(L, 1, 1) : luaL_checkint(L, 1);\n    luaL_argcheck(L, level >= 0, 1, \"level must be non-negative\");\n    if (lua_getstack(L, level, &ar) == 0)\n      luaL_argerror(L, 1, \"invalid level\");\n    lua_getinfo(L, \"f\", &ar);\n    if (lua_isnil(L, -1))\n      luaL_error(L, \"no function environment for tail call at level %d\",\n                    level);\n  }\n}\n\n\nstatic int luaB_getfenv (lua_State *L) {\n  getfunc(L, 1);\n  if (lua_iscfunction(L, -1))  /* is a C function? */\n    lua_pushvalue(L, LUA_GLOBALSINDEX);  /* return the thread's global env. */\n  else\n    lua_getfenv(L, -1);\n  return 1;\n}\n\n\nstatic int luaB_setfenv (lua_State *L) {\n  luaL_checktype(L, 2, LUA_TTABLE);\n  getfunc(L, 0);\n  lua_pushvalue(L, 2);\n  if (lua_isnumber(L, 1) && lua_tonumber(L, 1) == 0) {\n    /* change environment of current thread */\n    lua_pushthread(L);\n    lua_insert(L, -2);\n    lua_setfenv(L, -2);\n    return 0;\n  }\n  else if (lua_iscfunction(L, -2) || lua_setfenv(L, -2) == 0)\n    luaL_error(L,\n          LUA_QL(\"setfenv\") \" cannot change environment of given object\");\n  return 1;\n}\n\n\nstatic int luaB_rawequal (lua_State *L) {\n  luaL_checkany(L, 1);\n  luaL_checkany(L, 2);\n  lua_pushboolean(L, lua_rawequal(L, 1, 2));\n  return 1;\n}\n\n\nstatic int luaB_rawget (lua_State *L) {\n  luaL_checktype(L, 1, LUA_TTABLE);\n  luaL_checkany(L, 2);\n  lua_settop(L, 2);\n  lua_rawget(L, 1);\n  return 1;\n}\n\nstatic int luaB_rawset (lua_State *L) {\n  luaL_checktype(L, 1, LUA_TTABLE);\n  luaL_checkany(L, 2);\n  luaL_checkany(L, 3);\n  lua_settop(L, 3);\n  lua_rawset(L, 1);\n  return 1;\n}\n\n\nstatic int luaB_gcinfo (lua_State *L) {\n  lua_pushinteger(L, lua_getgccount(L));\n  return 1;\n}\n\n\nstatic int luaB_collectgarbage (lua_State *L) {\n  static const char *const opts[] = {\"stop\", \"restart\", \"collect\",\n    \"count\", \"step\", \"setpause\", \"setstepmul\", NULL};\n  static const int optsnum[] = {LUA_GCSTOP, LUA_GCRESTART, LUA_GCCOLLECT,\n    LUA_GCCOUNT, LUA_GCSTEP, LUA_GCSETPAUSE, LUA_GCSETSTEPMUL};\n  int o = luaL_checkoption(L, 1, \"collect\", opts);\n  int ex = luaL_optint(L, 2, 0);\n  int res = lua_gc(L, optsnum[o], ex);\n  switch (optsnum[o]) {\n    case LUA_GCCOUNT: {\n      int b = lua_gc(L, LUA_GCCOUNTB, 0);\n      lua_pushnumber(L, res + ((lua_Number)b/1024));\n      return 1;\n    }\n    case LUA_GCSTEP: {\n      lua_pushboolean(L, res);\n      return 1;\n    }\n    default: {\n      lua_pushnumber(L, res);\n      return 1;\n    }\n  }\n}\n\n\nstatic int luaB_type (lua_State *L) {\n  luaL_checkany(L, 1);\n  lua_pushstring(L, luaL_typename(L, 1));\n  return 1;\n}\n\n\nstatic int luaB_next (lua_State *L) {\n  luaL_checktype(L, 1, LUA_TTABLE);\n  lua_settop(L, 2);  /* create a 2nd argument if there isn't one */\n  if (lua_next(L, 1))\n    return 2;\n  else {\n    lua_pushnil(L);\n    return 1;\n  }\n}\n\n\nstatic int luaB_pairs (lua_State *L) {\n  luaL_checktype(L, 1, LUA_TTABLE);\n  lua_pushvalue(L, lua_upvalueindex(1));  /* return generator, */\n  lua_pushvalue(L, 1);  /* state, */\n  lua_pushnil(L);  /* and initial value */\n  return 3;\n}\n\n\nstatic int ipairsaux (lua_State *L) {\n  int i = luaL_checkint(L, 2);\n  luaL_checktype(L, 1, LUA_TTABLE);\n  i++;  /* next value */\n  lua_pushinteger(L, i);\n  lua_rawgeti(L, 1, i);\n  return (lua_isnil(L, -1)) ? 0 : 2;\n}\n\n\nstatic int luaB_ipairs (lua_State *L) {\n  luaL_checktype(L, 1, LUA_TTABLE);\n  lua_pushvalue(L, lua_upvalueindex(1));  /* return generator, */\n  lua_pushvalue(L, 1);  /* state, */\n  lua_pushinteger(L, 0);  /* and initial value */\n  return 3;\n}\n\n\nstatic int load_aux (lua_State *L, int status) {\n  if (status == 0)  /* OK? */\n    return 1;\n  else {\n    lua_pushnil(L);\n    lua_insert(L, -2);  /* put before error message */\n    return 2;  /* return nil plus error message */\n  }\n}\n\n\nstatic int luaB_loadstring (lua_State *L) {\n  size_t l;\n  const char *s = luaL_checklstring(L, 1, &l);\n  const char *chunkname = luaL_optstring(L, 2, s);\n  return load_aux(L, luaL_loadbuffer(L, s, l, chunkname));\n}\n\n\nstatic int luaB_loadfile (lua_State *L) {\n  const char *fname = luaL_optstring(L, 1, NULL);\n  return load_aux(L, luaL_loadfile(L, fname));\n}\n\n\n/*\n** Reader for generic `load' function: `lua_load' uses the\n** stack for internal stuff, so the reader cannot change the\n** stack top. Instead, it keeps its resulting string in a\n** reserved slot inside the stack.\n*/\nstatic const char *generic_reader (lua_State *L, void *ud, size_t *size) {\n  (void)ud;  /* to avoid warnings */\n  luaL_checkstack(L, 2, \"too many nested functions\");\n  lua_pushvalue(L, 1);  /* get function */\n  lua_call(L, 0, 1);  /* call it */\n  if (lua_isnil(L, -1)) {\n    *size = 0;\n    return NULL;\n  }\n  else if (lua_isstring(L, -1)) {\n    lua_replace(L, 3);  /* save string in a reserved stack slot */\n    return lua_tolstring(L, 3, size);\n  }\n  else luaL_error(L, \"reader function must return a string\");\n  return NULL;  /* to avoid warnings */\n}\n\n\nstatic int luaB_load (lua_State *L) {\n  int status;\n  const char *cname = luaL_optstring(L, 2, \"=(load)\");\n  luaL_checktype(L, 1, LUA_TFUNCTION);\n  lua_settop(L, 3);  /* function, eventual name, plus one reserved slot */\n  status = lua_load(L, generic_reader, NULL, cname);\n  return load_aux(L, status);\n}\n\n\nstatic int luaB_dofile (lua_State *L) {\n  const char *fname = luaL_optstring(L, 1, NULL);\n  int n = lua_gettop(L);\n  if (luaL_loadfile(L, fname) != 0) lua_error(L);\n  lua_call(L, 0, LUA_MULTRET);\n  return lua_gettop(L) - n;\n}\n\n\nstatic int luaB_assert (lua_State *L) {\n  luaL_checkany(L, 1);\n  if (!lua_toboolean(L, 1))\n    return luaL_error(L, \"%s\", luaL_optstring(L, 2, \"assertion failed!\"));\n  return lua_gettop(L);\n}\n\n\nstatic int luaB_unpack (lua_State *L) {\n  int i, e, n;\n  luaL_checktype(L, 1, LUA_TTABLE);\n  i = luaL_optint(L, 2, 1);\n  e = luaL_opt(L, luaL_checkint, 3, luaL_getn(L, 1));\n  if (i > e) return 0;  /* empty range */\n  n = e - i + 1;  /* number of elements */\n  if (n <= 0 || !lua_checkstack(L, n))  /* n <= 0 means arith. overflow */\n    return luaL_error(L, \"too many results to unpack\");\n  lua_rawgeti(L, 1, i);  /* push arg[i] (avoiding overflow problems) */\n  while (i++ < e)  /* push arg[i + 1...e] */\n    lua_rawgeti(L, 1, i);\n  return n;\n}\n\n\nstatic int luaB_select (lua_State *L) {\n  int n = lua_gettop(L);\n  if (lua_type(L, 1) == LUA_TSTRING && *lua_tostring(L, 1) == '#') {\n    lua_pushinteger(L, n-1);\n    return 1;\n  }\n  else {\n    int i = luaL_checkint(L, 1);\n    if (i < 0) i = n + i;\n    else if (i > n) i = n;\n    luaL_argcheck(L, 1 <= i, 1, \"index out of range\");\n    return n - i;\n  }\n}\n\n\nstatic int luaB_pcall (lua_State *L) {\n  int status;\n  luaL_checkany(L, 1);\n  status = lua_pcall(L, lua_gettop(L) - 1, LUA_MULTRET, 0);\n  lua_pushboolean(L, (status == 0));\n  lua_insert(L, 1);\n  return lua_gettop(L);  /* return status + all results */\n}\n\n\nstatic int luaB_xpcall (lua_State *L) {\n  int status;\n  luaL_checkany(L, 2);\n  lua_settop(L, 2);\n  lua_insert(L, 1);  /* put error function under function to be called */\n  status = lua_pcall(L, 0, LUA_MULTRET, 1);\n  lua_pushboolean(L, (status == 0));\n  lua_replace(L, 1);\n  return lua_gettop(L);  /* return status + all results */\n}\n\n\nstatic int luaB_tostring (lua_State *L) {\n  luaL_checkany(L, 1);\n  if (luaL_callmeta(L, 1, \"__tostring\"))  /* is there a metafield? */\n    return 1;  /* use its value */\n  switch (lua_type(L, 1)) {\n    case LUA_TNUMBER:\n      lua_pushstring(L, lua_tostring(L, 1));\n      break;\n    case LUA_TSTRING:\n      lua_pushvalue(L, 1);\n      break;\n    case LUA_TBOOLEAN:\n      lua_pushstring(L, (lua_toboolean(L, 1) ? \"true\" : \"false\"));\n      break;\n    case LUA_TNIL:\n      lua_pushliteral(L, \"nil\");\n      break;\n    default:\n      lua_pushfstring(L, \"%s: %p\", luaL_typename(L, 1), lua_topointer(L, 1));\n      break;\n  }\n  return 1;\n}\n\n\nstatic int luaB_newproxy (lua_State *L) {\n  lua_settop(L, 1);\n  lua_newuserdata(L, 0);  /* create proxy */\n  if (lua_toboolean(L, 1) == 0)\n    return 1;  /* no metatable */\n  else if (lua_isboolean(L, 1)) {\n    lua_newtable(L);  /* create a new metatable `m' ... */\n    lua_pushvalue(L, -1);  /* ... and mark `m' as a valid metatable */\n    lua_pushboolean(L, 1);\n    lua_rawset(L, lua_upvalueindex(1));  /* weaktable[m] = true */\n  }\n  else {\n    int validproxy = 0;  /* to check if weaktable[metatable(u)] == true */\n    if (lua_getmetatable(L, 1)) {\n      lua_rawget(L, lua_upvalueindex(1));\n      validproxy = lua_toboolean(L, -1);\n      lua_pop(L, 1);  /* remove value */\n    }\n    luaL_argcheck(L, validproxy, 1, \"boolean or proxy expected\");\n    lua_getmetatable(L, 1);  /* metatable is valid; get it */\n  }\n  lua_setmetatable(L, 2);\n  return 1;\n}\n\n\nstatic const luaL_Reg base_funcs[] = {\n  {\"assert\", luaB_assert},\n  {\"collectgarbage\", luaB_collectgarbage},\n  {\"dofile\", luaB_dofile},\n  {\"error\", luaB_error},\n  {\"gcinfo\", luaB_gcinfo},\n  {\"getfenv\", luaB_getfenv},\n  {\"getmetatable\", luaB_getmetatable},\n  {\"loadfile\", luaB_loadfile},\n  {\"load\", luaB_load},\n  {\"loadstring\", luaB_loadstring},\n  {\"next\", luaB_next},\n  {\"pcall\", luaB_pcall},\n  {\"print\", luaB_print},\n  {\"rawequal\", luaB_rawequal},\n  {\"rawget\", luaB_rawget},\n  {\"rawset\", luaB_rawset},\n  {\"select\", luaB_select},\n  {\"setfenv\", luaB_setfenv},\n  {\"setmetatable\", luaB_setmetatable},\n  {\"tonumber\", luaB_tonumber},\n  {\"tostring\", luaB_tostring},\n  {\"type\", luaB_type},\n  {\"unpack\", luaB_unpack},\n  {\"xpcall\", luaB_xpcall},\n  {NULL, NULL}\n};\n\n\n/*\n** {======================================================\n** Coroutine library\n** =======================================================\n*/\n\n#define CO_RUN\t0\t/* running */\n#define CO_SUS\t1\t/* suspended */\n#define CO_NOR\t2\t/* 'normal' (it resumed another coroutine) */\n#define CO_DEAD\t3\n\nstatic const char *const statnames[] =\n    {\"running\", \"suspended\", \"normal\", \"dead\"};\n\nstatic int costatus (lua_State *L, lua_State *co) {\n  if (L == co) return CO_RUN;\n  switch (lua_status(co)) {\n    case LUA_YIELD:\n      return CO_SUS;\n    case 0: {\n      lua_Debug ar;\n      if (lua_getstack(co, 0, &ar) > 0)  /* does it have frames? */\n        return CO_NOR;  /* it is running */\n      else if (lua_gettop(co) == 0)\n          return CO_DEAD;\n      else\n        return CO_SUS;  /* initial state */\n    }\n    default:  /* some error occured */\n      return CO_DEAD;\n  }\n}\n\n\nstatic int luaB_costatus (lua_State *L) {\n  lua_State *co = lua_tothread(L, 1);\n  luaL_argcheck(L, co, 1, \"coroutine expected\");\n  lua_pushstring(L, statnames[costatus(L, co)]);\n  return 1;\n}\n\n\nstatic int auxresume (lua_State *L, lua_State *co, int narg) {\n  int status = costatus(L, co);\n  if (!lua_checkstack(co, narg))\n    luaL_error(L, \"too many arguments to resume\");\n  if (status != CO_SUS) {\n    lua_pushfstring(L, \"cannot resume %s coroutine\", statnames[status]);\n    return -1;  /* error flag */\n  }\n  lua_xmove(L, co, narg);\n  lua_setlevel(L, co);\n  status = lua_resume(co, narg);\n  if (status == 0 || status == LUA_YIELD) {\n    int nres = lua_gettop(co);\n    if (!lua_checkstack(L, nres + 1))\n      luaL_error(L, \"too many results to resume\");\n    lua_xmove(co, L, nres);  /* move yielded values */\n    return nres;\n  }\n  else {\n    lua_xmove(co, L, 1);  /* move error message */\n    return -1;  /* error flag */\n  }\n}\n\n\nstatic int luaB_coresume (lua_State *L) {\n  lua_State *co = lua_tothread(L, 1);\n  int r;\n  luaL_argcheck(L, co, 1, \"coroutine expected\");\n  r = auxresume(L, co, lua_gettop(L) - 1);\n  if (r < 0) {\n    lua_pushboolean(L, 0);\n    lua_insert(L, -2);\n    return 2;  /* return false + error message */\n  }\n  else {\n    lua_pushboolean(L, 1);\n    lua_insert(L, -(r + 1));\n    return r + 1;  /* return true + `resume' returns */\n  }\n}\n\n\nstatic int luaB_auxwrap (lua_State *L) {\n  lua_State *co = lua_tothread(L, lua_upvalueindex(1));\n  int r = auxresume(L, co, lua_gettop(L));\n  if (r < 0) {\n    if (lua_isstring(L, -1)) {  /* error object is a string? */\n      luaL_where(L, 1);  /* add extra info */\n      lua_insert(L, -2);\n      lua_concat(L, 2);\n    }\n    lua_error(L);  /* propagate error */\n  }\n  return r;\n}\n\n\nstatic int luaB_cocreate (lua_State *L) {\n  lua_State *NL = lua_newthread(L);\n  luaL_argcheck(L, lua_isfunction(L, 1) && !lua_iscfunction(L, 1), 1,\n    \"Lua function expected\");\n  lua_pushvalue(L, 1);  /* move function to top */\n  lua_xmove(L, NL, 1);  /* move function from L to NL */\n  return 1;\n}\n\n\nstatic int luaB_cowrap (lua_State *L) {\n  luaB_cocreate(L);\n  lua_pushcclosure(L, luaB_auxwrap, 1);\n  return 1;\n}\n\n\nstatic int luaB_yield (lua_State *L) {\n  return lua_yield(L, lua_gettop(L));\n}\n\n\nstatic int luaB_corunning (lua_State *L) {\n  if (lua_pushthread(L))\n    lua_pushnil(L);  /* main thread is not a coroutine */\n  return 1;\n}\n\n\nstatic const luaL_Reg co_funcs[] = {\n  {\"create\", luaB_cocreate},\n  {\"resume\", luaB_coresume},\n  {\"running\", luaB_corunning},\n  {\"status\", luaB_costatus},\n  {\"wrap\", luaB_cowrap},\n  {\"yield\", luaB_yield},\n  {NULL, NULL}\n};\n\n/* }====================================================== */\n\n\nstatic void auxopen (lua_State *L, const char *name,\n                     lua_CFunction f, lua_CFunction u) {\n  lua_pushcfunction(L, u);\n  lua_pushcclosure(L, f, 1);\n  lua_setfield(L, -2, name);\n}\n\n\nstatic void base_open (lua_State *L) {\n  /* set global _G */\n  lua_pushvalue(L, LUA_GLOBALSINDEX);\n  lua_setglobal(L, \"_G\");\n  /* open lib into global table */\n  luaL_register(L, \"_G\", base_funcs);\n  lua_pushliteral(L, LUA_VERSION);\n  lua_setglobal(L, \"_VERSION\");  /* set global _VERSION */\n  /* `ipairs' and `pairs' need auxiliary functions as upvalues */\n  auxopen(L, \"ipairs\", luaB_ipairs, ipairsaux);\n  auxopen(L, \"pairs\", luaB_pairs, luaB_next);\n  /* `newproxy' needs a weaktable as upvalue */\n  lua_createtable(L, 0, 1);  /* new table `w' */\n  lua_pushvalue(L, -1);  /* `w' will be its own metatable */\n  lua_setmetatable(L, -2);\n  lua_pushliteral(L, \"kv\");\n  lua_setfield(L, -2, \"__mode\");  /* metatable(w).__mode = \"kv\" */\n  lua_pushcclosure(L, luaB_newproxy, 1);\n  lua_setglobal(L, \"newproxy\");  /* set global `newproxy' */\n}\n\n\nLUALIB_API int luaopen_base (lua_State *L) {\n  base_open(L);\n  luaL_register(L, LUA_COLIBNAME, co_funcs);\n  return 2;\n}\n\n"
  },
  {
    "path": "deps/lua/src/lcode.c",
    "content": "/*\n** $Id: lcode.c,v 2.25.1.5 2011/01/31 14:53:16 roberto Exp $\n** Code generator for Lua\n** See Copyright Notice in lua.h\n*/\n\n\n#include <stdlib.h>\n\n#define lcode_c\n#define LUA_CORE\n\n#include \"lua.h\"\n\n#include \"lcode.h\"\n#include \"ldebug.h\"\n#include \"ldo.h\"\n#include \"lgc.h\"\n#include \"llex.h\"\n#include \"lmem.h\"\n#include \"lobject.h\"\n#include \"lopcodes.h\"\n#include \"lparser.h\"\n#include \"ltable.h\"\n\n\n#define hasjumps(e)\t((e)->t != (e)->f)\n\n\nstatic int isnumeral(expdesc *e) {\n  return (e->k == VKNUM && e->t == NO_JUMP && e->f == NO_JUMP);\n}\n\n\nvoid luaK_nil (FuncState *fs, int from, int n) {\n  Instruction *previous;\n  if (fs->pc > fs->lasttarget) {  /* no jumps to current position? */\n    if (fs->pc == 0) {  /* function start? */\n      if (from >= fs->nactvar)\n        return;  /* positions are already clean */\n    }\n    else {\n      previous = &fs->f->code[fs->pc-1];\n      if (GET_OPCODE(*previous) == OP_LOADNIL) {\n        int pfrom = GETARG_A(*previous);\n        int pto = GETARG_B(*previous);\n        if (pfrom <= from && from <= pto+1) {  /* can connect both? */\n          if (from+n-1 > pto)\n            SETARG_B(*previous, from+n-1);\n          return;\n        }\n      }\n    }\n  }\n  luaK_codeABC(fs, OP_LOADNIL, from, from+n-1, 0);  /* else no optimization */\n}\n\n\nint luaK_jump (FuncState *fs) {\n  int jpc = fs->jpc;  /* save list of jumps to here */\n  int j;\n  fs->jpc = NO_JUMP;\n  j = luaK_codeAsBx(fs, OP_JMP, 0, NO_JUMP);\n  luaK_concat(fs, &j, jpc);  /* keep them on hold */\n  return j;\n}\n\n\nvoid luaK_ret (FuncState *fs, int first, int nret) {\n  luaK_codeABC(fs, OP_RETURN, first, nret+1, 0);\n}\n\n\nstatic int condjump (FuncState *fs, OpCode op, int A, int B, int C) {\n  luaK_codeABC(fs, op, A, B, C);\n  return luaK_jump(fs);\n}\n\n\nstatic void fixjump (FuncState *fs, int pc, int dest) {\n  Instruction *jmp = &fs->f->code[pc];\n  int offset = dest-(pc+1);\n  lua_assert(dest != NO_JUMP);\n  if (abs(offset) > MAXARG_sBx)\n    luaX_syntaxerror(fs->ls, \"control structure too long\");\n  SETARG_sBx(*jmp, offset);\n}\n\n\n/*\n** returns current `pc' and marks it as a jump target (to avoid wrong\n** optimizations with consecutive instructions not in the same basic block).\n*/\nint luaK_getlabel (FuncState *fs) {\n  fs->lasttarget = fs->pc;\n  return fs->pc;\n}\n\n\nstatic int getjump (FuncState *fs, int pc) {\n  int offset = GETARG_sBx(fs->f->code[pc]);\n  if (offset == NO_JUMP)  /* point to itself represents end of list */\n    return NO_JUMP;  /* end of list */\n  else\n    return (pc+1)+offset;  /* turn offset into absolute position */\n}\n\n\nstatic Instruction *getjumpcontrol (FuncState *fs, int pc) {\n  Instruction *pi = &fs->f->code[pc];\n  if (pc >= 1 && testTMode(GET_OPCODE(*(pi-1))))\n    return pi-1;\n  else\n    return pi;\n}\n\n\n/*\n** check whether list has any jump that do not produce a value\n** (or produce an inverted value)\n*/\nstatic int need_value (FuncState *fs, int list) {\n  for (; list != NO_JUMP; list = getjump(fs, list)) {\n    Instruction i = *getjumpcontrol(fs, list);\n    if (GET_OPCODE(i) != OP_TESTSET) return 1;\n  }\n  return 0;  /* not found */\n}\n\n\nstatic int patchtestreg (FuncState *fs, int node, int reg) {\n  Instruction *i = getjumpcontrol(fs, node);\n  if (GET_OPCODE(*i) != OP_TESTSET)\n    return 0;  /* cannot patch other instructions */\n  if (reg != NO_REG && reg != GETARG_B(*i))\n    SETARG_A(*i, reg);\n  else  /* no register to put value or register already has the value */\n    *i = CREATE_ABC(OP_TEST, GETARG_B(*i), 0, GETARG_C(*i));\n\n  return 1;\n}\n\n\nstatic void removevalues (FuncState *fs, int list) {\n  for (; list != NO_JUMP; list = getjump(fs, list))\n      patchtestreg(fs, list, NO_REG);\n}\n\n\nstatic void patchlistaux (FuncState *fs, int list, int vtarget, int reg,\n                          int dtarget) {\n  while (list != NO_JUMP) {\n    int next = getjump(fs, list);\n    if (patchtestreg(fs, list, reg))\n      fixjump(fs, list, vtarget);\n    else\n      fixjump(fs, list, dtarget);  /* jump to default target */\n    list = next;\n  }\n}\n\n\nstatic void dischargejpc (FuncState *fs) {\n  patchlistaux(fs, fs->jpc, fs->pc, NO_REG, fs->pc);\n  fs->jpc = NO_JUMP;\n}\n\n\nvoid luaK_patchlist (FuncState *fs, int list, int target) {\n  if (target == fs->pc)\n    luaK_patchtohere(fs, list);\n  else {\n    lua_assert(target < fs->pc);\n    patchlistaux(fs, list, target, NO_REG, target);\n  }\n}\n\n\nvoid luaK_patchtohere (FuncState *fs, int list) {\n  luaK_getlabel(fs);\n  luaK_concat(fs, &fs->jpc, list);\n}\n\n\nvoid luaK_concat (FuncState *fs, int *l1, int l2) {\n  if (l2 == NO_JUMP) return;\n  else if (*l1 == NO_JUMP)\n    *l1 = l2;\n  else {\n    int list = *l1;\n    int next;\n    while ((next = getjump(fs, list)) != NO_JUMP)  /* find last element */\n      list = next;\n    fixjump(fs, list, l2);\n  }\n}\n\n\nvoid luaK_checkstack (FuncState *fs, int n) {\n  int newstack = fs->freereg + n;\n  if (newstack > fs->f->maxstacksize) {\n    if (newstack >= MAXSTACK)\n      luaX_syntaxerror(fs->ls, \"function or expression too complex\");\n    fs->f->maxstacksize = cast_byte(newstack);\n  }\n}\n\n\nvoid luaK_reserveregs (FuncState *fs, int n) {\n  luaK_checkstack(fs, n);\n  fs->freereg += n;\n}\n\n\nstatic void freereg (FuncState *fs, int reg) {\n  if (!ISK(reg) && reg >= fs->nactvar) {\n    fs->freereg--;\n    lua_assert(reg == fs->freereg);\n  }\n}\n\n\nstatic void freeexp (FuncState *fs, expdesc *e) {\n  if (e->k == VNONRELOC)\n    freereg(fs, e->u.s.info);\n}\n\n\nstatic int addk (FuncState *fs, TValue *k, TValue *v) {\n  lua_State *L = fs->L;\n  TValue *idx = luaH_set(L, fs->h, k);\n  Proto *f = fs->f;\n  int oldsize = f->sizek;\n  if (ttisnumber(idx)) {\n    lua_assert(luaO_rawequalObj(&fs->f->k[cast_int(nvalue(idx))], v));\n    return cast_int(nvalue(idx));\n  }\n  else {  /* constant not found; create a new entry */\n    setnvalue(idx, cast_num(fs->nk));\n    luaM_growvector(L, f->k, fs->nk, f->sizek, TValue,\n                    MAXARG_Bx, \"constant table overflow\");\n    while (oldsize < f->sizek) setnilvalue(&f->k[oldsize++]);\n    setobj(L, &f->k[fs->nk], v);\n    luaC_barrier(L, f, v);\n    return fs->nk++;\n  }\n}\n\n\nint luaK_stringK (FuncState *fs, TString *s) {\n  TValue o;\n  setsvalue(fs->L, &o, s);\n  return addk(fs, &o, &o);\n}\n\n\nint luaK_numberK (FuncState *fs, lua_Number r) {\n  TValue o;\n  setnvalue(&o, r);\n  return addk(fs, &o, &o);\n}\n\n\nstatic int boolK (FuncState *fs, int b) {\n  TValue o;\n  setbvalue(&o, b);\n  return addk(fs, &o, &o);\n}\n\n\nstatic int nilK (FuncState *fs) {\n  TValue k, v;\n  setnilvalue(&v);\n  /* cannot use nil as key; instead use table itself to represent nil */\n  sethvalue(fs->L, &k, fs->h);\n  return addk(fs, &k, &v);\n}\n\n\nvoid luaK_setreturns (FuncState *fs, expdesc *e, int nresults) {\n  if (e->k == VCALL) {  /* expression is an open function call? */\n    SETARG_C(getcode(fs, e), nresults+1);\n  }\n  else if (e->k == VVARARG) {\n    SETARG_B(getcode(fs, e), nresults+1);\n    SETARG_A(getcode(fs, e), fs->freereg);\n    luaK_reserveregs(fs, 1);\n  }\n}\n\n\nvoid luaK_setoneret (FuncState *fs, expdesc *e) {\n  if (e->k == VCALL) {  /* expression is an open function call? */\n    e->k = VNONRELOC;\n    e->u.s.info = GETARG_A(getcode(fs, e));\n  }\n  else if (e->k == VVARARG) {\n    SETARG_B(getcode(fs, e), 2);\n    e->k = VRELOCABLE;  /* can relocate its simple result */\n  }\n}\n\n\nvoid luaK_dischargevars (FuncState *fs, expdesc *e) {\n  switch (e->k) {\n    case VLOCAL: {\n      e->k = VNONRELOC;\n      break;\n    }\n    case VUPVAL: {\n      e->u.s.info = luaK_codeABC(fs, OP_GETUPVAL, 0, e->u.s.info, 0);\n      e->k = VRELOCABLE;\n      break;\n    }\n    case VGLOBAL: {\n      e->u.s.info = luaK_codeABx(fs, OP_GETGLOBAL, 0, e->u.s.info);\n      e->k = VRELOCABLE;\n      break;\n    }\n    case VINDEXED: {\n      freereg(fs, e->u.s.aux);\n      freereg(fs, e->u.s.info);\n      e->u.s.info = luaK_codeABC(fs, OP_GETTABLE, 0, e->u.s.info, e->u.s.aux);\n      e->k = VRELOCABLE;\n      break;\n    }\n    case VVARARG:\n    case VCALL: {\n      luaK_setoneret(fs, e);\n      break;\n    }\n    default: break;  /* there is one value available (somewhere) */\n  }\n}\n\n\nstatic int code_label (FuncState *fs, int A, int b, int jump) {\n  luaK_getlabel(fs);  /* those instructions may be jump targets */\n  return luaK_codeABC(fs, OP_LOADBOOL, A, b, jump);\n}\n\n\nstatic void discharge2reg (FuncState *fs, expdesc *e, int reg) {\n  luaK_dischargevars(fs, e);\n  switch (e->k) {\n    case VNIL: {\n      luaK_nil(fs, reg, 1);\n      break;\n    }\n    case VFALSE:  case VTRUE: {\n      luaK_codeABC(fs, OP_LOADBOOL, reg, e->k == VTRUE, 0);\n      break;\n    }\n    case VK: {\n      luaK_codeABx(fs, OP_LOADK, reg, e->u.s.info);\n      break;\n    }\n    case VKNUM: {\n      luaK_codeABx(fs, OP_LOADK, reg, luaK_numberK(fs, e->u.nval));\n      break;\n    }\n    case VRELOCABLE: {\n      Instruction *pc = &getcode(fs, e);\n      SETARG_A(*pc, reg);\n      break;\n    }\n    case VNONRELOC: {\n      if (reg != e->u.s.info)\n        luaK_codeABC(fs, OP_MOVE, reg, e->u.s.info, 0);\n      break;\n    }\n    default: {\n      lua_assert(e->k == VVOID || e->k == VJMP);\n      return;  /* nothing to do... */\n    }\n  }\n  e->u.s.info = reg;\n  e->k = VNONRELOC;\n}\n\n\nstatic void discharge2anyreg (FuncState *fs, expdesc *e) {\n  if (e->k != VNONRELOC) {\n    luaK_reserveregs(fs, 1);\n    discharge2reg(fs, e, fs->freereg-1);\n  }\n}\n\n\nstatic void exp2reg (FuncState *fs, expdesc *e, int reg) {\n  discharge2reg(fs, e, reg);\n  if (e->k == VJMP)\n    luaK_concat(fs, &e->t, e->u.s.info);  /* put this jump in `t' list */\n  if (hasjumps(e)) {\n    int final;  /* position after whole expression */\n    int p_f = NO_JUMP;  /* position of an eventual LOAD false */\n    int p_t = NO_JUMP;  /* position of an eventual LOAD true */\n    if (need_value(fs, e->t) || need_value(fs, e->f)) {\n      int fj = (e->k == VJMP) ? NO_JUMP : luaK_jump(fs);\n      p_f = code_label(fs, reg, 0, 1);\n      p_t = code_label(fs, reg, 1, 0);\n      luaK_patchtohere(fs, fj);\n    }\n    final = luaK_getlabel(fs);\n    patchlistaux(fs, e->f, final, reg, p_f);\n    patchlistaux(fs, e->t, final, reg, p_t);\n  }\n  e->f = e->t = NO_JUMP;\n  e->u.s.info = reg;\n  e->k = VNONRELOC;\n}\n\n\nvoid luaK_exp2nextreg (FuncState *fs, expdesc *e) {\n  luaK_dischargevars(fs, e);\n  freeexp(fs, e);\n  luaK_reserveregs(fs, 1);\n  exp2reg(fs, e, fs->freereg - 1);\n}\n\n\nint luaK_exp2anyreg (FuncState *fs, expdesc *e) {\n  luaK_dischargevars(fs, e);\n  if (e->k == VNONRELOC) {\n    if (!hasjumps(e)) return e->u.s.info;  /* exp is already in a register */\n    if (e->u.s.info >= fs->nactvar) {  /* reg. is not a local? */\n      exp2reg(fs, e, e->u.s.info);  /* put value on it */\n      return e->u.s.info;\n    }\n  }\n  luaK_exp2nextreg(fs, e);  /* default */\n  return e->u.s.info;\n}\n\n\nvoid luaK_exp2val (FuncState *fs, expdesc *e) {\n  if (hasjumps(e))\n    luaK_exp2anyreg(fs, e);\n  else\n    luaK_dischargevars(fs, e);\n}\n\n\nint luaK_exp2RK (FuncState *fs, expdesc *e) {\n  luaK_exp2val(fs, e);\n  switch (e->k) {\n    case VKNUM:\n    case VTRUE:\n    case VFALSE:\n    case VNIL: {\n      if (fs->nk <= MAXINDEXRK) {  /* constant fit in RK operand? */\n        e->u.s.info = (e->k == VNIL)  ? nilK(fs) :\n                      (e->k == VKNUM) ? luaK_numberK(fs, e->u.nval) :\n                                        boolK(fs, (e->k == VTRUE));\n        e->k = VK;\n        return RKASK(e->u.s.info);\n      }\n      else break;\n    }\n    case VK: {\n      if (e->u.s.info <= MAXINDEXRK)  /* constant fit in argC? */\n        return RKASK(e->u.s.info);\n      else break;\n    }\n    default: break;\n  }\n  /* not a constant in the right range: put it in a register */\n  return luaK_exp2anyreg(fs, e);\n}\n\n\nvoid luaK_storevar (FuncState *fs, expdesc *var, expdesc *ex) {\n  switch (var->k) {\n    case VLOCAL: {\n      freeexp(fs, ex);\n      exp2reg(fs, ex, var->u.s.info);\n      return;\n    }\n    case VUPVAL: {\n      int e = luaK_exp2anyreg(fs, ex);\n      luaK_codeABC(fs, OP_SETUPVAL, e, var->u.s.info, 0);\n      break;\n    }\n    case VGLOBAL: {\n      int e = luaK_exp2anyreg(fs, ex);\n      luaK_codeABx(fs, OP_SETGLOBAL, e, var->u.s.info);\n      break;\n    }\n    case VINDEXED: {\n      int e = luaK_exp2RK(fs, ex);\n      luaK_codeABC(fs, OP_SETTABLE, var->u.s.info, var->u.s.aux, e);\n      break;\n    }\n    default: {\n      lua_assert(0);  /* invalid var kind to store */\n      break;\n    }\n  }\n  freeexp(fs, ex);\n}\n\n\nvoid luaK_self (FuncState *fs, expdesc *e, expdesc *key) {\n  int func;\n  luaK_exp2anyreg(fs, e);\n  freeexp(fs, e);\n  func = fs->freereg;\n  luaK_reserveregs(fs, 2);\n  luaK_codeABC(fs, OP_SELF, func, e->u.s.info, luaK_exp2RK(fs, key));\n  freeexp(fs, key);\n  e->u.s.info = func;\n  e->k = VNONRELOC;\n}\n\n\nstatic void invertjump (FuncState *fs, expdesc *e) {\n  Instruction *pc = getjumpcontrol(fs, e->u.s.info);\n  lua_assert(testTMode(GET_OPCODE(*pc)) && GET_OPCODE(*pc) != OP_TESTSET &&\n                                           GET_OPCODE(*pc) != OP_TEST);\n  SETARG_A(*pc, !(GETARG_A(*pc)));\n}\n\n\nstatic int jumponcond (FuncState *fs, expdesc *e, int cond) {\n  if (e->k == VRELOCABLE) {\n    Instruction ie = getcode(fs, e);\n    if (GET_OPCODE(ie) == OP_NOT) {\n      fs->pc--;  /* remove previous OP_NOT */\n      return condjump(fs, OP_TEST, GETARG_B(ie), 0, !cond);\n    }\n    /* else go through */\n  }\n  discharge2anyreg(fs, e);\n  freeexp(fs, e);\n  return condjump(fs, OP_TESTSET, NO_REG, e->u.s.info, cond);\n}\n\n\nvoid luaK_goiftrue (FuncState *fs, expdesc *e) {\n  int pc;  /* pc of last jump */\n  luaK_dischargevars(fs, e);\n  switch (e->k) {\n    case VK: case VKNUM: case VTRUE: {\n      pc = NO_JUMP;  /* always true; do nothing */\n      break;\n    }\n    case VJMP: {\n      invertjump(fs, e);\n      pc = e->u.s.info;\n      break;\n    }\n    default: {\n      pc = jumponcond(fs, e, 0);\n      break;\n    }\n  }\n  luaK_concat(fs, &e->f, pc);  /* insert last jump in `f' list */\n  luaK_patchtohere(fs, e->t);\n  e->t = NO_JUMP;\n}\n\n\nstatic void luaK_goiffalse (FuncState *fs, expdesc *e) {\n  int pc;  /* pc of last jump */\n  luaK_dischargevars(fs, e);\n  switch (e->k) {\n    case VNIL: case VFALSE: {\n      pc = NO_JUMP;  /* always false; do nothing */\n      break;\n    }\n    case VJMP: {\n      pc = e->u.s.info;\n      break;\n    }\n    default: {\n      pc = jumponcond(fs, e, 1);\n      break;\n    }\n  }\n  luaK_concat(fs, &e->t, pc);  /* insert last jump in `t' list */\n  luaK_patchtohere(fs, e->f);\n  e->f = NO_JUMP;\n}\n\n\nstatic void codenot (FuncState *fs, expdesc *e) {\n  luaK_dischargevars(fs, e);\n  switch (e->k) {\n    case VNIL: case VFALSE: {\n      e->k = VTRUE;\n      break;\n    }\n    case VK: case VKNUM: case VTRUE: {\n      e->k = VFALSE;\n      break;\n    }\n    case VJMP: {\n      invertjump(fs, e);\n      break;\n    }\n    case VRELOCABLE:\n    case VNONRELOC: {\n      discharge2anyreg(fs, e);\n      freeexp(fs, e);\n      e->u.s.info = luaK_codeABC(fs, OP_NOT, 0, e->u.s.info, 0);\n      e->k = VRELOCABLE;\n      break;\n    }\n    default: {\n      lua_assert(0);  /* cannot happen */\n      break;\n    }\n  }\n  /* interchange true and false lists */\n  { int temp = e->f; e->f = e->t; e->t = temp; }\n  removevalues(fs, e->f);\n  removevalues(fs, e->t);\n}\n\n\nvoid luaK_indexed (FuncState *fs, expdesc *t, expdesc *k) {\n  t->u.s.aux = luaK_exp2RK(fs, k);\n  t->k = VINDEXED;\n}\n\n\nstatic int constfolding (OpCode op, expdesc *e1, expdesc *e2) {\n  lua_Number v1, v2, r;\n  if (!isnumeral(e1) || !isnumeral(e2)) return 0;\n  v1 = e1->u.nval;\n  v2 = e2->u.nval;\n  switch (op) {\n    case OP_ADD: r = luai_numadd(v1, v2); break;\n    case OP_SUB: r = luai_numsub(v1, v2); break;\n    case OP_MUL: r = luai_nummul(v1, v2); break;\n    case OP_DIV:\n      if (v2 == 0) return 0;  /* do not attempt to divide by 0 */\n      r = luai_numdiv(v1, v2); break;\n    case OP_MOD:\n      if (v2 == 0) return 0;  /* do not attempt to divide by 0 */\n      r = luai_nummod(v1, v2); break;\n    case OP_POW: r = luai_numpow(v1, v2); break;\n    case OP_UNM: r = luai_numunm(v1); break;\n    case OP_LEN: return 0;  /* no constant folding for 'len' */\n    default: lua_assert(0); r = 0; break;\n  }\n  if (luai_numisnan(r)) return 0;  /* do not attempt to produce NaN */\n  e1->u.nval = r;\n  return 1;\n}\n\n\nstatic void codearith (FuncState *fs, OpCode op, expdesc *e1, expdesc *e2) {\n  if (constfolding(op, e1, e2))\n    return;\n  else {\n    int o2 = (op != OP_UNM && op != OP_LEN) ? luaK_exp2RK(fs, e2) : 0;\n    int o1 = luaK_exp2RK(fs, e1);\n    if (o1 > o2) {\n      freeexp(fs, e1);\n      freeexp(fs, e2);\n    }\n    else {\n      freeexp(fs, e2);\n      freeexp(fs, e1);\n    }\n    e1->u.s.info = luaK_codeABC(fs, op, 0, o1, o2);\n    e1->k = VRELOCABLE;\n  }\n}\n\n\nstatic void codecomp (FuncState *fs, OpCode op, int cond, expdesc *e1,\n                                                          expdesc *e2) {\n  int o1 = luaK_exp2RK(fs, e1);\n  int o2 = luaK_exp2RK(fs, e2);\n  freeexp(fs, e2);\n  freeexp(fs, e1);\n  if (cond == 0 && op != OP_EQ) {\n    int temp;  /* exchange args to replace by `<' or `<=' */\n    temp = o1; o1 = o2; o2 = temp;  /* o1 <==> o2 */\n    cond = 1;\n  }\n  e1->u.s.info = condjump(fs, op, cond, o1, o2);\n  e1->k = VJMP;\n}\n\n\nvoid luaK_prefix (FuncState *fs, UnOpr op, expdesc *e) {\n  expdesc e2;\n  e2.t = e2.f = NO_JUMP; e2.k = VKNUM; e2.u.nval = 0;\n  switch (op) {\n    case OPR_MINUS: {\n      if (!isnumeral(e))\n        luaK_exp2anyreg(fs, e);  /* cannot operate on non-numeric constants */\n      codearith(fs, OP_UNM, e, &e2);\n      break;\n    }\n    case OPR_NOT: codenot(fs, e); break;\n    case OPR_LEN: {\n      luaK_exp2anyreg(fs, e);  /* cannot operate on constants */\n      codearith(fs, OP_LEN, e, &e2);\n      break;\n    }\n    default: lua_assert(0);\n  }\n}\n\n\nvoid luaK_infix (FuncState *fs, BinOpr op, expdesc *v) {\n  switch (op) {\n    case OPR_AND: {\n      luaK_goiftrue(fs, v);\n      break;\n    }\n    case OPR_OR: {\n      luaK_goiffalse(fs, v);\n      break;\n    }\n    case OPR_CONCAT: {\n      luaK_exp2nextreg(fs, v);  /* operand must be on the `stack' */\n      break;\n    }\n    case OPR_ADD: case OPR_SUB: case OPR_MUL: case OPR_DIV:\n    case OPR_MOD: case OPR_POW: {\n      if (!isnumeral(v)) luaK_exp2RK(fs, v);\n      break;\n    }\n    default: {\n      luaK_exp2RK(fs, v);\n      break;\n    }\n  }\n}\n\n\nvoid luaK_posfix (FuncState *fs, BinOpr op, expdesc *e1, expdesc *e2) {\n  switch (op) {\n    case OPR_AND: {\n      lua_assert(e1->t == NO_JUMP);  /* list must be closed */\n      luaK_dischargevars(fs, e2);\n      luaK_concat(fs, &e2->f, e1->f);\n      *e1 = *e2;\n      break;\n    }\n    case OPR_OR: {\n      lua_assert(e1->f == NO_JUMP);  /* list must be closed */\n      luaK_dischargevars(fs, e2);\n      luaK_concat(fs, &e2->t, e1->t);\n      *e1 = *e2;\n      break;\n    }\n    case OPR_CONCAT: {\n      luaK_exp2val(fs, e2);\n      if (e2->k == VRELOCABLE && GET_OPCODE(getcode(fs, e2)) == OP_CONCAT) {\n        lua_assert(e1->u.s.info == GETARG_B(getcode(fs, e2))-1);\n        freeexp(fs, e1);\n        SETARG_B(getcode(fs, e2), e1->u.s.info);\n        e1->k = VRELOCABLE; e1->u.s.info = e2->u.s.info;\n      }\n      else {\n        luaK_exp2nextreg(fs, e2);  /* operand must be on the 'stack' */\n        codearith(fs, OP_CONCAT, e1, e2);\n      }\n      break;\n    }\n    case OPR_ADD: codearith(fs, OP_ADD, e1, e2); break;\n    case OPR_SUB: codearith(fs, OP_SUB, e1, e2); break;\n    case OPR_MUL: codearith(fs, OP_MUL, e1, e2); break;\n    case OPR_DIV: codearith(fs, OP_DIV, e1, e2); break;\n    case OPR_MOD: codearith(fs, OP_MOD, e1, e2); break;\n    case OPR_POW: codearith(fs, OP_POW, e1, e2); break;\n    case OPR_EQ: codecomp(fs, OP_EQ, 1, e1, e2); break;\n    case OPR_NE: codecomp(fs, OP_EQ, 0, e1, e2); break;\n    case OPR_LT: codecomp(fs, OP_LT, 1, e1, e2); break;\n    case OPR_LE: codecomp(fs, OP_LE, 1, e1, e2); break;\n    case OPR_GT: codecomp(fs, OP_LT, 0, e1, e2); break;\n    case OPR_GE: codecomp(fs, OP_LE, 0, e1, e2); break;\n    default: lua_assert(0);\n  }\n}\n\n\nvoid luaK_fixline (FuncState *fs, int line) {\n  fs->f->lineinfo[fs->pc - 1] = line;\n}\n\n\nstatic int luaK_code (FuncState *fs, Instruction i, int line) {\n  Proto *f = fs->f;\n  dischargejpc(fs);  /* `pc' will change */\n  /* put new instruction in code array */\n  luaM_growvector(fs->L, f->code, fs->pc, f->sizecode, Instruction,\n                  MAX_INT, \"code size overflow\");\n  f->code[fs->pc] = i;\n  /* save corresponding line information */\n  luaM_growvector(fs->L, f->lineinfo, fs->pc, f->sizelineinfo, int,\n                  MAX_INT, \"code size overflow\");\n  f->lineinfo[fs->pc] = line;\n  return fs->pc++;\n}\n\n\nint luaK_codeABC (FuncState *fs, OpCode o, int a, int b, int c) {\n  lua_assert(getOpMode(o) == iABC);\n  lua_assert(getBMode(o) != OpArgN || b == 0);\n  lua_assert(getCMode(o) != OpArgN || c == 0);\n  return luaK_code(fs, CREATE_ABC(o, a, b, c), fs->ls->lastline);\n}\n\n\nint luaK_codeABx (FuncState *fs, OpCode o, int a, unsigned int bc) {\n  lua_assert(getOpMode(o) == iABx || getOpMode(o) == iAsBx);\n  lua_assert(getCMode(o) == OpArgN);\n  return luaK_code(fs, CREATE_ABx(o, a, bc), fs->ls->lastline);\n}\n\n\nvoid luaK_setlist (FuncState *fs, int base, int nelems, int tostore) {\n  int c =  (nelems - 1)/LFIELDS_PER_FLUSH + 1;\n  int b = (tostore == LUA_MULTRET) ? 0 : tostore;\n  lua_assert(tostore != 0);\n  if (c <= MAXARG_C)\n    luaK_codeABC(fs, OP_SETLIST, base, b, c);\n  else {\n    luaK_codeABC(fs, OP_SETLIST, base, b, 0);\n    luaK_code(fs, cast(Instruction, c), fs->ls->lastline);\n  }\n  fs->freereg = base + 1;  /* free registers with list values */\n}\n\n"
  },
  {
    "path": "deps/lua/src/lcode.h",
    "content": "/*\n** $Id: lcode.h,v 1.48.1.1 2007/12/27 13:02:25 roberto Exp $\n** Code generator for Lua\n** See Copyright Notice in lua.h\n*/\n\n#ifndef lcode_h\n#define lcode_h\n\n#include \"llex.h\"\n#include \"lobject.h\"\n#include \"lopcodes.h\"\n#include \"lparser.h\"\n\n\n/*\n** Marks the end of a patch list. It is an invalid value both as an absolute\n** address, and as a list link (would link an element to itself).\n*/\n#define NO_JUMP (-1)\n\n\n/*\n** grep \"ORDER OPR\" if you change these enums\n*/\ntypedef enum BinOpr {\n  OPR_ADD, OPR_SUB, OPR_MUL, OPR_DIV, OPR_MOD, OPR_POW,\n  OPR_CONCAT,\n  OPR_NE, OPR_EQ,\n  OPR_LT, OPR_LE, OPR_GT, OPR_GE,\n  OPR_AND, OPR_OR,\n  OPR_NOBINOPR\n} BinOpr;\n\n\ntypedef enum UnOpr { OPR_MINUS, OPR_NOT, OPR_LEN, OPR_NOUNOPR } UnOpr;\n\n\n#define getcode(fs,e)\t((fs)->f->code[(e)->u.s.info])\n\n#define luaK_codeAsBx(fs,o,A,sBx)\tluaK_codeABx(fs,o,A,(sBx)+MAXARG_sBx)\n\n#define luaK_setmultret(fs,e)\tluaK_setreturns(fs, e, LUA_MULTRET)\n\nLUAI_FUNC int luaK_codeABx (FuncState *fs, OpCode o, int A, unsigned int Bx);\nLUAI_FUNC int luaK_codeABC (FuncState *fs, OpCode o, int A, int B, int C);\nLUAI_FUNC void luaK_fixline (FuncState *fs, int line);\nLUAI_FUNC void luaK_nil (FuncState *fs, int from, int n);\nLUAI_FUNC void luaK_reserveregs (FuncState *fs, int n);\nLUAI_FUNC void luaK_checkstack (FuncState *fs, int n);\nLUAI_FUNC int luaK_stringK (FuncState *fs, TString *s);\nLUAI_FUNC int luaK_numberK (FuncState *fs, lua_Number r);\nLUAI_FUNC void luaK_dischargevars (FuncState *fs, expdesc *e);\nLUAI_FUNC int luaK_exp2anyreg (FuncState *fs, expdesc *e);\nLUAI_FUNC void luaK_exp2nextreg (FuncState *fs, expdesc *e);\nLUAI_FUNC void luaK_exp2val (FuncState *fs, expdesc *e);\nLUAI_FUNC int luaK_exp2RK (FuncState *fs, expdesc *e);\nLUAI_FUNC void luaK_self (FuncState *fs, expdesc *e, expdesc *key);\nLUAI_FUNC void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k);\nLUAI_FUNC void luaK_goiftrue (FuncState *fs, expdesc *e);\nLUAI_FUNC void luaK_storevar (FuncState *fs, expdesc *var, expdesc *e);\nLUAI_FUNC void luaK_setreturns (FuncState *fs, expdesc *e, int nresults);\nLUAI_FUNC void luaK_setoneret (FuncState *fs, expdesc *e);\nLUAI_FUNC int luaK_jump (FuncState *fs);\nLUAI_FUNC void luaK_ret (FuncState *fs, int first, int nret);\nLUAI_FUNC void luaK_patchlist (FuncState *fs, int list, int target);\nLUAI_FUNC void luaK_patchtohere (FuncState *fs, int list);\nLUAI_FUNC void luaK_concat (FuncState *fs, int *l1, int l2);\nLUAI_FUNC int luaK_getlabel (FuncState *fs);\nLUAI_FUNC void luaK_prefix (FuncState *fs, UnOpr op, expdesc *v);\nLUAI_FUNC void luaK_infix (FuncState *fs, BinOpr op, expdesc *v);\nLUAI_FUNC void luaK_posfix (FuncState *fs, BinOpr op, expdesc *v1, expdesc *v2);\nLUAI_FUNC void luaK_setlist (FuncState *fs, int base, int nelems, int tostore);\n\n\n#endif\n"
  },
  {
    "path": "deps/lua/src/ldblib.c",
    "content": "/*\n** $Id: ldblib.c,v 1.104.1.4 2009/08/04 18:50:18 roberto Exp $\n** Interface from Lua to its debug API\n** See Copyright Notice in lua.h\n*/\n\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#define ldblib_c\n#define LUA_LIB\n\n#include \"lua.h\"\n\n#include \"lauxlib.h\"\n#include \"lualib.h\"\n\n\n\nstatic int db_getregistry (lua_State *L) {\n  lua_pushvalue(L, LUA_REGISTRYINDEX);\n  return 1;\n}\n\n\nstatic int db_getmetatable (lua_State *L) {\n  luaL_checkany(L, 1);\n  if (!lua_getmetatable(L, 1)) {\n    lua_pushnil(L);  /* no metatable */\n  }\n  return 1;\n}\n\n\nstatic int db_setmetatable (lua_State *L) {\n  int t = lua_type(L, 2);\n  luaL_argcheck(L, t == LUA_TNIL || t == LUA_TTABLE, 2,\n                    \"nil or table expected\");\n  lua_settop(L, 2);\n  lua_pushboolean(L, lua_setmetatable(L, 1));\n  return 1;\n}\n\n\nstatic int db_getfenv (lua_State *L) {\n  luaL_checkany(L, 1);\n  lua_getfenv(L, 1);\n  return 1;\n}\n\n\nstatic int db_setfenv (lua_State *L) {\n  luaL_checktype(L, 2, LUA_TTABLE);\n  lua_settop(L, 2);\n  if (lua_setfenv(L, 1) == 0)\n    luaL_error(L, LUA_QL(\"setfenv\")\n                  \" cannot change environment of given object\");\n  return 1;\n}\n\n\nstatic void settabss (lua_State *L, const char *i, const char *v) {\n  lua_pushstring(L, v);\n  lua_setfield(L, -2, i);\n}\n\n\nstatic void settabsi (lua_State *L, const char *i, int v) {\n  lua_pushinteger(L, v);\n  lua_setfield(L, -2, i);\n}\n\n\nstatic lua_State *getthread (lua_State *L, int *arg) {\n  if (lua_isthread(L, 1)) {\n    *arg = 1;\n    return lua_tothread(L, 1);\n  }\n  else {\n    *arg = 0;\n    return L;\n  }\n}\n\n\nstatic void treatstackoption (lua_State *L, lua_State *L1, const char *fname) {\n  if (L == L1) {\n    lua_pushvalue(L, -2);\n    lua_remove(L, -3);\n  }\n  else\n    lua_xmove(L1, L, 1);\n  lua_setfield(L, -2, fname);\n}\n\n\nstatic int db_getinfo (lua_State *L) {\n  lua_Debug ar;\n  int arg;\n  lua_State *L1 = getthread(L, &arg);\n  const char *options = luaL_optstring(L, arg+2, \"flnSu\");\n  if (lua_isnumber(L, arg+1)) {\n    if (!lua_getstack(L1, (int)lua_tointeger(L, arg+1), &ar)) {\n      lua_pushnil(L);  /* level out of range */\n      return 1;\n    }\n  }\n  else if (lua_isfunction(L, arg+1)) {\n    lua_pushfstring(L, \">%s\", options);\n    options = lua_tostring(L, -1);\n    lua_pushvalue(L, arg+1);\n    lua_xmove(L, L1, 1);\n  }\n  else\n    return luaL_argerror(L, arg+1, \"function or level expected\");\n  if (!lua_getinfo(L1, options, &ar))\n    return luaL_argerror(L, arg+2, \"invalid option\");\n  lua_createtable(L, 0, 2);\n  if (strchr(options, 'S')) {\n    settabss(L, \"source\", ar.source);\n    settabss(L, \"short_src\", ar.short_src);\n    settabsi(L, \"linedefined\", ar.linedefined);\n    settabsi(L, \"lastlinedefined\", ar.lastlinedefined);\n    settabss(L, \"what\", ar.what);\n  }\n  if (strchr(options, 'l'))\n    settabsi(L, \"currentline\", ar.currentline);\n  if (strchr(options, 'u'))\n    settabsi(L, \"nups\", ar.nups);\n  if (strchr(options, 'n')) {\n    settabss(L, \"name\", ar.name);\n    settabss(L, \"namewhat\", ar.namewhat);\n  }\n  if (strchr(options, 'L'))\n    treatstackoption(L, L1, \"activelines\");\n  if (strchr(options, 'f'))\n    treatstackoption(L, L1, \"func\");\n  return 1;  /* return table */\n}\n    \n\nstatic int db_getlocal (lua_State *L) {\n  int arg;\n  lua_State *L1 = getthread(L, &arg);\n  lua_Debug ar;\n  const char *name;\n  if (!lua_getstack(L1, luaL_checkint(L, arg+1), &ar))  /* out of range? */\n    return luaL_argerror(L, arg+1, \"level out of range\");\n  name = lua_getlocal(L1, &ar, luaL_checkint(L, arg+2));\n  if (name) {\n    lua_xmove(L1, L, 1);\n    lua_pushstring(L, name);\n    lua_pushvalue(L, -2);\n    return 2;\n  }\n  else {\n    lua_pushnil(L);\n    return 1;\n  }\n}\n\n\nstatic int db_setlocal (lua_State *L) {\n  int arg;\n  lua_State *L1 = getthread(L, &arg);\n  lua_Debug ar;\n  if (!lua_getstack(L1, luaL_checkint(L, arg+1), &ar))  /* out of range? */\n    return luaL_argerror(L, arg+1, \"level out of range\");\n  luaL_checkany(L, arg+3);\n  lua_settop(L, arg+3);\n  lua_xmove(L, L1, 1);\n  lua_pushstring(L, lua_setlocal(L1, &ar, luaL_checkint(L, arg+2)));\n  return 1;\n}\n\n\nstatic int auxupvalue (lua_State *L, int get) {\n  const char *name;\n  int n = luaL_checkint(L, 2);\n  luaL_checktype(L, 1, LUA_TFUNCTION);\n  if (lua_iscfunction(L, 1)) return 0;  /* cannot touch C upvalues from Lua */\n  name = get ? lua_getupvalue(L, 1, n) : lua_setupvalue(L, 1, n);\n  if (name == NULL) return 0;\n  lua_pushstring(L, name);\n  lua_insert(L, -(get+1));\n  return get + 1;\n}\n\n\nstatic int db_getupvalue (lua_State *L) {\n  return auxupvalue(L, 1);\n}\n\n\nstatic int db_setupvalue (lua_State *L) {\n  luaL_checkany(L, 3);\n  return auxupvalue(L, 0);\n}\n\n\n\nstatic const char KEY_HOOK = 'h';\n\n\nstatic void hookf (lua_State *L, lua_Debug *ar) {\n  static const char *const hooknames[] =\n    {\"call\", \"return\", \"line\", \"count\", \"tail return\"};\n  lua_pushlightuserdata(L, (void *)&KEY_HOOK);\n  lua_rawget(L, LUA_REGISTRYINDEX);\n  lua_pushlightuserdata(L, L);\n  lua_rawget(L, -2);\n  if (lua_isfunction(L, -1)) {\n    lua_pushstring(L, hooknames[(int)ar->event]);\n    if (ar->currentline >= 0)\n      lua_pushinteger(L, ar->currentline);\n    else lua_pushnil(L);\n    lua_assert(lua_getinfo(L, \"lS\", ar));\n    lua_call(L, 2, 0);\n  }\n}\n\n\nstatic int makemask (const char *smask, int count) {\n  int mask = 0;\n  if (strchr(smask, 'c')) mask |= LUA_MASKCALL;\n  if (strchr(smask, 'r')) mask |= LUA_MASKRET;\n  if (strchr(smask, 'l')) mask |= LUA_MASKLINE;\n  if (count > 0) mask |= LUA_MASKCOUNT;\n  return mask;\n}\n\n\nstatic char *unmakemask (int mask, char *smask) {\n  int i = 0;\n  if (mask & LUA_MASKCALL) smask[i++] = 'c';\n  if (mask & LUA_MASKRET) smask[i++] = 'r';\n  if (mask & LUA_MASKLINE) smask[i++] = 'l';\n  smask[i] = '\\0';\n  return smask;\n}\n\n\nstatic void gethooktable (lua_State *L) {\n  lua_pushlightuserdata(L, (void *)&KEY_HOOK);\n  lua_rawget(L, LUA_REGISTRYINDEX);\n  if (!lua_istable(L, -1)) {\n    lua_pop(L, 1);\n    lua_createtable(L, 0, 1);\n    lua_pushlightuserdata(L, (void *)&KEY_HOOK);\n    lua_pushvalue(L, -2);\n    lua_rawset(L, LUA_REGISTRYINDEX);\n  }\n}\n\n\nstatic int db_sethook (lua_State *L) {\n  int arg, mask, count;\n  lua_Hook func;\n  lua_State *L1 = getthread(L, &arg);\n  if (lua_isnoneornil(L, arg+1)) {\n    lua_settop(L, arg+1);\n    func = NULL; mask = 0; count = 0;  /* turn off hooks */\n  }\n  else {\n    const char *smask = luaL_checkstring(L, arg+2);\n    luaL_checktype(L, arg+1, LUA_TFUNCTION);\n    count = luaL_optint(L, arg+3, 0);\n    func = hookf; mask = makemask(smask, count);\n  }\n  gethooktable(L);\n  lua_pushlightuserdata(L, L1);\n  lua_pushvalue(L, arg+1);\n  lua_rawset(L, -3);  /* set new hook */\n  lua_pop(L, 1);  /* remove hook table */\n  lua_sethook(L1, func, mask, count);  /* set hooks */\n  return 0;\n}\n\n\nstatic int db_gethook (lua_State *L) {\n  int arg;\n  lua_State *L1 = getthread(L, &arg);\n  char buff[5];\n  int mask = lua_gethookmask(L1);\n  lua_Hook hook = lua_gethook(L1);\n  if (hook != NULL && hook != hookf)  /* external hook? */\n    lua_pushliteral(L, \"external hook\");\n  else {\n    gethooktable(L);\n    lua_pushlightuserdata(L, L1);\n    lua_rawget(L, -2);   /* get hook */\n    lua_remove(L, -2);  /* remove hook table */\n  }\n  lua_pushstring(L, unmakemask(mask, buff));\n  lua_pushinteger(L, lua_gethookcount(L1));\n  return 3;\n}\n\n\nstatic int db_debug (lua_State *L) {\n  for (;;) {\n    char buffer[250];\n    fputs(\"lua_debug> \", stderr);\n    if (fgets(buffer, sizeof(buffer), stdin) == 0 ||\n        strcmp(buffer, \"cont\\n\") == 0)\n      return 0;\n    if (luaL_loadbuffer(L, buffer, strlen(buffer), \"=(debug command)\") ||\n        lua_pcall(L, 0, 0, 0)) {\n      fputs(lua_tostring(L, -1), stderr);\n      fputs(\"\\n\", stderr);\n    }\n    lua_settop(L, 0);  /* remove eventual returns */\n  }\n}\n\n\n#define LEVELS1\t12\t/* size of the first part of the stack */\n#define LEVELS2\t10\t/* size of the second part of the stack */\n\nstatic int db_errorfb (lua_State *L) {\n  int level;\n  int firstpart = 1;  /* still before eventual `...' */\n  int arg;\n  lua_State *L1 = getthread(L, &arg);\n  lua_Debug ar;\n  if (lua_isnumber(L, arg+2)) {\n    level = (int)lua_tointeger(L, arg+2);\n    lua_pop(L, 1);\n  }\n  else\n    level = (L == L1) ? 1 : 0;  /* level 0 may be this own function */\n  if (lua_gettop(L) == arg)\n    lua_pushliteral(L, \"\");\n  else if (!lua_isstring(L, arg+1)) return 1;  /* message is not a string */\n  else lua_pushliteral(L, \"\\n\");\n  lua_pushliteral(L, \"stack traceback:\");\n  while (lua_getstack(L1, level++, &ar)) {\n    if (level > LEVELS1 && firstpart) {\n      /* no more than `LEVELS2' more levels? */\n      if (!lua_getstack(L1, level+LEVELS2, &ar))\n        level--;  /* keep going */\n      else {\n        lua_pushliteral(L, \"\\n\\t...\");  /* too many levels */\n        while (lua_getstack(L1, level+LEVELS2, &ar))  /* find last levels */\n          level++;\n      }\n      firstpart = 0;\n      continue;\n    }\n    lua_pushliteral(L, \"\\n\\t\");\n    lua_getinfo(L1, \"Snl\", &ar);\n    lua_pushfstring(L, \"%s:\", ar.short_src);\n    if (ar.currentline > 0)\n      lua_pushfstring(L, \"%d:\", ar.currentline);\n    if (*ar.namewhat != '\\0')  /* is there a name? */\n        lua_pushfstring(L, \" in function \" LUA_QS, ar.name);\n    else {\n      if (*ar.what == 'm')  /* main? */\n        lua_pushfstring(L, \" in main chunk\");\n      else if (*ar.what == 'C' || *ar.what == 't')\n        lua_pushliteral(L, \" ?\");  /* C function or tail call */\n      else\n        lua_pushfstring(L, \" in function <%s:%d>\",\n                           ar.short_src, ar.linedefined);\n    }\n    lua_concat(L, lua_gettop(L) - arg);\n  }\n  lua_concat(L, lua_gettop(L) - arg);\n  return 1;\n}\n\n\nstatic const luaL_Reg dblib[] = {\n  {\"debug\", db_debug},\n  {\"getfenv\", db_getfenv},\n  {\"gethook\", db_gethook},\n  {\"getinfo\", db_getinfo},\n  {\"getlocal\", db_getlocal},\n  {\"getregistry\", db_getregistry},\n  {\"getmetatable\", db_getmetatable},\n  {\"getupvalue\", db_getupvalue},\n  {\"setfenv\", db_setfenv},\n  {\"sethook\", db_sethook},\n  {\"setlocal\", db_setlocal},\n  {\"setmetatable\", db_setmetatable},\n  {\"setupvalue\", db_setupvalue},\n  {\"traceback\", db_errorfb},\n  {NULL, NULL}\n};\n\n\nLUALIB_API int luaopen_debug (lua_State *L) {\n  luaL_register(L, LUA_DBLIBNAME, dblib);\n  return 1;\n}\n\n"
  },
  {
    "path": "deps/lua/src/ldebug.c",
    "content": "/*\n** $Id: ldebug.c,v 2.29.1.6 2008/05/08 16:56:26 roberto Exp $\n** Debug Interface\n** See Copyright Notice in lua.h\n*/\n\n\n#include <stdarg.h>\n#include <stddef.h>\n#include <string.h>\n\n\n#define ldebug_c\n#define LUA_CORE\n\n#include \"lua.h\"\n\n#include \"lapi.h\"\n#include \"lcode.h\"\n#include \"ldebug.h\"\n#include \"ldo.h\"\n#include \"lfunc.h\"\n#include \"lobject.h\"\n#include \"lopcodes.h\"\n#include \"lstate.h\"\n#include \"lstring.h\"\n#include \"ltable.h\"\n#include \"ltm.h\"\n#include \"lvm.h\"\n\n\n\nstatic const char *getfuncname (lua_State *L, CallInfo *ci, const char **name);\n\n\nstatic int currentpc (lua_State *L, CallInfo *ci) {\n  if (!isLua(ci)) return -1;  /* function is not a Lua function? */\n  if (ci == L->ci)\n    ci->savedpc = L->savedpc;\n  return pcRel(ci->savedpc, ci_func(ci)->l.p);\n}\n\n\nstatic int currentline (lua_State *L, CallInfo *ci) {\n  int pc = currentpc(L, ci);\n  if (pc < 0)\n    return -1;  /* only active lua functions have current-line information */\n  else\n    return getline(ci_func(ci)->l.p, pc);\n}\n\n\n/*\n** this function can be called asynchronous (e.g. during a signal)\n*/\nLUA_API int lua_sethook (lua_State *L, lua_Hook func, int mask, int count) {\n  if (func == NULL || mask == 0) {  /* turn off hooks? */\n    mask = 0;\n    func = NULL;\n  }\n  L->hook = func;\n  L->basehookcount = count;\n  resethookcount(L);\n  L->hookmask = cast_byte(mask);\n  return 1;\n}\n\n\nLUA_API lua_Hook lua_gethook (lua_State *L) {\n  return L->hook;\n}\n\n\nLUA_API int lua_gethookmask (lua_State *L) {\n  return L->hookmask;\n}\n\n\nLUA_API int lua_gethookcount (lua_State *L) {\n  return L->basehookcount;\n}\n\n\nLUA_API int lua_getstack (lua_State *L, int level, lua_Debug *ar) {\n  int status;\n  CallInfo *ci;\n  lua_lock(L);\n  for (ci = L->ci; level > 0 && ci > L->base_ci; ci--) {\n    level--;\n    if (f_isLua(ci))  /* Lua function? */\n      level -= ci->tailcalls;  /* skip lost tail calls */\n  }\n  if (level == 0 && ci > L->base_ci) {  /* level found? */\n    status = 1;\n    ar->i_ci = cast_int(ci - L->base_ci);\n  }\n  else if (level < 0) {  /* level is of a lost tail call? */\n    status = 1;\n    ar->i_ci = 0;\n  }\n  else status = 0;  /* no such level */\n  lua_unlock(L);\n  return status;\n}\n\n\nstatic Proto *getluaproto (CallInfo *ci) {\n  return (isLua(ci) ? ci_func(ci)->l.p : NULL);\n}\n\n\nstatic const char *findlocal (lua_State *L, CallInfo *ci, int n) {\n  const char *name;\n  Proto *fp = getluaproto(ci);\n  if (fp && (name = luaF_getlocalname(fp, n, currentpc(L, ci))) != NULL)\n    return name;  /* is a local variable in a Lua function */\n  else {\n    StkId limit = (ci == L->ci) ? L->top : (ci+1)->func;\n    if (limit - ci->base >= n && n > 0)  /* is 'n' inside 'ci' stack? */\n      return \"(*temporary)\";\n    else\n      return NULL;\n  }\n}\n\n\nLUA_API const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n) {\n  CallInfo *ci = L->base_ci + ar->i_ci;\n  const char *name = findlocal(L, ci, n);\n  lua_lock(L);\n  if (name)\n      luaA_pushobject(L, ci->base + (n - 1));\n  lua_unlock(L);\n  return name;\n}\n\n\nLUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n) {\n  CallInfo *ci = L->base_ci + ar->i_ci;\n  const char *name = findlocal(L, ci, n);\n  lua_lock(L);\n  if (name)\n      setobjs2s(L, ci->base + (n - 1), L->top - 1);\n  L->top--;  /* pop value */\n  lua_unlock(L);\n  return name;\n}\n\n\nstatic void funcinfo (lua_Debug *ar, Closure *cl) {\n  if (cl->c.isC) {\n    ar->source = \"=[C]\";\n    ar->linedefined = -1;\n    ar->lastlinedefined = -1;\n    ar->what = \"C\";\n  }\n  else {\n    ar->source = getstr(cl->l.p->source);\n    ar->linedefined = cl->l.p->linedefined;\n    ar->lastlinedefined = cl->l.p->lastlinedefined;\n    ar->what = (ar->linedefined == 0) ? \"main\" : \"Lua\";\n  }\n  luaO_chunkid(ar->short_src, ar->source, LUA_IDSIZE);\n}\n\n\nstatic void info_tailcall (lua_Debug *ar) {\n  ar->name = ar->namewhat = \"\";\n  ar->what = \"tail\";\n  ar->lastlinedefined = ar->linedefined = ar->currentline = -1;\n  ar->source = \"=(tail call)\";\n  luaO_chunkid(ar->short_src, ar->source, LUA_IDSIZE);\n  ar->nups = 0;\n}\n\n\nstatic void collectvalidlines (lua_State *L, Closure *f) {\n  if (f == NULL || f->c.isC) {\n    setnilvalue(L->top);\n  }\n  else {\n    Table *t = luaH_new(L, 0, 0);\n    int *lineinfo = f->l.p->lineinfo;\n    int i;\n    for (i=0; i<f->l.p->sizelineinfo; i++)\n      setbvalue(luaH_setnum(L, t, lineinfo[i]), 1);\n    sethvalue(L, L->top, t); \n  }\n  incr_top(L);\n}\n\n\nstatic int auxgetinfo (lua_State *L, const char *what, lua_Debug *ar,\n                    Closure *f, CallInfo *ci) {\n  int status = 1;\n  if (f == NULL) {\n    info_tailcall(ar);\n    return status;\n  }\n  for (; *what; what++) {\n    switch (*what) {\n      case 'S': {\n        funcinfo(ar, f);\n        break;\n      }\n      case 'l': {\n        ar->currentline = (ci) ? currentline(L, ci) : -1;\n        break;\n      }\n      case 'u': {\n        ar->nups = f->c.nupvalues;\n        break;\n      }\n      case 'n': {\n        ar->namewhat = (ci) ? getfuncname(L, ci, &ar->name) : NULL;\n        if (ar->namewhat == NULL) {\n          ar->namewhat = \"\";  /* not found */\n          ar->name = NULL;\n        }\n        break;\n      }\n      case 'L':\n      case 'f':  /* handled by lua_getinfo */\n        break;\n      default: status = 0;  /* invalid option */\n    }\n  }\n  return status;\n}\n\n\nLUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar) {\n  int status;\n  Closure *f = NULL;\n  CallInfo *ci = NULL;\n  lua_lock(L);\n  if (*what == '>') {\n    StkId func = L->top - 1;\n    luai_apicheck(L, ttisfunction(func));\n    what++;  /* skip the '>' */\n    f = clvalue(func);\n    L->top--;  /* pop function */\n  }\n  else if (ar->i_ci != 0) {  /* no tail call? */\n    ci = L->base_ci + ar->i_ci;\n    lua_assert(ttisfunction(ci->func));\n    f = clvalue(ci->func);\n  }\n  status = auxgetinfo(L, what, ar, f, ci);\n  if (strchr(what, 'f')) {\n    if (f == NULL) setnilvalue(L->top);\n    else setclvalue(L, L->top, f);\n    incr_top(L);\n  }\n  if (strchr(what, 'L'))\n    collectvalidlines(L, f);\n  lua_unlock(L);\n  return status;\n}\n\n\n/*\n** {======================================================\n** Symbolic Execution and code checker\n** =======================================================\n*/\n\n#define check(x)\t\tif (!(x)) return 0;\n\n#define checkjump(pt,pc)\tcheck(0 <= pc && pc < pt->sizecode)\n\n#define checkreg(pt,reg)\tcheck((reg) < (pt)->maxstacksize)\n\n\n\nstatic int precheck (const Proto *pt) {\n  check(pt->maxstacksize <= MAXSTACK);\n  check(pt->numparams+(pt->is_vararg & VARARG_HASARG) <= pt->maxstacksize);\n  check(!(pt->is_vararg & VARARG_NEEDSARG) ||\n              (pt->is_vararg & VARARG_HASARG));\n  check(pt->sizeupvalues <= pt->nups);\n  check(pt->sizelineinfo == pt->sizecode || pt->sizelineinfo == 0);\n  check(pt->sizecode > 0 && GET_OPCODE(pt->code[pt->sizecode-1]) == OP_RETURN);\n  return 1;\n}\n\n\n#define checkopenop(pt,pc)\tluaG_checkopenop((pt)->code[(pc)+1])\n\nint luaG_checkopenop (Instruction i) {\n  switch (GET_OPCODE(i)) {\n    case OP_CALL:\n    case OP_TAILCALL:\n    case OP_RETURN:\n    case OP_SETLIST: {\n      check(GETARG_B(i) == 0);\n      return 1;\n    }\n    default: return 0;  /* invalid instruction after an open call */\n  }\n}\n\n\nstatic int checkArgMode (const Proto *pt, int r, enum OpArgMask mode) {\n  switch (mode) {\n    case OpArgN: check(r == 0); break;\n    case OpArgU: break;\n    case OpArgR: checkreg(pt, r); break;\n    case OpArgK:\n      check(ISK(r) ? INDEXK(r) < pt->sizek : r < pt->maxstacksize);\n      break;\n  }\n  return 1;\n}\n\n\nstatic Instruction symbexec (const Proto *pt, int lastpc, int reg) {\n  int pc;\n  int last;  /* stores position of last instruction that changed `reg' */\n  last = pt->sizecode-1;  /* points to final return (a `neutral' instruction) */\n  check(precheck(pt));\n  for (pc = 0; pc < lastpc; pc++) {\n    Instruction i = pt->code[pc];\n    OpCode op = GET_OPCODE(i);\n    int a = GETARG_A(i);\n    int b = 0;\n    int c = 0;\n    check(op < NUM_OPCODES);\n    checkreg(pt, a);\n    switch (getOpMode(op)) {\n      case iABC: {\n        b = GETARG_B(i);\n        c = GETARG_C(i);\n        check(checkArgMode(pt, b, getBMode(op)));\n        check(checkArgMode(pt, c, getCMode(op)));\n        break;\n      }\n      case iABx: {\n        b = GETARG_Bx(i);\n        if (getBMode(op) == OpArgK) check(b < pt->sizek);\n        break;\n      }\n      case iAsBx: {\n        b = GETARG_sBx(i);\n        if (getBMode(op) == OpArgR) {\n          int dest = pc+1+b;\n          check(0 <= dest && dest < pt->sizecode);\n          if (dest > 0) {\n            int j;\n            /* check that it does not jump to a setlist count; this\n               is tricky, because the count from a previous setlist may\n               have the same value of an invalid setlist; so, we must\n               go all the way back to the first of them (if any) */\n            for (j = 0; j < dest; j++) {\n              Instruction d = pt->code[dest-1-j];\n              if (!(GET_OPCODE(d) == OP_SETLIST && GETARG_C(d) == 0)) break;\n            }\n            /* if 'j' is even, previous value is not a setlist (even if\n               it looks like one) */\n            check((j&1) == 0);\n          }\n        }\n        break;\n      }\n    }\n    if (testAMode(op)) {\n      if (a == reg) last = pc;  /* change register `a' */\n    }\n    if (testTMode(op)) {\n      check(pc+2 < pt->sizecode);  /* check skip */\n      check(GET_OPCODE(pt->code[pc+1]) == OP_JMP);\n    }\n    switch (op) {\n      case OP_LOADBOOL: {\n        if (c == 1) {  /* does it jump? */\n          check(pc+2 < pt->sizecode);  /* check its jump */\n          check(GET_OPCODE(pt->code[pc+1]) != OP_SETLIST ||\n                GETARG_C(pt->code[pc+1]) != 0);\n        }\n        break;\n      }\n      case OP_LOADNIL: {\n        if (a <= reg && reg <= b)\n          last = pc;  /* set registers from `a' to `b' */\n        break;\n      }\n      case OP_GETUPVAL:\n      case OP_SETUPVAL: {\n        check(b < pt->nups);\n        break;\n      }\n      case OP_GETGLOBAL:\n      case OP_SETGLOBAL: {\n        check(ttisstring(&pt->k[b]));\n        break;\n      }\n      case OP_SELF: {\n        checkreg(pt, a+1);\n        if (reg == a+1) last = pc;\n        break;\n      }\n      case OP_CONCAT: {\n        check(b < c);  /* at least two operands */\n        break;\n      }\n      case OP_TFORLOOP: {\n        check(c >= 1);  /* at least one result (control variable) */\n        checkreg(pt, a+2+c);  /* space for results */\n        if (reg >= a+2) last = pc;  /* affect all regs above its base */\n        break;\n      }\n      case OP_FORLOOP:\n      case OP_FORPREP:\n        checkreg(pt, a+3);\n        /* go through */\n      case OP_JMP: {\n        int dest = pc+1+b;\n        /* not full check and jump is forward and do not skip `lastpc'? */\n        if (reg != NO_REG && pc < dest && dest <= lastpc)\n          pc += b;  /* do the jump */\n        break;\n      }\n      case OP_CALL:\n      case OP_TAILCALL: {\n        if (b != 0) {\n          checkreg(pt, a+b-1);\n        }\n        c--;  /* c = num. returns */\n        if (c == LUA_MULTRET) {\n          check(checkopenop(pt, pc));\n        }\n        else if (c != 0)\n          checkreg(pt, a+c-1);\n        if (reg >= a) last = pc;  /* affect all registers above base */\n        break;\n      }\n      case OP_RETURN: {\n        b--;  /* b = num. returns */\n        if (b > 0) checkreg(pt, a+b-1);\n        break;\n      }\n      case OP_SETLIST: {\n        if (b > 0) checkreg(pt, a + b);\n        if (c == 0) {\n          pc++;\n          check(pc < pt->sizecode - 1);\n        }\n        break;\n      }\n      case OP_CLOSURE: {\n        int nup, j;\n        check(b < pt->sizep);\n        nup = pt->p[b]->nups;\n        check(pc + nup < pt->sizecode);\n        for (j = 1; j <= nup; j++) {\n          OpCode op1 = GET_OPCODE(pt->code[pc + j]);\n          check(op1 == OP_GETUPVAL || op1 == OP_MOVE);\n        }\n        if (reg != NO_REG)  /* tracing? */\n          pc += nup;  /* do not 'execute' these pseudo-instructions */\n        break;\n      }\n      case OP_VARARG: {\n        check((pt->is_vararg & VARARG_ISVARARG) &&\n             !(pt->is_vararg & VARARG_NEEDSARG));\n        b--;\n        if (b == LUA_MULTRET) check(checkopenop(pt, pc));\n        checkreg(pt, a+b-1);\n        break;\n      }\n      default: break;\n    }\n  }\n  return pt->code[last];\n}\n\n#undef check\n#undef checkjump\n#undef checkreg\n\n/* }====================================================== */\n\n\nint luaG_checkcode (const Proto *pt) {\n  return (symbexec(pt, pt->sizecode, NO_REG) != 0);\n}\n\n\nstatic const char *kname (Proto *p, int c) {\n  if (ISK(c) && ttisstring(&p->k[INDEXK(c)]))\n    return svalue(&p->k[INDEXK(c)]);\n  else\n    return \"?\";\n}\n\n\nstatic const char *getobjname (lua_State *L, CallInfo *ci, int stackpos,\n                               const char **name) {\n  if (isLua(ci)) {  /* a Lua function? */\n    Proto *p = ci_func(ci)->l.p;\n    int pc = currentpc(L, ci);\n    Instruction i;\n    *name = luaF_getlocalname(p, stackpos+1, pc);\n    if (*name)  /* is a local? */\n      return \"local\";\n    i = symbexec(p, pc, stackpos);  /* try symbolic execution */\n    lua_assert(pc != -1);\n    switch (GET_OPCODE(i)) {\n      case OP_GETGLOBAL: {\n        int g = GETARG_Bx(i);  /* global index */\n        lua_assert(ttisstring(&p->k[g]));\n        *name = svalue(&p->k[g]);\n        return \"global\";\n      }\n      case OP_MOVE: {\n        int a = GETARG_A(i);\n        int b = GETARG_B(i);  /* move from `b' to `a' */\n        if (b < a)\n          return getobjname(L, ci, b, name);  /* get name for `b' */\n        break;\n      }\n      case OP_GETTABLE: {\n        int k = GETARG_C(i);  /* key index */\n        *name = kname(p, k);\n        return \"field\";\n      }\n      case OP_GETUPVAL: {\n        int u = GETARG_B(i);  /* upvalue index */\n        *name = p->upvalues ? getstr(p->upvalues[u]) : \"?\";\n        return \"upvalue\";\n      }\n      case OP_SELF: {\n        int k = GETARG_C(i);  /* key index */\n        *name = kname(p, k);\n        return \"method\";\n      }\n      default: break;\n    }\n  }\n  return NULL;  /* no useful name found */\n}\n\n\nstatic const char *getfuncname (lua_State *L, CallInfo *ci, const char **name) {\n  Instruction i;\n  if ((isLua(ci) && ci->tailcalls > 0) || !isLua(ci - 1))\n    return NULL;  /* calling function is not Lua (or is unknown) */\n  ci--;  /* calling function */\n  i = ci_func(ci)->l.p->code[currentpc(L, ci)];\n  if (GET_OPCODE(i) == OP_CALL || GET_OPCODE(i) == OP_TAILCALL ||\n      GET_OPCODE(i) == OP_TFORLOOP)\n    return getobjname(L, ci, GETARG_A(i), name);\n  else\n    return NULL;  /* no useful name can be found */\n}\n\n\n/* only ANSI way to check whether a pointer points to an array */\nstatic int isinstack (CallInfo *ci, const TValue *o) {\n  StkId p;\n  for (p = ci->base; p < ci->top; p++)\n    if (o == p) return 1;\n  return 0;\n}\n\n\nvoid luaG_typeerror (lua_State *L, const TValue *o, const char *op) {\n  const char *name = NULL;\n  const char *t = luaT_typenames[ttype(o)];\n  const char *kind = (isinstack(L->ci, o)) ?\n                         getobjname(L, L->ci, cast_int(o - L->base), &name) :\n                         NULL;\n  if (kind)\n    luaG_runerror(L, \"attempt to %s %s \" LUA_QS \" (a %s value)\",\n                op, kind, name, t);\n  else\n    luaG_runerror(L, \"attempt to %s a %s value\", op, t);\n}\n\n\nvoid luaG_concaterror (lua_State *L, StkId p1, StkId p2) {\n  if (ttisstring(p1) || ttisnumber(p1)) p1 = p2;\n  lua_assert(!ttisstring(p1) && !ttisnumber(p1));\n  luaG_typeerror(L, p1, \"concatenate\");\n}\n\n\nvoid luaG_aritherror (lua_State *L, const TValue *p1, const TValue *p2) {\n  TValue temp;\n  if (luaV_tonumber(p1, &temp) == NULL)\n    p2 = p1;  /* first operand is wrong */\n  luaG_typeerror(L, p2, \"perform arithmetic on\");\n}\n\n\nint luaG_ordererror (lua_State *L, const TValue *p1, const TValue *p2) {\n  const char *t1 = luaT_typenames[ttype(p1)];\n  const char *t2 = luaT_typenames[ttype(p2)];\n  if (t1[2] == t2[2])\n    luaG_runerror(L, \"attempt to compare two %s values\", t1);\n  else\n    luaG_runerror(L, \"attempt to compare %s with %s\", t1, t2);\n  return 0;\n}\n\n\nstatic void addinfo (lua_State *L, const char *msg) {\n  CallInfo *ci = L->ci;\n  if (isLua(ci)) {  /* is Lua code? */\n    char buff[LUA_IDSIZE];  /* add file:line information */\n    int line = currentline(L, ci);\n    luaO_chunkid(buff, getstr(getluaproto(ci)->source), LUA_IDSIZE);\n    luaO_pushfstring(L, \"%s:%d: %s\", buff, line, msg);\n  }\n}\n\n\nvoid luaG_errormsg (lua_State *L) {\n  if (L->errfunc != 0) {  /* is there an error handling function? */\n    StkId errfunc = restorestack(L, L->errfunc);\n    if (!ttisfunction(errfunc)) luaD_throw(L, LUA_ERRERR);\n    setobjs2s(L, L->top, L->top - 1);  /* move argument */\n    setobjs2s(L, L->top - 1, errfunc);  /* push function */\n    incr_top(L);\n    luaD_call(L, L->top - 2, 1);  /* call it */\n  }\n  luaD_throw(L, LUA_ERRRUN);\n}\n\n\nvoid luaG_runerror (lua_State *L, const char *fmt, ...) {\n  va_list argp;\n  va_start(argp, fmt);\n  addinfo(L, luaO_pushvfstring(L, fmt, argp));\n  va_end(argp);\n  luaG_errormsg(L);\n}\n\n"
  },
  {
    "path": "deps/lua/src/ldebug.h",
    "content": "/*\n** $Id: ldebug.h,v 2.3.1.1 2007/12/27 13:02:25 roberto Exp $\n** Auxiliary functions from Debug Interface module\n** See Copyright Notice in lua.h\n*/\n\n#ifndef ldebug_h\n#define ldebug_h\n\n\n#include \"lstate.h\"\n\n\n#define pcRel(pc, p)\t(cast(int, (pc) - (p)->code) - 1)\n\n#define getline(f,pc)\t(((f)->lineinfo) ? (f)->lineinfo[pc] : 0)\n\n#define resethookcount(L)\t(L->hookcount = L->basehookcount)\n\n\nLUAI_FUNC void luaG_typeerror (lua_State *L, const TValue *o,\n                                             const char *opname);\nLUAI_FUNC void luaG_concaterror (lua_State *L, StkId p1, StkId p2);\nLUAI_FUNC void luaG_aritherror (lua_State *L, const TValue *p1,\n                                              const TValue *p2);\nLUAI_FUNC int luaG_ordererror (lua_State *L, const TValue *p1,\n                                             const TValue *p2);\nLUAI_FUNC void luaG_runerror (lua_State *L, const char *fmt, ...);\nLUAI_FUNC void luaG_errormsg (lua_State *L);\nLUAI_FUNC int luaG_checkcode (const Proto *pt);\nLUAI_FUNC int luaG_checkopenop (Instruction i);\n\n#endif\n"
  },
  {
    "path": "deps/lua/src/ldo.c",
    "content": "/*\n** $Id: ldo.c,v 2.38.1.4 2012/01/18 02:27:10 roberto Exp $\n** Stack and Call structure of Lua\n** See Copyright Notice in lua.h\n*/\n\n\n#include <setjmp.h>\n#include <stdlib.h>\n#include <string.h>\n\n#define ldo_c\n#define LUA_CORE\n\n#include \"lua.h\"\n\n#include \"ldebug.h\"\n#include \"ldo.h\"\n#include \"lfunc.h\"\n#include \"lgc.h\"\n#include \"lmem.h\"\n#include \"lobject.h\"\n#include \"lopcodes.h\"\n#include \"lparser.h\"\n#include \"lstate.h\"\n#include \"lstring.h\"\n#include \"ltable.h\"\n#include \"ltm.h\"\n#include \"lundump.h\"\n#include \"lvm.h\"\n#include \"lzio.h\"\n\n\n\n\n/*\n** {======================================================\n** Error-recovery functions\n** =======================================================\n*/\n\n\n/* chain list of long jump buffers */\nstruct lua_longjmp {\n  struct lua_longjmp *previous;\n  luai_jmpbuf b;\n  volatile int status;  /* error code */\n};\n\n\nvoid luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop) {\n  switch (errcode) {\n    case LUA_ERRMEM: {\n      setsvalue2s(L, oldtop, luaS_newliteral(L, MEMERRMSG));\n      break;\n    }\n    case LUA_ERRERR: {\n      setsvalue2s(L, oldtop, luaS_newliteral(L, \"error in error handling\"));\n      break;\n    }\n    case LUA_ERRSYNTAX:\n    case LUA_ERRRUN: {\n      setobjs2s(L, oldtop, L->top - 1);  /* error message on current top */\n      break;\n    }\n  }\n  L->top = oldtop + 1;\n}\n\n\nstatic void restore_stack_limit (lua_State *L) {\n  lua_assert(L->stack_last - L->stack == L->stacksize - EXTRA_STACK - 1);\n  if (L->size_ci > LUAI_MAXCALLS) {  /* there was an overflow? */\n    int inuse = cast_int(L->ci - L->base_ci);\n    if (inuse + 1 < LUAI_MAXCALLS)  /* can `undo' overflow? */\n      luaD_reallocCI(L, LUAI_MAXCALLS);\n  }\n}\n\n\nstatic void resetstack (lua_State *L, int status) {\n  L->ci = L->base_ci;\n  L->base = L->ci->base;\n  luaF_close(L, L->base);  /* close eventual pending closures */\n  luaD_seterrorobj(L, status, L->base);\n  L->nCcalls = L->baseCcalls;\n  L->allowhook = 1;\n  restore_stack_limit(L);\n  L->errfunc = 0;\n  L->errorJmp = NULL;\n}\n\n\nvoid luaD_throw (lua_State *L, int errcode) {\n  if (L->errorJmp) {\n    L->errorJmp->status = errcode;\n    LUAI_THROW(L, L->errorJmp);\n  }\n  else {\n    L->status = cast_byte(errcode);\n    if (G(L)->panic) {\n      resetstack(L, errcode);\n      lua_unlock(L);\n      G(L)->panic(L);\n    }\n    exit(EXIT_FAILURE);\n  }\n}\n\n\nint luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) {\n  struct lua_longjmp lj;\n  lj.status = 0;\n  lj.previous = L->errorJmp;  /* chain new error handler */\n  L->errorJmp = &lj;\n  LUAI_TRY(L, &lj,\n    (*f)(L, ud);\n  );\n  L->errorJmp = lj.previous;  /* restore old error handler */\n  return lj.status;\n}\n\n/* }====================================================== */\n\n\nstatic void correctstack (lua_State *L, TValue *oldstack) {\n  CallInfo *ci;\n  GCObject *up;\n  L->top = (L->top - oldstack) + L->stack;\n  for (up = L->openupval; up != NULL; up = up->gch.next)\n    gco2uv(up)->v = (gco2uv(up)->v - oldstack) + L->stack;\n  for (ci = L->base_ci; ci <= L->ci; ci++) {\n    ci->top = (ci->top - oldstack) + L->stack;\n    ci->base = (ci->base - oldstack) + L->stack;\n    ci->func = (ci->func - oldstack) + L->stack;\n  }\n  L->base = (L->base - oldstack) + L->stack;\n}\n\n\nvoid luaD_reallocstack (lua_State *L, int newsize) {\n  TValue *oldstack = L->stack;\n  int realsize = newsize + 1 + EXTRA_STACK;\n  lua_assert(L->stack_last - L->stack == L->stacksize - EXTRA_STACK - 1);\n  luaM_reallocvector(L, L->stack, L->stacksize, realsize, TValue);\n  L->stacksize = realsize;\n  L->stack_last = L->stack+newsize;\n  correctstack(L, oldstack);\n}\n\n\nvoid luaD_reallocCI (lua_State *L, int newsize) {\n  CallInfo *oldci = L->base_ci;\n  luaM_reallocvector(L, L->base_ci, L->size_ci, newsize, CallInfo);\n  L->size_ci = newsize;\n  L->ci = (L->ci - oldci) + L->base_ci;\n  L->end_ci = L->base_ci + L->size_ci - 1;\n}\n\n\nvoid luaD_growstack (lua_State *L, int n) {\n  if (n <= L->stacksize)  /* double size is enough? */\n    luaD_reallocstack(L, 2*L->stacksize);\n  else\n    luaD_reallocstack(L, L->stacksize + n);\n}\n\n\nstatic CallInfo *growCI (lua_State *L) {\n  if (L->size_ci > LUAI_MAXCALLS)  /* overflow while handling overflow? */\n    luaD_throw(L, LUA_ERRERR);\n  else {\n    luaD_reallocCI(L, 2*L->size_ci);\n    if (L->size_ci > LUAI_MAXCALLS)\n      luaG_runerror(L, \"stack overflow\");\n  }\n  return ++L->ci;\n}\n\n\nvoid luaD_callhook (lua_State *L, int event, int line) {\n  lua_Hook hook = L->hook;\n  if (hook && L->allowhook) {\n    ptrdiff_t top = savestack(L, L->top);\n    ptrdiff_t ci_top = savestack(L, L->ci->top);\n    lua_Debug ar;\n    ar.event = event;\n    ar.currentline = line;\n    if (event == LUA_HOOKTAILRET)\n      ar.i_ci = 0;  /* tail call; no debug information about it */\n    else\n      ar.i_ci = cast_int(L->ci - L->base_ci);\n    luaD_checkstack(L, LUA_MINSTACK);  /* ensure minimum stack size */\n    L->ci->top = L->top + LUA_MINSTACK;\n    lua_assert(L->ci->top <= L->stack_last);\n    L->allowhook = 0;  /* cannot call hooks inside a hook */\n    lua_unlock(L);\n    (*hook)(L, &ar);\n    lua_lock(L);\n    lua_assert(!L->allowhook);\n    L->allowhook = 1;\n    L->ci->top = restorestack(L, ci_top);\n    L->top = restorestack(L, top);\n  }\n}\n\n\nstatic StkId adjust_varargs (lua_State *L, Proto *p, int actual) {\n  int i;\n  int nfixargs = p->numparams;\n  Table *htab = NULL;\n  StkId base, fixed;\n  for (; actual < nfixargs; ++actual)\n    setnilvalue(L->top++);\n#if defined(LUA_COMPAT_VARARG)\n  if (p->is_vararg & VARARG_NEEDSARG) { /* compat. with old-style vararg? */\n    int nvar = actual - nfixargs;  /* number of extra arguments */\n    lua_assert(p->is_vararg & VARARG_HASARG);\n    luaC_checkGC(L);\n    luaD_checkstack(L, p->maxstacksize);\n    htab = luaH_new(L, nvar, 1);  /* create `arg' table */\n    for (i=0; i<nvar; i++)  /* put extra arguments into `arg' table */\n      setobj2n(L, luaH_setnum(L, htab, i+1), L->top - nvar + i);\n    /* store counter in field `n' */\n    setnvalue(luaH_setstr(L, htab, luaS_newliteral(L, \"n\")), cast_num(nvar));\n  }\n#endif\n  /* move fixed parameters to final position */\n  fixed = L->top - actual;  /* first fixed argument */\n  base = L->top;  /* final position of first argument */\n  for (i=0; i<nfixargs; i++) {\n    setobjs2s(L, L->top++, fixed+i);\n    setnilvalue(fixed+i);\n  }\n  /* add `arg' parameter */\n  if (htab) {\n    sethvalue(L, L->top++, htab);\n    lua_assert(iswhite(obj2gco(htab)));\n  }\n  return base;\n}\n\n\nstatic StkId tryfuncTM (lua_State *L, StkId func) {\n  const TValue *tm = luaT_gettmbyobj(L, func, TM_CALL);\n  StkId p;\n  ptrdiff_t funcr = savestack(L, func);\n  if (!ttisfunction(tm))\n    luaG_typeerror(L, func, \"call\");\n  /* Open a hole inside the stack at `func' */\n  for (p = L->top; p > func; p--) setobjs2s(L, p, p-1);\n  incr_top(L);\n  func = restorestack(L, funcr);  /* previous call may change stack */\n  setobj2s(L, func, tm);  /* tag method is the new function to be called */\n  return func;\n}\n\n\n\n#define inc_ci(L) \\\n  ((L->ci == L->end_ci) ? growCI(L) : \\\n   (condhardstacktests(luaD_reallocCI(L, L->size_ci)), ++L->ci))\n\n\nint luaD_precall (lua_State *L, StkId func, int nresults) {\n  LClosure *cl;\n  ptrdiff_t funcr;\n  if (!ttisfunction(func)) /* `func' is not a function? */\n    func = tryfuncTM(L, func);  /* check the `function' tag method */\n  funcr = savestack(L, func);\n  cl = &clvalue(func)->l;\n  L->ci->savedpc = L->savedpc;\n  if (!cl->isC) {  /* Lua function? prepare its call */\n    CallInfo *ci;\n    StkId st, base;\n    Proto *p = cl->p;\n    luaD_checkstack(L, p->maxstacksize);\n    func = restorestack(L, funcr);\n    if (!p->is_vararg) {  /* no varargs? */\n      base = func + 1;\n      if (L->top > base + p->numparams)\n        L->top = base + p->numparams;\n    }\n    else {  /* vararg function */\n      int nargs = cast_int(L->top - func) - 1;\n      base = adjust_varargs(L, p, nargs);\n      func = restorestack(L, funcr);  /* previous call may change the stack */\n    }\n    ci = inc_ci(L);  /* now `enter' new function */\n    ci->func = func;\n    L->base = ci->base = base;\n    ci->top = L->base + p->maxstacksize;\n    lua_assert(ci->top <= L->stack_last);\n    L->savedpc = p->code;  /* starting point */\n    ci->tailcalls = 0;\n    ci->nresults = nresults;\n    for (st = L->top; st < ci->top; st++)\n      setnilvalue(st);\n    L->top = ci->top;\n    if (L->hookmask & LUA_MASKCALL) {\n      L->savedpc++;  /* hooks assume 'pc' is already incremented */\n      luaD_callhook(L, LUA_HOOKCALL, -1);\n      L->savedpc--;  /* correct 'pc' */\n    }\n    return PCRLUA;\n  }\n  else {  /* if is a C function, call it */\n    CallInfo *ci;\n    int n;\n    luaD_checkstack(L, LUA_MINSTACK);  /* ensure minimum stack size */\n    ci = inc_ci(L);  /* now `enter' new function */\n    ci->func = restorestack(L, funcr);\n    L->base = ci->base = ci->func + 1;\n    ci->top = L->top + LUA_MINSTACK;\n    lua_assert(ci->top <= L->stack_last);\n    ci->nresults = nresults;\n    if (L->hookmask & LUA_MASKCALL)\n      luaD_callhook(L, LUA_HOOKCALL, -1);\n    lua_unlock(L);\n    n = (*curr_func(L)->c.f)(L);  /* do the actual call */\n    lua_lock(L);\n    if (n < 0)  /* yielding? */\n      return PCRYIELD;\n    else {\n      luaD_poscall(L, L->top - n);\n      return PCRC;\n    }\n  }\n}\n\n\nstatic StkId callrethooks (lua_State *L, StkId firstResult) {\n  ptrdiff_t fr = savestack(L, firstResult);  /* next call may change stack */\n  luaD_callhook(L, LUA_HOOKRET, -1);\n  if (f_isLua(L->ci)) {  /* Lua function? */\n    while ((L->hookmask & LUA_MASKRET) && L->ci->tailcalls--) /* tail calls */\n      luaD_callhook(L, LUA_HOOKTAILRET, -1);\n  }\n  return restorestack(L, fr);\n}\n\n\nint luaD_poscall (lua_State *L, StkId firstResult) {\n  StkId res;\n  int wanted, i;\n  CallInfo *ci;\n  if (L->hookmask & LUA_MASKRET)\n    firstResult = callrethooks(L, firstResult);\n  ci = L->ci--;\n  res = ci->func;  /* res == final position of 1st result */\n  wanted = ci->nresults;\n  L->base = (ci - 1)->base;  /* restore base */\n  L->savedpc = (ci - 1)->savedpc;  /* restore savedpc */\n  /* move results to correct place */\n  for (i = wanted; i != 0 && firstResult < L->top; i--)\n    setobjs2s(L, res++, firstResult++);\n  while (i-- > 0)\n    setnilvalue(res++);\n  L->top = res;\n  return (wanted - LUA_MULTRET);  /* 0 iff wanted == LUA_MULTRET */\n}\n\n\n/*\n** Call a function (C or Lua). The function to be called is at *func.\n** The arguments are on the stack, right after the function.\n** When returns, all the results are on the stack, starting at the original\n** function position.\n*/ \nvoid luaD_call (lua_State *L, StkId func, int nResults) {\n  if (++L->nCcalls >= LUAI_MAXCCALLS) {\n    if (L->nCcalls == LUAI_MAXCCALLS)\n      luaG_runerror(L, \"C stack overflow\");\n    else if (L->nCcalls >= (LUAI_MAXCCALLS + (LUAI_MAXCCALLS>>3)))\n      luaD_throw(L, LUA_ERRERR);  /* error while handing stack error */\n  }\n  if (luaD_precall(L, func, nResults) == PCRLUA)  /* is a Lua function? */\n    luaV_execute(L, 1);  /* call it */\n  L->nCcalls--;\n  luaC_checkGC(L);\n}\n\n\nstatic void resume (lua_State *L, void *ud) {\n  StkId firstArg = cast(StkId, ud);\n  CallInfo *ci = L->ci;\n  if (L->status == 0) {  /* start coroutine? */\n    lua_assert(ci == L->base_ci && firstArg > L->base);\n    if (luaD_precall(L, firstArg - 1, LUA_MULTRET) != PCRLUA)\n      return;\n  }\n  else {  /* resuming from previous yield */\n    lua_assert(L->status == LUA_YIELD);\n    L->status = 0;\n    if (!f_isLua(ci)) {  /* `common' yield? */\n      /* finish interrupted execution of `OP_CALL' */\n      lua_assert(GET_OPCODE(*((ci-1)->savedpc - 1)) == OP_CALL ||\n                 GET_OPCODE(*((ci-1)->savedpc - 1)) == OP_TAILCALL);\n      if (luaD_poscall(L, firstArg))  /* complete it... */\n        L->top = L->ci->top;  /* and correct top if not multiple results */\n    }\n    else  /* yielded inside a hook: just continue its execution */\n      L->base = L->ci->base;\n  }\n  luaV_execute(L, cast_int(L->ci - L->base_ci));\n}\n\n\nstatic int resume_error (lua_State *L, const char *msg) {\n  L->top = L->ci->base;\n  setsvalue2s(L, L->top, luaS_new(L, msg));\n  incr_top(L);\n  lua_unlock(L);\n  return LUA_ERRRUN;\n}\n\n\nLUA_API int lua_resume (lua_State *L, int nargs) {\n  int status;\n  lua_lock(L);\n  if (L->status != LUA_YIELD && (L->status != 0 || L->ci != L->base_ci))\n      return resume_error(L, \"cannot resume non-suspended coroutine\");\n  if (L->nCcalls >= LUAI_MAXCCALLS)\n    return resume_error(L, \"C stack overflow\");\n  luai_userstateresume(L, nargs);\n  lua_assert(L->errfunc == 0);\n  L->baseCcalls = ++L->nCcalls;\n  status = luaD_rawrunprotected(L, resume, L->top - nargs);\n  if (status != 0) {  /* error? */\n    L->status = cast_byte(status);  /* mark thread as `dead' */\n    luaD_seterrorobj(L, status, L->top);\n    L->ci->top = L->top;\n  }\n  else {\n    lua_assert(L->nCcalls == L->baseCcalls);\n    status = L->status;\n  }\n  --L->nCcalls;\n  lua_unlock(L);\n  return status;\n}\n\n\nLUA_API int lua_yield (lua_State *L, int nresults) {\n  luai_userstateyield(L, nresults);\n  lua_lock(L);\n  if (L->nCcalls > L->baseCcalls)\n    luaG_runerror(L, \"attempt to yield across metamethod/C-call boundary\");\n  L->base = L->top - nresults;  /* protect stack slots below */\n  L->status = LUA_YIELD;\n  lua_unlock(L);\n  return -1;\n}\n\n\nint luaD_pcall (lua_State *L, Pfunc func, void *u,\n                ptrdiff_t old_top, ptrdiff_t ef) {\n  int status;\n  unsigned short oldnCcalls = L->nCcalls;\n  ptrdiff_t old_ci = saveci(L, L->ci);\n  lu_byte old_allowhooks = L->allowhook;\n  ptrdiff_t old_errfunc = L->errfunc;\n  L->errfunc = ef;\n  status = luaD_rawrunprotected(L, func, u);\n  if (status != 0) {  /* an error occurred? */\n    StkId oldtop = restorestack(L, old_top);\n    luaF_close(L, oldtop);  /* close eventual pending closures */\n    luaD_seterrorobj(L, status, oldtop);\n    L->nCcalls = oldnCcalls;\n    L->ci = restoreci(L, old_ci);\n    L->base = L->ci->base;\n    L->savedpc = L->ci->savedpc;\n    L->allowhook = old_allowhooks;\n    restore_stack_limit(L);\n  }\n  L->errfunc = old_errfunc;\n  return status;\n}\n\n\n\n/*\n** Execute a protected parser.\n*/\nstruct SParser {  /* data to `f_parser' */\n  ZIO *z;\n  Mbuffer buff;  /* buffer to be used by the scanner */\n  const char *name;\n};\n\nstatic void f_parser (lua_State *L, void *ud) {\n  int i;\n  Proto *tf;\n  Closure *cl;\n  struct SParser *p = cast(struct SParser *, ud);\n  int c = luaZ_lookahead(p->z);\n  luaC_checkGC(L);\n  tf = ((c == LUA_SIGNATURE[0]) ? luaU_undump : luaY_parser)(L, p->z,\n                                                             &p->buff, p->name);\n  cl = luaF_newLclosure(L, tf->nups, hvalue(gt(L)));\n  cl->l.p = tf;\n  for (i = 0; i < tf->nups; i++)  /* initialize eventual upvalues */\n    cl->l.upvals[i] = luaF_newupval(L);\n  setclvalue(L, L->top, cl);\n  incr_top(L);\n}\n\n\nint luaD_protectedparser (lua_State *L, ZIO *z, const char *name) {\n  struct SParser p;\n  int status;\n  p.z = z; p.name = name;\n  luaZ_initbuffer(L, &p.buff);\n  status = luaD_pcall(L, f_parser, &p, savestack(L, L->top), L->errfunc);\n  luaZ_freebuffer(L, &p.buff);\n  return status;\n}\n\n\n"
  },
  {
    "path": "deps/lua/src/ldo.h",
    "content": "/*\n** $Id: ldo.h,v 2.7.1.1 2007/12/27 13:02:25 roberto Exp $\n** Stack and Call structure of Lua\n** See Copyright Notice in lua.h\n*/\n\n#ifndef ldo_h\n#define ldo_h\n\n\n#include \"lobject.h\"\n#include \"lstate.h\"\n#include \"lzio.h\"\n\n\n#define luaD_checkstack(L,n)\t\\\n  if ((char *)L->stack_last - (char *)L->top <= (n)*(int)sizeof(TValue)) \\\n    luaD_growstack(L, n); \\\n  else condhardstacktests(luaD_reallocstack(L, L->stacksize - EXTRA_STACK - 1));\n\n\n#define incr_top(L) {luaD_checkstack(L,1); L->top++;}\n\n#define savestack(L,p)\t\t((char *)(p) - (char *)L->stack)\n#define restorestack(L,n)\t((TValue *)((char *)L->stack + (n)))\n\n#define saveci(L,p)\t\t((char *)(p) - (char *)L->base_ci)\n#define restoreci(L,n)\t\t((CallInfo *)((char *)L->base_ci + (n)))\n\n\n/* results from luaD_precall */\n#define PCRLUA\t\t0\t/* initiated a call to a Lua function */\n#define PCRC\t\t1\t/* did a call to a C function */\n#define PCRYIELD\t2\t/* C funtion yielded */\n\n\n/* type of protected functions, to be ran by `runprotected' */\ntypedef void (*Pfunc) (lua_State *L, void *ud);\n\nLUAI_FUNC int luaD_protectedparser (lua_State *L, ZIO *z, const char *name);\nLUAI_FUNC void luaD_callhook (lua_State *L, int event, int line);\nLUAI_FUNC int luaD_precall (lua_State *L, StkId func, int nresults);\nLUAI_FUNC void luaD_call (lua_State *L, StkId func, int nResults);\nLUAI_FUNC int luaD_pcall (lua_State *L, Pfunc func, void *u,\n                                        ptrdiff_t oldtop, ptrdiff_t ef);\nLUAI_FUNC int luaD_poscall (lua_State *L, StkId firstResult);\nLUAI_FUNC void luaD_reallocCI (lua_State *L, int newsize);\nLUAI_FUNC void luaD_reallocstack (lua_State *L, int newsize);\nLUAI_FUNC void luaD_growstack (lua_State *L, int n);\n\nLUAI_FUNC void luaD_throw (lua_State *L, int errcode);\nLUAI_FUNC int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud);\n\nLUAI_FUNC void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop);\n\n#endif\n\n"
  },
  {
    "path": "deps/lua/src/ldump.c",
    "content": "/*\n** $Id: ldump.c,v 2.8.1.1 2007/12/27 13:02:25 roberto Exp $\n** save precompiled Lua chunks\n** See Copyright Notice in lua.h\n*/\n\n#include <stddef.h>\n\n#define ldump_c\n#define LUA_CORE\n\n#include \"lua.h\"\n\n#include \"lobject.h\"\n#include \"lstate.h\"\n#include \"lundump.h\"\n\ntypedef struct {\n lua_State* L;\n lua_Writer writer;\n void* data;\n int strip;\n int status;\n} DumpState;\n\n#define DumpMem(b,n,size,D)\tDumpBlock(b,(n)*(size),D)\n#define DumpVar(x,D)\t \tDumpMem(&x,1,sizeof(x),D)\n\nstatic void DumpBlock(const void* b, size_t size, DumpState* D)\n{\n if (D->status==0)\n {\n  lua_unlock(D->L);\n  D->status=(*D->writer)(D->L,b,size,D->data);\n  lua_lock(D->L);\n }\n}\n\nstatic void DumpChar(int y, DumpState* D)\n{\n char x=(char)y;\n DumpVar(x,D);\n}\n\nstatic void DumpInt(int x, DumpState* D)\n{\n DumpVar(x,D);\n}\n\nstatic void DumpNumber(lua_Number x, DumpState* D)\n{\n DumpVar(x,D);\n}\n\nstatic void DumpVector(const void* b, int n, size_t size, DumpState* D)\n{\n DumpInt(n,D);\n DumpMem(b,n,size,D);\n}\n\nstatic void DumpString(const TString* s, DumpState* D)\n{\n if (s==NULL || getstr(s)==NULL)\n {\n  size_t size=0;\n  DumpVar(size,D);\n }\n else\n {\n  size_t size=s->tsv.len+1;\t\t/* include trailing '\\0' */\n  DumpVar(size,D);\n  DumpBlock(getstr(s),size,D);\n }\n}\n\n#define DumpCode(f,D)\t DumpVector(f->code,f->sizecode,sizeof(Instruction),D)\n\nstatic void DumpFunction(const Proto* f, const TString* p, DumpState* D);\n\nstatic void DumpConstants(const Proto* f, DumpState* D)\n{\n int i,n=f->sizek;\n DumpInt(n,D);\n for (i=0; i<n; i++)\n {\n  const TValue* o=&f->k[i];\n  DumpChar(ttype(o),D);\n  switch (ttype(o))\n  {\n   case LUA_TNIL:\n\tbreak;\n   case LUA_TBOOLEAN:\n\tDumpChar(bvalue(o),D);\n\tbreak;\n   case LUA_TNUMBER:\n\tDumpNumber(nvalue(o),D);\n\tbreak;\n   case LUA_TSTRING:\n\tDumpString(rawtsvalue(o),D);\n\tbreak;\n   default:\n\tlua_assert(0);\t\t\t/* cannot happen */\n\tbreak;\n  }\n }\n n=f->sizep;\n DumpInt(n,D);\n for (i=0; i<n; i++) DumpFunction(f->p[i],f->source,D);\n}\n\nstatic void DumpDebug(const Proto* f, DumpState* D)\n{\n int i,n;\n n= (D->strip) ? 0 : f->sizelineinfo;\n DumpVector(f->lineinfo,n,sizeof(int),D);\n n= (D->strip) ? 0 : f->sizelocvars;\n DumpInt(n,D);\n for (i=0; i<n; i++)\n {\n  DumpString(f->locvars[i].varname,D);\n  DumpInt(f->locvars[i].startpc,D);\n  DumpInt(f->locvars[i].endpc,D);\n }\n n= (D->strip) ? 0 : f->sizeupvalues;\n DumpInt(n,D);\n for (i=0; i<n; i++) DumpString(f->upvalues[i],D);\n}\n\nstatic void DumpFunction(const Proto* f, const TString* p, DumpState* D)\n{\n DumpString((f->source==p || D->strip) ? NULL : f->source,D);\n DumpInt(f->linedefined,D);\n DumpInt(f->lastlinedefined,D);\n DumpChar(f->nups,D);\n DumpChar(f->numparams,D);\n DumpChar(f->is_vararg,D);\n DumpChar(f->maxstacksize,D);\n DumpCode(f,D);\n DumpConstants(f,D);\n DumpDebug(f,D);\n}\n\nstatic void DumpHeader(DumpState* D)\n{\n char h[LUAC_HEADERSIZE];\n luaU_header(h);\n DumpBlock(h,LUAC_HEADERSIZE,D);\n}\n\n/*\n** dump Lua function as precompiled chunk\n*/\nint luaU_dump (lua_State* L, const Proto* f, lua_Writer w, void* data, int strip)\n{\n DumpState D;\n D.L=L;\n D.writer=w;\n D.data=data;\n D.strip=strip;\n D.status=0;\n DumpHeader(&D);\n DumpFunction(f,NULL,&D);\n return D.status;\n}\n"
  },
  {
    "path": "deps/lua/src/lfunc.c",
    "content": "/*\n** $Id: lfunc.c,v 2.12.1.2 2007/12/28 14:58:43 roberto Exp $\n** Auxiliary functions to manipulate prototypes and closures\n** See Copyright Notice in lua.h\n*/\n\n\n#include <stddef.h>\n\n#define lfunc_c\n#define LUA_CORE\n\n#include \"lua.h\"\n\n#include \"lfunc.h\"\n#include \"lgc.h\"\n#include \"lmem.h\"\n#include \"lobject.h\"\n#include \"lstate.h\"\n\n\n\nClosure *luaF_newCclosure (lua_State *L, int nelems, Table *e) {\n  Closure *c = cast(Closure *, luaM_malloc(L, sizeCclosure(nelems)));\n  luaC_link(L, obj2gco(c), LUA_TFUNCTION);\n  c->c.isC = 1;\n  c->c.env = e;\n  c->c.nupvalues = cast_byte(nelems);\n  return c;\n}\n\n\nClosure *luaF_newLclosure (lua_State *L, int nelems, Table *e) {\n  Closure *c = cast(Closure *, luaM_malloc(L, sizeLclosure(nelems)));\n  luaC_link(L, obj2gco(c), LUA_TFUNCTION);\n  c->l.isC = 0;\n  c->l.env = e;\n  c->l.nupvalues = cast_byte(nelems);\n  while (nelems--) c->l.upvals[nelems] = NULL;\n  return c;\n}\n\n\nUpVal *luaF_newupval (lua_State *L) {\n  UpVal *uv = luaM_new(L, UpVal);\n  luaC_link(L, obj2gco(uv), LUA_TUPVAL);\n  uv->v = &uv->u.value;\n  setnilvalue(uv->v);\n  return uv;\n}\n\n\nUpVal *luaF_findupval (lua_State *L, StkId level) {\n  global_State *g = G(L);\n  GCObject **pp = &L->openupval;\n  UpVal *p;\n  UpVal *uv;\n  while (*pp != NULL && (p = ngcotouv(*pp))->v >= level) {\n    lua_assert(p->v != &p->u.value);\n    if (p->v == level) {  /* found a corresponding upvalue? */\n      if (isdead(g, obj2gco(p)))  /* is it dead? */\n        changewhite(obj2gco(p));  /* ressurect it */\n      return p;\n    }\n    pp = &p->next;\n  }\n  uv = luaM_new(L, UpVal);  /* not found: create a new one */\n  uv->tt = LUA_TUPVAL;\n  uv->marked = luaC_white(g);\n  uv->v = level;  /* current value lives in the stack */\n  uv->next = *pp;  /* chain it in the proper position */\n  *pp = obj2gco(uv);\n  uv->u.l.prev = &g->uvhead;  /* double link it in `uvhead' list */\n  uv->u.l.next = g->uvhead.u.l.next;\n  uv->u.l.next->u.l.prev = uv;\n  g->uvhead.u.l.next = uv;\n  lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv);\n  return uv;\n}\n\n\nstatic void unlinkupval (UpVal *uv) {\n  lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv);\n  uv->u.l.next->u.l.prev = uv->u.l.prev;  /* remove from `uvhead' list */\n  uv->u.l.prev->u.l.next = uv->u.l.next;\n}\n\n\nvoid luaF_freeupval (lua_State *L, UpVal *uv) {\n  if (uv->v != &uv->u.value)  /* is it open? */\n    unlinkupval(uv);  /* remove from open list */\n  luaM_free(L, uv);  /* free upvalue */\n}\n\n\nvoid luaF_close (lua_State *L, StkId level) {\n  UpVal *uv;\n  global_State *g = G(L);\n  while (L->openupval != NULL && (uv = ngcotouv(L->openupval))->v >= level) {\n    GCObject *o = obj2gco(uv);\n    lua_assert(!isblack(o) && uv->v != &uv->u.value);\n    L->openupval = uv->next;  /* remove from `open' list */\n    if (isdead(g, o))\n      luaF_freeupval(L, uv);  /* free upvalue */\n    else {\n      unlinkupval(uv);\n      setobj(L, &uv->u.value, uv->v);\n      uv->v = &uv->u.value;  /* now current value lives here */\n      luaC_linkupval(L, uv);  /* link upvalue into `gcroot' list */\n    }\n  }\n}\n\n\nProto *luaF_newproto (lua_State *L) {\n  Proto *f = luaM_new(L, Proto);\n  luaC_link(L, obj2gco(f), LUA_TPROTO);\n  f->k = NULL;\n  f->sizek = 0;\n  f->p = NULL;\n  f->sizep = 0;\n  f->code = NULL;\n  f->sizecode = 0;\n  f->sizelineinfo = 0;\n  f->sizeupvalues = 0;\n  f->nups = 0;\n  f->upvalues = NULL;\n  f->numparams = 0;\n  f->is_vararg = 0;\n  f->maxstacksize = 0;\n  f->lineinfo = NULL;\n  f->sizelocvars = 0;\n  f->locvars = NULL;\n  f->linedefined = 0;\n  f->lastlinedefined = 0;\n  f->source = NULL;\n  return f;\n}\n\n\nvoid luaF_freeproto (lua_State *L, Proto *f) {\n  luaM_freearray(L, f->code, f->sizecode, Instruction);\n  luaM_freearray(L, f->p, f->sizep, Proto *);\n  luaM_freearray(L, f->k, f->sizek, TValue);\n  luaM_freearray(L, f->lineinfo, f->sizelineinfo, int);\n  luaM_freearray(L, f->locvars, f->sizelocvars, struct LocVar);\n  luaM_freearray(L, f->upvalues, f->sizeupvalues, TString *);\n  luaM_free(L, f);\n}\n\n\nvoid luaF_freeclosure (lua_State *L, Closure *c) {\n  int size = (c->c.isC) ? sizeCclosure(c->c.nupvalues) :\n                          sizeLclosure(c->l.nupvalues);\n  luaM_freemem(L, c, size);\n}\n\n\n/*\n** Look for n-th local variable at line `line' in function `func'.\n** Returns NULL if not found.\n*/\nconst char *luaF_getlocalname (const Proto *f, int local_number, int pc) {\n  int i;\n  for (i = 0; i<f->sizelocvars && f->locvars[i].startpc <= pc; i++) {\n    if (pc < f->locvars[i].endpc) {  /* is variable active? */\n      local_number--;\n      if (local_number == 0)\n        return getstr(f->locvars[i].varname);\n    }\n  }\n  return NULL;  /* not found */\n}\n\n"
  },
  {
    "path": "deps/lua/src/lfunc.h",
    "content": "/*\n** $Id: lfunc.h,v 2.4.1.1 2007/12/27 13:02:25 roberto Exp $\n** Auxiliary functions to manipulate prototypes and closures\n** See Copyright Notice in lua.h\n*/\n\n#ifndef lfunc_h\n#define lfunc_h\n\n\n#include \"lobject.h\"\n\n\n#define sizeCclosure(n)\t(cast(int, sizeof(CClosure)) + \\\n                         cast(int, sizeof(TValue)*((n)-1)))\n\n#define sizeLclosure(n)\t(cast(int, sizeof(LClosure)) + \\\n                         cast(int, sizeof(TValue *)*((n)-1)))\n\n\nLUAI_FUNC Proto *luaF_newproto (lua_State *L);\nLUAI_FUNC Closure *luaF_newCclosure (lua_State *L, int nelems, Table *e);\nLUAI_FUNC Closure *luaF_newLclosure (lua_State *L, int nelems, Table *e);\nLUAI_FUNC UpVal *luaF_newupval (lua_State *L);\nLUAI_FUNC UpVal *luaF_findupval (lua_State *L, StkId level);\nLUAI_FUNC void luaF_close (lua_State *L, StkId level);\nLUAI_FUNC void luaF_freeproto (lua_State *L, Proto *f);\nLUAI_FUNC void luaF_freeclosure (lua_State *L, Closure *c);\nLUAI_FUNC void luaF_freeupval (lua_State *L, UpVal *uv);\nLUAI_FUNC const char *luaF_getlocalname (const Proto *func, int local_number,\n                                         int pc);\n\n\n#endif\n"
  },
  {
    "path": "deps/lua/src/lgc.c",
    "content": "/*\n** $Id: lgc.c,v 2.38.1.2 2011/03/18 18:05:38 roberto Exp $\n** Garbage Collector\n** See Copyright Notice in lua.h\n*/\n\n#include <string.h>\n\n#define lgc_c\n#define LUA_CORE\n\n#include \"lua.h\"\n\n#include \"ldebug.h\"\n#include \"ldo.h\"\n#include \"lfunc.h\"\n#include \"lgc.h\"\n#include \"lmem.h\"\n#include \"lobject.h\"\n#include \"lstate.h\"\n#include \"lstring.h\"\n#include \"ltable.h\"\n#include \"ltm.h\"\n\n\n#define GCSTEPSIZE\t1024u\n#define GCSWEEPMAX\t40\n#define GCSWEEPCOST\t10\n#define GCFINALIZECOST\t100\n\n\n#define maskmarks\tcast_byte(~(bitmask(BLACKBIT)|WHITEBITS))\n\n#define makewhite(g,x)\t\\\n   ((x)->gch.marked = cast_byte(((x)->gch.marked & maskmarks) | luaC_white(g)))\n\n#define white2gray(x)\treset2bits((x)->gch.marked, WHITE0BIT, WHITE1BIT)\n#define black2gray(x)\tresetbit((x)->gch.marked, BLACKBIT)\n\n#define stringmark(s)\treset2bits((s)->tsv.marked, WHITE0BIT, WHITE1BIT)\n\n\n#define isfinalized(u)\t\ttestbit((u)->marked, FINALIZEDBIT)\n#define markfinalized(u)\tl_setbit((u)->marked, FINALIZEDBIT)\n\n\n#define KEYWEAK         bitmask(KEYWEAKBIT)\n#define VALUEWEAK       bitmask(VALUEWEAKBIT)\n\n\n\n#define markvalue(g,o) { checkconsistency(o); \\\n  if (iscollectable(o) && iswhite(gcvalue(o))) reallymarkobject(g,gcvalue(o)); }\n\n#define markobject(g,t) { if (iswhite(obj2gco(t))) \\\n\t\treallymarkobject(g, obj2gco(t)); }\n\n\n#define setthreshold(g)  (g->GCthreshold = (g->estimate/100) * g->gcpause)\n\n\nstatic void removeentry (Node *n) {\n  lua_assert(ttisnil(gval(n)));\n  if (iscollectable(gkey(n)))\n    setttype(gkey(n), LUA_TDEADKEY);  /* dead key; remove it */\n}\n\n\nstatic void reallymarkobject (global_State *g, GCObject *o) {\n  lua_assert(iswhite(o) && !isdead(g, o));\n  white2gray(o);\n  switch (o->gch.tt) {\n    case LUA_TSTRING: {\n      return;\n    }\n    case LUA_TUSERDATA: {\n      Table *mt = gco2u(o)->metatable;\n      gray2black(o);  /* udata are never gray */\n      if (mt) markobject(g, mt);\n      markobject(g, gco2u(o)->env);\n      return;\n    }\n    case LUA_TUPVAL: {\n      UpVal *uv = gco2uv(o);\n      markvalue(g, uv->v);\n      if (uv->v == &uv->u.value)  /* closed? */\n        gray2black(o);  /* open upvalues are never black */\n      return;\n    }\n    case LUA_TFUNCTION: {\n      gco2cl(o)->c.gclist = g->gray;\n      g->gray = o;\n      break;\n    }\n    case LUA_TTABLE: {\n      gco2h(o)->gclist = g->gray;\n      g->gray = o;\n      break;\n    }\n    case LUA_TTHREAD: {\n      gco2th(o)->gclist = g->gray;\n      g->gray = o;\n      break;\n    }\n    case LUA_TPROTO: {\n      gco2p(o)->gclist = g->gray;\n      g->gray = o;\n      break;\n    }\n    default: lua_assert(0);\n  }\n}\n\n\nstatic void marktmu (global_State *g) {\n  GCObject *u = g->tmudata;\n  if (u) {\n    do {\n      u = u->gch.next;\n      makewhite(g, u);  /* may be marked, if left from previous GC */\n      reallymarkobject(g, u);\n    } while (u != g->tmudata);\n  }\n}\n\n\n/* move `dead' udata that need finalization to list `tmudata' */\nsize_t luaC_separateudata (lua_State *L, int all) {\n  global_State *g = G(L);\n  size_t deadmem = 0;\n  GCObject **p = &g->mainthread->next;\n  GCObject *curr;\n  while ((curr = *p) != NULL) {\n    if (!(iswhite(curr) || all) || isfinalized(gco2u(curr)))\n      p = &curr->gch.next;  /* don't bother with them */\n    else if (fasttm(L, gco2u(curr)->metatable, TM_GC) == NULL) {\n      markfinalized(gco2u(curr));  /* don't need finalization */\n      p = &curr->gch.next;\n    }\n    else {  /* must call its gc method */\n      deadmem += sizeudata(gco2u(curr));\n      markfinalized(gco2u(curr));\n      *p = curr->gch.next;\n      /* link `curr' at the end of `tmudata' list */\n      if (g->tmudata == NULL)  /* list is empty? */\n        g->tmudata = curr->gch.next = curr;  /* creates a circular list */\n      else {\n        curr->gch.next = g->tmudata->gch.next;\n        g->tmudata->gch.next = curr;\n        g->tmudata = curr;\n      }\n    }\n  }\n  return deadmem;\n}\n\n\nstatic int traversetable (global_State *g, Table *h) {\n  int i;\n  int weakkey = 0;\n  int weakvalue = 0;\n  const TValue *mode;\n  if (h->metatable)\n    markobject(g, h->metatable);\n  mode = gfasttm(g, h->metatable, TM_MODE);\n  if (mode && ttisstring(mode)) {  /* is there a weak mode? */\n    weakkey = (strchr(svalue(mode), 'k') != NULL);\n    weakvalue = (strchr(svalue(mode), 'v') != NULL);\n    if (weakkey || weakvalue) {  /* is really weak? */\n      h->marked &= ~(KEYWEAK | VALUEWEAK);  /* clear bits */\n      h->marked |= cast_byte((weakkey << KEYWEAKBIT) |\n                             (weakvalue << VALUEWEAKBIT));\n      h->gclist = g->weak;  /* must be cleared after GC, ... */\n      g->weak = obj2gco(h);  /* ... so put in the appropriate list */\n    }\n  }\n  if (weakkey && weakvalue) return 1;\n  if (!weakvalue) {\n    i = h->sizearray;\n    while (i--)\n      markvalue(g, &h->array[i]);\n  }\n  i = sizenode(h);\n  while (i--) {\n    Node *n = gnode(h, i);\n    lua_assert(ttype(gkey(n)) != LUA_TDEADKEY || ttisnil(gval(n)));\n    if (ttisnil(gval(n)))\n      removeentry(n);  /* remove empty entries */\n    else {\n      lua_assert(!ttisnil(gkey(n)));\n      if (!weakkey) markvalue(g, gkey(n));\n      if (!weakvalue) markvalue(g, gval(n));\n    }\n  }\n  return weakkey || weakvalue;\n}\n\n\n/*\n** All marks are conditional because a GC may happen while the\n** prototype is still being created\n*/\nstatic void traverseproto (global_State *g, Proto *f) {\n  int i;\n  if (f->source) stringmark(f->source);\n  for (i=0; i<f->sizek; i++)  /* mark literals */\n    markvalue(g, &f->k[i]);\n  for (i=0; i<f->sizeupvalues; i++) {  /* mark upvalue names */\n    if (f->upvalues[i])\n      stringmark(f->upvalues[i]);\n  }\n  for (i=0; i<f->sizep; i++) {  /* mark nested protos */\n    if (f->p[i])\n      markobject(g, f->p[i]);\n  }\n  for (i=0; i<f->sizelocvars; i++) {  /* mark local-variable names */\n    if (f->locvars[i].varname)\n      stringmark(f->locvars[i].varname);\n  }\n}\n\n\n\nstatic void traverseclosure (global_State *g, Closure *cl) {\n  markobject(g, cl->c.env);\n  if (cl->c.isC) {\n    int i;\n    for (i=0; i<cl->c.nupvalues; i++)  /* mark its upvalues */\n      markvalue(g, &cl->c.upvalue[i]);\n  }\n  else {\n    int i;\n    lua_assert(cl->l.nupvalues == cl->l.p->nups);\n    markobject(g, cl->l.p);\n    for (i=0; i<cl->l.nupvalues; i++)  /* mark its upvalues */\n      markobject(g, cl->l.upvals[i]);\n  }\n}\n\n\nstatic void checkstacksizes (lua_State *L, StkId max) {\n  int ci_used = cast_int(L->ci - L->base_ci);  /* number of `ci' in use */\n  int s_used = cast_int(max - L->stack);  /* part of stack in use */\n  if (L->size_ci > LUAI_MAXCALLS)  /* handling overflow? */\n    return;  /* do not touch the stacks */\n  if (4*ci_used < L->size_ci && 2*BASIC_CI_SIZE < L->size_ci)\n    luaD_reallocCI(L, L->size_ci/2);  /* still big enough... */\n  condhardstacktests(luaD_reallocCI(L, ci_used + 1));\n  if (4*s_used < L->stacksize &&\n      2*(BASIC_STACK_SIZE+EXTRA_STACK) < L->stacksize)\n    luaD_reallocstack(L, L->stacksize/2);  /* still big enough... */\n  condhardstacktests(luaD_reallocstack(L, s_used));\n}\n\n\nstatic void traversestack (global_State *g, lua_State *l) {\n  StkId o, lim;\n  CallInfo *ci;\n  markvalue(g, gt(l));\n  lim = l->top;\n  for (ci = l->base_ci; ci <= l->ci; ci++) {\n    lua_assert(ci->top <= l->stack_last);\n    if (lim < ci->top) lim = ci->top;\n  }\n  for (o = l->stack; o < l->top; o++)\n    markvalue(g, o);\n  for (; o <= lim; o++)\n    setnilvalue(o);\n  checkstacksizes(l, lim);\n}\n\n\n/*\n** traverse one gray object, turning it to black.\n** Returns `quantity' traversed.\n*/\nstatic l_mem propagatemark (global_State *g) {\n  GCObject *o = g->gray;\n  lua_assert(isgray(o));\n  gray2black(o);\n  switch (o->gch.tt) {\n    case LUA_TTABLE: {\n      Table *h = gco2h(o);\n      g->gray = h->gclist;\n      if (traversetable(g, h))  /* table is weak? */\n        black2gray(o);  /* keep it gray */\n      return sizeof(Table) + sizeof(TValue) * h->sizearray +\n                             sizeof(Node) * sizenode(h);\n    }\n    case LUA_TFUNCTION: {\n      Closure *cl = gco2cl(o);\n      g->gray = cl->c.gclist;\n      traverseclosure(g, cl);\n      return (cl->c.isC) ? sizeCclosure(cl->c.nupvalues) :\n                           sizeLclosure(cl->l.nupvalues);\n    }\n    case LUA_TTHREAD: {\n      lua_State *th = gco2th(o);\n      g->gray = th->gclist;\n      th->gclist = g->grayagain;\n      g->grayagain = o;\n      black2gray(o);\n      traversestack(g, th);\n      return sizeof(lua_State) + sizeof(TValue) * th->stacksize +\n                                 sizeof(CallInfo) * th->size_ci;\n    }\n    case LUA_TPROTO: {\n      Proto *p = gco2p(o);\n      g->gray = p->gclist;\n      traverseproto(g, p);\n      return sizeof(Proto) + sizeof(Instruction) * p->sizecode +\n                             sizeof(Proto *) * p->sizep +\n                             sizeof(TValue) * p->sizek + \n                             sizeof(int) * p->sizelineinfo +\n                             sizeof(LocVar) * p->sizelocvars +\n                             sizeof(TString *) * p->sizeupvalues;\n    }\n    default: lua_assert(0); return 0;\n  }\n}\n\n\nstatic size_t propagateall (global_State *g) {\n  size_t m = 0;\n  while (g->gray) m += propagatemark(g);\n  return m;\n}\n\n\n/*\n** The next function tells whether a key or value can be cleared from\n** a weak table. Non-collectable objects are never removed from weak\n** tables. Strings behave as `values', so are never removed too. for\n** other objects: if really collected, cannot keep them; for userdata\n** being finalized, keep them in keys, but not in values\n*/\nstatic int iscleared (const TValue *o, int iskey) {\n  if (!iscollectable(o)) return 0;\n  if (ttisstring(o)) {\n    stringmark(rawtsvalue(o));  /* strings are `values', so are never weak */\n    return 0;\n  }\n  return iswhite(gcvalue(o)) ||\n    (ttisuserdata(o) && (!iskey && isfinalized(uvalue(o))));\n}\n\n\n/*\n** clear collected entries from weaktables\n*/\nstatic void cleartable (GCObject *l) {\n  while (l) {\n    Table *h = gco2h(l);\n    int i = h->sizearray;\n    lua_assert(testbit(h->marked, VALUEWEAKBIT) ||\n               testbit(h->marked, KEYWEAKBIT));\n    if (testbit(h->marked, VALUEWEAKBIT)) {\n      while (i--) {\n        TValue *o = &h->array[i];\n        if (iscleared(o, 0))  /* value was collected? */\n          setnilvalue(o);  /* remove value */\n      }\n    }\n    i = sizenode(h);\n    while (i--) {\n      Node *n = gnode(h, i);\n      if (!ttisnil(gval(n)) &&  /* non-empty entry? */\n          (iscleared(key2tval(n), 1) || iscleared(gval(n), 0))) {\n        setnilvalue(gval(n));  /* remove value ... */\n        removeentry(n);  /* remove entry from table */\n      }\n    }\n    l = h->gclist;\n  }\n}\n\n\nstatic void freeobj (lua_State *L, GCObject *o) {\n  switch (o->gch.tt) {\n    case LUA_TPROTO: luaF_freeproto(L, gco2p(o)); break;\n    case LUA_TFUNCTION: luaF_freeclosure(L, gco2cl(o)); break;\n    case LUA_TUPVAL: luaF_freeupval(L, gco2uv(o)); break;\n    case LUA_TTABLE: luaH_free(L, gco2h(o)); break;\n    case LUA_TTHREAD: {\n      lua_assert(gco2th(o) != L && gco2th(o) != G(L)->mainthread);\n      luaE_freethread(L, gco2th(o));\n      break;\n    }\n    case LUA_TSTRING: {\n      G(L)->strt.nuse--;\n      luaM_freemem(L, o, sizestring(gco2ts(o)));\n      break;\n    }\n    case LUA_TUSERDATA: {\n      luaM_freemem(L, o, sizeudata(gco2u(o)));\n      break;\n    }\n    default: lua_assert(0);\n  }\n}\n\n\n\n#define sweepwholelist(L,p)\tsweeplist(L,p,MAX_LUMEM)\n\n\nstatic GCObject **sweeplist (lua_State *L, GCObject **p, lu_mem count) {\n  GCObject *curr;\n  global_State *g = G(L);\n  int deadmask = otherwhite(g);\n  while ((curr = *p) != NULL && count-- > 0) {\n    if (curr->gch.tt == LUA_TTHREAD)  /* sweep open upvalues of each thread */\n      sweepwholelist(L, &gco2th(curr)->openupval);\n    if ((curr->gch.marked ^ WHITEBITS) & deadmask) {  /* not dead? */\n      lua_assert(!isdead(g, curr) || testbit(curr->gch.marked, FIXEDBIT));\n      makewhite(g, curr);  /* make it white (for next cycle) */\n      p = &curr->gch.next;\n    }\n    else {  /* must erase `curr' */\n      lua_assert(isdead(g, curr) || deadmask == bitmask(SFIXEDBIT));\n      *p = curr->gch.next;\n      if (curr == g->rootgc)  /* is the first element of the list? */\n        g->rootgc = curr->gch.next;  /* adjust first */\n      freeobj(L, curr);\n    }\n  }\n  return p;\n}\n\n\nstatic void checkSizes (lua_State *L) {\n  global_State *g = G(L);\n  /* check size of string hash */\n  if (g->strt.nuse < cast(lu_int32, g->strt.size/4) &&\n      g->strt.size > MINSTRTABSIZE*2)\n    luaS_resize(L, g->strt.size/2);  /* table is too big */\n  /* check size of buffer */\n  if (luaZ_sizebuffer(&g->buff) > LUA_MINBUFFER*2) {  /* buffer too big? */\n    size_t newsize = luaZ_sizebuffer(&g->buff) / 2;\n    luaZ_resizebuffer(L, &g->buff, newsize);\n  }\n}\n\n\nstatic void GCTM (lua_State *L) {\n  global_State *g = G(L);\n  GCObject *o = g->tmudata->gch.next;  /* get first element */\n  Udata *udata = rawgco2u(o);\n  const TValue *tm;\n  /* remove udata from `tmudata' */\n  if (o == g->tmudata)  /* last element? */\n    g->tmudata = NULL;\n  else\n    g->tmudata->gch.next = udata->uv.next;\n  udata->uv.next = g->mainthread->next;  /* return it to `root' list */\n  g->mainthread->next = o;\n  makewhite(g, o);\n  tm = fasttm(L, udata->uv.metatable, TM_GC);\n  if (tm != NULL) {\n    lu_byte oldah = L->allowhook;\n    lu_mem oldt = g->GCthreshold;\n    L->allowhook = 0;  /* stop debug hooks during GC tag method */\n    g->GCthreshold = 2*g->totalbytes;  /* avoid GC steps */\n    setobj2s(L, L->top, tm);\n    setuvalue(L, L->top+1, udata);\n    L->top += 2;\n    luaD_call(L, L->top - 2, 0);\n    L->allowhook = oldah;  /* restore hooks */\n    g->GCthreshold = oldt;  /* restore threshold */\n  }\n}\n\n\n/*\n** Call all GC tag methods\n*/\nvoid luaC_callGCTM (lua_State *L) {\n  while (G(L)->tmudata)\n    GCTM(L);\n}\n\n\nvoid luaC_freeall (lua_State *L) {\n  global_State *g = G(L);\n  int i;\n  g->currentwhite = WHITEBITS | bitmask(SFIXEDBIT);  /* mask to collect all elements */\n  sweepwholelist(L, &g->rootgc);\n  for (i = 0; i < g->strt.size; i++)  /* free all string lists */\n    sweepwholelist(L, &g->strt.hash[i]);\n}\n\n\nstatic void markmt (global_State *g) {\n  int i;\n  for (i=0; i<NUM_TAGS; i++)\n    if (g->mt[i]) markobject(g, g->mt[i]);\n}\n\n\n/* mark root set */\nstatic void markroot (lua_State *L) {\n  global_State *g = G(L);\n  g->gray = NULL;\n  g->grayagain = NULL;\n  g->weak = NULL;\n  markobject(g, g->mainthread);\n  /* make global table be traversed before main stack */\n  markvalue(g, gt(g->mainthread));\n  markvalue(g, registry(L));\n  markmt(g);\n  g->gcstate = GCSpropagate;\n}\n\n\nstatic void remarkupvals (global_State *g) {\n  UpVal *uv;\n  for (uv = g->uvhead.u.l.next; uv != &g->uvhead; uv = uv->u.l.next) {\n    lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv);\n    if (isgray(obj2gco(uv)))\n      markvalue(g, uv->v);\n  }\n}\n\n\nstatic void atomic (lua_State *L) {\n  global_State *g = G(L);\n  size_t udsize;  /* total size of userdata to be finalized */\n  /* remark occasional upvalues of (maybe) dead threads */\n  remarkupvals(g);\n  /* traverse objects cautch by write barrier and by 'remarkupvals' */\n  propagateall(g);\n  /* remark weak tables */\n  g->gray = g->weak;\n  g->weak = NULL;\n  lua_assert(!iswhite(obj2gco(g->mainthread)));\n  markobject(g, L);  /* mark running thread */\n  markmt(g);  /* mark basic metatables (again) */\n  propagateall(g);\n  /* remark gray again */\n  g->gray = g->grayagain;\n  g->grayagain = NULL;\n  propagateall(g);\n  udsize = luaC_separateudata(L, 0);  /* separate userdata to be finalized */\n  marktmu(g);  /* mark `preserved' userdata */\n  udsize += propagateall(g);  /* remark, to propagate `preserveness' */\n  cleartable(g->weak);  /* remove collected objects from weak tables */\n  /* flip current white */\n  g->currentwhite = cast_byte(otherwhite(g));\n  g->sweepstrgc = 0;\n  g->sweepgc = &g->rootgc;\n  g->gcstate = GCSsweepstring;\n  g->estimate = g->totalbytes - udsize;  /* first estimate */\n}\n\n\nstatic l_mem singlestep (lua_State *L) {\n  global_State *g = G(L);\n  /*lua_checkmemory(L);*/\n  switch (g->gcstate) {\n    case GCSpause: {\n      markroot(L);  /* start a new collection */\n      return 0;\n    }\n    case GCSpropagate: {\n      if (g->gray)\n        return propagatemark(g);\n      else {  /* no more `gray' objects */\n        atomic(L);  /* finish mark phase */\n        return 0;\n      }\n    }\n    case GCSsweepstring: {\n      lu_mem old = g->totalbytes;\n      sweepwholelist(L, &g->strt.hash[g->sweepstrgc++]);\n      if (g->sweepstrgc >= g->strt.size)  /* nothing more to sweep? */\n        g->gcstate = GCSsweep;  /* end sweep-string phase */\n      lua_assert(old >= g->totalbytes);\n      g->estimate -= old - g->totalbytes;\n      return GCSWEEPCOST;\n    }\n    case GCSsweep: {\n      lu_mem old = g->totalbytes;\n      g->sweepgc = sweeplist(L, g->sweepgc, GCSWEEPMAX);\n      if (*g->sweepgc == NULL) {  /* nothing more to sweep? */\n        checkSizes(L);\n        g->gcstate = GCSfinalize;  /* end sweep phase */\n      }\n      lua_assert(old >= g->totalbytes);\n      g->estimate -= old - g->totalbytes;\n      return GCSWEEPMAX*GCSWEEPCOST;\n    }\n    case GCSfinalize: {\n      if (g->tmudata) {\n        GCTM(L);\n        if (g->estimate > GCFINALIZECOST)\n          g->estimate -= GCFINALIZECOST;\n        return GCFINALIZECOST;\n      }\n      else {\n        g->gcstate = GCSpause;  /* end collection */\n        g->gcdept = 0;\n        return 0;\n      }\n    }\n    default: lua_assert(0); return 0;\n  }\n}\n\n\nvoid luaC_step (lua_State *L) {\n  global_State *g = G(L);\n  l_mem lim = (GCSTEPSIZE/100) * g->gcstepmul;\n  if (lim == 0)\n    lim = (MAX_LUMEM-1)/2;  /* no limit */\n  g->gcdept += g->totalbytes - g->GCthreshold;\n  do {\n    lim -= singlestep(L);\n    if (g->gcstate == GCSpause)\n      break;\n  } while (lim > 0);\n  if (g->gcstate != GCSpause) {\n    if (g->gcdept < GCSTEPSIZE)\n      g->GCthreshold = g->totalbytes + GCSTEPSIZE;  /* - lim/g->gcstepmul;*/\n    else {\n      g->gcdept -= GCSTEPSIZE;\n      g->GCthreshold = g->totalbytes;\n    }\n  }\n  else {\n    setthreshold(g);\n  }\n}\n\n\nvoid luaC_fullgc (lua_State *L) {\n  global_State *g = G(L);\n  if (g->gcstate <= GCSpropagate) {\n    /* reset sweep marks to sweep all elements (returning them to white) */\n    g->sweepstrgc = 0;\n    g->sweepgc = &g->rootgc;\n    /* reset other collector lists */\n    g->gray = NULL;\n    g->grayagain = NULL;\n    g->weak = NULL;\n    g->gcstate = GCSsweepstring;\n  }\n  lua_assert(g->gcstate != GCSpause && g->gcstate != GCSpropagate);\n  /* finish any pending sweep phase */\n  while (g->gcstate != GCSfinalize) {\n    lua_assert(g->gcstate == GCSsweepstring || g->gcstate == GCSsweep);\n    singlestep(L);\n  }\n  markroot(L);\n  while (g->gcstate != GCSpause) {\n    singlestep(L);\n  }\n  setthreshold(g);\n}\n\n\nvoid luaC_barrierf (lua_State *L, GCObject *o, GCObject *v) {\n  global_State *g = G(L);\n  lua_assert(isblack(o) && iswhite(v) && !isdead(g, v) && !isdead(g, o));\n  lua_assert(g->gcstate != GCSfinalize && g->gcstate != GCSpause);\n  lua_assert(ttype(&o->gch) != LUA_TTABLE);\n  /* must keep invariant? */\n  if (g->gcstate == GCSpropagate)\n    reallymarkobject(g, v);  /* restore invariant */\n  else  /* don't mind */\n    makewhite(g, o);  /* mark as white just to avoid other barriers */\n}\n\n\nvoid luaC_barrierback (lua_State *L, Table *t) {\n  global_State *g = G(L);\n  GCObject *o = obj2gco(t);\n  lua_assert(isblack(o) && !isdead(g, o));\n  lua_assert(g->gcstate != GCSfinalize && g->gcstate != GCSpause);\n  black2gray(o);  /* make table gray (again) */\n  t->gclist = g->grayagain;\n  g->grayagain = o;\n}\n\n\nvoid luaC_link (lua_State *L, GCObject *o, lu_byte tt) {\n  global_State *g = G(L);\n  o->gch.next = g->rootgc;\n  g->rootgc = o;\n  o->gch.marked = luaC_white(g);\n  o->gch.tt = tt;\n}\n\n\nvoid luaC_linkupval (lua_State *L, UpVal *uv) {\n  global_State *g = G(L);\n  GCObject *o = obj2gco(uv);\n  o->gch.next = g->rootgc;  /* link upvalue into `rootgc' list */\n  g->rootgc = o;\n  if (isgray(o)) { \n    if (g->gcstate == GCSpropagate) {\n      gray2black(o);  /* closed upvalues need barrier */\n      luaC_barrier(L, uv, uv->v);\n    }\n    else {  /* sweep phase: sweep it (turning it into white) */\n      makewhite(g, o);\n      lua_assert(g->gcstate != GCSfinalize && g->gcstate != GCSpause);\n    }\n  }\n}\n\n"
  },
  {
    "path": "deps/lua/src/lgc.h",
    "content": "/*\n** $Id: lgc.h,v 2.15.1.1 2007/12/27 13:02:25 roberto Exp $\n** Garbage Collector\n** See Copyright Notice in lua.h\n*/\n\n#ifndef lgc_h\n#define lgc_h\n\n\n#include \"lobject.h\"\n\n\n/*\n** Possible states of the Garbage Collector\n*/\n#define GCSpause\t0\n#define GCSpropagate\t1\n#define GCSsweepstring\t2\n#define GCSsweep\t3\n#define GCSfinalize\t4\n\n\n/*\n** some userful bit tricks\n*/\n#define resetbits(x,m)\t((x) &= cast(lu_byte, ~(m)))\n#define setbits(x,m)\t((x) |= (m))\n#define testbits(x,m)\t((x) & (m))\n#define bitmask(b)\t(1<<(b))\n#define bit2mask(b1,b2)\t(bitmask(b1) | bitmask(b2))\n#define l_setbit(x,b)\tsetbits(x, bitmask(b))\n#define resetbit(x,b)\tresetbits(x, bitmask(b))\n#define testbit(x,b)\ttestbits(x, bitmask(b))\n#define set2bits(x,b1,b2)\tsetbits(x, (bit2mask(b1, b2)))\n#define reset2bits(x,b1,b2)\tresetbits(x, (bit2mask(b1, b2)))\n#define test2bits(x,b1,b2)\ttestbits(x, (bit2mask(b1, b2)))\n\n\n\n/*\n** Layout for bit use in `marked' field:\n** bit 0 - object is white (type 0)\n** bit 1 - object is white (type 1)\n** bit 2 - object is black\n** bit 3 - for userdata: has been finalized\n** bit 3 - for tables: has weak keys\n** bit 4 - for tables: has weak values\n** bit 5 - object is fixed (should not be collected)\n** bit 6 - object is \"super\" fixed (only the main thread)\n*/\n\n\n#define WHITE0BIT\t0\n#define WHITE1BIT\t1\n#define BLACKBIT\t2\n#define FINALIZEDBIT\t3\n#define KEYWEAKBIT\t3\n#define VALUEWEAKBIT\t4\n#define FIXEDBIT\t5\n#define SFIXEDBIT\t6\n#define WHITEBITS\tbit2mask(WHITE0BIT, WHITE1BIT)\n\n\n#define iswhite(x)      test2bits((x)->gch.marked, WHITE0BIT, WHITE1BIT)\n#define isblack(x)      testbit((x)->gch.marked, BLACKBIT)\n#define isgray(x)\t(!isblack(x) && !iswhite(x))\n\n#define otherwhite(g)\t(g->currentwhite ^ WHITEBITS)\n#define isdead(g,v)\t((v)->gch.marked & otherwhite(g) & WHITEBITS)\n\n#define changewhite(x)\t((x)->gch.marked ^= WHITEBITS)\n#define gray2black(x)\tl_setbit((x)->gch.marked, BLACKBIT)\n\n#define valiswhite(x)\t(iscollectable(x) && iswhite(gcvalue(x)))\n\n#define luaC_white(g)\tcast(lu_byte, (g)->currentwhite & WHITEBITS)\n\n\n#define luaC_checkGC(L) { \\\n  condhardstacktests(luaD_reallocstack(L, L->stacksize - EXTRA_STACK - 1)); \\\n  if (G(L)->totalbytes >= G(L)->GCthreshold) \\\n\tluaC_step(L); }\n\n\n#define luaC_barrier(L,p,v) { if (valiswhite(v) && isblack(obj2gco(p)))  \\\n\tluaC_barrierf(L,obj2gco(p),gcvalue(v)); }\n\n#define luaC_barriert(L,t,v) { if (valiswhite(v) && isblack(obj2gco(t)))  \\\n\tluaC_barrierback(L,t); }\n\n#define luaC_objbarrier(L,p,o)  \\\n\t{ if (iswhite(obj2gco(o)) && isblack(obj2gco(p))) \\\n\t\tluaC_barrierf(L,obj2gco(p),obj2gco(o)); }\n\n#define luaC_objbarriert(L,t,o)  \\\n   { if (iswhite(obj2gco(o)) && isblack(obj2gco(t))) luaC_barrierback(L,t); }\n\nLUAI_FUNC size_t luaC_separateudata (lua_State *L, int all);\nLUAI_FUNC void luaC_callGCTM (lua_State *L);\nLUAI_FUNC void luaC_freeall (lua_State *L);\nLUAI_FUNC void luaC_step (lua_State *L);\nLUAI_FUNC void luaC_fullgc (lua_State *L);\nLUAI_FUNC void luaC_link (lua_State *L, GCObject *o, lu_byte tt);\nLUAI_FUNC void luaC_linkupval (lua_State *L, UpVal *uv);\nLUAI_FUNC void luaC_barrierf (lua_State *L, GCObject *o, GCObject *v);\nLUAI_FUNC void luaC_barrierback (lua_State *L, Table *t);\n\n\n#endif\n"
  },
  {
    "path": "deps/lua/src/linit.c",
    "content": "/*\n** $Id: linit.c,v 1.14.1.1 2007/12/27 13:02:25 roberto Exp $\n** Initialization of libraries for lua.c\n** See Copyright Notice in lua.h\n*/\n\n\n#define linit_c\n#define LUA_LIB\n\n#include \"lua.h\"\n\n#include \"lualib.h\"\n#include \"lauxlib.h\"\n\n\nstatic const luaL_Reg lualibs[] = {\n  {\"\", luaopen_base},\n  {LUA_LOADLIBNAME, luaopen_package},\n  {LUA_TABLIBNAME, luaopen_table},\n  {LUA_IOLIBNAME, luaopen_io},\n  {LUA_OSLIBNAME, luaopen_os},\n  {LUA_STRLIBNAME, luaopen_string},\n  {LUA_MATHLIBNAME, luaopen_math},\n  {LUA_DBLIBNAME, luaopen_debug},\n  {NULL, NULL}\n};\n\n\nLUALIB_API void luaL_openlibs (lua_State *L) {\n  const luaL_Reg *lib = lualibs;\n  for (; lib->func; lib++) {\n    lua_pushcfunction(L, lib->func);\n    lua_pushstring(L, lib->name);\n    lua_call(L, 1, 0);\n  }\n}\n\n"
  },
  {
    "path": "deps/lua/src/liolib.c",
    "content": "/*\n** $Id: liolib.c,v 2.73.1.4 2010/05/14 15:33:51 roberto Exp $\n** Standard I/O (and system) library\n** See Copyright Notice in lua.h\n*/\n\n\n#include <errno.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#define liolib_c\n#define LUA_LIB\n\n#include \"lua.h\"\n\n#include \"lauxlib.h\"\n#include \"lualib.h\"\n\n\n\n#define IO_INPUT\t1\n#define IO_OUTPUT\t2\n\n\nstatic const char *const fnames[] = {\"input\", \"output\"};\n\n\nstatic int pushresult (lua_State *L, int i, const char *filename) {\n  int en = errno;  /* calls to Lua API may change this value */\n  if (i) {\n    lua_pushboolean(L, 1);\n    return 1;\n  }\n  else {\n    lua_pushnil(L);\n    if (filename)\n      lua_pushfstring(L, \"%s: %s\", filename, strerror(en));\n    else\n      lua_pushfstring(L, \"%s\", strerror(en));\n    lua_pushinteger(L, en);\n    return 3;\n  }\n}\n\n\nstatic void fileerror (lua_State *L, int arg, const char *filename) {\n  lua_pushfstring(L, \"%s: %s\", filename, strerror(errno));\n  luaL_argerror(L, arg, lua_tostring(L, -1));\n}\n\n\n#define tofilep(L)\t((FILE **)luaL_checkudata(L, 1, LUA_FILEHANDLE))\n\n\nstatic int io_type (lua_State *L) {\n  void *ud;\n  luaL_checkany(L, 1);\n  ud = lua_touserdata(L, 1);\n  lua_getfield(L, LUA_REGISTRYINDEX, LUA_FILEHANDLE);\n  if (ud == NULL || !lua_getmetatable(L, 1) || !lua_rawequal(L, -2, -1))\n    lua_pushnil(L);  /* not a file */\n  else if (*((FILE **)ud) == NULL)\n    lua_pushliteral(L, \"closed file\");\n  else\n    lua_pushliteral(L, \"file\");\n  return 1;\n}\n\n\nstatic FILE *tofile (lua_State *L) {\n  FILE **f = tofilep(L);\n  if (*f == NULL)\n    luaL_error(L, \"attempt to use a closed file\");\n  return *f;\n}\n\n\n\n/*\n** When creating file handles, always creates a `closed' file handle\n** before opening the actual file; so, if there is a memory error, the\n** file is not left opened.\n*/\nstatic FILE **newfile (lua_State *L) {\n  FILE **pf = (FILE **)lua_newuserdata(L, sizeof(FILE *));\n  *pf = NULL;  /* file handle is currently `closed' */\n  luaL_getmetatable(L, LUA_FILEHANDLE);\n  lua_setmetatable(L, -2);\n  return pf;\n}\n\n\n/*\n** function to (not) close the standard files stdin, stdout, and stderr\n*/\nstatic int io_noclose (lua_State *L) {\n  lua_pushnil(L);\n  lua_pushliteral(L, \"cannot close standard file\");\n  return 2;\n}\n\n\n/*\n** function to close 'popen' files\n*/\nstatic int io_pclose (lua_State *L) {\n  FILE **p = tofilep(L);\n  int ok = lua_pclose(L, *p);\n  *p = NULL;\n  return pushresult(L, ok, NULL);\n}\n\n\n/*\n** function to close regular files\n*/\nstatic int io_fclose (lua_State *L) {\n  FILE **p = tofilep(L);\n  int ok = (fclose(*p) == 0);\n  *p = NULL;\n  return pushresult(L, ok, NULL);\n}\n\n\nstatic int aux_close (lua_State *L) {\n  lua_getfenv(L, 1);\n  lua_getfield(L, -1, \"__close\");\n  return (lua_tocfunction(L, -1))(L);\n}\n\n\nstatic int io_close (lua_State *L) {\n  if (lua_isnone(L, 1))\n    lua_rawgeti(L, LUA_ENVIRONINDEX, IO_OUTPUT);\n  tofile(L);  /* make sure argument is a file */\n  return aux_close(L);\n}\n\n\nstatic int io_gc (lua_State *L) {\n  FILE *f = *tofilep(L);\n  /* ignore closed files */\n  if (f != NULL)\n    aux_close(L);\n  return 0;\n}\n\n\nstatic int io_tostring (lua_State *L) {\n  FILE *f = *tofilep(L);\n  if (f == NULL)\n    lua_pushliteral(L, \"file (closed)\");\n  else\n    lua_pushfstring(L, \"file (%p)\", f);\n  return 1;\n}\n\n\nstatic int io_open (lua_State *L) {\n  const char *filename = luaL_checkstring(L, 1);\n  const char *mode = luaL_optstring(L, 2, \"r\");\n  FILE **pf = newfile(L);\n  *pf = fopen(filename, mode);\n  return (*pf == NULL) ? pushresult(L, 0, filename) : 1;\n}\n\n\n/*\n** this function has a separated environment, which defines the\n** correct __close for 'popen' files\n*/\nstatic int io_popen (lua_State *L) {\n  const char *filename = luaL_checkstring(L, 1);\n  const char *mode = luaL_optstring(L, 2, \"r\");\n  FILE **pf = newfile(L);\n  *pf = lua_popen(L, filename, mode);\n  return (*pf == NULL) ? pushresult(L, 0, filename) : 1;\n}\n\n\nstatic int io_tmpfile (lua_State *L) {\n  FILE **pf = newfile(L);\n  *pf = tmpfile();\n  return (*pf == NULL) ? pushresult(L, 0, NULL) : 1;\n}\n\n\nstatic FILE *getiofile (lua_State *L, int findex) {\n  FILE *f;\n  lua_rawgeti(L, LUA_ENVIRONINDEX, findex);\n  f = *(FILE **)lua_touserdata(L, -1);\n  if (f == NULL)\n    luaL_error(L, \"standard %s file is closed\", fnames[findex - 1]);\n  return f;\n}\n\n\nstatic int g_iofile (lua_State *L, int f, const char *mode) {\n  if (!lua_isnoneornil(L, 1)) {\n    const char *filename = lua_tostring(L, 1);\n    if (filename) {\n      FILE **pf = newfile(L);\n      *pf = fopen(filename, mode);\n      if (*pf == NULL)\n        fileerror(L, 1, filename);\n    }\n    else {\n      tofile(L);  /* check that it's a valid file handle */\n      lua_pushvalue(L, 1);\n    }\n    lua_rawseti(L, LUA_ENVIRONINDEX, f);\n  }\n  /* return current value */\n  lua_rawgeti(L, LUA_ENVIRONINDEX, f);\n  return 1;\n}\n\n\nstatic int io_input (lua_State *L) {\n  return g_iofile(L, IO_INPUT, \"r\");\n}\n\n\nstatic int io_output (lua_State *L) {\n  return g_iofile(L, IO_OUTPUT, \"w\");\n}\n\n\nstatic int io_readline (lua_State *L);\n\n\nstatic void aux_lines (lua_State *L, int idx, int toclose) {\n  lua_pushvalue(L, idx);\n  lua_pushboolean(L, toclose);  /* close/not close file when finished */\n  lua_pushcclosure(L, io_readline, 2);\n}\n\n\nstatic int f_lines (lua_State *L) {\n  tofile(L);  /* check that it's a valid file handle */\n  aux_lines(L, 1, 0);\n  return 1;\n}\n\n\nstatic int io_lines (lua_State *L) {\n  if (lua_isnoneornil(L, 1)) {  /* no arguments? */\n    /* will iterate over default input */\n    lua_rawgeti(L, LUA_ENVIRONINDEX, IO_INPUT);\n    return f_lines(L);\n  }\n  else {\n    const char *filename = luaL_checkstring(L, 1);\n    FILE **pf = newfile(L);\n    *pf = fopen(filename, \"r\");\n    if (*pf == NULL)\n      fileerror(L, 1, filename);\n    aux_lines(L, lua_gettop(L), 1);\n    return 1;\n  }\n}\n\n\n/*\n** {======================================================\n** READ\n** =======================================================\n*/\n\n\nstatic int read_number (lua_State *L, FILE *f) {\n  lua_Number d;\n  if (fscanf(f, LUA_NUMBER_SCAN, &d) == 1) {\n    lua_pushnumber(L, d);\n    return 1;\n  }\n  else {\n    lua_pushnil(L);  /* \"result\" to be removed */\n    return 0;  /* read fails */\n  }\n}\n\n\nstatic int test_eof (lua_State *L, FILE *f) {\n  int c = getc(f);\n  ungetc(c, f);\n  lua_pushlstring(L, NULL, 0);\n  return (c != EOF);\n}\n\n\nstatic int read_line (lua_State *L, FILE *f) {\n  luaL_Buffer b;\n  luaL_buffinit(L, &b);\n  for (;;) {\n    size_t l;\n    char *p = luaL_prepbuffer(&b);\n    if (fgets(p, LUAL_BUFFERSIZE, f) == NULL) {  /* eof? */\n      luaL_pushresult(&b);  /* close buffer */\n      return (lua_objlen(L, -1) > 0);  /* check whether read something */\n    }\n    l = strlen(p);\n    if (l == 0 || p[l-1] != '\\n')\n      luaL_addsize(&b, l);\n    else {\n      luaL_addsize(&b, l - 1);  /* do not include `eol' */\n      luaL_pushresult(&b);  /* close buffer */\n      return 1;  /* read at least an `eol' */\n    }\n  }\n}\n\n\nstatic int read_chars (lua_State *L, FILE *f, size_t n) {\n  size_t rlen;  /* how much to read */\n  size_t nr;  /* number of chars actually read */\n  luaL_Buffer b;\n  luaL_buffinit(L, &b);\n  rlen = LUAL_BUFFERSIZE;  /* try to read that much each time */\n  do {\n    char *p = luaL_prepbuffer(&b);\n    if (rlen > n) rlen = n;  /* cannot read more than asked */\n    nr = fread(p, sizeof(char), rlen, f);\n    luaL_addsize(&b, nr);\n    n -= nr;  /* still have to read `n' chars */\n  } while (n > 0 && nr == rlen);  /* until end of count or eof */\n  luaL_pushresult(&b);  /* close buffer */\n  return (n == 0 || lua_objlen(L, -1) > 0);\n}\n\n\nstatic int g_read (lua_State *L, FILE *f, int first) {\n  int nargs = lua_gettop(L) - 1;\n  int success;\n  int n;\n  clearerr(f);\n  if (nargs == 0) {  /* no arguments? */\n    success = read_line(L, f);\n    n = first+1;  /* to return 1 result */\n  }\n  else {  /* ensure stack space for all results and for auxlib's buffer */\n    luaL_checkstack(L, nargs+LUA_MINSTACK, \"too many arguments\");\n    success = 1;\n    for (n = first; nargs-- && success; n++) {\n      if (lua_type(L, n) == LUA_TNUMBER) {\n        size_t l = (size_t)lua_tointeger(L, n);\n        success = (l == 0) ? test_eof(L, f) : read_chars(L, f, l);\n      }\n      else {\n        const char *p = lua_tostring(L, n);\n        luaL_argcheck(L, p && p[0] == '*', n, \"invalid option\");\n        switch (p[1]) {\n          case 'n':  /* number */\n            success = read_number(L, f);\n            break;\n          case 'l':  /* line */\n            success = read_line(L, f);\n            break;\n          case 'a':  /* file */\n            read_chars(L, f, ~((size_t)0));  /* read MAX_SIZE_T chars */\n            success = 1; /* always success */\n            break;\n          default:\n            return luaL_argerror(L, n, \"invalid format\");\n        }\n      }\n    }\n  }\n  if (ferror(f))\n    return pushresult(L, 0, NULL);\n  if (!success) {\n    lua_pop(L, 1);  /* remove last result */\n    lua_pushnil(L);  /* push nil instead */\n  }\n  return n - first;\n}\n\n\nstatic int io_read (lua_State *L) {\n  return g_read(L, getiofile(L, IO_INPUT), 1);\n}\n\n\nstatic int f_read (lua_State *L) {\n  return g_read(L, tofile(L), 2);\n}\n\n\nstatic int io_readline (lua_State *L) {\n  FILE *f = *(FILE **)lua_touserdata(L, lua_upvalueindex(1));\n  int sucess;\n  if (f == NULL)  /* file is already closed? */\n    luaL_error(L, \"file is already closed\");\n  sucess = read_line(L, f);\n  if (ferror(f))\n    return luaL_error(L, \"%s\", strerror(errno));\n  if (sucess) return 1;\n  else {  /* EOF */\n    if (lua_toboolean(L, lua_upvalueindex(2))) {  /* generator created file? */\n      lua_settop(L, 0);\n      lua_pushvalue(L, lua_upvalueindex(1));\n      aux_close(L);  /* close it */\n    }\n    return 0;\n  }\n}\n\n/* }====================================================== */\n\n\nstatic int g_write (lua_State *L, FILE *f, int arg) {\n  int nargs = lua_gettop(L) - 1;\n  int status = 1;\n  for (; nargs--; arg++) {\n    if (lua_type(L, arg) == LUA_TNUMBER) {\n      /* optimization: could be done exactly as for strings */\n      status = status &&\n          fprintf(f, LUA_NUMBER_FMT, lua_tonumber(L, arg)) > 0;\n    }\n    else {\n      size_t l;\n      const char *s = luaL_checklstring(L, arg, &l);\n      status = status && (fwrite(s, sizeof(char), l, f) == l);\n    }\n  }\n  return pushresult(L, status, NULL);\n}\n\n\nstatic int io_write (lua_State *L) {\n  return g_write(L, getiofile(L, IO_OUTPUT), 1);\n}\n\n\nstatic int f_write (lua_State *L) {\n  return g_write(L, tofile(L), 2);\n}\n\n\nstatic int f_seek (lua_State *L) {\n  static const int mode[] = {SEEK_SET, SEEK_CUR, SEEK_END};\n  static const char *const modenames[] = {\"set\", \"cur\", \"end\", NULL};\n  FILE *f = tofile(L);\n  int op = luaL_checkoption(L, 2, \"cur\", modenames);\n  long offset = luaL_optlong(L, 3, 0);\n  op = fseek(f, offset, mode[op]);\n  if (op)\n    return pushresult(L, 0, NULL);  /* error */\n  else {\n    lua_pushinteger(L, ftell(f));\n    return 1;\n  }\n}\n\n\nstatic int f_setvbuf (lua_State *L) {\n  static const int mode[] = {_IONBF, _IOFBF, _IOLBF};\n  static const char *const modenames[] = {\"no\", \"full\", \"line\", NULL};\n  FILE *f = tofile(L);\n  int op = luaL_checkoption(L, 2, NULL, modenames);\n  lua_Integer sz = luaL_optinteger(L, 3, LUAL_BUFFERSIZE);\n  int res = setvbuf(f, NULL, mode[op], sz);\n  return pushresult(L, res == 0, NULL);\n}\n\n\n\nstatic int io_flush (lua_State *L) {\n  return pushresult(L, fflush(getiofile(L, IO_OUTPUT)) == 0, NULL);\n}\n\n\nstatic int f_flush (lua_State *L) {\n  return pushresult(L, fflush(tofile(L)) == 0, NULL);\n}\n\n\nstatic const luaL_Reg iolib[] = {\n  {\"close\", io_close},\n  {\"flush\", io_flush},\n  {\"input\", io_input},\n  {\"lines\", io_lines},\n  {\"open\", io_open},\n  {\"output\", io_output},\n  {\"popen\", io_popen},\n  {\"read\", io_read},\n  {\"tmpfile\", io_tmpfile},\n  {\"type\", io_type},\n  {\"write\", io_write},\n  {NULL, NULL}\n};\n\n\nstatic const luaL_Reg flib[] = {\n  {\"close\", io_close},\n  {\"flush\", f_flush},\n  {\"lines\", f_lines},\n  {\"read\", f_read},\n  {\"seek\", f_seek},\n  {\"setvbuf\", f_setvbuf},\n  {\"write\", f_write},\n  {\"__gc\", io_gc},\n  {\"__tostring\", io_tostring},\n  {NULL, NULL}\n};\n\n\nstatic void createmeta (lua_State *L) {\n  luaL_newmetatable(L, LUA_FILEHANDLE);  /* create metatable for file handles */\n  lua_pushvalue(L, -1);  /* push metatable */\n  lua_setfield(L, -2, \"__index\");  /* metatable.__index = metatable */\n  luaL_register(L, NULL, flib);  /* file methods */\n}\n\n\nstatic void createstdfile (lua_State *L, FILE *f, int k, const char *fname) {\n  *newfile(L) = f;\n  if (k > 0) {\n    lua_pushvalue(L, -1);\n    lua_rawseti(L, LUA_ENVIRONINDEX, k);\n  }\n  lua_pushvalue(L, -2);  /* copy environment */\n  lua_setfenv(L, -2);  /* set it */\n  lua_setfield(L, -3, fname);\n}\n\n\nstatic void newfenv (lua_State *L, lua_CFunction cls) {\n  lua_createtable(L, 0, 1);\n  lua_pushcfunction(L, cls);\n  lua_setfield(L, -2, \"__close\");\n}\n\n\nLUALIB_API int luaopen_io (lua_State *L) {\n  createmeta(L);\n  /* create (private) environment (with fields IO_INPUT, IO_OUTPUT, __close) */\n  newfenv(L, io_fclose);\n  lua_replace(L, LUA_ENVIRONINDEX);\n  /* open library */\n  luaL_register(L, LUA_IOLIBNAME, iolib);\n  /* create (and set) default files */\n  newfenv(L, io_noclose);  /* close function for default files */\n  createstdfile(L, stdin, IO_INPUT, \"stdin\");\n  createstdfile(L, stdout, IO_OUTPUT, \"stdout\");\n  createstdfile(L, stderr, 0, \"stderr\");\n  lua_pop(L, 1);  /* pop environment for default files */\n  lua_getfield(L, -1, \"popen\");\n  newfenv(L, io_pclose);  /* create environment for 'popen' */\n  lua_setfenv(L, -2);  /* set fenv for 'popen' */\n  lua_pop(L, 1);  /* pop 'popen' */\n  return 1;\n}\n\n"
  },
  {
    "path": "deps/lua/src/llex.c",
    "content": "/*\n** $Id: llex.c,v 2.20.1.2 2009/11/23 14:58:22 roberto Exp $\n** Lexical Analyzer\n** See Copyright Notice in lua.h\n*/\n\n\n#include <ctype.h>\n#include <locale.h>\n#include <string.h>\n\n#define llex_c\n#define LUA_CORE\n\n#include \"lua.h\"\n\n#include \"ldo.h\"\n#include \"llex.h\"\n#include \"lobject.h\"\n#include \"lparser.h\"\n#include \"lstate.h\"\n#include \"lstring.h\"\n#include \"ltable.h\"\n#include \"lzio.h\"\n\n\n\n#define next(ls) (ls->current = zgetc(ls->z))\n\n\n\n\n#define currIsNewline(ls)\t(ls->current == '\\n' || ls->current == '\\r')\n\n\n/* ORDER RESERVED */\nconst char *const luaX_tokens [] = {\n    \"and\", \"break\", \"do\", \"else\", \"elseif\",\n    \"end\", \"false\", \"for\", \"function\", \"if\",\n    \"in\", \"local\", \"nil\", \"not\", \"or\", \"repeat\",\n    \"return\", \"then\", \"true\", \"until\", \"while\",\n    \"..\", \"...\", \"==\", \">=\", \"<=\", \"~=\",\n    \"<number>\", \"<name>\", \"<string>\", \"<eof>\",\n    NULL\n};\n\n\n#define save_and_next(ls) (save(ls, ls->current), next(ls))\n\n\nstatic void save (LexState *ls, int c) {\n  Mbuffer *b = ls->buff;\n  if (b->n + 1 > b->buffsize) {\n    size_t newsize;\n    if (b->buffsize >= MAX_SIZET/2)\n      luaX_lexerror(ls, \"lexical element too long\", 0);\n    newsize = b->buffsize * 2;\n    luaZ_resizebuffer(ls->L, b, newsize);\n  }\n  b->buffer[b->n++] = cast(char, c);\n}\n\n\nvoid luaX_init (lua_State *L) {\n  int i;\n  for (i=0; i<NUM_RESERVED; i++) {\n    TString *ts = luaS_new(L, luaX_tokens[i]);\n    luaS_fix(ts);  /* reserved words are never collected */\n    lua_assert(strlen(luaX_tokens[i])+1 <= TOKEN_LEN);\n    ts->tsv.reserved = cast_byte(i+1);  /* reserved word */\n  }\n}\n\n\n#define MAXSRC          80\n\n\nconst char *luaX_token2str (LexState *ls, int token) {\n  if (token < FIRST_RESERVED) {\n    lua_assert(token == cast(unsigned char, token));\n    return (iscntrl(token)) ? luaO_pushfstring(ls->L, \"char(%d)\", token) :\n                              luaO_pushfstring(ls->L, \"%c\", token);\n  }\n  else\n    return luaX_tokens[token-FIRST_RESERVED];\n}\n\n\nstatic const char *txtToken (LexState *ls, int token) {\n  switch (token) {\n    case TK_NAME:\n    case TK_STRING:\n    case TK_NUMBER:\n      save(ls, '\\0');\n      return luaZ_buffer(ls->buff);\n    default:\n      return luaX_token2str(ls, token);\n  }\n}\n\n\nvoid luaX_lexerror (LexState *ls, const char *msg, int token) {\n  char buff[MAXSRC];\n  luaO_chunkid(buff, getstr(ls->source), MAXSRC);\n  msg = luaO_pushfstring(ls->L, \"%s:%d: %s\", buff, ls->linenumber, msg);\n  if (token)\n    luaO_pushfstring(ls->L, \"%s near \" LUA_QS, msg, txtToken(ls, token));\n  luaD_throw(ls->L, LUA_ERRSYNTAX);\n}\n\n\nvoid luaX_syntaxerror (LexState *ls, const char *msg) {\n  luaX_lexerror(ls, msg, ls->t.token);\n}\n\n\nTString *luaX_newstring (LexState *ls, const char *str, size_t l) {\n  lua_State *L = ls->L;\n  TString *ts = luaS_newlstr(L, str, l);\n  TValue *o = luaH_setstr(L, ls->fs->h, ts);  /* entry for `str' */\n  if (ttisnil(o)) {\n    setbvalue(o, 1);  /* make sure `str' will not be collected */\n    luaC_checkGC(L);\n  }\n  return ts;\n}\n\n\nstatic void inclinenumber (LexState *ls) {\n  int old = ls->current;\n  lua_assert(currIsNewline(ls));\n  next(ls);  /* skip `\\n' or `\\r' */\n  if (currIsNewline(ls) && ls->current != old)\n    next(ls);  /* skip `\\n\\r' or `\\r\\n' */\n  if (++ls->linenumber >= MAX_INT)\n    luaX_syntaxerror(ls, \"chunk has too many lines\");\n}\n\n\nvoid luaX_setinput (lua_State *L, LexState *ls, ZIO *z, TString *source) {\n  ls->decpoint = '.';\n  ls->L = L;\n  ls->lookahead.token = TK_EOS;  /* no look-ahead token */\n  ls->z = z;\n  ls->fs = NULL;\n  ls->linenumber = 1;\n  ls->lastline = 1;\n  ls->source = source;\n  luaZ_resizebuffer(ls->L, ls->buff, LUA_MINBUFFER);  /* initialize buffer */\n  next(ls);  /* read first char */\n}\n\n\n\n/*\n** =======================================================\n** LEXICAL ANALYZER\n** =======================================================\n*/\n\n\n\nstatic int check_next (LexState *ls, const char *set) {\n  if (!strchr(set, ls->current))\n    return 0;\n  save_and_next(ls);\n  return 1;\n}\n\n\nstatic void buffreplace (LexState *ls, char from, char to) {\n  size_t n = luaZ_bufflen(ls->buff);\n  char *p = luaZ_buffer(ls->buff);\n  while (n--)\n    if (p[n] == from) p[n] = to;\n}\n\n\nstatic void trydecpoint (LexState *ls, SemInfo *seminfo) {\n  /* format error: try to update decimal point separator */\n  struct lconv *cv = localeconv();\n  char old = ls->decpoint;\n  ls->decpoint = (cv ? cv->decimal_point[0] : '.');\n  buffreplace(ls, old, ls->decpoint);  /* try updated decimal separator */\n  if (!luaO_str2d(luaZ_buffer(ls->buff), &seminfo->r)) {\n    /* format error with correct decimal point: no more options */\n    buffreplace(ls, ls->decpoint, '.');  /* undo change (for error message) */\n    luaX_lexerror(ls, \"malformed number\", TK_NUMBER);\n  }\n}\n\n\n/* LUA_NUMBER */\nstatic void read_numeral (LexState *ls, SemInfo *seminfo) {\n  lua_assert(isdigit(ls->current));\n  do {\n    save_and_next(ls);\n  } while (isdigit(ls->current) || ls->current == '.');\n  if (check_next(ls, \"Ee\"))  /* `E'? */\n    check_next(ls, \"+-\");  /* optional exponent sign */\n  while (isalnum(ls->current) || ls->current == '_')\n    save_and_next(ls);\n  save(ls, '\\0');\n  buffreplace(ls, '.', ls->decpoint);  /* follow locale for decimal point */\n  if (!luaO_str2d(luaZ_buffer(ls->buff), &seminfo->r))  /* format error? */\n    trydecpoint(ls, seminfo); /* try to update decimal point separator */\n}\n\n\nstatic int skip_sep (LexState *ls) {\n  int count = 0;\n  int s = ls->current;\n  lua_assert(s == '[' || s == ']');\n  save_and_next(ls);\n  while (ls->current == '=') {\n    save_and_next(ls);\n    count++;\n  }\n  return (ls->current == s) ? count : (-count) - 1;\n}\n\n\nstatic void read_long_string (LexState *ls, SemInfo *seminfo, int sep) {\n  int cont = 0;\n  (void)(cont);  /* avoid warnings when `cont' is not used */\n  save_and_next(ls);  /* skip 2nd `[' */\n  if (currIsNewline(ls))  /* string starts with a newline? */\n    inclinenumber(ls);  /* skip it */\n  for (;;) {\n    switch (ls->current) {\n      case EOZ:\n        luaX_lexerror(ls, (seminfo) ? \"unfinished long string\" :\n                                   \"unfinished long comment\", TK_EOS);\n        break;  /* to avoid warnings */\n#if defined(LUA_COMPAT_LSTR)\n      case '[': {\n        if (skip_sep(ls) == sep) {\n          save_and_next(ls);  /* skip 2nd `[' */\n          cont++;\n#if LUA_COMPAT_LSTR == 1\n          if (sep == 0)\n            luaX_lexerror(ls, \"nesting of [[...]] is deprecated\", '[');\n#endif\n        }\n        break;\n      }\n#endif\n      case ']': {\n        if (skip_sep(ls) == sep) {\n          save_and_next(ls);  /* skip 2nd `]' */\n#if defined(LUA_COMPAT_LSTR) && LUA_COMPAT_LSTR == 2\n          cont--;\n          if (sep == 0 && cont >= 0) break;\n#endif\n          goto endloop;\n        }\n        break;\n      }\n      case '\\n':\n      case '\\r': {\n        save(ls, '\\n');\n        inclinenumber(ls);\n        if (!seminfo) luaZ_resetbuffer(ls->buff);  /* avoid wasting space */\n        break;\n      }\n      default: {\n        if (seminfo) save_and_next(ls);\n        else next(ls);\n      }\n    }\n  } endloop:\n  if (seminfo)\n    seminfo->ts = luaX_newstring(ls, luaZ_buffer(ls->buff) + (2 + sep),\n                                     luaZ_bufflen(ls->buff) - 2*(2 + sep));\n}\n\n\nstatic void read_string (LexState *ls, int del, SemInfo *seminfo) {\n  save_and_next(ls);\n  while (ls->current != del) {\n    switch (ls->current) {\n      case EOZ:\n        luaX_lexerror(ls, \"unfinished string\", TK_EOS);\n        continue;  /* to avoid warnings */\n      case '\\n':\n      case '\\r':\n        luaX_lexerror(ls, \"unfinished string\", TK_STRING);\n        continue;  /* to avoid warnings */\n      case '\\\\': {\n        int c;\n        next(ls);  /* do not save the `\\' */\n        switch (ls->current) {\n          case 'a': c = '\\a'; break;\n          case 'b': c = '\\b'; break;\n          case 'f': c = '\\f'; break;\n          case 'n': c = '\\n'; break;\n          case 'r': c = '\\r'; break;\n          case 't': c = '\\t'; break;\n          case 'v': c = '\\v'; break;\n          case '\\n':  /* go through */\n          case '\\r': save(ls, '\\n'); inclinenumber(ls); continue;\n          case EOZ: continue;  /* will raise an error next loop */\n          default: {\n            if (!isdigit(ls->current))\n              save_and_next(ls);  /* handles \\\\, \\\", \\', and \\? */\n            else {  /* \\xxx */\n              int i = 0;\n              c = 0;\n              do {\n                c = 10*c + (ls->current-'0');\n                next(ls);\n              } while (++i<3 && isdigit(ls->current));\n              if (c > UCHAR_MAX)\n                luaX_lexerror(ls, \"escape sequence too large\", TK_STRING);\n              save(ls, c);\n            }\n            continue;\n          }\n        }\n        save(ls, c);\n        next(ls);\n        continue;\n      }\n      default:\n        save_and_next(ls);\n    }\n  }\n  save_and_next(ls);  /* skip delimiter */\n  seminfo->ts = luaX_newstring(ls, luaZ_buffer(ls->buff) + 1,\n                                   luaZ_bufflen(ls->buff) - 2);\n}\n\n\nstatic int llex (LexState *ls, SemInfo *seminfo) {\n  luaZ_resetbuffer(ls->buff);\n  for (;;) {\n    switch (ls->current) {\n      case '\\n':\n      case '\\r': {\n        inclinenumber(ls);\n        continue;\n      }\n      case '-': {\n        next(ls);\n        if (ls->current != '-') return '-';\n        /* else is a comment */\n        next(ls);\n        if (ls->current == '[') {\n          int sep = skip_sep(ls);\n          luaZ_resetbuffer(ls->buff);  /* `skip_sep' may dirty the buffer */\n          if (sep >= 0) {\n            read_long_string(ls, NULL, sep);  /* long comment */\n            luaZ_resetbuffer(ls->buff);\n            continue;\n          }\n        }\n        /* else short comment */\n        while (!currIsNewline(ls) && ls->current != EOZ)\n          next(ls);\n        continue;\n      }\n      case '[': {\n        int sep = skip_sep(ls);\n        if (sep >= 0) {\n          read_long_string(ls, seminfo, sep);\n          return TK_STRING;\n        }\n        else if (sep == -1) return '[';\n        else luaX_lexerror(ls, \"invalid long string delimiter\", TK_STRING);\n      }\n      case '=': {\n        next(ls);\n        if (ls->current != '=') return '=';\n        else { next(ls); return TK_EQ; }\n      }\n      case '<': {\n        next(ls);\n        if (ls->current != '=') return '<';\n        else { next(ls); return TK_LE; }\n      }\n      case '>': {\n        next(ls);\n        if (ls->current != '=') return '>';\n        else { next(ls); return TK_GE; }\n      }\n      case '~': {\n        next(ls);\n        if (ls->current != '=') return '~';\n        else { next(ls); return TK_NE; }\n      }\n      case '\"':\n      case '\\'': {\n        read_string(ls, ls->current, seminfo);\n        return TK_STRING;\n      }\n      case '.': {\n        save_and_next(ls);\n        if (check_next(ls, \".\")) {\n          if (check_next(ls, \".\"))\n            return TK_DOTS;   /* ... */\n          else return TK_CONCAT;   /* .. */\n        }\n        else if (!isdigit(ls->current)) return '.';\n        else {\n          read_numeral(ls, seminfo);\n          return TK_NUMBER;\n        }\n      }\n      case EOZ: {\n        return TK_EOS;\n      }\n      default: {\n        if (isspace(ls->current)) {\n          lua_assert(!currIsNewline(ls));\n          next(ls);\n          continue;\n        }\n        else if (isdigit(ls->current)) {\n          read_numeral(ls, seminfo);\n          return TK_NUMBER;\n        }\n        else if (isalpha(ls->current) || ls->current == '_') {\n          /* identifier or reserved word */\n          TString *ts;\n          do {\n            save_and_next(ls);\n          } while (isalnum(ls->current) || ls->current == '_');\n          ts = luaX_newstring(ls, luaZ_buffer(ls->buff),\n                                  luaZ_bufflen(ls->buff));\n          if (ts->tsv.reserved > 0)  /* reserved word? */\n            return ts->tsv.reserved - 1 + FIRST_RESERVED;\n          else {\n            seminfo->ts = ts;\n            return TK_NAME;\n          }\n        }\n        else {\n          int c = ls->current;\n          next(ls);\n          return c;  /* single-char tokens (+ - / ...) */\n        }\n      }\n    }\n  }\n}\n\n\nvoid luaX_next (LexState *ls) {\n  ls->lastline = ls->linenumber;\n  if (ls->lookahead.token != TK_EOS) {  /* is there a look-ahead token? */\n    ls->t = ls->lookahead;  /* use this one */\n    ls->lookahead.token = TK_EOS;  /* and discharge it */\n  }\n  else\n    ls->t.token = llex(ls, &ls->t.seminfo);  /* read next token */\n}\n\n\nvoid luaX_lookahead (LexState *ls) {\n  lua_assert(ls->lookahead.token == TK_EOS);\n  ls->lookahead.token = llex(ls, &ls->lookahead.seminfo);\n}\n\n"
  },
  {
    "path": "deps/lua/src/llex.h",
    "content": "/*\n** $Id: llex.h,v 1.58.1.1 2007/12/27 13:02:25 roberto Exp $\n** Lexical Analyzer\n** See Copyright Notice in lua.h\n*/\n\n#ifndef llex_h\n#define llex_h\n\n#include \"lobject.h\"\n#include \"lzio.h\"\n\n\n#define FIRST_RESERVED\t257\n\n/* maximum length of a reserved word */\n#define TOKEN_LEN\t(sizeof(\"function\")/sizeof(char))\n\n\n/*\n* WARNING: if you change the order of this enumeration,\n* grep \"ORDER RESERVED\"\n*/\nenum RESERVED {\n  /* terminal symbols denoted by reserved words */\n  TK_AND = FIRST_RESERVED, TK_BREAK,\n  TK_DO, TK_ELSE, TK_ELSEIF, TK_END, TK_FALSE, TK_FOR, TK_FUNCTION,\n  TK_IF, TK_IN, TK_LOCAL, TK_NIL, TK_NOT, TK_OR, TK_REPEAT,\n  TK_RETURN, TK_THEN, TK_TRUE, TK_UNTIL, TK_WHILE,\n  /* other terminal symbols */\n  TK_CONCAT, TK_DOTS, TK_EQ, TK_GE, TK_LE, TK_NE, TK_NUMBER,\n  TK_NAME, TK_STRING, TK_EOS\n};\n\n/* number of reserved words */\n#define NUM_RESERVED\t(cast(int, TK_WHILE-FIRST_RESERVED+1))\n\n\n/* array with token `names' */\nLUAI_DATA const char *const luaX_tokens [];\n\n\ntypedef union {\n  lua_Number r;\n  TString *ts;\n} SemInfo;  /* semantics information */\n\n\ntypedef struct Token {\n  int token;\n  SemInfo seminfo;\n} Token;\n\n\ntypedef struct LexState {\n  int current;  /* current character (charint) */\n  int linenumber;  /* input line counter */\n  int lastline;  /* line of last token `consumed' */\n  Token t;  /* current token */\n  Token lookahead;  /* look ahead token */\n  struct FuncState *fs;  /* `FuncState' is private to the parser */\n  struct lua_State *L;\n  ZIO *z;  /* input stream */\n  Mbuffer *buff;  /* buffer for tokens */\n  TString *source;  /* current source name */\n  char decpoint;  /* locale decimal point */\n} LexState;\n\n\nLUAI_FUNC void luaX_init (lua_State *L);\nLUAI_FUNC void luaX_setinput (lua_State *L, LexState *ls, ZIO *z,\n                              TString *source);\nLUAI_FUNC TString *luaX_newstring (LexState *ls, const char *str, size_t l);\nLUAI_FUNC void luaX_next (LexState *ls);\nLUAI_FUNC void luaX_lookahead (LexState *ls);\nLUAI_FUNC void luaX_lexerror (LexState *ls, const char *msg, int token);\nLUAI_FUNC void luaX_syntaxerror (LexState *ls, const char *s);\nLUAI_FUNC const char *luaX_token2str (LexState *ls, int token);\n\n\n#endif\n"
  },
  {
    "path": "deps/lua/src/llimits.h",
    "content": "/*\n** $Id: llimits.h,v 1.69.1.1 2007/12/27 13:02:25 roberto Exp $\n** Limits, basic types, and some other `installation-dependent' definitions\n** See Copyright Notice in lua.h\n*/\n\n#ifndef llimits_h\n#define llimits_h\n\n\n#include <limits.h>\n#include <stddef.h>\n\n\n#include \"lua.h\"\n\n\ntypedef LUAI_UINT32 lu_int32;\n\ntypedef LUAI_UMEM lu_mem;\n\ntypedef LUAI_MEM l_mem;\n\n\n\n/* chars used as small naturals (so that `char' is reserved for characters) */\ntypedef unsigned char lu_byte;\n\n\n#define MAX_SIZET\t((size_t)(~(size_t)0)-2)\n\n#define MAX_LUMEM\t((lu_mem)(~(lu_mem)0)-2)\n\n\n#define MAX_INT (INT_MAX-2)  /* maximum value of an int (-2 for safety) */\n\n/*\n** conversion of pointer to integer\n** this is for hashing only; there is no problem if the integer\n** cannot hold the whole pointer value\n*/\n#define IntPoint(p)  ((unsigned int)(lu_mem)(p))\n\n\n\n/* type to ensure maximum alignment */\ntypedef LUAI_USER_ALIGNMENT_T L_Umaxalign;\n\n\n/* result of a `usual argument conversion' over lua_Number */\ntypedef LUAI_UACNUMBER l_uacNumber;\n\n\n/* internal assertions for in-house debugging */\n#ifdef lua_assert\n\n#define check_exp(c,e)\t\t(lua_assert(c), (e))\n#define api_check(l,e)\t\tlua_assert(e)\n\n#else\n\n#define lua_assert(c)\t\t((void)0)\n#define check_exp(c,e)\t\t(e)\n#define api_check\t\tluai_apicheck\n\n#endif\n\n\n#ifndef UNUSED\n#define UNUSED(x)\t((void)(x))\t/* to avoid warnings */\n#endif\n\n\n#ifndef cast\n#define cast(t, exp)\t((t)(exp))\n#endif\n\n#define cast_byte(i)\tcast(lu_byte, (i))\n#define cast_num(i)\tcast(lua_Number, (i))\n#define cast_int(i)\tcast(int, (i))\n\n\n\n/*\n** type for virtual-machine instructions\n** must be an unsigned with (at least) 4 bytes (see details in lopcodes.h)\n*/\ntypedef lu_int32 Instruction;\n\n\n\n/* maximum stack for a Lua function */\n#define MAXSTACK\t250\n\n\n\n/* minimum size for the string table (must be power of 2) */\n#ifndef MINSTRTABSIZE\n#define MINSTRTABSIZE\t32\n#endif\n\n\n/* minimum size for string buffer */\n#ifndef LUA_MINBUFFER\n#define LUA_MINBUFFER\t32\n#endif\n\n\n#ifndef lua_lock\n#define lua_lock(L)     ((void) 0) \n#define lua_unlock(L)   ((void) 0)\n#endif\n\n#ifndef luai_threadyield\n#define luai_threadyield(L)     {lua_unlock(L); lua_lock(L);}\n#endif\n\n\n/*\n** macro to control inclusion of some hard tests on stack reallocation\n*/ \n#ifndef HARDSTACKTESTS\n#define condhardstacktests(x)\t((void)0)\n#else\n#define condhardstacktests(x)\tx\n#endif\n\n#endif\n"
  },
  {
    "path": "deps/lua/src/lmathlib.c",
    "content": "/*\n** $Id: lmathlib.c,v 1.67.1.1 2007/12/27 13:02:25 roberto Exp $\n** Standard mathematical library\n** See Copyright Notice in lua.h\n*/\n\n\n#include <stdlib.h>\n#include <math.h>\n\n#define lmathlib_c\n#define LUA_LIB\n\n#include \"lua.h\"\n\n#include \"lauxlib.h\"\n#include \"lualib.h\"\n\n\n#undef PI\n#define PI (3.14159265358979323846)\n#define RADIANS_PER_DEGREE (PI/180.0)\n\n\n\nstatic int math_abs (lua_State *L) {\n  lua_pushnumber(L, fabs(luaL_checknumber(L, 1)));\n  return 1;\n}\n\nstatic int math_sin (lua_State *L) {\n  lua_pushnumber(L, sin(luaL_checknumber(L, 1)));\n  return 1;\n}\n\nstatic int math_sinh (lua_State *L) {\n  lua_pushnumber(L, sinh(luaL_checknumber(L, 1)));\n  return 1;\n}\n\nstatic int math_cos (lua_State *L) {\n  lua_pushnumber(L, cos(luaL_checknumber(L, 1)));\n  return 1;\n}\n\nstatic int math_cosh (lua_State *L) {\n  lua_pushnumber(L, cosh(luaL_checknumber(L, 1)));\n  return 1;\n}\n\nstatic int math_tan (lua_State *L) {\n  lua_pushnumber(L, tan(luaL_checknumber(L, 1)));\n  return 1;\n}\n\nstatic int math_tanh (lua_State *L) {\n  lua_pushnumber(L, tanh(luaL_checknumber(L, 1)));\n  return 1;\n}\n\nstatic int math_asin (lua_State *L) {\n  lua_pushnumber(L, asin(luaL_checknumber(L, 1)));\n  return 1;\n}\n\nstatic int math_acos (lua_State *L) {\n  lua_pushnumber(L, acos(luaL_checknumber(L, 1)));\n  return 1;\n}\n\nstatic int math_atan (lua_State *L) {\n  lua_pushnumber(L, atan(luaL_checknumber(L, 1)));\n  return 1;\n}\n\nstatic int math_atan2 (lua_State *L) {\n  lua_pushnumber(L, atan2(luaL_checknumber(L, 1), luaL_checknumber(L, 2)));\n  return 1;\n}\n\nstatic int math_ceil (lua_State *L) {\n  lua_pushnumber(L, ceil(luaL_checknumber(L, 1)));\n  return 1;\n}\n\nstatic int math_floor (lua_State *L) {\n  lua_pushnumber(L, floor(luaL_checknumber(L, 1)));\n  return 1;\n}\n\nstatic int math_fmod (lua_State *L) {\n  lua_pushnumber(L, fmod(luaL_checknumber(L, 1), luaL_checknumber(L, 2)));\n  return 1;\n}\n\nstatic int math_modf (lua_State *L) {\n  double ip;\n  double fp = modf(luaL_checknumber(L, 1), &ip);\n  lua_pushnumber(L, ip);\n  lua_pushnumber(L, fp);\n  return 2;\n}\n\nstatic int math_sqrt (lua_State *L) {\n  lua_pushnumber(L, sqrt(luaL_checknumber(L, 1)));\n  return 1;\n}\n\nstatic int math_pow (lua_State *L) {\n  lua_pushnumber(L, pow(luaL_checknumber(L, 1), luaL_checknumber(L, 2)));\n  return 1;\n}\n\nstatic int math_log (lua_State *L) {\n  lua_pushnumber(L, log(luaL_checknumber(L, 1)));\n  return 1;\n}\n\nstatic int math_log10 (lua_State *L) {\n  lua_pushnumber(L, log10(luaL_checknumber(L, 1)));\n  return 1;\n}\n\nstatic int math_exp (lua_State *L) {\n  lua_pushnumber(L, exp(luaL_checknumber(L, 1)));\n  return 1;\n}\n\nstatic int math_deg (lua_State *L) {\n  lua_pushnumber(L, luaL_checknumber(L, 1)/RADIANS_PER_DEGREE);\n  return 1;\n}\n\nstatic int math_rad (lua_State *L) {\n  lua_pushnumber(L, luaL_checknumber(L, 1)*RADIANS_PER_DEGREE);\n  return 1;\n}\n\nstatic int math_frexp (lua_State *L) {\n  int e;\n  lua_pushnumber(L, frexp(luaL_checknumber(L, 1), &e));\n  lua_pushinteger(L, e);\n  return 2;\n}\n\nstatic int math_ldexp (lua_State *L) {\n  lua_pushnumber(L, ldexp(luaL_checknumber(L, 1), luaL_checkint(L, 2)));\n  return 1;\n}\n\n\n\nstatic int math_min (lua_State *L) {\n  int n = lua_gettop(L);  /* number of arguments */\n  lua_Number dmin = luaL_checknumber(L, 1);\n  int i;\n  for (i=2; i<=n; i++) {\n    lua_Number d = luaL_checknumber(L, i);\n    if (d < dmin)\n      dmin = d;\n  }\n  lua_pushnumber(L, dmin);\n  return 1;\n}\n\n\nstatic int math_max (lua_State *L) {\n  int n = lua_gettop(L);  /* number of arguments */\n  lua_Number dmax = luaL_checknumber(L, 1);\n  int i;\n  for (i=2; i<=n; i++) {\n    lua_Number d = luaL_checknumber(L, i);\n    if (d > dmax)\n      dmax = d;\n  }\n  lua_pushnumber(L, dmax);\n  return 1;\n}\n\n\nstatic int math_random (lua_State *L) {\n  /* the `%' avoids the (rare) case of r==1, and is needed also because on\n     some systems (SunOS!) `rand()' may return a value larger than RAND_MAX */\n  lua_Number r = (lua_Number)(rand()%RAND_MAX) / (lua_Number)RAND_MAX;\n  switch (lua_gettop(L)) {  /* check number of arguments */\n    case 0: {  /* no arguments */\n      lua_pushnumber(L, r);  /* Number between 0 and 1 */\n      break;\n    }\n    case 1: {  /* only upper limit */\n      int u = luaL_checkint(L, 1);\n      luaL_argcheck(L, 1<=u, 1, \"interval is empty\");\n      lua_pushnumber(L, floor(r*u)+1);  /* int between 1 and `u' */\n      break;\n    }\n    case 2: {  /* lower and upper limits */\n      int l = luaL_checkint(L, 1);\n      int u = luaL_checkint(L, 2);\n      luaL_argcheck(L, l<=u, 2, \"interval is empty\");\n      lua_pushnumber(L, floor(r*(u-l+1))+l);  /* int between `l' and `u' */\n      break;\n    }\n    default: return luaL_error(L, \"wrong number of arguments\");\n  }\n  return 1;\n}\n\n\nstatic int math_randomseed (lua_State *L) {\n  srand(luaL_checkint(L, 1));\n  return 0;\n}\n\n\nstatic const luaL_Reg mathlib[] = {\n  {\"abs\",   math_abs},\n  {\"acos\",  math_acos},\n  {\"asin\",  math_asin},\n  {\"atan2\", math_atan2},\n  {\"atan\",  math_atan},\n  {\"ceil\",  math_ceil},\n  {\"cosh\",   math_cosh},\n  {\"cos\",   math_cos},\n  {\"deg\",   math_deg},\n  {\"exp\",   math_exp},\n  {\"floor\", math_floor},\n  {\"fmod\",   math_fmod},\n  {\"frexp\", math_frexp},\n  {\"ldexp\", math_ldexp},\n  {\"log10\", math_log10},\n  {\"log\",   math_log},\n  {\"max\",   math_max},\n  {\"min\",   math_min},\n  {\"modf\",   math_modf},\n  {\"pow\",   math_pow},\n  {\"rad\",   math_rad},\n  {\"random\",     math_random},\n  {\"randomseed\", math_randomseed},\n  {\"sinh\",   math_sinh},\n  {\"sin\",   math_sin},\n  {\"sqrt\",  math_sqrt},\n  {\"tanh\",   math_tanh},\n  {\"tan\",   math_tan},\n  {NULL, NULL}\n};\n\n\n/*\n** Open math library\n*/\nLUALIB_API int luaopen_math (lua_State *L) {\n  luaL_register(L, LUA_MATHLIBNAME, mathlib);\n  lua_pushnumber(L, PI);\n  lua_setfield(L, -2, \"pi\");\n  lua_pushnumber(L, HUGE_VAL);\n  lua_setfield(L, -2, \"huge\");\n#if defined(LUA_COMPAT_MOD)\n  lua_getfield(L, -1, \"fmod\");\n  lua_setfield(L, -2, \"mod\");\n#endif\n  return 1;\n}\n\n"
  },
  {
    "path": "deps/lua/src/lmem.c",
    "content": "/*\n** $Id: lmem.c,v 1.70.1.1 2007/12/27 13:02:25 roberto Exp $\n** Interface to Memory Manager\n** See Copyright Notice in lua.h\n*/\n\n\n#include <stddef.h>\n\n#define lmem_c\n#define LUA_CORE\n\n#include \"lua.h\"\n\n#include \"ldebug.h\"\n#include \"ldo.h\"\n#include \"lmem.h\"\n#include \"lobject.h\"\n#include \"lstate.h\"\n\n\n\n/*\n** About the realloc function:\n** void * frealloc (void *ud, void *ptr, size_t osize, size_t nsize);\n** (`osize' is the old size, `nsize' is the new size)\n**\n** Lua ensures that (ptr == NULL) iff (osize == 0).\n**\n** * frealloc(ud, NULL, 0, x) creates a new block of size `x'\n**\n** * frealloc(ud, p, x, 0) frees the block `p'\n** (in this specific case, frealloc must return NULL).\n** particularly, frealloc(ud, NULL, 0, 0) does nothing\n** (which is equivalent to free(NULL) in ANSI C)\n**\n** frealloc returns NULL if it cannot create or reallocate the area\n** (any reallocation to an equal or smaller size cannot fail!)\n*/\n\n\n\n#define MINSIZEARRAY\t4\n\n\nvoid *luaM_growaux_ (lua_State *L, void *block, int *size, size_t size_elems,\n                     int limit, const char *errormsg) {\n  void *newblock;\n  int newsize;\n  if (*size >= limit/2) {  /* cannot double it? */\n    if (*size >= limit)  /* cannot grow even a little? */\n      luaG_runerror(L, errormsg);\n    newsize = limit;  /* still have at least one free place */\n  }\n  else {\n    newsize = (*size)*2;\n    if (newsize < MINSIZEARRAY)\n      newsize = MINSIZEARRAY;  /* minimum size */\n  }\n  newblock = luaM_reallocv(L, block, *size, newsize, size_elems);\n  *size = newsize;  /* update only when everything else is OK */\n  return newblock;\n}\n\n\nvoid *luaM_toobig (lua_State *L) {\n  luaG_runerror(L, \"memory allocation error: block too big\");\n  return NULL;  /* to avoid warnings */\n}\n\n\n\n/*\n** generic allocation routine.\n*/\nvoid *luaM_realloc_ (lua_State *L, void *block, size_t osize, size_t nsize) {\n  global_State *g = G(L);\n  lua_assert((osize == 0) == (block == NULL));\n  block = (*g->frealloc)(g->ud, block, osize, nsize);\n  if (block == NULL && nsize > 0)\n    luaD_throw(L, LUA_ERRMEM);\n  lua_assert((nsize == 0) == (block == NULL));\n  g->totalbytes = (g->totalbytes - osize) + nsize;\n  return block;\n}\n\n"
  },
  {
    "path": "deps/lua/src/lmem.h",
    "content": "/*\n** $Id: lmem.h,v 1.31.1.1 2007/12/27 13:02:25 roberto Exp $\n** Interface to Memory Manager\n** See Copyright Notice in lua.h\n*/\n\n#ifndef lmem_h\n#define lmem_h\n\n\n#include <stddef.h>\n\n#include \"llimits.h\"\n#include \"lua.h\"\n\n#define MEMERRMSG\t\"not enough memory\"\n\n\n#define luaM_reallocv(L,b,on,n,e) \\\n\t((cast(size_t, (n)+1) <= MAX_SIZET/(e)) ?  /* +1 to avoid warnings */ \\\n\t\tluaM_realloc_(L, (b), (on)*(e), (n)*(e)) : \\\n\t\tluaM_toobig(L))\n\n#define luaM_freemem(L, b, s)\tluaM_realloc_(L, (b), (s), 0)\n#define luaM_free(L, b)\t\tluaM_realloc_(L, (b), sizeof(*(b)), 0)\n#define luaM_freearray(L, b, n, t)   luaM_reallocv(L, (b), n, 0, sizeof(t))\n\n#define luaM_malloc(L,t)\tluaM_realloc_(L, NULL, 0, (t))\n#define luaM_new(L,t)\t\tcast(t *, luaM_malloc(L, sizeof(t)))\n#define luaM_newvector(L,n,t) \\\n\t\tcast(t *, luaM_reallocv(L, NULL, 0, n, sizeof(t)))\n\n#define luaM_growvector(L,v,nelems,size,t,limit,e) \\\n          if ((nelems)+1 > (size)) \\\n            ((v)=cast(t *, luaM_growaux_(L,v,&(size),sizeof(t),limit,e)))\n\n#define luaM_reallocvector(L, v,oldn,n,t) \\\n   ((v)=cast(t *, luaM_reallocv(L, v, oldn, n, sizeof(t))))\n\n\nLUAI_FUNC void *luaM_realloc_ (lua_State *L, void *block, size_t oldsize,\n                                                          size_t size);\nLUAI_FUNC void *luaM_toobig (lua_State *L);\nLUAI_FUNC void *luaM_growaux_ (lua_State *L, void *block, int *size,\n                               size_t size_elem, int limit,\n                               const char *errormsg);\n\n#endif\n\n"
  },
  {
    "path": "deps/lua/src/loadlib.c",
    "content": "/*\n** $Id: loadlib.c,v 1.52.1.4 2009/09/09 13:17:16 roberto Exp $\n** Dynamic library loader for Lua\n** See Copyright Notice in lua.h\n**\n** This module contains an implementation of loadlib for Unix systems\n** that have dlfcn, an implementation for Darwin (Mac OS X), an\n** implementation for Windows, and a stub for other systems.\n*/\n\n\n#include <stdlib.h>\n#include <string.h>\n\n\n#define loadlib_c\n#define LUA_LIB\n\n#include \"lua.h\"\n\n#include \"lauxlib.h\"\n#include \"lualib.h\"\n\n\n/* prefix for open functions in C libraries */\n#define LUA_POF\t\t\"luaopen_\"\n\n/* separator for open functions in C libraries */\n#define LUA_OFSEP\t\"_\"\n\n\n#define LIBPREFIX\t\"LOADLIB: \"\n\n#define POF\t\tLUA_POF\n#define LIB_FAIL\t\"open\"\n\n\n/* error codes for ll_loadfunc */\n#define ERRLIB\t\t1\n#define ERRFUNC\t\t2\n\n#define setprogdir(L)\t\t((void)0)\n\n\nstatic void ll_unloadlib (void *lib);\nstatic void *ll_load (lua_State *L, const char *path);\nstatic lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym);\n\n\n\n#if defined(LUA_DL_DLOPEN)\n/*\n** {========================================================================\n** This is an implementation of loadlib based on the dlfcn interface.\n** The dlfcn interface is available in Linux, SunOS, Solaris, IRIX, FreeBSD,\n** NetBSD, AIX 4.2, HPUX 11, and  probably most other Unix flavors, at least\n** as an emulation layer on top of native functions.\n** =========================================================================\n*/\n\n#include <dlfcn.h>\n\nstatic void ll_unloadlib (void *lib) {\n  dlclose(lib);\n}\n\n\nstatic void *ll_load (lua_State *L, const char *path) {\n  void *lib = dlopen(path, RTLD_NOW);\n  if (lib == NULL) lua_pushstring(L, dlerror());\n  return lib;\n}\n\n\nstatic lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym) {\n  lua_CFunction f = (lua_CFunction)dlsym(lib, sym);\n  if (f == NULL) lua_pushstring(L, dlerror());\n  return f;\n}\n\n/* }====================================================== */\n\n\n\n#elif defined(LUA_DL_DLL)\n/*\n** {======================================================================\n** This is an implementation of loadlib for Windows using native functions.\n** =======================================================================\n*/\n\n#include <windows.h>\n\n\n#undef setprogdir\n\nstatic void setprogdir (lua_State *L) {\n  char buff[MAX_PATH + 1];\n  char *lb;\n  DWORD nsize = sizeof(buff)/sizeof(char);\n  DWORD n = GetModuleFileNameA(NULL, buff, nsize);\n  if (n == 0 || n == nsize || (lb = strrchr(buff, '\\\\')) == NULL)\n    luaL_error(L, \"unable to get ModuleFileName\");\n  else {\n    *lb = '\\0';\n    luaL_gsub(L, lua_tostring(L, -1), LUA_EXECDIR, buff);\n    lua_remove(L, -2);  /* remove original string */\n  }\n}\n\n\nstatic void pusherror (lua_State *L) {\n  int error = GetLastError();\n  char buffer[128];\n  if (FormatMessageA(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM,\n      NULL, error, 0, buffer, sizeof(buffer), NULL))\n    lua_pushstring(L, buffer);\n  else\n    lua_pushfstring(L, \"system error %d\\n\", error);\n}\n\nstatic void ll_unloadlib (void *lib) {\n  FreeLibrary((HINSTANCE)lib);\n}\n\n\nstatic void *ll_load (lua_State *L, const char *path) {\n  HINSTANCE lib = LoadLibraryA(path);\n  if (lib == NULL) pusherror(L);\n  return lib;\n}\n\n\nstatic lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym) {\n  lua_CFunction f = (lua_CFunction)GetProcAddress((HINSTANCE)lib, sym);\n  if (f == NULL) pusherror(L);\n  return f;\n}\n\n/* }====================================================== */\n\n\n\n#elif defined(LUA_DL_DYLD)\n/*\n** {======================================================================\n** Native Mac OS X / Darwin Implementation\n** =======================================================================\n*/\n\n#include <mach-o/dyld.h>\n\n\n/* Mac appends a `_' before C function names */\n#undef POF\n#define POF\t\"_\" LUA_POF\n\n\nstatic void pusherror (lua_State *L) {\n  const char *err_str;\n  const char *err_file;\n  NSLinkEditErrors err;\n  int err_num;\n  NSLinkEditError(&err, &err_num, &err_file, &err_str);\n  lua_pushstring(L, err_str);\n}\n\n\nstatic const char *errorfromcode (NSObjectFileImageReturnCode ret) {\n  switch (ret) {\n    case NSObjectFileImageInappropriateFile:\n      return \"file is not a bundle\";\n    case NSObjectFileImageArch:\n      return \"library is for wrong CPU type\";\n    case NSObjectFileImageFormat:\n      return \"bad format\";\n    case NSObjectFileImageAccess:\n      return \"cannot access file\";\n    case NSObjectFileImageFailure:\n    default:\n      return \"unable to load library\";\n  }\n}\n\n\nstatic void ll_unloadlib (void *lib) {\n  NSUnLinkModule((NSModule)lib, NSUNLINKMODULE_OPTION_RESET_LAZY_REFERENCES);\n}\n\n\nstatic void *ll_load (lua_State *L, const char *path) {\n  NSObjectFileImage img;\n  NSObjectFileImageReturnCode ret;\n  /* this would be a rare case, but prevents crashing if it happens */\n  if(!_dyld_present()) {\n    lua_pushliteral(L, \"dyld not present\");\n    return NULL;\n  }\n  ret = NSCreateObjectFileImageFromFile(path, &img);\n  if (ret == NSObjectFileImageSuccess) {\n    NSModule mod = NSLinkModule(img, path, NSLINKMODULE_OPTION_PRIVATE |\n                       NSLINKMODULE_OPTION_RETURN_ON_ERROR);\n    NSDestroyObjectFileImage(img);\n    if (mod == NULL) pusherror(L);\n    return mod;\n  }\n  lua_pushstring(L, errorfromcode(ret));\n  return NULL;\n}\n\n\nstatic lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym) {\n  NSSymbol nss = NSLookupSymbolInModule((NSModule)lib, sym);\n  if (nss == NULL) {\n    lua_pushfstring(L, \"symbol \" LUA_QS \" not found\", sym);\n    return NULL;\n  }\n  return (lua_CFunction)NSAddressOfSymbol(nss);\n}\n\n/* }====================================================== */\n\n\n\n#else\n/*\n** {======================================================\n** Fallback for other systems\n** =======================================================\n*/\n\n#undef LIB_FAIL\n#define LIB_FAIL\t\"absent\"\n\n\n#define DLMSG\t\"dynamic libraries not enabled; check your Lua installation\"\n\n\nstatic void ll_unloadlib (void *lib) {\n  (void)lib;  /* to avoid warnings */\n}\n\n\nstatic void *ll_load (lua_State *L, const char *path) {\n  (void)path;  /* to avoid warnings */\n  lua_pushliteral(L, DLMSG);\n  return NULL;\n}\n\n\nstatic lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym) {\n  (void)lib; (void)sym;  /* to avoid warnings */\n  lua_pushliteral(L, DLMSG);\n  return NULL;\n}\n\n/* }====================================================== */\n#endif\n\n\n\nstatic void **ll_register (lua_State *L, const char *path) {\n  void **plib;\n  lua_pushfstring(L, \"%s%s\", LIBPREFIX, path);\n  lua_gettable(L, LUA_REGISTRYINDEX);  /* check library in registry? */\n  if (!lua_isnil(L, -1))  /* is there an entry? */\n    plib = (void **)lua_touserdata(L, -1);\n  else {  /* no entry yet; create one */\n    lua_pop(L, 1);\n    plib = (void **)lua_newuserdata(L, sizeof(const void *));\n    *plib = NULL;\n    luaL_getmetatable(L, \"_LOADLIB\");\n    lua_setmetatable(L, -2);\n    lua_pushfstring(L, \"%s%s\", LIBPREFIX, path);\n    lua_pushvalue(L, -2);\n    lua_settable(L, LUA_REGISTRYINDEX);\n  }\n  return plib;\n}\n\n\n/*\n** __gc tag method: calls library's `ll_unloadlib' function with the lib\n** handle\n*/\nstatic int gctm (lua_State *L) {\n  void **lib = (void **)luaL_checkudata(L, 1, \"_LOADLIB\");\n  if (*lib) ll_unloadlib(*lib);\n  *lib = NULL;  /* mark library as closed */\n  return 0;\n}\n\n\nstatic int ll_loadfunc (lua_State *L, const char *path, const char *sym) {\n  void **reg = ll_register(L, path);\n  if (*reg == NULL) *reg = ll_load(L, path);\n  if (*reg == NULL)\n    return ERRLIB;  /* unable to load library */\n  else {\n    lua_CFunction f = ll_sym(L, *reg, sym);\n    if (f == NULL)\n      return ERRFUNC;  /* unable to find function */\n    lua_pushcfunction(L, f);\n    return 0;  /* return function */\n  }\n}\n\n\nstatic int ll_loadlib (lua_State *L) {\n  const char *path = luaL_checkstring(L, 1);\n  const char *init = luaL_checkstring(L, 2);\n  int stat = ll_loadfunc(L, path, init);\n  if (stat == 0)  /* no errors? */\n    return 1;  /* return the loaded function */\n  else {  /* error; error message is on stack top */\n    lua_pushnil(L);\n    lua_insert(L, -2);\n    lua_pushstring(L, (stat == ERRLIB) ?  LIB_FAIL : \"init\");\n    return 3;  /* return nil, error message, and where */\n  }\n}\n\n\n\n/*\n** {======================================================\n** 'require' function\n** =======================================================\n*/\n\n\nstatic int readable (const char *filename) {\n  FILE *f = fopen(filename, \"r\");  /* try to open file */\n  if (f == NULL) return 0;  /* open failed */\n  fclose(f);\n  return 1;\n}\n\n\nstatic const char *pushnexttemplate (lua_State *L, const char *path) {\n  const char *l;\n  while (*path == *LUA_PATHSEP) path++;  /* skip separators */\n  if (*path == '\\0') return NULL;  /* no more templates */\n  l = strchr(path, *LUA_PATHSEP);  /* find next separator */\n  if (l == NULL) l = path + strlen(path);\n  lua_pushlstring(L, path, l - path);  /* template */\n  return l;\n}\n\n\nstatic const char *findfile (lua_State *L, const char *name,\n                                           const char *pname) {\n  const char *path;\n  name = luaL_gsub(L, name, \".\", LUA_DIRSEP);\n  lua_getfield(L, LUA_ENVIRONINDEX, pname);\n  path = lua_tostring(L, -1);\n  if (path == NULL)\n    luaL_error(L, LUA_QL(\"package.%s\") \" must be a string\", pname);\n  lua_pushliteral(L, \"\");  /* error accumulator */\n  while ((path = pushnexttemplate(L, path)) != NULL) {\n    const char *filename;\n    filename = luaL_gsub(L, lua_tostring(L, -1), LUA_PATH_MARK, name);\n    lua_remove(L, -2);  /* remove path template */\n    if (readable(filename))  /* does file exist and is readable? */\n      return filename;  /* return that file name */\n    lua_pushfstring(L, \"\\n\\tno file \" LUA_QS, filename);\n    lua_remove(L, -2);  /* remove file name */\n    lua_concat(L, 2);  /* add entry to possible error message */\n  }\n  return NULL;  /* not found */\n}\n\n\nstatic void loaderror (lua_State *L, const char *filename) {\n  luaL_error(L, \"error loading module \" LUA_QS \" from file \" LUA_QS \":\\n\\t%s\",\n                lua_tostring(L, 1), filename, lua_tostring(L, -1));\n}\n\n\nstatic int loader_Lua (lua_State *L) {\n  const char *filename;\n  const char *name = luaL_checkstring(L, 1);\n  filename = findfile(L, name, \"path\");\n  if (filename == NULL) return 1;  /* library not found in this path */\n  if (luaL_loadfile(L, filename) != 0)\n    loaderror(L, filename);\n  return 1;  /* library loaded successfully */\n}\n\n\nstatic const char *mkfuncname (lua_State *L, const char *modname) {\n  const char *funcname;\n  const char *mark = strchr(modname, *LUA_IGMARK);\n  if (mark) modname = mark + 1;\n  funcname = luaL_gsub(L, modname, \".\", LUA_OFSEP);\n  funcname = lua_pushfstring(L, POF\"%s\", funcname);\n  lua_remove(L, -2);  /* remove 'gsub' result */\n  return funcname;\n}\n\n\nstatic int loader_C (lua_State *L) {\n  const char *funcname;\n  const char *name = luaL_checkstring(L, 1);\n  const char *filename = findfile(L, name, \"cpath\");\n  if (filename == NULL) return 1;  /* library not found in this path */\n  funcname = mkfuncname(L, name);\n  if (ll_loadfunc(L, filename, funcname) != 0)\n    loaderror(L, filename);\n  return 1;  /* library loaded successfully */\n}\n\n\nstatic int loader_Croot (lua_State *L) {\n  const char *funcname;\n  const char *filename;\n  const char *name = luaL_checkstring(L, 1);\n  const char *p = strchr(name, '.');\n  int stat;\n  if (p == NULL) return 0;  /* is root */\n  lua_pushlstring(L, name, p - name);\n  filename = findfile(L, lua_tostring(L, -1), \"cpath\");\n  if (filename == NULL) return 1;  /* root not found */\n  funcname = mkfuncname(L, name);\n  if ((stat = ll_loadfunc(L, filename, funcname)) != 0) {\n    if (stat != ERRFUNC) loaderror(L, filename);  /* real error */\n    lua_pushfstring(L, \"\\n\\tno module \" LUA_QS \" in file \" LUA_QS,\n                       name, filename);\n    return 1;  /* function not found */\n  }\n  return 1;\n}\n\n\nstatic int loader_preload (lua_State *L) {\n  const char *name = luaL_checkstring(L, 1);\n  lua_getfield(L, LUA_ENVIRONINDEX, \"preload\");\n  if (!lua_istable(L, -1))\n    luaL_error(L, LUA_QL(\"package.preload\") \" must be a table\");\n  lua_getfield(L, -1, name);\n  if (lua_isnil(L, -1))  /* not found? */\n    lua_pushfstring(L, \"\\n\\tno field package.preload['%s']\", name);\n  return 1;\n}\n\n\nstatic const int sentinel_ = 0;\n#define sentinel\t((void *)&sentinel_)\n\n\nstatic int ll_require (lua_State *L) {\n  const char *name = luaL_checkstring(L, 1);\n  int i;\n  lua_settop(L, 1);  /* _LOADED table will be at index 2 */\n  lua_getfield(L, LUA_REGISTRYINDEX, \"_LOADED\");\n  lua_getfield(L, 2, name);\n  if (lua_toboolean(L, -1)) {  /* is it there? */\n    if (lua_touserdata(L, -1) == sentinel)  /* check loops */\n      luaL_error(L, \"loop or previous error loading module \" LUA_QS, name);\n    return 1;  /* package is already loaded */\n  }\n  /* else must load it; iterate over available loaders */\n  lua_getfield(L, LUA_ENVIRONINDEX, \"loaders\");\n  if (!lua_istable(L, -1))\n    luaL_error(L, LUA_QL(\"package.loaders\") \" must be a table\");\n  lua_pushliteral(L, \"\");  /* error message accumulator */\n  for (i=1; ; i++) {\n    lua_rawgeti(L, -2, i);  /* get a loader */\n    if (lua_isnil(L, -1))\n      luaL_error(L, \"module \" LUA_QS \" not found:%s\",\n                    name, lua_tostring(L, -2));\n    lua_pushstring(L, name);\n    lua_call(L, 1, 1);  /* call it */\n    if (lua_isfunction(L, -1))  /* did it find module? */\n      break;  /* module loaded successfully */\n    else if (lua_isstring(L, -1))  /* loader returned error message? */\n      lua_concat(L, 2);  /* accumulate it */\n    else\n      lua_pop(L, 1);\n  }\n  lua_pushlightuserdata(L, sentinel);\n  lua_setfield(L, 2, name);  /* _LOADED[name] = sentinel */\n  lua_pushstring(L, name);  /* pass name as argument to module */\n  lua_call(L, 1, 1);  /* run loaded module */\n  if (!lua_isnil(L, -1))  /* non-nil return? */\n    lua_setfield(L, 2, name);  /* _LOADED[name] = returned value */\n  lua_getfield(L, 2, name);\n  if (lua_touserdata(L, -1) == sentinel) {   /* module did not set a value? */\n    lua_pushboolean(L, 1);  /* use true as result */\n    lua_pushvalue(L, -1);  /* extra copy to be returned */\n    lua_setfield(L, 2, name);  /* _LOADED[name] = true */\n  }\n  return 1;\n}\n\n/* }====================================================== */\n\n\n\n/*\n** {======================================================\n** 'module' function\n** =======================================================\n*/\n  \n\nstatic void setfenv (lua_State *L) {\n  lua_Debug ar;\n  if (lua_getstack(L, 1, &ar) == 0 ||\n      lua_getinfo(L, \"f\", &ar) == 0 ||  /* get calling function */\n      lua_iscfunction(L, -1))\n    luaL_error(L, LUA_QL(\"module\") \" not called from a Lua function\");\n  lua_pushvalue(L, -2);\n  lua_setfenv(L, -2);\n  lua_pop(L, 1);\n}\n\n\nstatic void dooptions (lua_State *L, int n) {\n  int i;\n  for (i = 2; i <= n; i++) {\n    lua_pushvalue(L, i);  /* get option (a function) */\n    lua_pushvalue(L, -2);  /* module */\n    lua_call(L, 1, 0);\n  }\n}\n\n\nstatic void modinit (lua_State *L, const char *modname) {\n  const char *dot;\n  lua_pushvalue(L, -1);\n  lua_setfield(L, -2, \"_M\");  /* module._M = module */\n  lua_pushstring(L, modname);\n  lua_setfield(L, -2, \"_NAME\");\n  dot = strrchr(modname, '.');  /* look for last dot in module name */\n  if (dot == NULL) dot = modname;\n  else dot++;\n  /* set _PACKAGE as package name (full module name minus last part) */\n  lua_pushlstring(L, modname, dot - modname);\n  lua_setfield(L, -2, \"_PACKAGE\");\n}\n\n\nstatic int ll_module (lua_State *L) {\n  const char *modname = luaL_checkstring(L, 1);\n  int loaded = lua_gettop(L) + 1;  /* index of _LOADED table */\n  lua_getfield(L, LUA_REGISTRYINDEX, \"_LOADED\");\n  lua_getfield(L, loaded, modname);  /* get _LOADED[modname] */\n  if (!lua_istable(L, -1)) {  /* not found? */\n    lua_pop(L, 1);  /* remove previous result */\n    /* try global variable (and create one if it does not exist) */\n    if (luaL_findtable(L, LUA_GLOBALSINDEX, modname, 1) != NULL)\n      return luaL_error(L, \"name conflict for module \" LUA_QS, modname);\n    lua_pushvalue(L, -1);\n    lua_setfield(L, loaded, modname);  /* _LOADED[modname] = new table */\n  }\n  /* check whether table already has a _NAME field */\n  lua_getfield(L, -1, \"_NAME\");\n  if (!lua_isnil(L, -1))  /* is table an initialized module? */\n    lua_pop(L, 1);\n  else {  /* no; initialize it */\n    lua_pop(L, 1);\n    modinit(L, modname);\n  }\n  lua_pushvalue(L, -1);\n  setfenv(L);\n  dooptions(L, loaded - 1);\n  return 0;\n}\n\n\nstatic int ll_seeall (lua_State *L) {\n  luaL_checktype(L, 1, LUA_TTABLE);\n  if (!lua_getmetatable(L, 1)) {\n    lua_createtable(L, 0, 1); /* create new metatable */\n    lua_pushvalue(L, -1);\n    lua_setmetatable(L, 1);\n  }\n  lua_pushvalue(L, LUA_GLOBALSINDEX);\n  lua_setfield(L, -2, \"__index\");  /* mt.__index = _G */\n  return 0;\n}\n\n\n/* }====================================================== */\n\n\n\n/* auxiliary mark (for internal use) */\n#define AUXMARK\t\t\"\\1\"\n\nstatic void setpath (lua_State *L, const char *fieldname, const char *envname,\n                                   const char *def) {\n  const char *path = getenv(envname);\n  if (path == NULL)  /* no environment variable? */\n    lua_pushstring(L, def);  /* use default */\n  else {\n    /* replace \";;\" by \";AUXMARK;\" and then AUXMARK by default path */\n    path = luaL_gsub(L, path, LUA_PATHSEP LUA_PATHSEP,\n                              LUA_PATHSEP AUXMARK LUA_PATHSEP);\n    luaL_gsub(L, path, AUXMARK, def);\n    lua_remove(L, -2);\n  }\n  setprogdir(L);\n  lua_setfield(L, -2, fieldname);\n}\n\n\nstatic const luaL_Reg pk_funcs[] = {\n  {\"loadlib\", ll_loadlib},\n  {\"seeall\", ll_seeall},\n  {NULL, NULL}\n};\n\n\nstatic const luaL_Reg ll_funcs[] = {\n  {\"module\", ll_module},\n  {\"require\", ll_require},\n  {NULL, NULL}\n};\n\n\nstatic const lua_CFunction loaders[] =\n  {loader_preload, loader_Lua, loader_C, loader_Croot, NULL};\n\n\nLUALIB_API int luaopen_package (lua_State *L) {\n  int i;\n  /* create new type _LOADLIB */\n  luaL_newmetatable(L, \"_LOADLIB\");\n  lua_pushcfunction(L, gctm);\n  lua_setfield(L, -2, \"__gc\");\n  /* create `package' table */\n  luaL_register(L, LUA_LOADLIBNAME, pk_funcs);\n#if defined(LUA_COMPAT_LOADLIB) \n  lua_getfield(L, -1, \"loadlib\");\n  lua_setfield(L, LUA_GLOBALSINDEX, \"loadlib\");\n#endif\n  lua_pushvalue(L, -1);\n  lua_replace(L, LUA_ENVIRONINDEX);\n  /* create `loaders' table */\n  lua_createtable(L, sizeof(loaders)/sizeof(loaders[0]) - 1, 0);\n  /* fill it with pre-defined loaders */\n  for (i=0; loaders[i] != NULL; i++) {\n    lua_pushcfunction(L, loaders[i]);\n    lua_rawseti(L, -2, i+1);\n  }\n  lua_setfield(L, -2, \"loaders\");  /* put it in field `loaders' */\n  setpath(L, \"path\", LUA_PATH, LUA_PATH_DEFAULT);  /* set field `path' */\n  setpath(L, \"cpath\", LUA_CPATH, LUA_CPATH_DEFAULT); /* set field `cpath' */\n  /* store config information */\n  lua_pushliteral(L, LUA_DIRSEP \"\\n\" LUA_PATHSEP \"\\n\" LUA_PATH_MARK \"\\n\"\n                     LUA_EXECDIR \"\\n\" LUA_IGMARK);\n  lua_setfield(L, -2, \"config\");\n  /* set field `loaded' */\n  luaL_findtable(L, LUA_REGISTRYINDEX, \"_LOADED\", 2);\n  lua_setfield(L, -2, \"loaded\");\n  /* set field `preload' */\n  lua_newtable(L);\n  lua_setfield(L, -2, \"preload\");\n  lua_pushvalue(L, LUA_GLOBALSINDEX);\n  luaL_register(L, NULL, ll_funcs);  /* open lib into global table */\n  lua_pop(L, 1);\n  return 1;  /* return 'package' table */\n}\n\n"
  },
  {
    "path": "deps/lua/src/lobject.c",
    "content": "/*\n** $Id: lobject.c,v 2.22.1.1 2007/12/27 13:02:25 roberto Exp $\n** Some generic functions over Lua objects\n** See Copyright Notice in lua.h\n*/\n\n#include <ctype.h>\n#include <stdarg.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#define lobject_c\n#define LUA_CORE\n\n#include \"lua.h\"\n\n#include \"ldo.h\"\n#include \"lmem.h\"\n#include \"lobject.h\"\n#include \"lstate.h\"\n#include \"lstring.h\"\n#include \"lvm.h\"\n\n\n\nconst TValue luaO_nilobject_ = {{NULL}, LUA_TNIL};\n\n\n/*\n** converts an integer to a \"floating point byte\", represented as\n** (eeeeexxx), where the real value is (1xxx) * 2^(eeeee - 1) if\n** eeeee != 0 and (xxx) otherwise.\n*/\nint luaO_int2fb (unsigned int x) {\n  int e = 0;  /* expoent */\n  while (x >= 16) {\n    x = (x+1) >> 1;\n    e++;\n  }\n  if (x < 8) return x;\n  else return ((e+1) << 3) | (cast_int(x) - 8);\n}\n\n\n/* converts back */\nint luaO_fb2int (int x) {\n  int e = (x >> 3) & 31;\n  if (e == 0) return x;\n  else return ((x & 7)+8) << (e - 1);\n}\n\n\nint luaO_log2 (unsigned int x) {\n  static const lu_byte log_2[256] = {\n    0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,\n    6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,\n    7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,\n    7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,\n    8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,\n    8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,\n    8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,\n    8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8\n  };\n  int l = -1;\n  while (x >= 256) { l += 8; x >>= 8; }\n  return l + log_2[x];\n\n}\n\n\nint luaO_rawequalObj (const TValue *t1, const TValue *t2) {\n  if (ttype(t1) != ttype(t2)) return 0;\n  else switch (ttype(t1)) {\n    case LUA_TNIL:\n      return 1;\n    case LUA_TNUMBER:\n      return luai_numeq(nvalue(t1), nvalue(t2));\n    case LUA_TBOOLEAN:\n      return bvalue(t1) == bvalue(t2);  /* boolean true must be 1 !! */\n    case LUA_TLIGHTUSERDATA:\n      return pvalue(t1) == pvalue(t2);\n    default:\n      lua_assert(iscollectable(t1));\n      return gcvalue(t1) == gcvalue(t2);\n  }\n}\n\n\nint luaO_str2d (const char *s, lua_Number *result) {\n  char *endptr;\n  *result = lua_str2number(s, &endptr);\n  if (endptr == s) return 0;  /* conversion failed */\n  if (*endptr == 'x' || *endptr == 'X')  /* maybe an hexadecimal constant? */\n    *result = cast_num(strtoul(s, &endptr, 16));\n  if (*endptr == '\\0') return 1;  /* most common case */\n  while (isspace(cast(unsigned char, *endptr))) endptr++;\n  if (*endptr != '\\0') return 0;  /* invalid trailing characters? */\n  return 1;\n}\n\n\n\nstatic void pushstr (lua_State *L, const char *str) {\n  setsvalue2s(L, L->top, luaS_new(L, str));\n  incr_top(L);\n}\n\n\n/* this function handles only `%d', `%c', %f, %p, and `%s' formats */\nconst char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) {\n  int n = 1;\n  pushstr(L, \"\");\n  for (;;) {\n    const char *e = strchr(fmt, '%');\n    if (e == NULL) break;\n    setsvalue2s(L, L->top, luaS_newlstr(L, fmt, e-fmt));\n    incr_top(L);\n    switch (*(e+1)) {\n      case 's': {\n        const char *s = va_arg(argp, char *);\n        if (s == NULL) s = \"(null)\";\n        pushstr(L, s);\n        break;\n      }\n      case 'c': {\n        char buff[2];\n        buff[0] = cast(char, va_arg(argp, int));\n        buff[1] = '\\0';\n        pushstr(L, buff);\n        break;\n      }\n      case 'd': {\n        setnvalue(L->top, cast_num(va_arg(argp, int)));\n        incr_top(L);\n        break;\n      }\n      case 'f': {\n        setnvalue(L->top, cast_num(va_arg(argp, l_uacNumber)));\n        incr_top(L);\n        break;\n      }\n      case 'p': {\n        char buff[4*sizeof(void *) + 8]; /* should be enough space for a `%p' */\n        sprintf(buff, \"%p\", va_arg(argp, void *));\n        pushstr(L, buff);\n        break;\n      }\n      case '%': {\n        pushstr(L, \"%\");\n        break;\n      }\n      default: {\n        char buff[3];\n        buff[0] = '%';\n        buff[1] = *(e+1);\n        buff[2] = '\\0';\n        pushstr(L, buff);\n        break;\n      }\n    }\n    n += 2;\n    fmt = e+2;\n  }\n  pushstr(L, fmt);\n  luaV_concat(L, n+1, cast_int(L->top - L->base) - 1);\n  L->top -= n;\n  return svalue(L->top - 1);\n}\n\n\nconst char *luaO_pushfstring (lua_State *L, const char *fmt, ...) {\n  const char *msg;\n  va_list argp;\n  va_start(argp, fmt);\n  msg = luaO_pushvfstring(L, fmt, argp);\n  va_end(argp);\n  return msg;\n}\n\n\nvoid luaO_chunkid (char *out, const char *source, size_t bufflen) {\n  if (*source == '=') {\n    strncpy(out, source+1, bufflen);  /* remove first char */\n    out[bufflen-1] = '\\0';  /* ensures null termination */\n  }\n  else {  /* out = \"source\", or \"...source\" */\n    if (*source == '@') {\n      size_t l;\n      source++;  /* skip the `@' */\n      bufflen -= sizeof(\" '...' \");\n      l = strlen(source);\n      strcpy(out, \"\");\n      if (l > bufflen) {\n        source += (l-bufflen);  /* get last part of file name */\n        strcat(out, \"...\");\n      }\n      strcat(out, source);\n    }\n    else {  /* out = [string \"string\"] */\n      size_t len = strcspn(source, \"\\n\\r\");  /* stop at first newline */\n      bufflen -= sizeof(\" [string \\\"...\\\"] \");\n      if (len > bufflen) len = bufflen;\n      strcpy(out, \"[string \\\"\");\n      if (source[len] != '\\0') {  /* must truncate? */\n        strncat(out, source, len);\n        strcat(out, \"...\");\n      }\n      else\n        strcat(out, source);\n      strcat(out, \"\\\"]\");\n    }\n  }\n}\n"
  },
  {
    "path": "deps/lua/src/lobject.h",
    "content": "/*\n** $Id: lobject.h,v 2.20.1.2 2008/08/06 13:29:48 roberto Exp $\n** Type definitions for Lua objects\n** See Copyright Notice in lua.h\n*/\n\n\n#ifndef lobject_h\n#define lobject_h\n\n\n#include <stdarg.h>\n\n\n#include \"llimits.h\"\n#include \"lua.h\"\n\n\n/* tags for values visible from Lua */\n#define LAST_TAG\tLUA_TTHREAD\n\n#define NUM_TAGS\t(LAST_TAG+1)\n\n\n/*\n** Extra tags for non-values\n*/\n#define LUA_TPROTO\t(LAST_TAG+1)\n#define LUA_TUPVAL\t(LAST_TAG+2)\n#define LUA_TDEADKEY\t(LAST_TAG+3)\n\n\n/*\n** Union of all collectable objects\n*/\ntypedef union GCObject GCObject;\n\n\n/*\n** Common Header for all collectable objects (in macro form, to be\n** included in other objects)\n*/\n#define CommonHeader\tGCObject *next; lu_byte tt; lu_byte marked\n\n\n/*\n** Common header in struct form\n*/\ntypedef struct GCheader {\n  CommonHeader;\n} GCheader;\n\n\n\n\n/*\n** Union of all Lua values\n*/\ntypedef union {\n  GCObject *gc;\n  void *p;\n  lua_Number n;\n  int b;\n} Value;\n\n\n/*\n** Tagged Values\n*/\n\n#define TValuefields\tValue value; int tt\n\ntypedef struct lua_TValue {\n  TValuefields;\n} TValue;\n\n\n/* Macros to test type */\n#define ttisnil(o)\t(ttype(o) == LUA_TNIL)\n#define ttisnumber(o)\t(ttype(o) == LUA_TNUMBER)\n#define ttisstring(o)\t(ttype(o) == LUA_TSTRING)\n#define ttistable(o)\t(ttype(o) == LUA_TTABLE)\n#define ttisfunction(o)\t(ttype(o) == LUA_TFUNCTION)\n#define ttisboolean(o)\t(ttype(o) == LUA_TBOOLEAN)\n#define ttisuserdata(o)\t(ttype(o) == LUA_TUSERDATA)\n#define ttisthread(o)\t(ttype(o) == LUA_TTHREAD)\n#define ttislightuserdata(o)\t(ttype(o) == LUA_TLIGHTUSERDATA)\n\n/* Macros to access values */\n#define ttype(o)\t((o)->tt)\n#define gcvalue(o)\tcheck_exp(iscollectable(o), (o)->value.gc)\n#define pvalue(o)\tcheck_exp(ttislightuserdata(o), (o)->value.p)\n#define nvalue(o)\tcheck_exp(ttisnumber(o), (o)->value.n)\n#define rawtsvalue(o)\tcheck_exp(ttisstring(o), &(o)->value.gc->ts)\n#define tsvalue(o)\t(&rawtsvalue(o)->tsv)\n#define rawuvalue(o)\tcheck_exp(ttisuserdata(o), &(o)->value.gc->u)\n#define uvalue(o)\t(&rawuvalue(o)->uv)\n#define clvalue(o)\tcheck_exp(ttisfunction(o), &(o)->value.gc->cl)\n#define hvalue(o)\tcheck_exp(ttistable(o), &(o)->value.gc->h)\n#define bvalue(o)\tcheck_exp(ttisboolean(o), (o)->value.b)\n#define thvalue(o)\tcheck_exp(ttisthread(o), &(o)->value.gc->th)\n\n#define l_isfalse(o)\t(ttisnil(o) || (ttisboolean(o) && bvalue(o) == 0))\n\n/*\n** for internal debug only\n*/\n#define checkconsistency(obj) \\\n  lua_assert(!iscollectable(obj) || (ttype(obj) == (obj)->value.gc->gch.tt))\n\n#define checkliveness(g,obj) \\\n  lua_assert(!iscollectable(obj) || \\\n  ((ttype(obj) == (obj)->value.gc->gch.tt) && !isdead(g, (obj)->value.gc)))\n\n\n/* Macros to set values */\n#define setnilvalue(obj) ((obj)->tt=LUA_TNIL)\n\n#define setnvalue(obj,x) \\\n  { TValue *i_o=(obj); i_o->value.n=(x); i_o->tt=LUA_TNUMBER; }\n\n#define setpvalue(obj,x) \\\n  { TValue *i_o=(obj); i_o->value.p=(x); i_o->tt=LUA_TLIGHTUSERDATA; }\n\n#define setbvalue(obj,x) \\\n  { TValue *i_o=(obj); i_o->value.b=(x); i_o->tt=LUA_TBOOLEAN; }\n\n#define setsvalue(L,obj,x) \\\n  { TValue *i_o=(obj); \\\n    i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TSTRING; \\\n    checkliveness(G(L),i_o); }\n\n#define setuvalue(L,obj,x) \\\n  { TValue *i_o=(obj); \\\n    i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TUSERDATA; \\\n    checkliveness(G(L),i_o); }\n\n#define setthvalue(L,obj,x) \\\n  { TValue *i_o=(obj); \\\n    i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TTHREAD; \\\n    checkliveness(G(L),i_o); }\n\n#define setclvalue(L,obj,x) \\\n  { TValue *i_o=(obj); \\\n    i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TFUNCTION; \\\n    checkliveness(G(L),i_o); }\n\n#define sethvalue(L,obj,x) \\\n  { TValue *i_o=(obj); \\\n    i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TTABLE; \\\n    checkliveness(G(L),i_o); }\n\n#define setptvalue(L,obj,x) \\\n  { TValue *i_o=(obj); \\\n    i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TPROTO; \\\n    checkliveness(G(L),i_o); }\n\n\n\n\n#define setobj(L,obj1,obj2) \\\n  { const TValue *o2=(obj2); TValue *o1=(obj1); \\\n    o1->value = o2->value; o1->tt=o2->tt; \\\n    checkliveness(G(L),o1); }\n\n\n/*\n** different types of sets, according to destination\n*/\n\n/* from stack to (same) stack */\n#define setobjs2s\tsetobj\n/* to stack (not from same stack) */\n#define setobj2s\tsetobj\n#define setsvalue2s\tsetsvalue\n#define sethvalue2s\tsethvalue\n#define setptvalue2s\tsetptvalue\n/* from table to same table */\n#define setobjt2t\tsetobj\n/* to table */\n#define setobj2t\tsetobj\n/* to new object */\n#define setobj2n\tsetobj\n#define setsvalue2n\tsetsvalue\n\n#define setttype(obj, tt) (ttype(obj) = (tt))\n\n\n#define iscollectable(o)\t(ttype(o) >= LUA_TSTRING)\n\n\n\ntypedef TValue *StkId;  /* index to stack elements */\n\n\n/*\n** String headers for string table\n*/\ntypedef union TString {\n  L_Umaxalign dummy;  /* ensures maximum alignment for strings */\n  struct {\n    CommonHeader;\n    lu_byte reserved;\n    unsigned int hash;\n    size_t len;\n  } tsv;\n} TString;\n\n\n#define getstr(ts)\tcast(const char *, (ts) + 1)\n#define svalue(o)       getstr(rawtsvalue(o))\n\n\n\ntypedef union Udata {\n  L_Umaxalign dummy;  /* ensures maximum alignment for `local' udata */\n  struct {\n    CommonHeader;\n    struct Table *metatable;\n    struct Table *env;\n    size_t len;\n  } uv;\n} Udata;\n\n\n\n\n/*\n** Function Prototypes\n*/\ntypedef struct Proto {\n  CommonHeader;\n  TValue *k;  /* constants used by the function */\n  Instruction *code;\n  struct Proto **p;  /* functions defined inside the function */\n  int *lineinfo;  /* map from opcodes to source lines */\n  struct LocVar *locvars;  /* information about local variables */\n  TString **upvalues;  /* upvalue names */\n  TString  *source;\n  int sizeupvalues;\n  int sizek;  /* size of `k' */\n  int sizecode;\n  int sizelineinfo;\n  int sizep;  /* size of `p' */\n  int sizelocvars;\n  int linedefined;\n  int lastlinedefined;\n  GCObject *gclist;\n  lu_byte nups;  /* number of upvalues */\n  lu_byte numparams;\n  lu_byte is_vararg;\n  lu_byte maxstacksize;\n} Proto;\n\n\n/* masks for new-style vararg */\n#define VARARG_HASARG\t\t1\n#define VARARG_ISVARARG\t\t2\n#define VARARG_NEEDSARG\t\t4\n\n\ntypedef struct LocVar {\n  TString *varname;\n  int startpc;  /* first point where variable is active */\n  int endpc;    /* first point where variable is dead */\n} LocVar;\n\n\n\n/*\n** Upvalues\n*/\n\ntypedef struct UpVal {\n  CommonHeader;\n  TValue *v;  /* points to stack or to its own value */\n  union {\n    TValue value;  /* the value (when closed) */\n    struct {  /* double linked list (when open) */\n      struct UpVal *prev;\n      struct UpVal *next;\n    } l;\n  } u;\n} UpVal;\n\n\n/*\n** Closures\n*/\n\n#define ClosureHeader \\\n\tCommonHeader; lu_byte isC; lu_byte nupvalues; GCObject *gclist; \\\n\tstruct Table *env\n\ntypedef struct CClosure {\n  ClosureHeader;\n  lua_CFunction f;\n  TValue upvalue[1];\n} CClosure;\n\n\ntypedef struct LClosure {\n  ClosureHeader;\n  struct Proto *p;\n  UpVal *upvals[1];\n} LClosure;\n\n\ntypedef union Closure {\n  CClosure c;\n  LClosure l;\n} Closure;\n\n\n#define iscfunction(o)\t(ttype(o) == LUA_TFUNCTION && clvalue(o)->c.isC)\n#define isLfunction(o)\t(ttype(o) == LUA_TFUNCTION && !clvalue(o)->c.isC)\n\n\n/*\n** Tables\n*/\n\ntypedef union TKey {\n  struct {\n    TValuefields;\n    struct Node *next;  /* for chaining */\n  } nk;\n  TValue tvk;\n} TKey;\n\n\ntypedef struct Node {\n  TValue i_val;\n  TKey i_key;\n} Node;\n\n\ntypedef struct Table {\n  CommonHeader;\n  lu_byte flags;  /* 1<<p means tagmethod(p) is not present */ \n  lu_byte lsizenode;  /* log2 of size of `node' array */\n  struct Table *metatable;\n  TValue *array;  /* array part */\n  Node *node;\n  Node *lastfree;  /* any free position is before this position */\n  GCObject *gclist;\n  int sizearray;  /* size of `array' array */\n} Table;\n\n\n\n/*\n** `module' operation for hashing (size is always a power of 2)\n*/\n#define lmod(s,size) \\\n\t(check_exp((size&(size-1))==0, (cast(int, (s) & ((size)-1)))))\n\n\n#define twoto(x)\t(1<<(x))\n#define sizenode(t)\t(twoto((t)->lsizenode))\n\n\n#define luaO_nilobject\t\t(&luaO_nilobject_)\n\nLUAI_DATA const TValue luaO_nilobject_;\n\n#define ceillog2(x)\t(luaO_log2((x)-1) + 1)\n\nLUAI_FUNC int luaO_log2 (unsigned int x);\nLUAI_FUNC int luaO_int2fb (unsigned int x);\nLUAI_FUNC int luaO_fb2int (int x);\nLUAI_FUNC int luaO_rawequalObj (const TValue *t1, const TValue *t2);\nLUAI_FUNC int luaO_str2d (const char *s, lua_Number *result);\nLUAI_FUNC const char *luaO_pushvfstring (lua_State *L, const char *fmt,\n                                                       va_list argp);\nLUAI_FUNC const char *luaO_pushfstring (lua_State *L, const char *fmt, ...);\nLUAI_FUNC void luaO_chunkid (char *out, const char *source, size_t len);\n\n\n#endif\n\n"
  },
  {
    "path": "deps/lua/src/lopcodes.c",
    "content": "/*\n** $Id: lopcodes.c,v 1.37.1.1 2007/12/27 13:02:25 roberto Exp $\n** See Copyright Notice in lua.h\n*/\n\n\n#define lopcodes_c\n#define LUA_CORE\n\n\n#include \"lopcodes.h\"\n\n\n/* ORDER OP */\n\nconst char *const luaP_opnames[NUM_OPCODES+1] = {\n  \"MOVE\",\n  \"LOADK\",\n  \"LOADBOOL\",\n  \"LOADNIL\",\n  \"GETUPVAL\",\n  \"GETGLOBAL\",\n  \"GETTABLE\",\n  \"SETGLOBAL\",\n  \"SETUPVAL\",\n  \"SETTABLE\",\n  \"NEWTABLE\",\n  \"SELF\",\n  \"ADD\",\n  \"SUB\",\n  \"MUL\",\n  \"DIV\",\n  \"MOD\",\n  \"POW\",\n  \"UNM\",\n  \"NOT\",\n  \"LEN\",\n  \"CONCAT\",\n  \"JMP\",\n  \"EQ\",\n  \"LT\",\n  \"LE\",\n  \"TEST\",\n  \"TESTSET\",\n  \"CALL\",\n  \"TAILCALL\",\n  \"RETURN\",\n  \"FORLOOP\",\n  \"FORPREP\",\n  \"TFORLOOP\",\n  \"SETLIST\",\n  \"CLOSE\",\n  \"CLOSURE\",\n  \"VARARG\",\n  NULL\n};\n\n\n#define opmode(t,a,b,c,m) (((t)<<7) | ((a)<<6) | ((b)<<4) | ((c)<<2) | (m))\n\nconst lu_byte luaP_opmodes[NUM_OPCODES] = {\n/*       T  A    B       C     mode\t\t   opcode\t*/\n  opmode(0, 1, OpArgR, OpArgN, iABC) \t\t/* OP_MOVE */\n ,opmode(0, 1, OpArgK, OpArgN, iABx)\t\t/* OP_LOADK */\n ,opmode(0, 1, OpArgU, OpArgU, iABC)\t\t/* OP_LOADBOOL */\n ,opmode(0, 1, OpArgR, OpArgN, iABC)\t\t/* OP_LOADNIL */\n ,opmode(0, 1, OpArgU, OpArgN, iABC)\t\t/* OP_GETUPVAL */\n ,opmode(0, 1, OpArgK, OpArgN, iABx)\t\t/* OP_GETGLOBAL */\n ,opmode(0, 1, OpArgR, OpArgK, iABC)\t\t/* OP_GETTABLE */\n ,opmode(0, 0, OpArgK, OpArgN, iABx)\t\t/* OP_SETGLOBAL */\n ,opmode(0, 0, OpArgU, OpArgN, iABC)\t\t/* OP_SETUPVAL */\n ,opmode(0, 0, OpArgK, OpArgK, iABC)\t\t/* OP_SETTABLE */\n ,opmode(0, 1, OpArgU, OpArgU, iABC)\t\t/* OP_NEWTABLE */\n ,opmode(0, 1, OpArgR, OpArgK, iABC)\t\t/* OP_SELF */\n ,opmode(0, 1, OpArgK, OpArgK, iABC)\t\t/* OP_ADD */\n ,opmode(0, 1, OpArgK, OpArgK, iABC)\t\t/* OP_SUB */\n ,opmode(0, 1, OpArgK, OpArgK, iABC)\t\t/* OP_MUL */\n ,opmode(0, 1, OpArgK, OpArgK, iABC)\t\t/* OP_DIV */\n ,opmode(0, 1, OpArgK, OpArgK, iABC)\t\t/* OP_MOD */\n ,opmode(0, 1, OpArgK, OpArgK, iABC)\t\t/* OP_POW */\n ,opmode(0, 1, OpArgR, OpArgN, iABC)\t\t/* OP_UNM */\n ,opmode(0, 1, OpArgR, OpArgN, iABC)\t\t/* OP_NOT */\n ,opmode(0, 1, OpArgR, OpArgN, iABC)\t\t/* OP_LEN */\n ,opmode(0, 1, OpArgR, OpArgR, iABC)\t\t/* OP_CONCAT */\n ,opmode(0, 0, OpArgR, OpArgN, iAsBx)\t\t/* OP_JMP */\n ,opmode(1, 0, OpArgK, OpArgK, iABC)\t\t/* OP_EQ */\n ,opmode(1, 0, OpArgK, OpArgK, iABC)\t\t/* OP_LT */\n ,opmode(1, 0, OpArgK, OpArgK, iABC)\t\t/* OP_LE */\n ,opmode(1, 1, OpArgR, OpArgU, iABC)\t\t/* OP_TEST */\n ,opmode(1, 1, OpArgR, OpArgU, iABC)\t\t/* OP_TESTSET */\n ,opmode(0, 1, OpArgU, OpArgU, iABC)\t\t/* OP_CALL */\n ,opmode(0, 1, OpArgU, OpArgU, iABC)\t\t/* OP_TAILCALL */\n ,opmode(0, 0, OpArgU, OpArgN, iABC)\t\t/* OP_RETURN */\n ,opmode(0, 1, OpArgR, OpArgN, iAsBx)\t\t/* OP_FORLOOP */\n ,opmode(0, 1, OpArgR, OpArgN, iAsBx)\t\t/* OP_FORPREP */\n ,opmode(1, 0, OpArgN, OpArgU, iABC)\t\t/* OP_TFORLOOP */\n ,opmode(0, 0, OpArgU, OpArgU, iABC)\t\t/* OP_SETLIST */\n ,opmode(0, 0, OpArgN, OpArgN, iABC)\t\t/* OP_CLOSE */\n ,opmode(0, 1, OpArgU, OpArgN, iABx)\t\t/* OP_CLOSURE */\n ,opmode(0, 1, OpArgU, OpArgN, iABC)\t\t/* OP_VARARG */\n};\n\n"
  },
  {
    "path": "deps/lua/src/lopcodes.h",
    "content": "/*\n** $Id: lopcodes.h,v 1.125.1.1 2007/12/27 13:02:25 roberto Exp $\n** Opcodes for Lua virtual machine\n** See Copyright Notice in lua.h\n*/\n\n#ifndef lopcodes_h\n#define lopcodes_h\n\n#include \"llimits.h\"\n\n\n/*===========================================================================\n  We assume that instructions are unsigned numbers.\n  All instructions have an opcode in the first 6 bits.\n  Instructions can have the following fields:\n\t`A' : 8 bits\n\t`B' : 9 bits\n\t`C' : 9 bits\n\t`Bx' : 18 bits (`B' and `C' together)\n\t`sBx' : signed Bx\n\n  A signed argument is represented in excess K; that is, the number\n  value is the unsigned value minus K. K is exactly the maximum value\n  for that argument (so that -max is represented by 0, and +max is\n  represented by 2*max), which is half the maximum for the corresponding\n  unsigned argument.\n===========================================================================*/\n\n\nenum OpMode {iABC, iABx, iAsBx};  /* basic instruction format */\n\n\n/*\n** size and position of opcode arguments.\n*/\n#define SIZE_C\t\t9\n#define SIZE_B\t\t9\n#define SIZE_Bx\t\t(SIZE_C + SIZE_B)\n#define SIZE_A\t\t8\n\n#define SIZE_OP\t\t6\n\n#define POS_OP\t\t0\n#define POS_A\t\t(POS_OP + SIZE_OP)\n#define POS_C\t\t(POS_A + SIZE_A)\n#define POS_B\t\t(POS_C + SIZE_C)\n#define POS_Bx\t\tPOS_C\n\n\n/*\n** limits for opcode arguments.\n** we use (signed) int to manipulate most arguments,\n** so they must fit in LUAI_BITSINT-1 bits (-1 for sign)\n*/\n#if SIZE_Bx < LUAI_BITSINT-1\n#define MAXARG_Bx        ((1<<SIZE_Bx)-1)\n#define MAXARG_sBx        (MAXARG_Bx>>1)         /* `sBx' is signed */\n#else\n#define MAXARG_Bx        MAX_INT\n#define MAXARG_sBx        MAX_INT\n#endif\n\n\n#define MAXARG_A        ((1<<SIZE_A)-1)\n#define MAXARG_B        ((1<<SIZE_B)-1)\n#define MAXARG_C        ((1<<SIZE_C)-1)\n\n\n/* creates a mask with `n' 1 bits at position `p' */\n#define MASK1(n,p)\t((~((~(Instruction)0)<<n))<<p)\n\n/* creates a mask with `n' 0 bits at position `p' */\n#define MASK0(n,p)\t(~MASK1(n,p))\n\n/*\n** the following macros help to manipulate instructions\n*/\n\n#define GET_OPCODE(i)\t(cast(OpCode, ((i)>>POS_OP) & MASK1(SIZE_OP,0)))\n#define SET_OPCODE(i,o)\t((i) = (((i)&MASK0(SIZE_OP,POS_OP)) | \\\n\t\t((cast(Instruction, o)<<POS_OP)&MASK1(SIZE_OP,POS_OP))))\n\n#define GETARG_A(i)\t(cast(int, ((i)>>POS_A) & MASK1(SIZE_A,0)))\n#define SETARG_A(i,u)\t((i) = (((i)&MASK0(SIZE_A,POS_A)) | \\\n\t\t((cast(Instruction, u)<<POS_A)&MASK1(SIZE_A,POS_A))))\n\n#define GETARG_B(i)\t(cast(int, ((i)>>POS_B) & MASK1(SIZE_B,0)))\n#define SETARG_B(i,b)\t((i) = (((i)&MASK0(SIZE_B,POS_B)) | \\\n\t\t((cast(Instruction, b)<<POS_B)&MASK1(SIZE_B,POS_B))))\n\n#define GETARG_C(i)\t(cast(int, ((i)>>POS_C) & MASK1(SIZE_C,0)))\n#define SETARG_C(i,b)\t((i) = (((i)&MASK0(SIZE_C,POS_C)) | \\\n\t\t((cast(Instruction, b)<<POS_C)&MASK1(SIZE_C,POS_C))))\n\n#define GETARG_Bx(i)\t(cast(int, ((i)>>POS_Bx) & MASK1(SIZE_Bx,0)))\n#define SETARG_Bx(i,b)\t((i) = (((i)&MASK0(SIZE_Bx,POS_Bx)) | \\\n\t\t((cast(Instruction, b)<<POS_Bx)&MASK1(SIZE_Bx,POS_Bx))))\n\n#define GETARG_sBx(i)\t(GETARG_Bx(i)-MAXARG_sBx)\n#define SETARG_sBx(i,b)\tSETARG_Bx((i),cast(unsigned int, (b)+MAXARG_sBx))\n\n\n#define CREATE_ABC(o,a,b,c)\t((cast(Instruction, o)<<POS_OP) \\\n\t\t\t| (cast(Instruction, a)<<POS_A) \\\n\t\t\t| (cast(Instruction, b)<<POS_B) \\\n\t\t\t| (cast(Instruction, c)<<POS_C))\n\n#define CREATE_ABx(o,a,bc)\t((cast(Instruction, o)<<POS_OP) \\\n\t\t\t| (cast(Instruction, a)<<POS_A) \\\n\t\t\t| (cast(Instruction, bc)<<POS_Bx))\n\n\n/*\n** Macros to operate RK indices\n*/\n\n/* this bit 1 means constant (0 means register) */\n#define BITRK\t\t(1 << (SIZE_B - 1))\n\n/* test whether value is a constant */\n#define ISK(x)\t\t((x) & BITRK)\n\n/* gets the index of the constant */\n#define INDEXK(r)\t((int)(r) & ~BITRK)\n\n#define MAXINDEXRK\t(BITRK - 1)\n\n/* code a constant index as a RK value */\n#define RKASK(x)\t((x) | BITRK)\n\n\n/*\n** invalid register that fits in 8 bits\n*/\n#define NO_REG\t\tMAXARG_A\n\n\n/*\n** R(x) - register\n** Kst(x) - constant (in constant table)\n** RK(x) == if ISK(x) then Kst(INDEXK(x)) else R(x)\n*/\n\n\n/*\n** grep \"ORDER OP\" if you change these enums\n*/\n\ntypedef enum {\n/*----------------------------------------------------------------------\nname\t\targs\tdescription\n------------------------------------------------------------------------*/\nOP_MOVE,/*\tA B\tR(A) := R(B)\t\t\t\t\t*/\nOP_LOADK,/*\tA Bx\tR(A) := Kst(Bx)\t\t\t\t\t*/\nOP_LOADBOOL,/*\tA B C\tR(A) := (Bool)B; if (C) pc++\t\t\t*/\nOP_LOADNIL,/*\tA B\tR(A) := ... := R(B) := nil\t\t\t*/\nOP_GETUPVAL,/*\tA B\tR(A) := UpValue[B]\t\t\t\t*/\n\nOP_GETGLOBAL,/*\tA Bx\tR(A) := Gbl[Kst(Bx)]\t\t\t\t*/\nOP_GETTABLE,/*\tA B C\tR(A) := R(B)[RK(C)]\t\t\t\t*/\n\nOP_SETGLOBAL,/*\tA Bx\tGbl[Kst(Bx)] := R(A)\t\t\t\t*/\nOP_SETUPVAL,/*\tA B\tUpValue[B] := R(A)\t\t\t\t*/\nOP_SETTABLE,/*\tA B C\tR(A)[RK(B)] := RK(C)\t\t\t\t*/\n\nOP_NEWTABLE,/*\tA B C\tR(A) := {} (size = B,C)\t\t\t\t*/\n\nOP_SELF,/*\tA B C\tR(A+1) := R(B); R(A) := R(B)[RK(C)]\t\t*/\n\nOP_ADD,/*\tA B C\tR(A) := RK(B) + RK(C)\t\t\t\t*/\nOP_SUB,/*\tA B C\tR(A) := RK(B) - RK(C)\t\t\t\t*/\nOP_MUL,/*\tA B C\tR(A) := RK(B) * RK(C)\t\t\t\t*/\nOP_DIV,/*\tA B C\tR(A) := RK(B) / RK(C)\t\t\t\t*/\nOP_MOD,/*\tA B C\tR(A) := RK(B) % RK(C)\t\t\t\t*/\nOP_POW,/*\tA B C\tR(A) := RK(B) ^ RK(C)\t\t\t\t*/\nOP_UNM,/*\tA B\tR(A) := -R(B)\t\t\t\t\t*/\nOP_NOT,/*\tA B\tR(A) := not R(B)\t\t\t\t*/\nOP_LEN,/*\tA B\tR(A) := length of R(B)\t\t\t\t*/\n\nOP_CONCAT,/*\tA B C\tR(A) := R(B).. ... ..R(C)\t\t\t*/\n\nOP_JMP,/*\tsBx\tpc+=sBx\t\t\t\t\t*/\n\nOP_EQ,/*\tA B C\tif ((RK(B) == RK(C)) ~= A) then pc++\t\t*/\nOP_LT,/*\tA B C\tif ((RK(B) <  RK(C)) ~= A) then pc++  \t\t*/\nOP_LE,/*\tA B C\tif ((RK(B) <= RK(C)) ~= A) then pc++  \t\t*/\n\nOP_TEST,/*\tA C\tif not (R(A) <=> C) then pc++\t\t\t*/ \nOP_TESTSET,/*\tA B C\tif (R(B) <=> C) then R(A) := R(B) else pc++\t*/ \n\nOP_CALL,/*\tA B C\tR(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1)) */\nOP_TAILCALL,/*\tA B C\treturn R(A)(R(A+1), ... ,R(A+B-1))\t\t*/\nOP_RETURN,/*\tA B\treturn R(A), ... ,R(A+B-2)\t(see note)\t*/\n\nOP_FORLOOP,/*\tA sBx\tR(A)+=R(A+2);\n\t\t\tif R(A) <?= R(A+1) then { pc+=sBx; R(A+3)=R(A) }*/\nOP_FORPREP,/*\tA sBx\tR(A)-=R(A+2); pc+=sBx\t\t\t\t*/\n\nOP_TFORLOOP,/*\tA C\tR(A+3), ... ,R(A+2+C) := R(A)(R(A+1), R(A+2)); \n                        if R(A+3) ~= nil then R(A+2)=R(A+3) else pc++\t*/ \nOP_SETLIST,/*\tA B C\tR(A)[(C-1)*FPF+i] := R(A+i), 1 <= i <= B\t*/\n\nOP_CLOSE,/*\tA \tclose all variables in the stack up to (>=) R(A)*/\nOP_CLOSURE,/*\tA Bx\tR(A) := closure(KPROTO[Bx], R(A), ... ,R(A+n))\t*/\n\nOP_VARARG/*\tA B\tR(A), R(A+1), ..., R(A+B-1) = vararg\t\t*/\n} OpCode;\n\n\n#define NUM_OPCODES\t(cast(int, OP_VARARG) + 1)\n\n\n\n/*===========================================================================\n  Notes:\n  (*) In OP_CALL, if (B == 0) then B = top. C is the number of returns - 1,\n      and can be 0: OP_CALL then sets `top' to last_result+1, so\n      next open instruction (OP_CALL, OP_RETURN, OP_SETLIST) may use `top'.\n\n  (*) In OP_VARARG, if (B == 0) then use actual number of varargs and\n      set top (like in OP_CALL with C == 0).\n\n  (*) In OP_RETURN, if (B == 0) then return up to `top'\n\n  (*) In OP_SETLIST, if (B == 0) then B = `top';\n      if (C == 0) then next `instruction' is real C\n\n  (*) For comparisons, A specifies what condition the test should accept\n      (true or false).\n\n  (*) All `skips' (pc++) assume that next instruction is a jump\n===========================================================================*/\n\n\n/*\n** masks for instruction properties. The format is:\n** bits 0-1: op mode\n** bits 2-3: C arg mode\n** bits 4-5: B arg mode\n** bit 6: instruction set register A\n** bit 7: operator is a test\n*/  \n\nenum OpArgMask {\n  OpArgN,  /* argument is not used */\n  OpArgU,  /* argument is used */\n  OpArgR,  /* argument is a register or a jump offset */\n  OpArgK   /* argument is a constant or register/constant */\n};\n\nLUAI_DATA const lu_byte luaP_opmodes[NUM_OPCODES];\n\n#define getOpMode(m)\t(cast(enum OpMode, luaP_opmodes[m] & 3))\n#define getBMode(m)\t(cast(enum OpArgMask, (luaP_opmodes[m] >> 4) & 3))\n#define getCMode(m)\t(cast(enum OpArgMask, (luaP_opmodes[m] >> 2) & 3))\n#define testAMode(m)\t(luaP_opmodes[m] & (1 << 6))\n#define testTMode(m)\t(luaP_opmodes[m] & (1 << 7))\n\n\nLUAI_DATA const char *const luaP_opnames[NUM_OPCODES+1];  /* opcode names */\n\n\n/* number of list items to accumulate before a SETLIST instruction */\n#define LFIELDS_PER_FLUSH\t50\n\n\n#endif\n"
  },
  {
    "path": "deps/lua/src/loslib.c",
    "content": "/*\n** $Id: loslib.c,v 1.19.1.3 2008/01/18 16:38:18 roberto Exp $\n** Standard Operating System library\n** See Copyright Notice in lua.h\n*/\n\n\n#include <errno.h>\n#include <locale.h>\n#include <stdlib.h>\n#include <string.h>\n#include <time.h>\n\n#define loslib_c\n#define LUA_LIB\n\n#include \"lua.h\"\n\n#include \"lauxlib.h\"\n#include \"lualib.h\"\n\n\nstatic int os_pushresult (lua_State *L, int i, const char *filename) {\n  int en = errno;  /* calls to Lua API may change this value */\n  if (i) {\n    lua_pushboolean(L, 1);\n    return 1;\n  }\n  else {\n    lua_pushnil(L);\n    lua_pushfstring(L, \"%s: %s\", filename, strerror(en));\n    lua_pushinteger(L, en);\n    return 3;\n  }\n}\n\n\nstatic int os_execute (lua_State *L) {\n  lua_pushinteger(L, system(luaL_optstring(L, 1, NULL)));\n  return 1;\n}\n\n\nstatic int os_remove (lua_State *L) {\n  const char *filename = luaL_checkstring(L, 1);\n  return os_pushresult(L, remove(filename) == 0, filename);\n}\n\n\nstatic int os_rename (lua_State *L) {\n  const char *fromname = luaL_checkstring(L, 1);\n  const char *toname = luaL_checkstring(L, 2);\n  return os_pushresult(L, rename(fromname, toname) == 0, fromname);\n}\n\n\nstatic int os_tmpname (lua_State *L) {\n  char buff[LUA_TMPNAMBUFSIZE];\n  int err;\n  lua_tmpnam(buff, err);\n  if (err)\n    return luaL_error(L, \"unable to generate a unique filename\");\n  lua_pushstring(L, buff);\n  return 1;\n}\n\n\nstatic int os_getenv (lua_State *L) {\n  lua_pushstring(L, getenv(luaL_checkstring(L, 1)));  /* if NULL push nil */\n  return 1;\n}\n\n\nstatic int os_clock (lua_State *L) {\n  lua_pushnumber(L, ((lua_Number)clock())/(lua_Number)CLOCKS_PER_SEC);\n  return 1;\n}\n\n\n/*\n** {======================================================\n** Time/Date operations\n** { year=%Y, month=%m, day=%d, hour=%H, min=%M, sec=%S,\n**   wday=%w+1, yday=%j, isdst=? }\n** =======================================================\n*/\n\nstatic void setfield (lua_State *L, const char *key, int value) {\n  lua_pushinteger(L, value);\n  lua_setfield(L, -2, key);\n}\n\nstatic void setboolfield (lua_State *L, const char *key, int value) {\n  if (value < 0)  /* undefined? */\n    return;  /* does not set field */\n  lua_pushboolean(L, value);\n  lua_setfield(L, -2, key);\n}\n\nstatic int getboolfield (lua_State *L, const char *key) {\n  int res;\n  lua_getfield(L, -1, key);\n  res = lua_isnil(L, -1) ? -1 : lua_toboolean(L, -1);\n  lua_pop(L, 1);\n  return res;\n}\n\n\nstatic int getfield (lua_State *L, const char *key, int d) {\n  int res;\n  lua_getfield(L, -1, key);\n  if (lua_isnumber(L, -1))\n    res = (int)lua_tointeger(L, -1);\n  else {\n    if (d < 0)\n      return luaL_error(L, \"field \" LUA_QS \" missing in date table\", key);\n    res = d;\n  }\n  lua_pop(L, 1);\n  return res;\n}\n\n\nstatic int os_date (lua_State *L) {\n  const char *s = luaL_optstring(L, 1, \"%c\");\n  time_t t = luaL_opt(L, (time_t)luaL_checknumber, 2, time(NULL));\n  struct tm *stm;\n  if (*s == '!') {  /* UTC? */\n    stm = gmtime(&t);\n    s++;  /* skip `!' */\n  }\n  else\n    stm = localtime(&t);\n  if (stm == NULL)  /* invalid date? */\n    lua_pushnil(L);\n  else if (strcmp(s, \"*t\") == 0) {\n    lua_createtable(L, 0, 9);  /* 9 = number of fields */\n    setfield(L, \"sec\", stm->tm_sec);\n    setfield(L, \"min\", stm->tm_min);\n    setfield(L, \"hour\", stm->tm_hour);\n    setfield(L, \"day\", stm->tm_mday);\n    setfield(L, \"month\", stm->tm_mon+1);\n    setfield(L, \"year\", stm->tm_year+1900);\n    setfield(L, \"wday\", stm->tm_wday+1);\n    setfield(L, \"yday\", stm->tm_yday+1);\n    setboolfield(L, \"isdst\", stm->tm_isdst);\n  }\n  else {\n    char cc[3];\n    luaL_Buffer b;\n    cc[0] = '%'; cc[2] = '\\0';\n    luaL_buffinit(L, &b);\n    for (; *s; s++) {\n      if (*s != '%' || *(s + 1) == '\\0')  /* no conversion specifier? */\n        luaL_addchar(&b, *s);\n      else {\n        size_t reslen;\n        char buff[200];  /* should be big enough for any conversion result */\n        cc[1] = *(++s);\n        reslen = strftime(buff, sizeof(buff), cc, stm);\n        luaL_addlstring(&b, buff, reslen);\n      }\n    }\n    luaL_pushresult(&b);\n  }\n  return 1;\n}\n\n\nstatic int os_time (lua_State *L) {\n  time_t t;\n  if (lua_isnoneornil(L, 1))  /* called without args? */\n    t = time(NULL);  /* get current time */\n  else {\n    struct tm ts;\n    luaL_checktype(L, 1, LUA_TTABLE);\n    lua_settop(L, 1);  /* make sure table is at the top */\n    ts.tm_sec = getfield(L, \"sec\", 0);\n    ts.tm_min = getfield(L, \"min\", 0);\n    ts.tm_hour = getfield(L, \"hour\", 12);\n    ts.tm_mday = getfield(L, \"day\", -1);\n    ts.tm_mon = getfield(L, \"month\", -1) - 1;\n    ts.tm_year = getfield(L, \"year\", -1) - 1900;\n    ts.tm_isdst = getboolfield(L, \"isdst\");\n    t = mktime(&ts);\n  }\n  if (t == (time_t)(-1))\n    lua_pushnil(L);\n  else\n    lua_pushnumber(L, (lua_Number)t);\n  return 1;\n}\n\n\nstatic int os_difftime (lua_State *L) {\n  lua_pushnumber(L, difftime((time_t)(luaL_checknumber(L, 1)),\n                             (time_t)(luaL_optnumber(L, 2, 0))));\n  return 1;\n}\n\n/* }====================================================== */\n\n\nstatic int os_setlocale (lua_State *L) {\n  static const int cat[] = {LC_ALL, LC_COLLATE, LC_CTYPE, LC_MONETARY,\n                      LC_NUMERIC, LC_TIME};\n  static const char *const catnames[] = {\"all\", \"collate\", \"ctype\", \"monetary\",\n     \"numeric\", \"time\", NULL};\n  const char *l = luaL_optstring(L, 1, NULL);\n  int op = luaL_checkoption(L, 2, \"all\", catnames);\n  lua_pushstring(L, setlocale(cat[op], l));\n  return 1;\n}\n\n\nstatic int os_exit (lua_State *L) {\n  exit(luaL_optint(L, 1, EXIT_SUCCESS));\n}\n\nstatic const luaL_Reg syslib[] = {\n  {\"clock\",     os_clock},\n  {\"date\",      os_date},\n  {\"difftime\",  os_difftime},\n  {\"execute\",   os_execute},\n  {\"exit\",      os_exit},\n  {\"getenv\",    os_getenv},\n  {\"remove\",    os_remove},\n  {\"rename\",    os_rename},\n  {\"setlocale\", os_setlocale},\n  {\"time\",      os_time},\n  {\"tmpname\",   os_tmpname},\n  {NULL, NULL}\n};\n\n/* }====================================================== */\n\n\n\nLUALIB_API int luaopen_os (lua_State *L) {\n  luaL_register(L, LUA_OSLIBNAME, syslib);\n  return 1;\n}\n\n"
  },
  {
    "path": "deps/lua/src/lparser.c",
    "content": "/*\n** $Id: lparser.c,v 2.42.1.4 2011/10/21 19:31:42 roberto Exp $\n** Lua Parser\n** See Copyright Notice in lua.h\n*/\n\n\n#include <string.h>\n\n#define lparser_c\n#define LUA_CORE\n\n#include \"lua.h\"\n\n#include \"lcode.h\"\n#include \"ldebug.h\"\n#include \"ldo.h\"\n#include \"lfunc.h\"\n#include \"llex.h\"\n#include \"lmem.h\"\n#include \"lobject.h\"\n#include \"lopcodes.h\"\n#include \"lparser.h\"\n#include \"lstate.h\"\n#include \"lstring.h\"\n#include \"ltable.h\"\n\n\n\n#define hasmultret(k)\t\t((k) == VCALL || (k) == VVARARG)\n\n#define getlocvar(fs, i)\t((fs)->f->locvars[(fs)->actvar[i]])\n\n#define luaY_checklimit(fs,v,l,m)\tif ((v)>(l)) errorlimit(fs,l,m)\n\n\n/*\n** nodes for block list (list of active blocks)\n*/\ntypedef struct BlockCnt {\n  struct BlockCnt *previous;  /* chain */\n  int breaklist;  /* list of jumps out of this loop */\n  lu_byte nactvar;  /* # active locals outside the breakable structure */\n  lu_byte upval;  /* true if some variable in the block is an upvalue */\n  lu_byte isbreakable;  /* true if `block' is a loop */\n} BlockCnt;\n\n\n\n/*\n** prototypes for recursive non-terminal functions\n*/\nstatic void chunk (LexState *ls);\nstatic void expr (LexState *ls, expdesc *v);\n\n\nstatic void anchor_token (LexState *ls) {\n  if (ls->t.token == TK_NAME || ls->t.token == TK_STRING) {\n    TString *ts = ls->t.seminfo.ts;\n    luaX_newstring(ls, getstr(ts), ts->tsv.len);\n  }\n}\n\n\nstatic void error_expected (LexState *ls, int token) {\n  luaX_syntaxerror(ls,\n      luaO_pushfstring(ls->L, LUA_QS \" expected\", luaX_token2str(ls, token)));\n}\n\n\nstatic void errorlimit (FuncState *fs, int limit, const char *what) {\n  const char *msg = (fs->f->linedefined == 0) ?\n    luaO_pushfstring(fs->L, \"main function has more than %d %s\", limit, what) :\n    luaO_pushfstring(fs->L, \"function at line %d has more than %d %s\",\n                            fs->f->linedefined, limit, what);\n  luaX_lexerror(fs->ls, msg, 0);\n}\n\n\nstatic int testnext (LexState *ls, int c) {\n  if (ls->t.token == c) {\n    luaX_next(ls);\n    return 1;\n  }\n  else return 0;\n}\n\n\nstatic void check (LexState *ls, int c) {\n  if (ls->t.token != c)\n    error_expected(ls, c);\n}\n\nstatic void checknext (LexState *ls, int c) {\n  check(ls, c);\n  luaX_next(ls);\n}\n\n\n#define check_condition(ls,c,msg)\t{ if (!(c)) luaX_syntaxerror(ls, msg); }\n\n\n\nstatic void check_match (LexState *ls, int what, int who, int where) {\n  if (!testnext(ls, what)) {\n    if (where == ls->linenumber)\n      error_expected(ls, what);\n    else {\n      luaX_syntaxerror(ls, luaO_pushfstring(ls->L,\n             LUA_QS \" expected (to close \" LUA_QS \" at line %d)\",\n              luaX_token2str(ls, what), luaX_token2str(ls, who), where));\n    }\n  }\n}\n\n\nstatic TString *str_checkname (LexState *ls) {\n  TString *ts;\n  check(ls, TK_NAME);\n  ts = ls->t.seminfo.ts;\n  luaX_next(ls);\n  return ts;\n}\n\n\nstatic void init_exp (expdesc *e, expkind k, int i) {\n  e->f = e->t = NO_JUMP;\n  e->k = k;\n  e->u.s.info = i;\n}\n\n\nstatic void codestring (LexState *ls, expdesc *e, TString *s) {\n  init_exp(e, VK, luaK_stringK(ls->fs, s));\n}\n\n\nstatic void checkname(LexState *ls, expdesc *e) {\n  codestring(ls, e, str_checkname(ls));\n}\n\n\nstatic int registerlocalvar (LexState *ls, TString *varname) {\n  FuncState *fs = ls->fs;\n  Proto *f = fs->f;\n  int oldsize = f->sizelocvars;\n  luaM_growvector(ls->L, f->locvars, fs->nlocvars, f->sizelocvars,\n                  LocVar, SHRT_MAX, \"too many local variables\");\n  while (oldsize < f->sizelocvars) f->locvars[oldsize++].varname = NULL;\n  f->locvars[fs->nlocvars].varname = varname;\n  luaC_objbarrier(ls->L, f, varname);\n  return fs->nlocvars++;\n}\n\n\n#define new_localvarliteral(ls,v,n) \\\n  new_localvar(ls, luaX_newstring(ls, \"\" v, (sizeof(v)/sizeof(char))-1), n)\n\n\nstatic void new_localvar (LexState *ls, TString *name, int n) {\n  FuncState *fs = ls->fs;\n  luaY_checklimit(fs, fs->nactvar+n+1, LUAI_MAXVARS, \"local variables\");\n  fs->actvar[fs->nactvar+n] = cast(unsigned short, registerlocalvar(ls, name));\n}\n\n\nstatic void adjustlocalvars (LexState *ls, int nvars) {\n  FuncState *fs = ls->fs;\n  fs->nactvar = cast_byte(fs->nactvar + nvars);\n  for (; nvars; nvars--) {\n    getlocvar(fs, fs->nactvar - nvars).startpc = fs->pc;\n  }\n}\n\n\nstatic void removevars (LexState *ls, int tolevel) {\n  FuncState *fs = ls->fs;\n  while (fs->nactvar > tolevel)\n    getlocvar(fs, --fs->nactvar).endpc = fs->pc;\n}\n\n\nstatic int indexupvalue (FuncState *fs, TString *name, expdesc *v) {\n  int i;\n  Proto *f = fs->f;\n  int oldsize = f->sizeupvalues;\n  for (i=0; i<f->nups; i++) {\n    if (fs->upvalues[i].k == v->k && fs->upvalues[i].info == v->u.s.info) {\n      lua_assert(f->upvalues[i] == name);\n      return i;\n    }\n  }\n  /* new one */\n  luaY_checklimit(fs, f->nups + 1, LUAI_MAXUPVALUES, \"upvalues\");\n  luaM_growvector(fs->L, f->upvalues, f->nups, f->sizeupvalues,\n                  TString *, MAX_INT, \"\");\n  while (oldsize < f->sizeupvalues) f->upvalues[oldsize++] = NULL;\n  f->upvalues[f->nups] = name;\n  luaC_objbarrier(fs->L, f, name);\n  lua_assert(v->k == VLOCAL || v->k == VUPVAL);\n  fs->upvalues[f->nups].k = cast_byte(v->k);\n  fs->upvalues[f->nups].info = cast_byte(v->u.s.info);\n  return f->nups++;\n}\n\n\nstatic int searchvar (FuncState *fs, TString *n) {\n  int i;\n  for (i=fs->nactvar-1; i >= 0; i--) {\n    if (n == getlocvar(fs, i).varname)\n      return i;\n  }\n  return -1;  /* not found */\n}\n\n\nstatic void markupval (FuncState *fs, int level) {\n  BlockCnt *bl = fs->bl;\n  while (bl && bl->nactvar > level) bl = bl->previous;\n  if (bl) bl->upval = 1;\n}\n\n\nstatic int singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) {\n  if (fs == NULL) {  /* no more levels? */\n    init_exp(var, VGLOBAL, NO_REG);  /* default is global variable */\n    return VGLOBAL;\n  }\n  else {\n    int v = searchvar(fs, n);  /* look up at current level */\n    if (v >= 0) {\n      init_exp(var, VLOCAL, v);\n      if (!base)\n        markupval(fs, v);  /* local will be used as an upval */\n      return VLOCAL;\n    }\n    else {  /* not found at current level; try upper one */\n      if (singlevaraux(fs->prev, n, var, 0) == VGLOBAL)\n        return VGLOBAL;\n      var->u.s.info = indexupvalue(fs, n, var);  /* else was LOCAL or UPVAL */\n      var->k = VUPVAL;  /* upvalue in this level */\n      return VUPVAL;\n    }\n  }\n}\n\n\nstatic void singlevar (LexState *ls, expdesc *var) {\n  TString *varname = str_checkname(ls);\n  FuncState *fs = ls->fs;\n  if (singlevaraux(fs, varname, var, 1) == VGLOBAL)\n    var->u.s.info = luaK_stringK(fs, varname);  /* info points to global name */\n}\n\n\nstatic void adjust_assign (LexState *ls, int nvars, int nexps, expdesc *e) {\n  FuncState *fs = ls->fs;\n  int extra = nvars - nexps;\n  if (hasmultret(e->k)) {\n    extra++;  /* includes call itself */\n    if (extra < 0) extra = 0;\n    luaK_setreturns(fs, e, extra);  /* last exp. provides the difference */\n    if (extra > 1) luaK_reserveregs(fs, extra-1);\n  }\n  else {\n    if (e->k != VVOID) luaK_exp2nextreg(fs, e);  /* close last expression */\n    if (extra > 0) {\n      int reg = fs->freereg;\n      luaK_reserveregs(fs, extra);\n      luaK_nil(fs, reg, extra);\n    }\n  }\n}\n\n\nstatic void enterlevel (LexState *ls) {\n  if (++ls->L->nCcalls > LUAI_MAXCCALLS)\n\tluaX_lexerror(ls, \"chunk has too many syntax levels\", 0);\n}\n\n\n#define leavelevel(ls)\t((ls)->L->nCcalls--)\n\n\nstatic void enterblock (FuncState *fs, BlockCnt *bl, lu_byte isbreakable) {\n  bl->breaklist = NO_JUMP;\n  bl->isbreakable = isbreakable;\n  bl->nactvar = fs->nactvar;\n  bl->upval = 0;\n  bl->previous = fs->bl;\n  fs->bl = bl;\n  lua_assert(fs->freereg == fs->nactvar);\n}\n\n\nstatic void leaveblock (FuncState *fs) {\n  BlockCnt *bl = fs->bl;\n  fs->bl = bl->previous;\n  removevars(fs->ls, bl->nactvar);\n  if (bl->upval)\n    luaK_codeABC(fs, OP_CLOSE, bl->nactvar, 0, 0);\n  /* a block either controls scope or breaks (never both) */\n  lua_assert(!bl->isbreakable || !bl->upval);\n  lua_assert(bl->nactvar == fs->nactvar);\n  fs->freereg = fs->nactvar;  /* free registers */\n  luaK_patchtohere(fs, bl->breaklist);\n}\n\n\nstatic void pushclosure (LexState *ls, FuncState *func, expdesc *v) {\n  FuncState *fs = ls->fs;\n  Proto *f = fs->f;\n  int oldsize = f->sizep;\n  int i;\n  luaM_growvector(ls->L, f->p, fs->np, f->sizep, Proto *,\n                  MAXARG_Bx, \"constant table overflow\");\n  while (oldsize < f->sizep) f->p[oldsize++] = NULL;\n  f->p[fs->np++] = func->f;\n  luaC_objbarrier(ls->L, f, func->f);\n  init_exp(v, VRELOCABLE, luaK_codeABx(fs, OP_CLOSURE, 0, fs->np-1));\n  for (i=0; i<func->f->nups; i++) {\n    OpCode o = (func->upvalues[i].k == VLOCAL) ? OP_MOVE : OP_GETUPVAL;\n    luaK_codeABC(fs, o, 0, func->upvalues[i].info, 0);\n  }\n}\n\n\nstatic void open_func (LexState *ls, FuncState *fs) {\n  lua_State *L = ls->L;\n  Proto *f = luaF_newproto(L);\n  fs->f = f;\n  fs->prev = ls->fs;  /* linked list of funcstates */\n  fs->ls = ls;\n  fs->L = L;\n  ls->fs = fs;\n  fs->pc = 0;\n  fs->lasttarget = -1;\n  fs->jpc = NO_JUMP;\n  fs->freereg = 0;\n  fs->nk = 0;\n  fs->np = 0;\n  fs->nlocvars = 0;\n  fs->nactvar = 0;\n  fs->bl = NULL;\n  f->source = ls->source;\n  f->maxstacksize = 2;  /* registers 0/1 are always valid */\n  fs->h = luaH_new(L, 0, 0);\n  /* anchor table of constants and prototype (to avoid being collected) */\n  sethvalue2s(L, L->top, fs->h);\n  incr_top(L);\n  setptvalue2s(L, L->top, f);\n  incr_top(L);\n}\n\n\nstatic void close_func (LexState *ls) {\n  lua_State *L = ls->L;\n  FuncState *fs = ls->fs;\n  Proto *f = fs->f;\n  removevars(ls, 0);\n  luaK_ret(fs, 0, 0);  /* final return */\n  luaM_reallocvector(L, f->code, f->sizecode, fs->pc, Instruction);\n  f->sizecode = fs->pc;\n  luaM_reallocvector(L, f->lineinfo, f->sizelineinfo, fs->pc, int);\n  f->sizelineinfo = fs->pc;\n  luaM_reallocvector(L, f->k, f->sizek, fs->nk, TValue);\n  f->sizek = fs->nk;\n  luaM_reallocvector(L, f->p, f->sizep, fs->np, Proto *);\n  f->sizep = fs->np;\n  luaM_reallocvector(L, f->locvars, f->sizelocvars, fs->nlocvars, LocVar);\n  f->sizelocvars = fs->nlocvars;\n  luaM_reallocvector(L, f->upvalues, f->sizeupvalues, f->nups, TString *);\n  f->sizeupvalues = f->nups;\n  lua_assert(luaG_checkcode(f));\n  lua_assert(fs->bl == NULL);\n  ls->fs = fs->prev;\n  /* last token read was anchored in defunct function; must reanchor it */\n  if (fs) anchor_token(ls);\n  L->top -= 2;  /* remove table and prototype from the stack */\n}\n\n\nProto *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, const char *name) {\n  struct LexState lexstate;\n  struct FuncState funcstate;\n  lexstate.buff = buff;\n  luaX_setinput(L, &lexstate, z, luaS_new(L, name));\n  open_func(&lexstate, &funcstate);\n  funcstate.f->is_vararg = VARARG_ISVARARG;  /* main func. is always vararg */\n  luaX_next(&lexstate);  /* read first token */\n  chunk(&lexstate);\n  check(&lexstate, TK_EOS);\n  close_func(&lexstate);\n  lua_assert(funcstate.prev == NULL);\n  lua_assert(funcstate.f->nups == 0);\n  lua_assert(lexstate.fs == NULL);\n  return funcstate.f;\n}\n\n\n\n/*============================================================*/\n/* GRAMMAR RULES */\n/*============================================================*/\n\n\nstatic void field (LexState *ls, expdesc *v) {\n  /* field -> ['.' | ':'] NAME */\n  FuncState *fs = ls->fs;\n  expdesc key;\n  luaK_exp2anyreg(fs, v);\n  luaX_next(ls);  /* skip the dot or colon */\n  checkname(ls, &key);\n  luaK_indexed(fs, v, &key);\n}\n\n\nstatic void yindex (LexState *ls, expdesc *v) {\n  /* index -> '[' expr ']' */\n  luaX_next(ls);  /* skip the '[' */\n  expr(ls, v);\n  luaK_exp2val(ls->fs, v);\n  checknext(ls, ']');\n}\n\n\n/*\n** {======================================================================\n** Rules for Constructors\n** =======================================================================\n*/\n\n\nstruct ConsControl {\n  expdesc v;  /* last list item read */\n  expdesc *t;  /* table descriptor */\n  int nh;  /* total number of `record' elements */\n  int na;  /* total number of array elements */\n  int tostore;  /* number of array elements pending to be stored */\n};\n\n\nstatic void recfield (LexState *ls, struct ConsControl *cc) {\n  /* recfield -> (NAME | `['exp1`]') = exp1 */\n  FuncState *fs = ls->fs;\n  int reg = ls->fs->freereg;\n  expdesc key, val;\n  int rkkey;\n  if (ls->t.token == TK_NAME) {\n    luaY_checklimit(fs, cc->nh, MAX_INT, \"items in a constructor\");\n    checkname(ls, &key);\n  }\n  else  /* ls->t.token == '[' */\n    yindex(ls, &key);\n  cc->nh++;\n  checknext(ls, '=');\n  rkkey = luaK_exp2RK(fs, &key);\n  expr(ls, &val);\n  luaK_codeABC(fs, OP_SETTABLE, cc->t->u.s.info, rkkey, luaK_exp2RK(fs, &val));\n  fs->freereg = reg;  /* free registers */\n}\n\n\nstatic void closelistfield (FuncState *fs, struct ConsControl *cc) {\n  if (cc->v.k == VVOID) return;  /* there is no list item */\n  luaK_exp2nextreg(fs, &cc->v);\n  cc->v.k = VVOID;\n  if (cc->tostore == LFIELDS_PER_FLUSH) {\n    luaK_setlist(fs, cc->t->u.s.info, cc->na, cc->tostore);  /* flush */\n    cc->tostore = 0;  /* no more items pending */\n  }\n}\n\n\nstatic void lastlistfield (FuncState *fs, struct ConsControl *cc) {\n  if (cc->tostore == 0) return;\n  if (hasmultret(cc->v.k)) {\n    luaK_setmultret(fs, &cc->v);\n    luaK_setlist(fs, cc->t->u.s.info, cc->na, LUA_MULTRET);\n    cc->na--;  /* do not count last expression (unknown number of elements) */\n  }\n  else {\n    if (cc->v.k != VVOID)\n      luaK_exp2nextreg(fs, &cc->v);\n    luaK_setlist(fs, cc->t->u.s.info, cc->na, cc->tostore);\n  }\n}\n\n\nstatic void listfield (LexState *ls, struct ConsControl *cc) {\n  expr(ls, &cc->v);\n  luaY_checklimit(ls->fs, cc->na, MAX_INT, \"items in a constructor\");\n  cc->na++;\n  cc->tostore++;\n}\n\n\nstatic void constructor (LexState *ls, expdesc *t) {\n  /* constructor -> ?? */\n  FuncState *fs = ls->fs;\n  int line = ls->linenumber;\n  int pc = luaK_codeABC(fs, OP_NEWTABLE, 0, 0, 0);\n  struct ConsControl cc;\n  cc.na = cc.nh = cc.tostore = 0;\n  cc.t = t;\n  init_exp(t, VRELOCABLE, pc);\n  init_exp(&cc.v, VVOID, 0);  /* no value (yet) */\n  luaK_exp2nextreg(ls->fs, t);  /* fix it at stack top (for gc) */\n  checknext(ls, '{');\n  do {\n    lua_assert(cc.v.k == VVOID || cc.tostore > 0);\n    if (ls->t.token == '}') break;\n    closelistfield(fs, &cc);\n    switch(ls->t.token) {\n      case TK_NAME: {  /* may be listfields or recfields */\n        luaX_lookahead(ls);\n        if (ls->lookahead.token != '=')  /* expression? */\n          listfield(ls, &cc);\n        else\n          recfield(ls, &cc);\n        break;\n      }\n      case '[': {  /* constructor_item -> recfield */\n        recfield(ls, &cc);\n        break;\n      }\n      default: {  /* constructor_part -> listfield */\n        listfield(ls, &cc);\n        break;\n      }\n    }\n  } while (testnext(ls, ',') || testnext(ls, ';'));\n  check_match(ls, '}', '{', line);\n  lastlistfield(fs, &cc);\n  SETARG_B(fs->f->code[pc], luaO_int2fb(cc.na)); /* set initial array size */\n  SETARG_C(fs->f->code[pc], luaO_int2fb(cc.nh));  /* set initial table size */\n}\n\n/* }====================================================================== */\n\n\n\nstatic void parlist (LexState *ls) {\n  /* parlist -> [ param { `,' param } ] */\n  FuncState *fs = ls->fs;\n  Proto *f = fs->f;\n  int nparams = 0;\n  f->is_vararg = 0;\n  if (ls->t.token != ')') {  /* is `parlist' not empty? */\n    do {\n      switch (ls->t.token) {\n        case TK_NAME: {  /* param -> NAME */\n          new_localvar(ls, str_checkname(ls), nparams++);\n          break;\n        }\n        case TK_DOTS: {  /* param -> `...' */\n          luaX_next(ls);\n#if defined(LUA_COMPAT_VARARG)\n          /* use `arg' as default name */\n          new_localvarliteral(ls, \"arg\", nparams++);\n          f->is_vararg = VARARG_HASARG | VARARG_NEEDSARG;\n#endif\n          f->is_vararg |= VARARG_ISVARARG;\n          break;\n        }\n        default: luaX_syntaxerror(ls, \"<name> or \" LUA_QL(\"...\") \" expected\");\n      }\n    } while (!f->is_vararg && testnext(ls, ','));\n  }\n  adjustlocalvars(ls, nparams);\n  f->numparams = cast_byte(fs->nactvar - (f->is_vararg & VARARG_HASARG));\n  luaK_reserveregs(fs, fs->nactvar);  /* reserve register for parameters */\n}\n\n\nstatic void body (LexState *ls, expdesc *e, int needself, int line) {\n  /* body ->  `(' parlist `)' chunk END */\n  FuncState new_fs;\n  open_func(ls, &new_fs);\n  new_fs.f->linedefined = line;\n  checknext(ls, '(');\n  if (needself) {\n    new_localvarliteral(ls, \"self\", 0);\n    adjustlocalvars(ls, 1);\n  }\n  parlist(ls);\n  checknext(ls, ')');\n  chunk(ls);\n  new_fs.f->lastlinedefined = ls->linenumber;\n  check_match(ls, TK_END, TK_FUNCTION, line);\n  close_func(ls);\n  pushclosure(ls, &new_fs, e);\n}\n\n\nstatic int explist1 (LexState *ls, expdesc *v) {\n  /* explist1 -> expr { `,' expr } */\n  int n = 1;  /* at least one expression */\n  expr(ls, v);\n  while (testnext(ls, ',')) {\n    luaK_exp2nextreg(ls->fs, v);\n    expr(ls, v);\n    n++;\n  }\n  return n;\n}\n\n\nstatic void funcargs (LexState *ls, expdesc *f) {\n  FuncState *fs = ls->fs;\n  expdesc args;\n  int base, nparams;\n  int line = ls->linenumber;\n  switch (ls->t.token) {\n    case '(': {  /* funcargs -> `(' [ explist1 ] `)' */\n      if (line != ls->lastline)\n        luaX_syntaxerror(ls,\"ambiguous syntax (function call x new statement)\");\n      luaX_next(ls);\n      if (ls->t.token == ')')  /* arg list is empty? */\n        args.k = VVOID;\n      else {\n        explist1(ls, &args);\n        luaK_setmultret(fs, &args);\n      }\n      check_match(ls, ')', '(', line);\n      break;\n    }\n    case '{': {  /* funcargs -> constructor */\n      constructor(ls, &args);\n      break;\n    }\n    case TK_STRING: {  /* funcargs -> STRING */\n      codestring(ls, &args, ls->t.seminfo.ts);\n      luaX_next(ls);  /* must use `seminfo' before `next' */\n      break;\n    }\n    default: {\n      luaX_syntaxerror(ls, \"function arguments expected\");\n      return;\n    }\n  }\n  lua_assert(f->k == VNONRELOC);\n  base = f->u.s.info;  /* base register for call */\n  if (hasmultret(args.k))\n    nparams = LUA_MULTRET;  /* open call */\n  else {\n    if (args.k != VVOID)\n      luaK_exp2nextreg(fs, &args);  /* close last argument */\n    nparams = fs->freereg - (base+1);\n  }\n  init_exp(f, VCALL, luaK_codeABC(fs, OP_CALL, base, nparams+1, 2));\n  luaK_fixline(fs, line);\n  fs->freereg = base+1;  /* call remove function and arguments and leaves\n                            (unless changed) one result */\n}\n\n\n\n\n/*\n** {======================================================================\n** Expression parsing\n** =======================================================================\n*/\n\n\nstatic void prefixexp (LexState *ls, expdesc *v) {\n  /* prefixexp -> NAME | '(' expr ')' */\n  switch (ls->t.token) {\n    case '(': {\n      int line = ls->linenumber;\n      luaX_next(ls);\n      expr(ls, v);\n      check_match(ls, ')', '(', line);\n      luaK_dischargevars(ls->fs, v);\n      return;\n    }\n    case TK_NAME: {\n      singlevar(ls, v);\n      return;\n    }\n    default: {\n      luaX_syntaxerror(ls, \"unexpected symbol\");\n      return;\n    }\n  }\n}\n\n\nstatic void primaryexp (LexState *ls, expdesc *v) {\n  /* primaryexp ->\n        prefixexp { `.' NAME | `[' exp `]' | `:' NAME funcargs | funcargs } */\n  FuncState *fs = ls->fs;\n  prefixexp(ls, v);\n  for (;;) {\n    switch (ls->t.token) {\n      case '.': {  /* field */\n        field(ls, v);\n        break;\n      }\n      case '[': {  /* `[' exp1 `]' */\n        expdesc key;\n        luaK_exp2anyreg(fs, v);\n        yindex(ls, &key);\n        luaK_indexed(fs, v, &key);\n        break;\n      }\n      case ':': {  /* `:' NAME funcargs */\n        expdesc key;\n        luaX_next(ls);\n        checkname(ls, &key);\n        luaK_self(fs, v, &key);\n        funcargs(ls, v);\n        break;\n      }\n      case '(': case TK_STRING: case '{': {  /* funcargs */\n        luaK_exp2nextreg(fs, v);\n        funcargs(ls, v);\n        break;\n      }\n      default: return;\n    }\n  }\n}\n\n\nstatic void simpleexp (LexState *ls, expdesc *v) {\n  /* simpleexp -> NUMBER | STRING | NIL | true | false | ... |\n                  constructor | FUNCTION body | primaryexp */\n  switch (ls->t.token) {\n    case TK_NUMBER: {\n      init_exp(v, VKNUM, 0);\n      v->u.nval = ls->t.seminfo.r;\n      break;\n    }\n    case TK_STRING: {\n      codestring(ls, v, ls->t.seminfo.ts);\n      break;\n    }\n    case TK_NIL: {\n      init_exp(v, VNIL, 0);\n      break;\n    }\n    case TK_TRUE: {\n      init_exp(v, VTRUE, 0);\n      break;\n    }\n    case TK_FALSE: {\n      init_exp(v, VFALSE, 0);\n      break;\n    }\n    case TK_DOTS: {  /* vararg */\n      FuncState *fs = ls->fs;\n      check_condition(ls, fs->f->is_vararg,\n                      \"cannot use \" LUA_QL(\"...\") \" outside a vararg function\");\n      fs->f->is_vararg &= ~VARARG_NEEDSARG;  /* don't need 'arg' */\n      init_exp(v, VVARARG, luaK_codeABC(fs, OP_VARARG, 0, 1, 0));\n      break;\n    }\n    case '{': {  /* constructor */\n      constructor(ls, v);\n      return;\n    }\n    case TK_FUNCTION: {\n      luaX_next(ls);\n      body(ls, v, 0, ls->linenumber);\n      return;\n    }\n    default: {\n      primaryexp(ls, v);\n      return;\n    }\n  }\n  luaX_next(ls);\n}\n\n\nstatic UnOpr getunopr (int op) {\n  switch (op) {\n    case TK_NOT: return OPR_NOT;\n    case '-': return OPR_MINUS;\n    case '#': return OPR_LEN;\n    default: return OPR_NOUNOPR;\n  }\n}\n\n\nstatic BinOpr getbinopr (int op) {\n  switch (op) {\n    case '+': return OPR_ADD;\n    case '-': return OPR_SUB;\n    case '*': return OPR_MUL;\n    case '/': return OPR_DIV;\n    case '%': return OPR_MOD;\n    case '^': return OPR_POW;\n    case TK_CONCAT: return OPR_CONCAT;\n    case TK_NE: return OPR_NE;\n    case TK_EQ: return OPR_EQ;\n    case '<': return OPR_LT;\n    case TK_LE: return OPR_LE;\n    case '>': return OPR_GT;\n    case TK_GE: return OPR_GE;\n    case TK_AND: return OPR_AND;\n    case TK_OR: return OPR_OR;\n    default: return OPR_NOBINOPR;\n  }\n}\n\n\nstatic const struct {\n  lu_byte left;  /* left priority for each binary operator */\n  lu_byte right; /* right priority */\n} priority[] = {  /* ORDER OPR */\n   {6, 6}, {6, 6}, {7, 7}, {7, 7}, {7, 7},  /* `+' `-' `/' `%' */\n   {10, 9}, {5, 4},                 /* power and concat (right associative) */\n   {3, 3}, {3, 3},                  /* equality and inequality */\n   {3, 3}, {3, 3}, {3, 3}, {3, 3},  /* order */\n   {2, 2}, {1, 1}                   /* logical (and/or) */\n};\n\n#define UNARY_PRIORITY\t8  /* priority for unary operators */\n\n\n/*\n** subexpr -> (simpleexp | unop subexpr) { binop subexpr }\n** where `binop' is any binary operator with a priority higher than `limit'\n*/\nstatic BinOpr subexpr (LexState *ls, expdesc *v, unsigned int limit) {\n  BinOpr op;\n  UnOpr uop;\n  enterlevel(ls);\n  uop = getunopr(ls->t.token);\n  if (uop != OPR_NOUNOPR) {\n    luaX_next(ls);\n    subexpr(ls, v, UNARY_PRIORITY);\n    luaK_prefix(ls->fs, uop, v);\n  }\n  else simpleexp(ls, v);\n  /* expand while operators have priorities higher than `limit' */\n  op = getbinopr(ls->t.token);\n  while (op != OPR_NOBINOPR && priority[op].left > limit) {\n    expdesc v2;\n    BinOpr nextop;\n    luaX_next(ls);\n    luaK_infix(ls->fs, op, v);\n    /* read sub-expression with higher priority */\n    nextop = subexpr(ls, &v2, priority[op].right);\n    luaK_posfix(ls->fs, op, v, &v2);\n    op = nextop;\n  }\n  leavelevel(ls);\n  return op;  /* return first untreated operator */\n}\n\n\nstatic void expr (LexState *ls, expdesc *v) {\n  subexpr(ls, v, 0);\n}\n\n/* }==================================================================== */\n\n\n\n/*\n** {======================================================================\n** Rules for Statements\n** =======================================================================\n*/\n\n\nstatic int block_follow (int token) {\n  switch (token) {\n    case TK_ELSE: case TK_ELSEIF: case TK_END:\n    case TK_UNTIL: case TK_EOS:\n      return 1;\n    default: return 0;\n  }\n}\n\n\nstatic void block (LexState *ls) {\n  /* block -> chunk */\n  FuncState *fs = ls->fs;\n  BlockCnt bl;\n  enterblock(fs, &bl, 0);\n  chunk(ls);\n  lua_assert(bl.breaklist == NO_JUMP);\n  leaveblock(fs);\n}\n\n\n/*\n** structure to chain all variables in the left-hand side of an\n** assignment\n*/\nstruct LHS_assign {\n  struct LHS_assign *prev;\n  expdesc v;  /* variable (global, local, upvalue, or indexed) */\n};\n\n\n/*\n** check whether, in an assignment to a local variable, the local variable\n** is needed in a previous assignment (to a table). If so, save original\n** local value in a safe place and use this safe copy in the previous\n** assignment.\n*/\nstatic void check_conflict (LexState *ls, struct LHS_assign *lh, expdesc *v) {\n  FuncState *fs = ls->fs;\n  int extra = fs->freereg;  /* eventual position to save local variable */\n  int conflict = 0;\n  for (; lh; lh = lh->prev) {\n    if (lh->v.k == VINDEXED) {\n      if (lh->v.u.s.info == v->u.s.info) {  /* conflict? */\n        conflict = 1;\n        lh->v.u.s.info = extra;  /* previous assignment will use safe copy */\n      }\n      if (lh->v.u.s.aux == v->u.s.info) {  /* conflict? */\n        conflict = 1;\n        lh->v.u.s.aux = extra;  /* previous assignment will use safe copy */\n      }\n    }\n  }\n  if (conflict) {\n    luaK_codeABC(fs, OP_MOVE, fs->freereg, v->u.s.info, 0);  /* make copy */\n    luaK_reserveregs(fs, 1);\n  }\n}\n\n\nstatic void assignment (LexState *ls, struct LHS_assign *lh, int nvars) {\n  expdesc e;\n  check_condition(ls, VLOCAL <= lh->v.k && lh->v.k <= VINDEXED,\n                      \"syntax error\");\n  if (testnext(ls, ',')) {  /* assignment -> `,' primaryexp assignment */\n    struct LHS_assign nv;\n    nv.prev = lh;\n    primaryexp(ls, &nv.v);\n    if (nv.v.k == VLOCAL)\n      check_conflict(ls, lh, &nv.v);\n    luaY_checklimit(ls->fs, nvars, LUAI_MAXCCALLS - ls->L->nCcalls,\n                    \"variables in assignment\");\n    assignment(ls, &nv, nvars+1);\n  }\n  else {  /* assignment -> `=' explist1 */\n    int nexps;\n    checknext(ls, '=');\n    nexps = explist1(ls, &e);\n    if (nexps != nvars) {\n      adjust_assign(ls, nvars, nexps, &e);\n      if (nexps > nvars)\n        ls->fs->freereg -= nexps - nvars;  /* remove extra values */\n    }\n    else {\n      luaK_setoneret(ls->fs, &e);  /* close last expression */\n      luaK_storevar(ls->fs, &lh->v, &e);\n      return;  /* avoid default */\n    }\n  }\n  init_exp(&e, VNONRELOC, ls->fs->freereg-1);  /* default assignment */\n  luaK_storevar(ls->fs, &lh->v, &e);\n}\n\n\nstatic int cond (LexState *ls) {\n  /* cond -> exp */\n  expdesc v;\n  expr(ls, &v);  /* read condition */\n  if (v.k == VNIL) v.k = VFALSE;  /* `falses' are all equal here */\n  luaK_goiftrue(ls->fs, &v);\n  return v.f;\n}\n\n\nstatic void breakstat (LexState *ls) {\n  FuncState *fs = ls->fs;\n  BlockCnt *bl = fs->bl;\n  int upval = 0;\n  while (bl && !bl->isbreakable) {\n    upval |= bl->upval;\n    bl = bl->previous;\n  }\n  if (!bl)\n    luaX_syntaxerror(ls, \"no loop to break\");\n  if (upval)\n    luaK_codeABC(fs, OP_CLOSE, bl->nactvar, 0, 0);\n  luaK_concat(fs, &bl->breaklist, luaK_jump(fs));\n}\n\n\nstatic void whilestat (LexState *ls, int line) {\n  /* whilestat -> WHILE cond DO block END */\n  FuncState *fs = ls->fs;\n  int whileinit;\n  int condexit;\n  BlockCnt bl;\n  luaX_next(ls);  /* skip WHILE */\n  whileinit = luaK_getlabel(fs);\n  condexit = cond(ls);\n  enterblock(fs, &bl, 1);\n  checknext(ls, TK_DO);\n  block(ls);\n  luaK_patchlist(fs, luaK_jump(fs), whileinit);\n  check_match(ls, TK_END, TK_WHILE, line);\n  leaveblock(fs);\n  luaK_patchtohere(fs, condexit);  /* false conditions finish the loop */\n}\n\n\nstatic void repeatstat (LexState *ls, int line) {\n  /* repeatstat -> REPEAT block UNTIL cond */\n  int condexit;\n  FuncState *fs = ls->fs;\n  int repeat_init = luaK_getlabel(fs);\n  BlockCnt bl1, bl2;\n  enterblock(fs, &bl1, 1);  /* loop block */\n  enterblock(fs, &bl2, 0);  /* scope block */\n  luaX_next(ls);  /* skip REPEAT */\n  chunk(ls);\n  check_match(ls, TK_UNTIL, TK_REPEAT, line);\n  condexit = cond(ls);  /* read condition (inside scope block) */\n  if (!bl2.upval) {  /* no upvalues? */\n    leaveblock(fs);  /* finish scope */\n    luaK_patchlist(ls->fs, condexit, repeat_init);  /* close the loop */\n  }\n  else {  /* complete semantics when there are upvalues */\n    breakstat(ls);  /* if condition then break */\n    luaK_patchtohere(ls->fs, condexit);  /* else... */\n    leaveblock(fs);  /* finish scope... */\n    luaK_patchlist(ls->fs, luaK_jump(fs), repeat_init);  /* and repeat */\n  }\n  leaveblock(fs);  /* finish loop */\n}\n\n\nstatic int exp1 (LexState *ls) {\n  expdesc e;\n  int k;\n  expr(ls, &e);\n  k = e.k;\n  luaK_exp2nextreg(ls->fs, &e);\n  return k;\n}\n\n\nstatic void forbody (LexState *ls, int base, int line, int nvars, int isnum) {\n  /* forbody -> DO block */\n  BlockCnt bl;\n  FuncState *fs = ls->fs;\n  int prep, endfor;\n  adjustlocalvars(ls, 3);  /* control variables */\n  checknext(ls, TK_DO);\n  prep = isnum ? luaK_codeAsBx(fs, OP_FORPREP, base, NO_JUMP) : luaK_jump(fs);\n  enterblock(fs, &bl, 0);  /* scope for declared variables */\n  adjustlocalvars(ls, nvars);\n  luaK_reserveregs(fs, nvars);\n  block(ls);\n  leaveblock(fs);  /* end of scope for declared variables */\n  luaK_patchtohere(fs, prep);\n  endfor = (isnum) ? luaK_codeAsBx(fs, OP_FORLOOP, base, NO_JUMP) :\n                     luaK_codeABC(fs, OP_TFORLOOP, base, 0, nvars);\n  luaK_fixline(fs, line);  /* pretend that `OP_FOR' starts the loop */\n  luaK_patchlist(fs, (isnum ? endfor : luaK_jump(fs)), prep + 1);\n}\n\n\nstatic void fornum (LexState *ls, TString *varname, int line) {\n  /* fornum -> NAME = exp1,exp1[,exp1] forbody */\n  FuncState *fs = ls->fs;\n  int base = fs->freereg;\n  new_localvarliteral(ls, \"(for index)\", 0);\n  new_localvarliteral(ls, \"(for limit)\", 1);\n  new_localvarliteral(ls, \"(for step)\", 2);\n  new_localvar(ls, varname, 3);\n  checknext(ls, '=');\n  exp1(ls);  /* initial value */\n  checknext(ls, ',');\n  exp1(ls);  /* limit */\n  if (testnext(ls, ','))\n    exp1(ls);  /* optional step */\n  else {  /* default step = 1 */\n    luaK_codeABx(fs, OP_LOADK, fs->freereg, luaK_numberK(fs, 1));\n    luaK_reserveregs(fs, 1);\n  }\n  forbody(ls, base, line, 1, 1);\n}\n\n\nstatic void forlist (LexState *ls, TString *indexname) {\n  /* forlist -> NAME {,NAME} IN explist1 forbody */\n  FuncState *fs = ls->fs;\n  expdesc e;\n  int nvars = 0;\n  int line;\n  int base = fs->freereg;\n  /* create control variables */\n  new_localvarliteral(ls, \"(for generator)\", nvars++);\n  new_localvarliteral(ls, \"(for state)\", nvars++);\n  new_localvarliteral(ls, \"(for control)\", nvars++);\n  /* create declared variables */\n  new_localvar(ls, indexname, nvars++);\n  while (testnext(ls, ','))\n    new_localvar(ls, str_checkname(ls), nvars++);\n  checknext(ls, TK_IN);\n  line = ls->linenumber;\n  adjust_assign(ls, 3, explist1(ls, &e), &e);\n  luaK_checkstack(fs, 3);  /* extra space to call generator */\n  forbody(ls, base, line, nvars - 3, 0);\n}\n\n\nstatic void forstat (LexState *ls, int line) {\n  /* forstat -> FOR (fornum | forlist) END */\n  FuncState *fs = ls->fs;\n  TString *varname;\n  BlockCnt bl;\n  enterblock(fs, &bl, 1);  /* scope for loop and control variables */\n  luaX_next(ls);  /* skip `for' */\n  varname = str_checkname(ls);  /* first variable name */\n  switch (ls->t.token) {\n    case '=': fornum(ls, varname, line); break;\n    case ',': case TK_IN: forlist(ls, varname); break;\n    default: luaX_syntaxerror(ls, LUA_QL(\"=\") \" or \" LUA_QL(\"in\") \" expected\");\n  }\n  check_match(ls, TK_END, TK_FOR, line);\n  leaveblock(fs);  /* loop scope (`break' jumps to this point) */\n}\n\n\nstatic int test_then_block (LexState *ls) {\n  /* test_then_block -> [IF | ELSEIF] cond THEN block */\n  int condexit;\n  luaX_next(ls);  /* skip IF or ELSEIF */\n  condexit = cond(ls);\n  checknext(ls, TK_THEN);\n  block(ls);  /* `then' part */\n  return condexit;\n}\n\n\nstatic void ifstat (LexState *ls, int line) {\n  /* ifstat -> IF cond THEN block {ELSEIF cond THEN block} [ELSE block] END */\n  FuncState *fs = ls->fs;\n  int flist;\n  int escapelist = NO_JUMP;\n  flist = test_then_block(ls);  /* IF cond THEN block */\n  while (ls->t.token == TK_ELSEIF) {\n    luaK_concat(fs, &escapelist, luaK_jump(fs));\n    luaK_patchtohere(fs, flist);\n    flist = test_then_block(ls);  /* ELSEIF cond THEN block */\n  }\n  if (ls->t.token == TK_ELSE) {\n    luaK_concat(fs, &escapelist, luaK_jump(fs));\n    luaK_patchtohere(fs, flist);\n    luaX_next(ls);  /* skip ELSE (after patch, for correct line info) */\n    block(ls);  /* `else' part */\n  }\n  else\n    luaK_concat(fs, &escapelist, flist);\n  luaK_patchtohere(fs, escapelist);\n  check_match(ls, TK_END, TK_IF, line);\n}\n\n\nstatic void localfunc (LexState *ls) {\n  expdesc v, b;\n  FuncState *fs = ls->fs;\n  new_localvar(ls, str_checkname(ls), 0);\n  init_exp(&v, VLOCAL, fs->freereg);\n  luaK_reserveregs(fs, 1);\n  adjustlocalvars(ls, 1);\n  body(ls, &b, 0, ls->linenumber);\n  luaK_storevar(fs, &v, &b);\n  /* debug information will only see the variable after this point! */\n  getlocvar(fs, fs->nactvar - 1).startpc = fs->pc;\n}\n\n\nstatic void localstat (LexState *ls) {\n  /* stat -> LOCAL NAME {`,' NAME} [`=' explist1] */\n  int nvars = 0;\n  int nexps;\n  expdesc e;\n  do {\n    new_localvar(ls, str_checkname(ls), nvars++);\n  } while (testnext(ls, ','));\n  if (testnext(ls, '='))\n    nexps = explist1(ls, &e);\n  else {\n    e.k = VVOID;\n    nexps = 0;\n  }\n  adjust_assign(ls, nvars, nexps, &e);\n  adjustlocalvars(ls, nvars);\n}\n\n\nstatic int funcname (LexState *ls, expdesc *v) {\n  /* funcname -> NAME {field} [`:' NAME] */\n  int needself = 0;\n  singlevar(ls, v);\n  while (ls->t.token == '.')\n    field(ls, v);\n  if (ls->t.token == ':') {\n    needself = 1;\n    field(ls, v);\n  }\n  return needself;\n}\n\n\nstatic void funcstat (LexState *ls, int line) {\n  /* funcstat -> FUNCTION funcname body */\n  int needself;\n  expdesc v, b;\n  luaX_next(ls);  /* skip FUNCTION */\n  needself = funcname(ls, &v);\n  body(ls, &b, needself, line);\n  luaK_storevar(ls->fs, &v, &b);\n  luaK_fixline(ls->fs, line);  /* definition `happens' in the first line */\n}\n\n\nstatic void exprstat (LexState *ls) {\n  /* stat -> func | assignment */\n  FuncState *fs = ls->fs;\n  struct LHS_assign v;\n  primaryexp(ls, &v.v);\n  if (v.v.k == VCALL)  /* stat -> func */\n    SETARG_C(getcode(fs, &v.v), 1);  /* call statement uses no results */\n  else {  /* stat -> assignment */\n    v.prev = NULL;\n    assignment(ls, &v, 1);\n  }\n}\n\n\nstatic void retstat (LexState *ls) {\n  /* stat -> RETURN explist */\n  FuncState *fs = ls->fs;\n  expdesc e;\n  int first, nret;  /* registers with returned values */\n  luaX_next(ls);  /* skip RETURN */\n  if (block_follow(ls->t.token) || ls->t.token == ';')\n    first = nret = 0;  /* return no values */\n  else {\n    nret = explist1(ls, &e);  /* optional return values */\n    if (hasmultret(e.k)) {\n      luaK_setmultret(fs, &e);\n      if (e.k == VCALL && nret == 1) {  /* tail call? */\n        SET_OPCODE(getcode(fs,&e), OP_TAILCALL);\n        lua_assert(GETARG_A(getcode(fs,&e)) == fs->nactvar);\n      }\n      first = fs->nactvar;\n      nret = LUA_MULTRET;  /* return all values */\n    }\n    else {\n      if (nret == 1)  /* only one single value? */\n        first = luaK_exp2anyreg(fs, &e);\n      else {\n        luaK_exp2nextreg(fs, &e);  /* values must go to the `stack' */\n        first = fs->nactvar;  /* return all `active' values */\n        lua_assert(nret == fs->freereg - first);\n      }\n    }\n  }\n  luaK_ret(fs, first, nret);\n}\n\n\nstatic int statement (LexState *ls) {\n  int line = ls->linenumber;  /* may be needed for error messages */\n  switch (ls->t.token) {\n    case TK_IF: {  /* stat -> ifstat */\n      ifstat(ls, line);\n      return 0;\n    }\n    case TK_WHILE: {  /* stat -> whilestat */\n      whilestat(ls, line);\n      return 0;\n    }\n    case TK_DO: {  /* stat -> DO block END */\n      luaX_next(ls);  /* skip DO */\n      block(ls);\n      check_match(ls, TK_END, TK_DO, line);\n      return 0;\n    }\n    case TK_FOR: {  /* stat -> forstat */\n      forstat(ls, line);\n      return 0;\n    }\n    case TK_REPEAT: {  /* stat -> repeatstat */\n      repeatstat(ls, line);\n      return 0;\n    }\n    case TK_FUNCTION: {\n      funcstat(ls, line);  /* stat -> funcstat */\n      return 0;\n    }\n    case TK_LOCAL: {  /* stat -> localstat */\n      luaX_next(ls);  /* skip LOCAL */\n      if (testnext(ls, TK_FUNCTION))  /* local function? */\n        localfunc(ls);\n      else\n        localstat(ls);\n      return 0;\n    }\n    case TK_RETURN: {  /* stat -> retstat */\n      retstat(ls);\n      return 1;  /* must be last statement */\n    }\n    case TK_BREAK: {  /* stat -> breakstat */\n      luaX_next(ls);  /* skip BREAK */\n      breakstat(ls);\n      return 1;  /* must be last statement */\n    }\n    default: {\n      exprstat(ls);\n      return 0;  /* to avoid warnings */\n    }\n  }\n}\n\n\nstatic void chunk (LexState *ls) {\n  /* chunk -> { stat [`;'] } */\n  int islast = 0;\n  enterlevel(ls);\n  while (!islast && !block_follow(ls->t.token)) {\n    islast = statement(ls);\n    testnext(ls, ';');\n    lua_assert(ls->fs->f->maxstacksize >= ls->fs->freereg &&\n               ls->fs->freereg >= ls->fs->nactvar);\n    ls->fs->freereg = ls->fs->nactvar;  /* free registers */\n  }\n  leavelevel(ls);\n}\n\n/* }====================================================================== */\n"
  },
  {
    "path": "deps/lua/src/lparser.h",
    "content": "/*\n** $Id: lparser.h,v 1.57.1.1 2007/12/27 13:02:25 roberto Exp $\n** Lua Parser\n** See Copyright Notice in lua.h\n*/\n\n#ifndef lparser_h\n#define lparser_h\n\n#include \"llimits.h\"\n#include \"lobject.h\"\n#include \"lzio.h\"\n\n\n/*\n** Expression descriptor\n*/\n\ntypedef enum {\n  VVOID,\t/* no value */\n  VNIL,\n  VTRUE,\n  VFALSE,\n  VK,\t\t/* info = index of constant in `k' */\n  VKNUM,\t/* nval = numerical value */\n  VLOCAL,\t/* info = local register */\n  VUPVAL,       /* info = index of upvalue in `upvalues' */\n  VGLOBAL,\t/* info = index of table; aux = index of global name in `k' */\n  VINDEXED,\t/* info = table register; aux = index register (or `k') */\n  VJMP,\t\t/* info = instruction pc */\n  VRELOCABLE,\t/* info = instruction pc */\n  VNONRELOC,\t/* info = result register */\n  VCALL,\t/* info = instruction pc */\n  VVARARG\t/* info = instruction pc */\n} expkind;\n\ntypedef struct expdesc {\n  expkind k;\n  union {\n    struct { int info, aux; } s;\n    lua_Number nval;\n  } u;\n  int t;  /* patch list of `exit when true' */\n  int f;  /* patch list of `exit when false' */\n} expdesc;\n\n\ntypedef struct upvaldesc {\n  lu_byte k;\n  lu_byte info;\n} upvaldesc;\n\n\nstruct BlockCnt;  /* defined in lparser.c */\n\n\n/* state needed to generate code for a given function */\ntypedef struct FuncState {\n  Proto *f;  /* current function header */\n  Table *h;  /* table to find (and reuse) elements in `k' */\n  struct FuncState *prev;  /* enclosing function */\n  struct LexState *ls;  /* lexical state */\n  struct lua_State *L;  /* copy of the Lua state */\n  struct BlockCnt *bl;  /* chain of current blocks */\n  int pc;  /* next position to code (equivalent to `ncode') */\n  int lasttarget;   /* `pc' of last `jump target' */\n  int jpc;  /* list of pending jumps to `pc' */\n  int freereg;  /* first free register */\n  int nk;  /* number of elements in `k' */\n  int np;  /* number of elements in `p' */\n  short nlocvars;  /* number of elements in `locvars' */\n  lu_byte nactvar;  /* number of active local variables */\n  upvaldesc upvalues[LUAI_MAXUPVALUES];  /* upvalues */\n  unsigned short actvar[LUAI_MAXVARS];  /* declared-variable stack */\n} FuncState;\n\n\nLUAI_FUNC Proto *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff,\n                                            const char *name);\n\n\n#endif\n"
  },
  {
    "path": "deps/lua/src/lstate.c",
    "content": "/*\n** $Id: lstate.c,v 2.36.1.2 2008/01/03 15:20:39 roberto Exp $\n** Global State\n** See Copyright Notice in lua.h\n*/\n\n\n#include <stddef.h>\n\n#define lstate_c\n#define LUA_CORE\n\n#include \"lua.h\"\n\n#include \"ldebug.h\"\n#include \"ldo.h\"\n#include \"lfunc.h\"\n#include \"lgc.h\"\n#include \"llex.h\"\n#include \"lmem.h\"\n#include \"lstate.h\"\n#include \"lstring.h\"\n#include \"ltable.h\"\n#include \"ltm.h\"\n\n\n#define state_size(x)\t(sizeof(x) + LUAI_EXTRASPACE)\n#define fromstate(l)\t(cast(lu_byte *, (l)) - LUAI_EXTRASPACE)\n#define tostate(l)   (cast(lua_State *, cast(lu_byte *, l) + LUAI_EXTRASPACE))\n\n\n/*\n** Main thread combines a thread state and the global state\n*/\ntypedef struct LG {\n  lua_State l;\n  global_State g;\n} LG;\n  \n\n\nstatic void stack_init (lua_State *L1, lua_State *L) {\n  /* initialize CallInfo array */\n  L1->base_ci = luaM_newvector(L, BASIC_CI_SIZE, CallInfo);\n  L1->ci = L1->base_ci;\n  L1->size_ci = BASIC_CI_SIZE;\n  L1->end_ci = L1->base_ci + L1->size_ci - 1;\n  /* initialize stack array */\n  L1->stack = luaM_newvector(L, BASIC_STACK_SIZE + EXTRA_STACK, TValue);\n  L1->stacksize = BASIC_STACK_SIZE + EXTRA_STACK;\n  L1->top = L1->stack;\n  L1->stack_last = L1->stack+(L1->stacksize - EXTRA_STACK)-1;\n  /* initialize first ci */\n  L1->ci->func = L1->top;\n  setnilvalue(L1->top++);  /* `function' entry for this `ci' */\n  L1->base = L1->ci->base = L1->top;\n  L1->ci->top = L1->top + LUA_MINSTACK;\n}\n\n\nstatic void freestack (lua_State *L, lua_State *L1) {\n  luaM_freearray(L, L1->base_ci, L1->size_ci, CallInfo);\n  luaM_freearray(L, L1->stack, L1->stacksize, TValue);\n}\n\n\n/*\n** open parts that may cause memory-allocation errors\n*/\nstatic void f_luaopen (lua_State *L, void *ud) {\n  global_State *g = G(L);\n  UNUSED(ud);\n  stack_init(L, L);  /* init stack */\n  sethvalue(L, gt(L), luaH_new(L, 0, 2));  /* table of globals */\n  sethvalue(L, registry(L), luaH_new(L, 0, 2));  /* registry */\n  luaS_resize(L, MINSTRTABSIZE);  /* initial size of string table */\n  luaT_init(L);\n  luaX_init(L);\n  luaS_fix(luaS_newliteral(L, MEMERRMSG));\n  g->GCthreshold = 4*g->totalbytes;\n}\n\n\nstatic void preinit_state (lua_State *L, global_State *g) {\n  G(L) = g;\n  L->stack = NULL;\n  L->stacksize = 0;\n  L->errorJmp = NULL;\n  L->hook = NULL;\n  L->hookmask = 0;\n  L->basehookcount = 0;\n  L->allowhook = 1;\n  resethookcount(L);\n  L->openupval = NULL;\n  L->size_ci = 0;\n  L->nCcalls = L->baseCcalls = 0;\n  L->status = 0;\n  L->base_ci = L->ci = NULL;\n  L->savedpc = NULL;\n  L->errfunc = 0;\n  setnilvalue(gt(L));\n}\n\n\nstatic void close_state (lua_State *L) {\n  global_State *g = G(L);\n  luaF_close(L, L->stack);  /* close all upvalues for this thread */\n  luaC_freeall(L);  /* collect all objects */\n  lua_assert(g->rootgc == obj2gco(L));\n  lua_assert(g->strt.nuse == 0);\n  luaM_freearray(L, G(L)->strt.hash, G(L)->strt.size, TString *);\n  luaZ_freebuffer(L, &g->buff);\n  freestack(L, L);\n  lua_assert(g->totalbytes == sizeof(LG));\n  (*g->frealloc)(g->ud, fromstate(L), state_size(LG), 0);\n}\n\n\nlua_State *luaE_newthread (lua_State *L) {\n  lua_State *L1 = tostate(luaM_malloc(L, state_size(lua_State)));\n  luaC_link(L, obj2gco(L1), LUA_TTHREAD);\n  preinit_state(L1, G(L));\n  stack_init(L1, L);  /* init stack */\n  setobj2n(L, gt(L1), gt(L));  /* share table of globals */\n  L1->hookmask = L->hookmask;\n  L1->basehookcount = L->basehookcount;\n  L1->hook = L->hook;\n  resethookcount(L1);\n  lua_assert(iswhite(obj2gco(L1)));\n  return L1;\n}\n\n\nvoid luaE_freethread (lua_State *L, lua_State *L1) {\n  luaF_close(L1, L1->stack);  /* close all upvalues for this thread */\n  lua_assert(L1->openupval == NULL);\n  luai_userstatefree(L1);\n  freestack(L, L1);\n  luaM_freemem(L, fromstate(L1), state_size(lua_State));\n}\n\n\nLUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {\n  int i;\n  lua_State *L;\n  global_State *g;\n  void *l = (*f)(ud, NULL, 0, state_size(LG));\n  if (l == NULL) return NULL;\n  L = tostate(l);\n  g = &((LG *)L)->g;\n  L->next = NULL;\n  L->tt = LUA_TTHREAD;\n  g->currentwhite = bit2mask(WHITE0BIT, FIXEDBIT);\n  L->marked = luaC_white(g);\n  set2bits(L->marked, FIXEDBIT, SFIXEDBIT);\n  preinit_state(L, g);\n  g->frealloc = f;\n  g->ud = ud;\n  g->mainthread = L;\n  g->uvhead.u.l.prev = &g->uvhead;\n  g->uvhead.u.l.next = &g->uvhead;\n  g->GCthreshold = 0;  /* mark it as unfinished state */\n  g->strt.size = 0;\n  g->strt.nuse = 0;\n  g->strt.hash = NULL;\n  setnilvalue(registry(L));\n  luaZ_initbuffer(L, &g->buff);\n  g->panic = NULL;\n  g->gcstate = GCSpause;\n  g->rootgc = obj2gco(L);\n  g->sweepstrgc = 0;\n  g->sweepgc = &g->rootgc;\n  g->gray = NULL;\n  g->grayagain = NULL;\n  g->weak = NULL;\n  g->tmudata = NULL;\n  g->totalbytes = sizeof(LG);\n  g->gcpause = LUAI_GCPAUSE;\n  g->gcstepmul = LUAI_GCMUL;\n  g->gcdept = 0;\n  for (i=0; i<NUM_TAGS; i++) g->mt[i] = NULL;\n  if (luaD_rawrunprotected(L, f_luaopen, NULL) != 0) {\n    /* memory allocation error: free partial state */\n    close_state(L);\n    L = NULL;\n  }\n  else\n    luai_userstateopen(L);\n  return L;\n}\n\n\nstatic void callallgcTM (lua_State *L, void *ud) {\n  UNUSED(ud);\n  luaC_callGCTM(L);  /* call GC metamethods for all udata */\n}\n\n\nLUA_API void lua_close (lua_State *L) {\n  L = G(L)->mainthread;  /* only the main thread can be closed */\n  lua_lock(L);\n  luaF_close(L, L->stack);  /* close all upvalues for this thread */\n  luaC_separateudata(L, 1);  /* separate udata that have GC metamethods */\n  L->errfunc = 0;  /* no error function during GC metamethods */\n  do {  /* repeat until no more errors */\n    L->ci = L->base_ci;\n    L->base = L->top = L->ci->base;\n    L->nCcalls = L->baseCcalls = 0;\n  } while (luaD_rawrunprotected(L, callallgcTM, NULL) != 0);\n  lua_assert(G(L)->tmudata == NULL);\n  luai_userstateclose(L);\n  close_state(L);\n}\n\n"
  },
  {
    "path": "deps/lua/src/lstate.h",
    "content": "/*\n** $Id: lstate.h,v 2.24.1.2 2008/01/03 15:20:39 roberto Exp $\n** Global State\n** See Copyright Notice in lua.h\n*/\n\n#ifndef lstate_h\n#define lstate_h\n\n#include \"lua.h\"\n\n#include \"lobject.h\"\n#include \"ltm.h\"\n#include \"lzio.h\"\n\n\n\nstruct lua_longjmp;  /* defined in ldo.c */\n\n\n/* table of globals */\n#define gt(L)\t(&L->l_gt)\n\n/* registry */\n#define registry(L)\t(&G(L)->l_registry)\n\n\n/* extra stack space to handle TM calls and some other extras */\n#define EXTRA_STACK   5\n\n\n#define BASIC_CI_SIZE           8\n\n#define BASIC_STACK_SIZE        (2*LUA_MINSTACK)\n\n\n\ntypedef struct stringtable {\n  GCObject **hash;\n  lu_int32 nuse;  /* number of elements */\n  int size;\n} stringtable;\n\n\n/*\n** informations about a call\n*/\ntypedef struct CallInfo {\n  StkId base;  /* base for this function */\n  StkId func;  /* function index in the stack */\n  StkId\ttop;  /* top for this function */\n  const Instruction *savedpc;\n  int nresults;  /* expected number of results from this function */\n  int tailcalls;  /* number of tail calls lost under this entry */\n} CallInfo;\n\n\n\n#define curr_func(L)\t(clvalue(L->ci->func))\n#define ci_func(ci)\t(clvalue((ci)->func))\n#define f_isLua(ci)\t(!ci_func(ci)->c.isC)\n#define isLua(ci)\t(ttisfunction((ci)->func) && f_isLua(ci))\n\n\n/*\n** `global state', shared by all threads of this state\n*/\ntypedef struct global_State {\n  stringtable strt;  /* hash table for strings */\n  lua_Alloc frealloc;  /* function to reallocate memory */\n  void *ud;         /* auxiliary data to `frealloc' */\n  lu_byte currentwhite;\n  lu_byte gcstate;  /* state of garbage collector */\n  int sweepstrgc;  /* position of sweep in `strt' */\n  GCObject *rootgc;  /* list of all collectable objects */\n  GCObject **sweepgc;  /* position of sweep in `rootgc' */\n  GCObject *gray;  /* list of gray objects */\n  GCObject *grayagain;  /* list of objects to be traversed atomically */\n  GCObject *weak;  /* list of weak tables (to be cleared) */\n  GCObject *tmudata;  /* last element of list of userdata to be GC */\n  Mbuffer buff;  /* temporary buffer for string concatentation */\n  lu_mem GCthreshold;\n  lu_mem totalbytes;  /* number of bytes currently allocated */\n  lu_mem estimate;  /* an estimate of number of bytes actually in use */\n  lu_mem gcdept;  /* how much GC is `behind schedule' */\n  int gcpause;  /* size of pause between successive GCs */\n  int gcstepmul;  /* GC `granularity' */\n  lua_CFunction panic;  /* to be called in unprotected errors */\n  TValue l_registry;\n  struct lua_State *mainthread;\n  UpVal uvhead;  /* head of double-linked list of all open upvalues */\n  struct Table *mt[NUM_TAGS];  /* metatables for basic types */\n  TString *tmname[TM_N];  /* array with tag-method names */\n} global_State;\n\n\n/*\n** `per thread' state\n*/\nstruct lua_State {\n  CommonHeader;\n  lu_byte status;\n  StkId top;  /* first free slot in the stack */\n  StkId base;  /* base of current function */\n  global_State *l_G;\n  CallInfo *ci;  /* call info for current function */\n  const Instruction *savedpc;  /* `savedpc' of current function */\n  StkId stack_last;  /* last free slot in the stack */\n  StkId stack;  /* stack base */\n  CallInfo *end_ci;  /* points after end of ci array*/\n  CallInfo *base_ci;  /* array of CallInfo's */\n  int stacksize;\n  int size_ci;  /* size of array `base_ci' */\n  unsigned short nCcalls;  /* number of nested C calls */\n  unsigned short baseCcalls;  /* nested C calls when resuming coroutine */\n  lu_byte hookmask;\n  lu_byte allowhook;\n  int basehookcount;\n  int hookcount;\n  lua_Hook hook;\n  TValue l_gt;  /* table of globals */\n  TValue env;  /* temporary place for environments */\n  GCObject *openupval;  /* list of open upvalues in this stack */\n  GCObject *gclist;\n  struct lua_longjmp *errorJmp;  /* current error recover point */\n  ptrdiff_t errfunc;  /* current error handling function (stack index) */\n};\n\n\n#define G(L)\t(L->l_G)\n\n\n/*\n** Union of all collectable objects\n*/\nunion GCObject {\n  GCheader gch;\n  union TString ts;\n  union Udata u;\n  union Closure cl;\n  struct Table h;\n  struct Proto p;\n  struct UpVal uv;\n  struct lua_State th;  /* thread */\n};\n\n\n/* macros to convert a GCObject into a specific value */\n#define rawgco2ts(o)\tcheck_exp((o)->gch.tt == LUA_TSTRING, &((o)->ts))\n#define gco2ts(o)\t(&rawgco2ts(o)->tsv)\n#define rawgco2u(o)\tcheck_exp((o)->gch.tt == LUA_TUSERDATA, &((o)->u))\n#define gco2u(o)\t(&rawgco2u(o)->uv)\n#define gco2cl(o)\tcheck_exp((o)->gch.tt == LUA_TFUNCTION, &((o)->cl))\n#define gco2h(o)\tcheck_exp((o)->gch.tt == LUA_TTABLE, &((o)->h))\n#define gco2p(o)\tcheck_exp((o)->gch.tt == LUA_TPROTO, &((o)->p))\n#define gco2uv(o)\tcheck_exp((o)->gch.tt == LUA_TUPVAL, &((o)->uv))\n#define ngcotouv(o) \\\n\tcheck_exp((o) == NULL || (o)->gch.tt == LUA_TUPVAL, &((o)->uv))\n#define gco2th(o)\tcheck_exp((o)->gch.tt == LUA_TTHREAD, &((o)->th))\n\n/* macro to convert any Lua object into a GCObject */\n#define obj2gco(v)\t(cast(GCObject *, (v)))\n\n\nLUAI_FUNC lua_State *luaE_newthread (lua_State *L);\nLUAI_FUNC void luaE_freethread (lua_State *L, lua_State *L1);\n\n#endif\n\n"
  },
  {
    "path": "deps/lua/src/lstring.c",
    "content": "/*\n** $Id: lstring.c,v 2.8.1.1 2007/12/27 13:02:25 roberto Exp $\n** String table (keeps all strings handled by Lua)\n** See Copyright Notice in lua.h\n*/\n\n\n#include <string.h>\n\n#define lstring_c\n#define LUA_CORE\n\n#include \"lua.h\"\n\n#include \"lmem.h\"\n#include \"lobject.h\"\n#include \"lstate.h\"\n#include \"lstring.h\"\n\n\n\nvoid luaS_resize (lua_State *L, int newsize) {\n  GCObject **newhash;\n  stringtable *tb;\n  int i;\n  if (G(L)->gcstate == GCSsweepstring)\n    return;  /* cannot resize during GC traverse */\n  newhash = luaM_newvector(L, newsize, GCObject *);\n  tb = &G(L)->strt;\n  for (i=0; i<newsize; i++) newhash[i] = NULL;\n  /* rehash */\n  for (i=0; i<tb->size; i++) {\n    GCObject *p = tb->hash[i];\n    while (p) {  /* for each node in the list */\n      GCObject *next = p->gch.next;  /* save next */\n      unsigned int h = gco2ts(p)->hash;\n      int h1 = lmod(h, newsize);  /* new position */\n      lua_assert(cast_int(h%newsize) == lmod(h, newsize));\n      p->gch.next = newhash[h1];  /* chain it */\n      newhash[h1] = p;\n      p = next;\n    }\n  }\n  luaM_freearray(L, tb->hash, tb->size, TString *);\n  tb->size = newsize;\n  tb->hash = newhash;\n}\n\n\nstatic TString *newlstr (lua_State *L, const char *str, size_t l,\n                                       unsigned int h) {\n  TString *ts;\n  stringtable *tb;\n  if (l+1 > (MAX_SIZET - sizeof(TString))/sizeof(char))\n    luaM_toobig(L);\n  ts = cast(TString *, luaM_malloc(L, (l+1)*sizeof(char)+sizeof(TString)));\n  ts->tsv.len = l;\n  ts->tsv.hash = h;\n  ts->tsv.marked = luaC_white(G(L));\n  ts->tsv.tt = LUA_TSTRING;\n  ts->tsv.reserved = 0;\n  memcpy(ts+1, str, l*sizeof(char));\n  ((char *)(ts+1))[l] = '\\0';  /* ending 0 */\n  tb = &G(L)->strt;\n  h = lmod(h, tb->size);\n  ts->tsv.next = tb->hash[h];  /* chain new entry */\n  tb->hash[h] = obj2gco(ts);\n  tb->nuse++;\n  if (tb->nuse > cast(lu_int32, tb->size) && tb->size <= MAX_INT/2)\n    luaS_resize(L, tb->size*2);  /* too crowded */\n  return ts;\n}\n\n\nTString *luaS_newlstr (lua_State *L, const char *str, size_t l) {\n  GCObject *o;\n  unsigned int h = cast(unsigned int, l);  /* seed */\n  size_t step = (l>>5)+1;  /* if string is too long, don't hash all its chars */\n  size_t l1;\n  for (l1=l; l1>=step; l1-=step)  /* compute hash */\n    h = h ^ ((h<<5)+(h>>2)+cast(unsigned char, str[l1-1]));\n  for (o = G(L)->strt.hash[lmod(h, G(L)->strt.size)];\n       o != NULL;\n       o = o->gch.next) {\n    TString *ts = rawgco2ts(o);\n    if (ts->tsv.len == l && (memcmp(str, getstr(ts), l) == 0)) {\n      /* string may be dead */\n      if (isdead(G(L), o)) changewhite(o);\n      return ts;\n    }\n  }\n  return newlstr(L, str, l, h);  /* not found */\n}\n\n\nUdata *luaS_newudata (lua_State *L, size_t s, Table *e) {\n  Udata *u;\n  if (s > MAX_SIZET - sizeof(Udata))\n    luaM_toobig(L);\n  u = cast(Udata *, luaM_malloc(L, s + sizeof(Udata)));\n  u->uv.marked = luaC_white(G(L));  /* is not finalized */\n  u->uv.tt = LUA_TUSERDATA;\n  u->uv.len = s;\n  u->uv.metatable = NULL;\n  u->uv.env = e;\n  /* chain it on udata list (after main thread) */\n  u->uv.next = G(L)->mainthread->next;\n  G(L)->mainthread->next = obj2gco(u);\n  return u;\n}\n\n"
  },
  {
    "path": "deps/lua/src/lstring.h",
    "content": "/*\n** $Id: lstring.h,v 1.43.1.1 2007/12/27 13:02:25 roberto Exp $\n** String table (keep all strings handled by Lua)\n** See Copyright Notice in lua.h\n*/\n\n#ifndef lstring_h\n#define lstring_h\n\n\n#include \"lgc.h\"\n#include \"lobject.h\"\n#include \"lstate.h\"\n\n\n#define sizestring(s)\t(sizeof(union TString)+((s)->len+1)*sizeof(char))\n\n#define sizeudata(u)\t(sizeof(union Udata)+(u)->len)\n\n#define luaS_new(L, s)\t(luaS_newlstr(L, s, strlen(s)))\n#define luaS_newliteral(L, s)\t(luaS_newlstr(L, \"\" s, \\\n                                 (sizeof(s)/sizeof(char))-1))\n\n#define luaS_fix(s)\tl_setbit((s)->tsv.marked, FIXEDBIT)\n\nLUAI_FUNC void luaS_resize (lua_State *L, int newsize);\nLUAI_FUNC Udata *luaS_newudata (lua_State *L, size_t s, Table *e);\nLUAI_FUNC TString *luaS_newlstr (lua_State *L, const char *str, size_t l);\n\n\n#endif\n"
  },
  {
    "path": "deps/lua/src/lstrlib.c",
    "content": "/*\n** $Id: lstrlib.c,v 1.132.1.5 2010/05/14 15:34:19 roberto Exp $\n** Standard library for string operations and pattern-matching\n** See Copyright Notice in lua.h\n*/\n\n\n#include <ctype.h>\n#include <stddef.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#define lstrlib_c\n#define LUA_LIB\n\n#include \"lua.h\"\n\n#include \"lauxlib.h\"\n#include \"lualib.h\"\n\n\n/* macro to `unsign' a character */\n#define uchar(c)        ((unsigned char)(c))\n\n\n\nstatic int str_len (lua_State *L) {\n  size_t l;\n  luaL_checklstring(L, 1, &l);\n  lua_pushinteger(L, l);\n  return 1;\n}\n\n\nstatic ptrdiff_t posrelat (ptrdiff_t pos, size_t len) {\n  /* relative string position: negative means back from end */\n  if (pos < 0) pos += (ptrdiff_t)len + 1;\n  return (pos >= 0) ? pos : 0;\n}\n\n\nstatic int str_sub (lua_State *L) {\n  size_t l;\n  const char *s = luaL_checklstring(L, 1, &l);\n  ptrdiff_t start = posrelat(luaL_checkinteger(L, 2), l);\n  ptrdiff_t end = posrelat(luaL_optinteger(L, 3, -1), l);\n  if (start < 1) start = 1;\n  if (end > (ptrdiff_t)l) end = (ptrdiff_t)l;\n  if (start <= end)\n    lua_pushlstring(L, s+start-1, end-start+1);\n  else lua_pushliteral(L, \"\");\n  return 1;\n}\n\n\nstatic int str_reverse (lua_State *L) {\n  size_t l;\n  luaL_Buffer b;\n  const char *s = luaL_checklstring(L, 1, &l);\n  luaL_buffinit(L, &b);\n  while (l--) luaL_addchar(&b, s[l]);\n  luaL_pushresult(&b);\n  return 1;\n}\n\n\nstatic int str_lower (lua_State *L) {\n  size_t l;\n  size_t i;\n  luaL_Buffer b;\n  const char *s = luaL_checklstring(L, 1, &l);\n  luaL_buffinit(L, &b);\n  for (i=0; i<l; i++)\n    luaL_addchar(&b, tolower(uchar(s[i])));\n  luaL_pushresult(&b);\n  return 1;\n}\n\n\nstatic int str_upper (lua_State *L) {\n  size_t l;\n  size_t i;\n  luaL_Buffer b;\n  const char *s = luaL_checklstring(L, 1, &l);\n  luaL_buffinit(L, &b);\n  for (i=0; i<l; i++)\n    luaL_addchar(&b, toupper(uchar(s[i])));\n  luaL_pushresult(&b);\n  return 1;\n}\n\nstatic int str_rep (lua_State *L) {\n  size_t l;\n  luaL_Buffer b;\n  const char *s = luaL_checklstring(L, 1, &l);\n  int n = luaL_checkint(L, 2);\n  luaL_buffinit(L, &b);\n  while (n-- > 0)\n    luaL_addlstring(&b, s, l);\n  luaL_pushresult(&b);\n  return 1;\n}\n\n\nstatic int str_byte (lua_State *L) {\n  size_t l;\n  const char *s = luaL_checklstring(L, 1, &l);\n  ptrdiff_t posi = posrelat(luaL_optinteger(L, 2, 1), l);\n  ptrdiff_t pose = posrelat(luaL_optinteger(L, 3, posi), l);\n  int n, i;\n  if (posi <= 0) posi = 1;\n  if ((size_t)pose > l) pose = l;\n  if (posi > pose) return 0;  /* empty interval; return no values */\n  n = (int)(pose -  posi + 1);\n  if (posi + n <= pose)  /* overflow? */\n    luaL_error(L, \"string slice too long\");\n  luaL_checkstack(L, n, \"string slice too long\");\n  for (i=0; i<n; i++)\n    lua_pushinteger(L, uchar(s[posi+i-1]));\n  return n;\n}\n\n\nstatic int str_char (lua_State *L) {\n  int n = lua_gettop(L);  /* number of arguments */\n  int i;\n  luaL_Buffer b;\n  luaL_buffinit(L, &b);\n  for (i=1; i<=n; i++) {\n    int c = luaL_checkint(L, i);\n    luaL_argcheck(L, uchar(c) == c, i, \"invalid value\");\n    luaL_addchar(&b, uchar(c));\n  }\n  luaL_pushresult(&b);\n  return 1;\n}\n\n\nstatic int writer (lua_State *L, const void* b, size_t size, void* B) {\n  (void)L;\n  luaL_addlstring((luaL_Buffer*) B, (const char *)b, size);\n  return 0;\n}\n\n\nstatic int str_dump (lua_State *L) {\n  luaL_Buffer b;\n  luaL_checktype(L, 1, LUA_TFUNCTION);\n  lua_settop(L, 1);\n  luaL_buffinit(L,&b);\n  if (lua_dump(L, writer, &b) != 0)\n    luaL_error(L, \"unable to dump given function\");\n  luaL_pushresult(&b);\n  return 1;\n}\n\n\n\n/*\n** {======================================================\n** PATTERN MATCHING\n** =======================================================\n*/\n\n\n#define CAP_UNFINISHED\t(-1)\n#define CAP_POSITION\t(-2)\n\ntypedef struct MatchState {\n  const char *src_init;  /* init of source string */\n  const char *src_end;  /* end (`\\0') of source string */\n  lua_State *L;\n  int level;  /* total number of captures (finished or unfinished) */\n  struct {\n    const char *init;\n    ptrdiff_t len;\n  } capture[LUA_MAXCAPTURES];\n} MatchState;\n\n\n#define L_ESC\t\t'%'\n#define SPECIALS\t\"^$*+?.([%-\"\n\n\nstatic int check_capture (MatchState *ms, int l) {\n  l -= '1';\n  if (l < 0 || l >= ms->level || ms->capture[l].len == CAP_UNFINISHED)\n    return luaL_error(ms->L, \"invalid capture index\");\n  return l;\n}\n\n\nstatic int capture_to_close (MatchState *ms) {\n  int level = ms->level;\n  for (level--; level>=0; level--)\n    if (ms->capture[level].len == CAP_UNFINISHED) return level;\n  return luaL_error(ms->L, \"invalid pattern capture\");\n}\n\n\nstatic const char *classend (MatchState *ms, const char *p) {\n  switch (*p++) {\n    case L_ESC: {\n      if (*p == '\\0')\n        luaL_error(ms->L, \"malformed pattern (ends with \" LUA_QL(\"%%\") \")\");\n      return p+1;\n    }\n    case '[': {\n      if (*p == '^') p++;\n      do {  /* look for a `]' */\n        if (*p == '\\0')\n          luaL_error(ms->L, \"malformed pattern (missing \" LUA_QL(\"]\") \")\");\n        if (*(p++) == L_ESC && *p != '\\0')\n          p++;  /* skip escapes (e.g. `%]') */\n      } while (*p != ']');\n      return p+1;\n    }\n    default: {\n      return p;\n    }\n  }\n}\n\n\nstatic int match_class (int c, int cl) {\n  int res;\n  switch (tolower(cl)) {\n    case 'a' : res = isalpha(c); break;\n    case 'c' : res = iscntrl(c); break;\n    case 'd' : res = isdigit(c); break;\n    case 'l' : res = islower(c); break;\n    case 'p' : res = ispunct(c); break;\n    case 's' : res = isspace(c); break;\n    case 'u' : res = isupper(c); break;\n    case 'w' : res = isalnum(c); break;\n    case 'x' : res = isxdigit(c); break;\n    case 'z' : res = (c == 0); break;\n    default: return (cl == c);\n  }\n  return (islower(cl) ? res : !res);\n}\n\n\nstatic int matchbracketclass (int c, const char *p, const char *ec) {\n  int sig = 1;\n  if (*(p+1) == '^') {\n    sig = 0;\n    p++;  /* skip the `^' */\n  }\n  while (++p < ec) {\n    if (*p == L_ESC) {\n      p++;\n      if (match_class(c, uchar(*p)))\n        return sig;\n    }\n    else if ((*(p+1) == '-') && (p+2 < ec)) {\n      p+=2;\n      if (uchar(*(p-2)) <= c && c <= uchar(*p))\n        return sig;\n    }\n    else if (uchar(*p) == c) return sig;\n  }\n  return !sig;\n}\n\n\nstatic int singlematch (int c, const char *p, const char *ep) {\n  switch (*p) {\n    case '.': return 1;  /* matches any char */\n    case L_ESC: return match_class(c, uchar(*(p+1)));\n    case '[': return matchbracketclass(c, p, ep-1);\n    default:  return (uchar(*p) == c);\n  }\n}\n\n\nstatic const char *match (MatchState *ms, const char *s, const char *p);\n\n\nstatic const char *matchbalance (MatchState *ms, const char *s,\n                                   const char *p) {\n  if (*p == 0 || *(p+1) == 0)\n    luaL_error(ms->L, \"unbalanced pattern\");\n  if (*s != *p) return NULL;\n  else {\n    int b = *p;\n    int e = *(p+1);\n    int cont = 1;\n    while (++s < ms->src_end) {\n      if (*s == e) {\n        if (--cont == 0) return s+1;\n      }\n      else if (*s == b) cont++;\n    }\n  }\n  return NULL;  /* string ends out of balance */\n}\n\n\nstatic const char *max_expand (MatchState *ms, const char *s,\n                                 const char *p, const char *ep) {\n  ptrdiff_t i = 0;  /* counts maximum expand for item */\n  while ((s+i)<ms->src_end && singlematch(uchar(*(s+i)), p, ep))\n    i++;\n  /* keeps trying to match with the maximum repetitions */\n  while (i>=0) {\n    const char *res = match(ms, (s+i), ep+1);\n    if (res) return res;\n    i--;  /* else didn't match; reduce 1 repetition to try again */\n  }\n  return NULL;\n}\n\n\nstatic const char *min_expand (MatchState *ms, const char *s,\n                                 const char *p, const char *ep) {\n  for (;;) {\n    const char *res = match(ms, s, ep+1);\n    if (res != NULL)\n      return res;\n    else if (s<ms->src_end && singlematch(uchar(*s), p, ep))\n      s++;  /* try with one more repetition */\n    else return NULL;\n  }\n}\n\n\nstatic const char *start_capture (MatchState *ms, const char *s,\n                                    const char *p, int what) {\n  const char *res;\n  int level = ms->level;\n  if (level >= LUA_MAXCAPTURES) luaL_error(ms->L, \"too many captures\");\n  ms->capture[level].init = s;\n  ms->capture[level].len = what;\n  ms->level = level+1;\n  if ((res=match(ms, s, p)) == NULL)  /* match failed? */\n    ms->level--;  /* undo capture */\n  return res;\n}\n\n\nstatic const char *end_capture (MatchState *ms, const char *s,\n                                  const char *p) {\n  int l = capture_to_close(ms);\n  const char *res;\n  ms->capture[l].len = s - ms->capture[l].init;  /* close capture */\n  if ((res = match(ms, s, p)) == NULL)  /* match failed? */\n    ms->capture[l].len = CAP_UNFINISHED;  /* undo capture */\n  return res;\n}\n\n\nstatic const char *match_capture (MatchState *ms, const char *s, int l) {\n  size_t len;\n  l = check_capture(ms, l);\n  len = ms->capture[l].len;\n  if ((size_t)(ms->src_end-s) >= len &&\n      memcmp(ms->capture[l].init, s, len) == 0)\n    return s+len;\n  else return NULL;\n}\n\n\nstatic const char *match (MatchState *ms, const char *s, const char *p) {\n  init: /* using goto's to optimize tail recursion */\n  switch (*p) {\n    case '(': {  /* start capture */\n      if (*(p+1) == ')')  /* position capture? */\n        return start_capture(ms, s, p+2, CAP_POSITION);\n      else\n        return start_capture(ms, s, p+1, CAP_UNFINISHED);\n    }\n    case ')': {  /* end capture */\n      return end_capture(ms, s, p+1);\n    }\n    case L_ESC: {\n      switch (*(p+1)) {\n        case 'b': {  /* balanced string? */\n          s = matchbalance(ms, s, p+2);\n          if (s == NULL) return NULL;\n          p+=4; goto init;  /* else return match(ms, s, p+4); */\n        }\n        case 'f': {  /* frontier? */\n          const char *ep; char previous;\n          p += 2;\n          if (*p != '[')\n            luaL_error(ms->L, \"missing \" LUA_QL(\"[\") \" after \"\n                               LUA_QL(\"%%f\") \" in pattern\");\n          ep = classend(ms, p);  /* points to what is next */\n          previous = (s == ms->src_init) ? '\\0' : *(s-1);\n          if (matchbracketclass(uchar(previous), p, ep-1) ||\n             !matchbracketclass(uchar(*s), p, ep-1)) return NULL;\n          p=ep; goto init;  /* else return match(ms, s, ep); */\n        }\n        default: {\n          if (isdigit(uchar(*(p+1)))) {  /* capture results (%0-%9)? */\n            s = match_capture(ms, s, uchar(*(p+1)));\n            if (s == NULL) return NULL;\n            p+=2; goto init;  /* else return match(ms, s, p+2) */\n          }\n          goto dflt;  /* case default */\n        }\n      }\n    }\n    case '\\0': {  /* end of pattern */\n      return s;  /* match succeeded */\n    }\n    case '$': {\n      if (*(p+1) == '\\0')  /* is the `$' the last char in pattern? */\n        return (s == ms->src_end) ? s : NULL;  /* check end of string */\n      else goto dflt;\n    }\n    default: dflt: {  /* it is a pattern item */\n      const char *ep = classend(ms, p);  /* points to what is next */\n      int m = s<ms->src_end && singlematch(uchar(*s), p, ep);\n      switch (*ep) {\n        case '?': {  /* optional */\n          const char *res;\n          if (m && ((res=match(ms, s+1, ep+1)) != NULL))\n            return res;\n          p=ep+1; goto init;  /* else return match(ms, s, ep+1); */\n        }\n        case '*': {  /* 0 or more repetitions */\n          return max_expand(ms, s, p, ep);\n        }\n        case '+': {  /* 1 or more repetitions */\n          return (m ? max_expand(ms, s+1, p, ep) : NULL);\n        }\n        case '-': {  /* 0 or more repetitions (minimum) */\n          return min_expand(ms, s, p, ep);\n        }\n        default: {\n          if (!m) return NULL;\n          s++; p=ep; goto init;  /* else return match(ms, s+1, ep); */\n        }\n      }\n    }\n  }\n}\n\n\n\nstatic const char *lmemfind (const char *s1, size_t l1,\n                               const char *s2, size_t l2) {\n  if (l2 == 0) return s1;  /* empty strings are everywhere */\n  else if (l2 > l1) return NULL;  /* avoids a negative `l1' */\n  else {\n    const char *init;  /* to search for a `*s2' inside `s1' */\n    l2--;  /* 1st char will be checked by `memchr' */\n    l1 = l1-l2;  /* `s2' cannot be found after that */\n    while (l1 > 0 && (init = (const char *)memchr(s1, *s2, l1)) != NULL) {\n      init++;   /* 1st char is already checked */\n      if (memcmp(init, s2+1, l2) == 0)\n        return init-1;\n      else {  /* correct `l1' and `s1' to try again */\n        l1 -= init-s1;\n        s1 = init;\n      }\n    }\n    return NULL;  /* not found */\n  }\n}\n\n\nstatic void push_onecapture (MatchState *ms, int i, const char *s,\n                                                    const char *e) {\n  if (i >= ms->level) {\n    if (i == 0)  /* ms->level == 0, too */\n      lua_pushlstring(ms->L, s, e - s);  /* add whole match */\n    else\n      luaL_error(ms->L, \"invalid capture index\");\n  }\n  else {\n    ptrdiff_t l = ms->capture[i].len;\n    if (l == CAP_UNFINISHED) luaL_error(ms->L, \"unfinished capture\");\n    if (l == CAP_POSITION)\n      lua_pushinteger(ms->L, ms->capture[i].init - ms->src_init + 1);\n    else\n      lua_pushlstring(ms->L, ms->capture[i].init, l);\n  }\n}\n\n\nstatic int push_captures (MatchState *ms, const char *s, const char *e) {\n  int i;\n  int nlevels = (ms->level == 0 && s) ? 1 : ms->level;\n  luaL_checkstack(ms->L, nlevels, \"too many captures\");\n  for (i = 0; i < nlevels; i++)\n    push_onecapture(ms, i, s, e);\n  return nlevels;  /* number of strings pushed */\n}\n\n\nstatic int str_find_aux (lua_State *L, int find) {\n  size_t l1, l2;\n  const char *s = luaL_checklstring(L, 1, &l1);\n  const char *p = luaL_checklstring(L, 2, &l2);\n  ptrdiff_t init = posrelat(luaL_optinteger(L, 3, 1), l1) - 1;\n  if (init < 0) init = 0;\n  else if ((size_t)(init) > l1) init = (ptrdiff_t)l1;\n  if (find && (lua_toboolean(L, 4) ||  /* explicit request? */\n      strpbrk(p, SPECIALS) == NULL)) {  /* or no special characters? */\n    /* do a plain search */\n    const char *s2 = lmemfind(s+init, l1-init, p, l2);\n    if (s2) {\n      lua_pushinteger(L, s2-s+1);\n      lua_pushinteger(L, s2-s+l2);\n      return 2;\n    }\n  }\n  else {\n    MatchState ms;\n    int anchor = (*p == '^') ? (p++, 1) : 0;\n    const char *s1=s+init;\n    ms.L = L;\n    ms.src_init = s;\n    ms.src_end = s+l1;\n    do {\n      const char *res;\n      ms.level = 0;\n      if ((res=match(&ms, s1, p)) != NULL) {\n        if (find) {\n          lua_pushinteger(L, s1-s+1);  /* start */\n          lua_pushinteger(L, res-s);   /* end */\n          return push_captures(&ms, NULL, 0) + 2;\n        }\n        else\n          return push_captures(&ms, s1, res);\n      }\n    } while (s1++ < ms.src_end && !anchor);\n  }\n  lua_pushnil(L);  /* not found */\n  return 1;\n}\n\n\nstatic int str_find (lua_State *L) {\n  return str_find_aux(L, 1);\n}\n\n\nstatic int str_match (lua_State *L) {\n  return str_find_aux(L, 0);\n}\n\n\nstatic int gmatch_aux (lua_State *L) {\n  MatchState ms;\n  size_t ls;\n  const char *s = lua_tolstring(L, lua_upvalueindex(1), &ls);\n  const char *p = lua_tostring(L, lua_upvalueindex(2));\n  const char *src;\n  ms.L = L;\n  ms.src_init = s;\n  ms.src_end = s+ls;\n  for (src = s + (size_t)lua_tointeger(L, lua_upvalueindex(3));\n       src <= ms.src_end;\n       src++) {\n    const char *e;\n    ms.level = 0;\n    if ((e = match(&ms, src, p)) != NULL) {\n      lua_Integer newstart = e-s;\n      if (e == src) newstart++;  /* empty match? go at least one position */\n      lua_pushinteger(L, newstart);\n      lua_replace(L, lua_upvalueindex(3));\n      return push_captures(&ms, src, e);\n    }\n  }\n  return 0;  /* not found */\n}\n\n\nstatic int gmatch (lua_State *L) {\n  luaL_checkstring(L, 1);\n  luaL_checkstring(L, 2);\n  lua_settop(L, 2);\n  lua_pushinteger(L, 0);\n  lua_pushcclosure(L, gmatch_aux, 3);\n  return 1;\n}\n\n\nstatic int gfind_nodef (lua_State *L) {\n  return luaL_error(L, LUA_QL(\"string.gfind\") \" was renamed to \"\n                       LUA_QL(\"string.gmatch\"));\n}\n\n\nstatic void add_s (MatchState *ms, luaL_Buffer *b, const char *s,\n                                                   const char *e) {\n  size_t l, i;\n  const char *news = lua_tolstring(ms->L, 3, &l);\n  for (i = 0; i < l; i++) {\n    if (news[i] != L_ESC)\n      luaL_addchar(b, news[i]);\n    else {\n      i++;  /* skip ESC */\n      if (!isdigit(uchar(news[i])))\n        luaL_addchar(b, news[i]);\n      else if (news[i] == '0')\n          luaL_addlstring(b, s, e - s);\n      else {\n        push_onecapture(ms, news[i] - '1', s, e);\n        luaL_addvalue(b);  /* add capture to accumulated result */\n      }\n    }\n  }\n}\n\n\nstatic void add_value (MatchState *ms, luaL_Buffer *b, const char *s,\n                                                       const char *e) {\n  lua_State *L = ms->L;\n  switch (lua_type(L, 3)) {\n    case LUA_TNUMBER:\n    case LUA_TSTRING: {\n      add_s(ms, b, s, e);\n      return;\n    }\n    case LUA_TFUNCTION: {\n      int n;\n      lua_pushvalue(L, 3);\n      n = push_captures(ms, s, e);\n      lua_call(L, n, 1);\n      break;\n    }\n    case LUA_TTABLE: {\n      push_onecapture(ms, 0, s, e);\n      lua_gettable(L, 3);\n      break;\n    }\n  }\n  if (!lua_toboolean(L, -1)) {  /* nil or false? */\n    lua_pop(L, 1);\n    lua_pushlstring(L, s, e - s);  /* keep original text */\n  }\n  else if (!lua_isstring(L, -1))\n    luaL_error(L, \"invalid replacement value (a %s)\", luaL_typename(L, -1)); \n  luaL_addvalue(b);  /* add result to accumulator */\n}\n\n\nstatic int str_gsub (lua_State *L) {\n  size_t srcl;\n  const char *src = luaL_checklstring(L, 1, &srcl);\n  const char *p = luaL_checkstring(L, 2);\n  int  tr = lua_type(L, 3);\n  int max_s = luaL_optint(L, 4, srcl+1);\n  int anchor = (*p == '^') ? (p++, 1) : 0;\n  int n = 0;\n  MatchState ms;\n  luaL_Buffer b;\n  luaL_argcheck(L, tr == LUA_TNUMBER || tr == LUA_TSTRING ||\n                   tr == LUA_TFUNCTION || tr == LUA_TTABLE, 3,\n                      \"string/function/table expected\");\n  luaL_buffinit(L, &b);\n  ms.L = L;\n  ms.src_init = src;\n  ms.src_end = src+srcl;\n  while (n < max_s) {\n    const char *e;\n    ms.level = 0;\n    e = match(&ms, src, p);\n    if (e) {\n      n++;\n      add_value(&ms, &b, src, e);\n    }\n    if (e && e>src) /* non empty match? */\n      src = e;  /* skip it */\n    else if (src < ms.src_end)\n      luaL_addchar(&b, *src++);\n    else break;\n    if (anchor) break;\n  }\n  luaL_addlstring(&b, src, ms.src_end-src);\n  luaL_pushresult(&b);\n  lua_pushinteger(L, n);  /* number of substitutions */\n  return 2;\n}\n\n/* }====================================================== */\n\n\n/* maximum size of each formatted item (> len(format('%99.99f', -1e308))) */\n#define MAX_ITEM\t512\n/* valid flags in a format specification */\n#define FLAGS\t\"-+ #0\"\n/*\n** maximum size of each format specification (such as '%-099.99d')\n** (+10 accounts for %99.99x plus margin of error)\n*/\n#define MAX_FORMAT\t(sizeof(FLAGS) + sizeof(LUA_INTFRMLEN) + 10)\n\n\nstatic void addquoted (lua_State *L, luaL_Buffer *b, int arg) {\n  size_t l;\n  const char *s = luaL_checklstring(L, arg, &l);\n  luaL_addchar(b, '\"');\n  while (l--) {\n    switch (*s) {\n      case '\"': case '\\\\': case '\\n': {\n        luaL_addchar(b, '\\\\');\n        luaL_addchar(b, *s);\n        break;\n      }\n      case '\\r': {\n        luaL_addlstring(b, \"\\\\r\", 2);\n        break;\n      }\n      case '\\0': {\n        luaL_addlstring(b, \"\\\\000\", 4);\n        break;\n      }\n      default: {\n        luaL_addchar(b, *s);\n        break;\n      }\n    }\n    s++;\n  }\n  luaL_addchar(b, '\"');\n}\n\nstatic const char *scanformat (lua_State *L, const char *strfrmt, char *form) {\n  const char *p = strfrmt;\n  while (*p != '\\0' && strchr(FLAGS, *p) != NULL) p++;  /* skip flags */\n  if ((size_t)(p - strfrmt) >= sizeof(FLAGS))\n    luaL_error(L, \"invalid format (repeated flags)\");\n  if (isdigit(uchar(*p))) p++;  /* skip width */\n  if (isdigit(uchar(*p))) p++;  /* (2 digits at most) */\n  if (*p == '.') {\n    p++;\n    if (isdigit(uchar(*p))) p++;  /* skip precision */\n    if (isdigit(uchar(*p))) p++;  /* (2 digits at most) */\n  }\n  if (isdigit(uchar(*p)))\n    luaL_error(L, \"invalid format (width or precision too long)\");\n  *(form++) = '%';\n  strncpy(form, strfrmt, p - strfrmt + 1);\n  form += p - strfrmt + 1;\n  *form = '\\0';\n  return p;\n}\n\n\nstatic void addintlen (char *form) {\n  size_t l = strlen(form);\n  char spec = form[l - 1];\n  strcpy(form + l - 1, LUA_INTFRMLEN);\n  form[l + sizeof(LUA_INTFRMLEN) - 2] = spec;\n  form[l + sizeof(LUA_INTFRMLEN) - 1] = '\\0';\n}\n\n\nstatic int str_format (lua_State *L) {\n  int top = lua_gettop(L);\n  int arg = 1;\n  size_t sfl;\n  const char *strfrmt = luaL_checklstring(L, arg, &sfl);\n  const char *strfrmt_end = strfrmt+sfl;\n  luaL_Buffer b;\n  luaL_buffinit(L, &b);\n  while (strfrmt < strfrmt_end) {\n    if (*strfrmt != L_ESC)\n      luaL_addchar(&b, *strfrmt++);\n    else if (*++strfrmt == L_ESC)\n      luaL_addchar(&b, *strfrmt++);  /* %% */\n    else { /* format item */\n      char form[MAX_FORMAT];  /* to store the format (`%...') */\n      char buff[MAX_ITEM];  /* to store the formatted item */\n      if (++arg > top)\n        luaL_argerror(L, arg, \"no value\");\n      strfrmt = scanformat(L, strfrmt, form);\n      switch (*strfrmt++) {\n        case 'c': {\n          sprintf(buff, form, (int)luaL_checknumber(L, arg));\n          break;\n        }\n        case 'd':  case 'i': {\n          addintlen(form);\n          sprintf(buff, form, (LUA_INTFRM_T)luaL_checknumber(L, arg));\n          break;\n        }\n        case 'o':  case 'u':  case 'x':  case 'X': {\n          addintlen(form);\n          sprintf(buff, form, (unsigned LUA_INTFRM_T)luaL_checknumber(L, arg));\n          break;\n        }\n        case 'e':  case 'E': case 'f':\n        case 'g': case 'G': {\n          sprintf(buff, form, (double)luaL_checknumber(L, arg));\n          break;\n        }\n        case 'q': {\n          addquoted(L, &b, arg);\n          continue;  /* skip the 'addsize' at the end */\n        }\n        case 's': {\n          size_t l;\n          const char *s = luaL_checklstring(L, arg, &l);\n          if (!strchr(form, '.') && l >= 100) {\n            /* no precision and string is too long to be formatted;\n               keep original string */\n            lua_pushvalue(L, arg);\n            luaL_addvalue(&b);\n            continue;  /* skip the `addsize' at the end */\n          }\n          else {\n            sprintf(buff, form, s);\n            break;\n          }\n        }\n        default: {  /* also treat cases `pnLlh' */\n          return luaL_error(L, \"invalid option \" LUA_QL(\"%%%c\") \" to \"\n                               LUA_QL(\"format\"), *(strfrmt - 1));\n        }\n      }\n      luaL_addlstring(&b, buff, strlen(buff));\n    }\n  }\n  luaL_pushresult(&b);\n  return 1;\n}\n\n\nstatic const luaL_Reg strlib[] = {\n  {\"byte\", str_byte},\n  {\"char\", str_char},\n  {\"dump\", str_dump},\n  {\"find\", str_find},\n  {\"format\", str_format},\n  {\"gfind\", gfind_nodef},\n  {\"gmatch\", gmatch},\n  {\"gsub\", str_gsub},\n  {\"len\", str_len},\n  {\"lower\", str_lower},\n  {\"match\", str_match},\n  {\"rep\", str_rep},\n  {\"reverse\", str_reverse},\n  {\"sub\", str_sub},\n  {\"upper\", str_upper},\n  {NULL, NULL}\n};\n\n\nstatic void createmetatable (lua_State *L) {\n  lua_createtable(L, 0, 1);  /* create metatable for strings */\n  lua_pushliteral(L, \"\");  /* dummy string */\n  lua_pushvalue(L, -2);\n  lua_setmetatable(L, -2);  /* set string metatable */\n  lua_pop(L, 1);  /* pop dummy string */\n  lua_pushvalue(L, -2);  /* string library... */\n  lua_setfield(L, -2, \"__index\");  /* ...is the __index metamethod */\n  lua_pop(L, 1);  /* pop metatable */\n}\n\n\n/*\n** Open string library\n*/\nLUALIB_API int luaopen_string (lua_State *L) {\n  luaL_register(L, LUA_STRLIBNAME, strlib);\n#if defined(LUA_COMPAT_GFIND)\n  lua_getfield(L, -1, \"gmatch\");\n  lua_setfield(L, -2, \"gfind\");\n#endif\n  createmetatable(L);\n  return 1;\n}\n\n"
  },
  {
    "path": "deps/lua/src/ltable.c",
    "content": "/*\n** $Id: ltable.c,v 2.32.1.2 2007/12/28 15:32:23 roberto Exp $\n** Lua tables (hash)\n** See Copyright Notice in lua.h\n*/\n\n\n/*\n** Implementation of tables (aka arrays, objects, or hash tables).\n** Tables keep its elements in two parts: an array part and a hash part.\n** Non-negative integer keys are all candidates to be kept in the array\n** part. The actual size of the array is the largest `n' such that at\n** least half the slots between 0 and n are in use.\n** Hash uses a mix of chained scatter table with Brent's variation.\n** A main invariant of these tables is that, if an element is not\n** in its main position (i.e. the `original' position that its hash gives\n** to it), then the colliding element is in its own main position.\n** Hence even when the load factor reaches 100%, performance remains good.\n*/\n\n#include <math.h>\n#include <string.h>\n\n#define ltable_c\n#define LUA_CORE\n\n#include \"lua.h\"\n\n#include \"ldebug.h\"\n#include \"ldo.h\"\n#include \"lgc.h\"\n#include \"lmem.h\"\n#include \"lobject.h\"\n#include \"lstate.h\"\n#include \"ltable.h\"\n\n\n/*\n** max size of array part is 2^MAXBITS\n*/\n#if LUAI_BITSINT > 26\n#define MAXBITS\t\t26\n#else\n#define MAXBITS\t\t(LUAI_BITSINT-2)\n#endif\n\n#define MAXASIZE\t(1 << MAXBITS)\n\n\n#define hashpow2(t,n)      (gnode(t, lmod((n), sizenode(t))))\n  \n#define hashstr(t,str)  hashpow2(t, (str)->tsv.hash)\n#define hashboolean(t,p)        hashpow2(t, p)\n\n\n/*\n** for some types, it is better to avoid modulus by power of 2, as\n** they tend to have many 2 factors.\n*/\n#define hashmod(t,n)\t(gnode(t, ((n) % ((sizenode(t)-1)|1))))\n\n\n#define hashpointer(t,p)\thashmod(t, IntPoint(p))\n\n\n/*\n** number of ints inside a lua_Number\n*/\n#define numints\t\tcast_int(sizeof(lua_Number)/sizeof(int))\n\n\n\n#define dummynode\t\t(&dummynode_)\n\nstatic const Node dummynode_ = {\n  {{NULL}, LUA_TNIL},  /* value */\n  {{{NULL}, LUA_TNIL, NULL}}  /* key */\n};\n\n\n/*\n** hash for lua_Numbers\n*/\nstatic Node *hashnum (const Table *t, lua_Number n) {\n  unsigned int a[numints];\n  int i;\n  if (luai_numeq(n, 0))  /* avoid problems with -0 */\n    return gnode(t, 0);\n  memcpy(a, &n, sizeof(a));\n  for (i = 1; i < numints; i++) a[0] += a[i];\n  return hashmod(t, a[0]);\n}\n\n\n\n/*\n** returns the `main' position of an element in a table (that is, the index\n** of its hash value)\n*/\nstatic Node *mainposition (const Table *t, const TValue *key) {\n  switch (ttype(key)) {\n    case LUA_TNUMBER:\n      return hashnum(t, nvalue(key));\n    case LUA_TSTRING:\n      return hashstr(t, rawtsvalue(key));\n    case LUA_TBOOLEAN:\n      return hashboolean(t, bvalue(key));\n    case LUA_TLIGHTUSERDATA:\n      return hashpointer(t, pvalue(key));\n    default:\n      return hashpointer(t, gcvalue(key));\n  }\n}\n\n\n/*\n** returns the index for `key' if `key' is an appropriate key to live in\n** the array part of the table, -1 otherwise.\n*/\nstatic int arrayindex (const TValue *key) {\n  if (ttisnumber(key)) {\n    lua_Number n = nvalue(key);\n    int k;\n    lua_number2int(k, n);\n    if (luai_numeq(cast_num(k), n))\n      return k;\n  }\n  return -1;  /* `key' did not match some condition */\n}\n\n\n/*\n** returns the index of a `key' for table traversals. First goes all\n** elements in the array part, then elements in the hash part. The\n** beginning of a traversal is signalled by -1.\n*/\nstatic int findindex (lua_State *L, Table *t, StkId key) {\n  int i;\n  if (ttisnil(key)) return -1;  /* first iteration */\n  i = arrayindex(key);\n  if (0 < i && i <= t->sizearray)  /* is `key' inside array part? */\n    return i-1;  /* yes; that's the index (corrected to C) */\n  else {\n    Node *n = mainposition(t, key);\n    do {  /* check whether `key' is somewhere in the chain */\n      /* key may be dead already, but it is ok to use it in `next' */\n      if (luaO_rawequalObj(key2tval(n), key) ||\n            (ttype(gkey(n)) == LUA_TDEADKEY && iscollectable(key) &&\n             gcvalue(gkey(n)) == gcvalue(key))) {\n        i = cast_int(n - gnode(t, 0));  /* key index in hash table */\n        /* hash elements are numbered after array ones */\n        return i + t->sizearray;\n      }\n      else n = gnext(n);\n    } while (n);\n    luaG_runerror(L, \"invalid key to \" LUA_QL(\"next\"));  /* key not found */\n    return 0;  /* to avoid warnings */\n  }\n}\n\n\nint luaH_next (lua_State *L, Table *t, StkId key) {\n  int i = findindex(L, t, key);  /* find original element */\n  for (i++; i < t->sizearray; i++) {  /* try first array part */\n    if (!ttisnil(&t->array[i])) {  /* a non-nil value? */\n      setnvalue(key, cast_num(i+1));\n      setobj2s(L, key+1, &t->array[i]);\n      return 1;\n    }\n  }\n  for (i -= t->sizearray; i < sizenode(t); i++) {  /* then hash part */\n    if (!ttisnil(gval(gnode(t, i)))) {  /* a non-nil value? */\n      setobj2s(L, key, key2tval(gnode(t, i)));\n      setobj2s(L, key+1, gval(gnode(t, i)));\n      return 1;\n    }\n  }\n  return 0;  /* no more elements */\n}\n\n\n/*\n** {=============================================================\n** Rehash\n** ==============================================================\n*/\n\n\nstatic int computesizes (int nums[], int *narray) {\n  int i;\n  int twotoi;  /* 2^i */\n  int a = 0;  /* number of elements smaller than 2^i */\n  int na = 0;  /* number of elements to go to array part */\n  int n = 0;  /* optimal size for array part */\n  for (i = 0, twotoi = 1; twotoi/2 < *narray; i++, twotoi *= 2) {\n    if (nums[i] > 0) {\n      a += nums[i];\n      if (a > twotoi/2) {  /* more than half elements present? */\n        n = twotoi;  /* optimal size (till now) */\n        na = a;  /* all elements smaller than n will go to array part */\n      }\n    }\n    if (a == *narray) break;  /* all elements already counted */\n  }\n  *narray = n;\n  lua_assert(*narray/2 <= na && na <= *narray);\n  return na;\n}\n\n\nstatic int countint (const TValue *key, int *nums) {\n  int k = arrayindex(key);\n  if (0 < k && k <= MAXASIZE) {  /* is `key' an appropriate array index? */\n    nums[ceillog2(k)]++;  /* count as such */\n    return 1;\n  }\n  else\n    return 0;\n}\n\n\nstatic int numusearray (const Table *t, int *nums) {\n  int lg;\n  int ttlg;  /* 2^lg */\n  int ause = 0;  /* summation of `nums' */\n  int i = 1;  /* count to traverse all array keys */\n  for (lg=0, ttlg=1; lg<=MAXBITS; lg++, ttlg*=2) {  /* for each slice */\n    int lc = 0;  /* counter */\n    int lim = ttlg;\n    if (lim > t->sizearray) {\n      lim = t->sizearray;  /* adjust upper limit */\n      if (i > lim)\n        break;  /* no more elements to count */\n    }\n    /* count elements in range (2^(lg-1), 2^lg] */\n    for (; i <= lim; i++) {\n      if (!ttisnil(&t->array[i-1]))\n        lc++;\n    }\n    nums[lg] += lc;\n    ause += lc;\n  }\n  return ause;\n}\n\n\nstatic int numusehash (const Table *t, int *nums, int *pnasize) {\n  int totaluse = 0;  /* total number of elements */\n  int ause = 0;  /* summation of `nums' */\n  int i = sizenode(t);\n  while (i--) {\n    Node *n = &t->node[i];\n    if (!ttisnil(gval(n))) {\n      ause += countint(key2tval(n), nums);\n      totaluse++;\n    }\n  }\n  *pnasize += ause;\n  return totaluse;\n}\n\n\nstatic void setarrayvector (lua_State *L, Table *t, int size) {\n  int i;\n  luaM_reallocvector(L, t->array, t->sizearray, size, TValue);\n  for (i=t->sizearray; i<size; i++)\n     setnilvalue(&t->array[i]);\n  t->sizearray = size;\n}\n\n\nstatic void setnodevector (lua_State *L, Table *t, int size) {\n  int lsize;\n  if (size == 0) {  /* no elements to hash part? */\n    t->node = cast(Node *, dummynode);  /* use common `dummynode' */\n    lsize = 0;\n  }\n  else {\n    int i;\n    lsize = ceillog2(size);\n    if (lsize > MAXBITS)\n      luaG_runerror(L, \"table overflow\");\n    size = twoto(lsize);\n    t->node = luaM_newvector(L, size, Node);\n    for (i=0; i<size; i++) {\n      Node *n = gnode(t, i);\n      gnext(n) = NULL;\n      setnilvalue(gkey(n));\n      setnilvalue(gval(n));\n    }\n  }\n  t->lsizenode = cast_byte(lsize);\n  t->lastfree = gnode(t, size);  /* all positions are free */\n}\n\n\nstatic void resize (lua_State *L, Table *t, int nasize, int nhsize) {\n  int i;\n  int oldasize = t->sizearray;\n  int oldhsize = t->lsizenode;\n  Node *nold = t->node;  /* save old hash ... */\n  if (nasize > oldasize)  /* array part must grow? */\n    setarrayvector(L, t, nasize);\n  /* create new hash part with appropriate size */\n  setnodevector(L, t, nhsize);  \n  if (nasize < oldasize) {  /* array part must shrink? */\n    t->sizearray = nasize;\n    /* re-insert elements from vanishing slice */\n    for (i=nasize; i<oldasize; i++) {\n      if (!ttisnil(&t->array[i]))\n        setobjt2t(L, luaH_setnum(L, t, i+1), &t->array[i]);\n    }\n    /* shrink array */\n    luaM_reallocvector(L, t->array, oldasize, nasize, TValue);\n  }\n  /* re-insert elements from hash part */\n  for (i = twoto(oldhsize) - 1; i >= 0; i--) {\n    Node *old = nold+i;\n    if (!ttisnil(gval(old)))\n      setobjt2t(L, luaH_set(L, t, key2tval(old)), gval(old));\n  }\n  if (nold != dummynode)\n    luaM_freearray(L, nold, twoto(oldhsize), Node);  /* free old array */\n}\n\n\nvoid luaH_resizearray (lua_State *L, Table *t, int nasize) {\n  int nsize = (t->node == dummynode) ? 0 : sizenode(t);\n  resize(L, t, nasize, nsize);\n}\n\n\nstatic void rehash (lua_State *L, Table *t, const TValue *ek) {\n  int nasize, na;\n  int nums[MAXBITS+1];  /* nums[i] = number of keys between 2^(i-1) and 2^i */\n  int i;\n  int totaluse;\n  for (i=0; i<=MAXBITS; i++) nums[i] = 0;  /* reset counts */\n  nasize = numusearray(t, nums);  /* count keys in array part */\n  totaluse = nasize;  /* all those keys are integer keys */\n  totaluse += numusehash(t, nums, &nasize);  /* count keys in hash part */\n  /* count extra key */\n  nasize += countint(ek, nums);\n  totaluse++;\n  /* compute new size for array part */\n  na = computesizes(nums, &nasize);\n  /* resize the table to new computed sizes */\n  resize(L, t, nasize, totaluse - na);\n}\n\n\n\n/*\n** }=============================================================\n*/\n\n\nTable *luaH_new (lua_State *L, int narray, int nhash) {\n  Table *t = luaM_new(L, Table);\n  luaC_link(L, obj2gco(t), LUA_TTABLE);\n  t->metatable = NULL;\n  t->flags = cast_byte(~0);\n  /* temporary values (kept only if some malloc fails) */\n  t->array = NULL;\n  t->sizearray = 0;\n  t->lsizenode = 0;\n  t->node = cast(Node *, dummynode);\n  setarrayvector(L, t, narray);\n  setnodevector(L, t, nhash);\n  return t;\n}\n\n\nvoid luaH_free (lua_State *L, Table *t) {\n  if (t->node != dummynode)\n    luaM_freearray(L, t->node, sizenode(t), Node);\n  luaM_freearray(L, t->array, t->sizearray, TValue);\n  luaM_free(L, t);\n}\n\n\nstatic Node *getfreepos (Table *t) {\n  while (t->lastfree-- > t->node) {\n    if (ttisnil(gkey(t->lastfree)))\n      return t->lastfree;\n  }\n  return NULL;  /* could not find a free place */\n}\n\n\n\n/*\n** inserts a new key into a hash table; first, check whether key's main \n** position is free. If not, check whether colliding node is in its main \n** position or not: if it is not, move colliding node to an empty place and \n** put new key in its main position; otherwise (colliding node is in its main \n** position), new key goes to an empty position. \n*/\nstatic TValue *newkey (lua_State *L, Table *t, const TValue *key) {\n  Node *mp = mainposition(t, key);\n  if (!ttisnil(gval(mp)) || mp == dummynode) {\n    Node *othern;\n    Node *n = getfreepos(t);  /* get a free place */\n    if (n == NULL) {  /* cannot find a free place? */\n      rehash(L, t, key);  /* grow table */\n      return luaH_set(L, t, key);  /* re-insert key into grown table */\n    }\n    lua_assert(n != dummynode);\n    othern = mainposition(t, key2tval(mp));\n    if (othern != mp) {  /* is colliding node out of its main position? */\n      /* yes; move colliding node into free position */\n      while (gnext(othern) != mp) othern = gnext(othern);  /* find previous */\n      gnext(othern) = n;  /* redo the chain with `n' in place of `mp' */\n      *n = *mp;  /* copy colliding node into free pos. (mp->next also goes) */\n      gnext(mp) = NULL;  /* now `mp' is free */\n      setnilvalue(gval(mp));\n    }\n    else {  /* colliding node is in its own main position */\n      /* new node will go into free position */\n      gnext(n) = gnext(mp);  /* chain new position */\n      gnext(mp) = n;\n      mp = n;\n    }\n  }\n  gkey(mp)->value = key->value; gkey(mp)->tt = key->tt;\n  luaC_barriert(L, t, key);\n  lua_assert(ttisnil(gval(mp)));\n  return gval(mp);\n}\n\n\n/*\n** search function for integers\n*/\nconst TValue *luaH_getnum (Table *t, int key) {\n  /* (1 <= key && key <= t->sizearray) */\n  if (cast(unsigned int, key-1) < cast(unsigned int, t->sizearray))\n    return &t->array[key-1];\n  else {\n    lua_Number nk = cast_num(key);\n    Node *n = hashnum(t, nk);\n    do {  /* check whether `key' is somewhere in the chain */\n      if (ttisnumber(gkey(n)) && luai_numeq(nvalue(gkey(n)), nk))\n        return gval(n);  /* that's it */\n      else n = gnext(n);\n    } while (n);\n    return luaO_nilobject;\n  }\n}\n\n\n/*\n** search function for strings\n*/\nconst TValue *luaH_getstr (Table *t, TString *key) {\n  Node *n = hashstr(t, key);\n  do {  /* check whether `key' is somewhere in the chain */\n    if (ttisstring(gkey(n)) && rawtsvalue(gkey(n)) == key)\n      return gval(n);  /* that's it */\n    else n = gnext(n);\n  } while (n);\n  return luaO_nilobject;\n}\n\n\n/*\n** main search function\n*/\nconst TValue *luaH_get (Table *t, const TValue *key) {\n  switch (ttype(key)) {\n    case LUA_TNIL: return luaO_nilobject;\n    case LUA_TSTRING: return luaH_getstr(t, rawtsvalue(key));\n    case LUA_TNUMBER: {\n      int k;\n      lua_Number n = nvalue(key);\n      lua_number2int(k, n);\n      if (luai_numeq(cast_num(k), nvalue(key))) /* index is int? */\n        return luaH_getnum(t, k);  /* use specialized version */\n      /* else go through */\n    }\n    default: {\n      Node *n = mainposition(t, key);\n      do {  /* check whether `key' is somewhere in the chain */\n        if (luaO_rawequalObj(key2tval(n), key))\n          return gval(n);  /* that's it */\n        else n = gnext(n);\n      } while (n);\n      return luaO_nilobject;\n    }\n  }\n}\n\n\nTValue *luaH_set (lua_State *L, Table *t, const TValue *key) {\n  const TValue *p = luaH_get(t, key);\n  t->flags = 0;\n  if (p != luaO_nilobject)\n    return cast(TValue *, p);\n  else {\n    if (ttisnil(key)) luaG_runerror(L, \"table index is nil\");\n    else if (ttisnumber(key) && luai_numisnan(nvalue(key)))\n      luaG_runerror(L, \"table index is NaN\");\n    return newkey(L, t, key);\n  }\n}\n\n\nTValue *luaH_setnum (lua_State *L, Table *t, int key) {\n  const TValue *p = luaH_getnum(t, key);\n  if (p != luaO_nilobject)\n    return cast(TValue *, p);\n  else {\n    TValue k;\n    setnvalue(&k, cast_num(key));\n    return newkey(L, t, &k);\n  }\n}\n\n\nTValue *luaH_setstr (lua_State *L, Table *t, TString *key) {\n  const TValue *p = luaH_getstr(t, key);\n  if (p != luaO_nilobject)\n    return cast(TValue *, p);\n  else {\n    TValue k;\n    setsvalue(L, &k, key);\n    return newkey(L, t, &k);\n  }\n}\n\n\nstatic int unbound_search (Table *t, unsigned int j) {\n  unsigned int i = j;  /* i is zero or a present index */\n  j++;\n  /* find `i' and `j' such that i is present and j is not */\n  while (!ttisnil(luaH_getnum(t, j))) {\n    i = j;\n    j *= 2;\n    if (j > cast(unsigned int, MAX_INT)) {  /* overflow? */\n      /* table was built with bad purposes: resort to linear search */\n      i = 1;\n      while (!ttisnil(luaH_getnum(t, i))) i++;\n      return i - 1;\n    }\n  }\n  /* now do a binary search between them */\n  while (j - i > 1) {\n    unsigned int m = (i+j)/2;\n    if (ttisnil(luaH_getnum(t, m))) j = m;\n    else i = m;\n  }\n  return i;\n}\n\n\n/*\n** Try to find a boundary in table `t'. A `boundary' is an integer index\n** such that t[i] is non-nil and t[i+1] is nil (and 0 if t[1] is nil).\n*/\nint luaH_getn (Table *t) {\n  unsigned int j = t->sizearray;\n  if (j > 0 && ttisnil(&t->array[j - 1])) {\n    /* there is a boundary in the array part: (binary) search for it */\n    unsigned int i = 0;\n    while (j - i > 1) {\n      unsigned int m = (i+j)/2;\n      if (ttisnil(&t->array[m - 1])) j = m;\n      else i = m;\n    }\n    return i;\n  }\n  /* else must find a boundary in hash part */\n  else if (t->node == dummynode)  /* hash part is empty? */\n    return j;  /* that is easy... */\n  else return unbound_search(t, j);\n}\n\n\n\n#if defined(LUA_DEBUG)\n\nNode *luaH_mainposition (const Table *t, const TValue *key) {\n  return mainposition(t, key);\n}\n\nint luaH_isdummy (Node *n) { return n == dummynode; }\n\n#endif\n"
  },
  {
    "path": "deps/lua/src/ltable.h",
    "content": "/*\n** $Id: ltable.h,v 2.10.1.1 2007/12/27 13:02:25 roberto Exp $\n** Lua tables (hash)\n** See Copyright Notice in lua.h\n*/\n\n#ifndef ltable_h\n#define ltable_h\n\n#include \"lobject.h\"\n\n\n#define gnode(t,i)\t(&(t)->node[i])\n#define gkey(n)\t\t(&(n)->i_key.nk)\n#define gval(n)\t\t(&(n)->i_val)\n#define gnext(n)\t((n)->i_key.nk.next)\n\n#define key2tval(n)\t(&(n)->i_key.tvk)\n\n\nLUAI_FUNC const TValue *luaH_getnum (Table *t, int key);\nLUAI_FUNC TValue *luaH_setnum (lua_State *L, Table *t, int key);\nLUAI_FUNC const TValue *luaH_getstr (Table *t, TString *key);\nLUAI_FUNC TValue *luaH_setstr (lua_State *L, Table *t, TString *key);\nLUAI_FUNC const TValue *luaH_get (Table *t, const TValue *key);\nLUAI_FUNC TValue *luaH_set (lua_State *L, Table *t, const TValue *key);\nLUAI_FUNC Table *luaH_new (lua_State *L, int narray, int lnhash);\nLUAI_FUNC void luaH_resizearray (lua_State *L, Table *t, int nasize);\nLUAI_FUNC void luaH_free (lua_State *L, Table *t);\nLUAI_FUNC int luaH_next (lua_State *L, Table *t, StkId key);\nLUAI_FUNC int luaH_getn (Table *t);\n\n\n#if defined(LUA_DEBUG)\nLUAI_FUNC Node *luaH_mainposition (const Table *t, const TValue *key);\nLUAI_FUNC int luaH_isdummy (Node *n);\n#endif\n\n\n#endif\n"
  },
  {
    "path": "deps/lua/src/ltablib.c",
    "content": "/*\n** $Id: ltablib.c,v 1.38.1.3 2008/02/14 16:46:58 roberto Exp $\n** Library for Table Manipulation\n** See Copyright Notice in lua.h\n*/\n\n\n#include <stddef.h>\n\n#define ltablib_c\n#define LUA_LIB\n\n#include \"lua.h\"\n\n#include \"lauxlib.h\"\n#include \"lualib.h\"\n\n\n#define aux_getn(L,n)\t(luaL_checktype(L, n, LUA_TTABLE), luaL_getn(L, n))\n\n\nstatic int foreachi (lua_State *L) {\n  int i;\n  int n = aux_getn(L, 1);\n  luaL_checktype(L, 2, LUA_TFUNCTION);\n  for (i=1; i <= n; i++) {\n    lua_pushvalue(L, 2);  /* function */\n    lua_pushinteger(L, i);  /* 1st argument */\n    lua_rawgeti(L, 1, i);  /* 2nd argument */\n    lua_call(L, 2, 1);\n    if (!lua_isnil(L, -1))\n      return 1;\n    lua_pop(L, 1);  /* remove nil result */\n  }\n  return 0;\n}\n\n\nstatic int foreach (lua_State *L) {\n  luaL_checktype(L, 1, LUA_TTABLE);\n  luaL_checktype(L, 2, LUA_TFUNCTION);\n  lua_pushnil(L);  /* first key */\n  while (lua_next(L, 1)) {\n    lua_pushvalue(L, 2);  /* function */\n    lua_pushvalue(L, -3);  /* key */\n    lua_pushvalue(L, -3);  /* value */\n    lua_call(L, 2, 1);\n    if (!lua_isnil(L, -1))\n      return 1;\n    lua_pop(L, 2);  /* remove value and result */\n  }\n  return 0;\n}\n\n\nstatic int maxn (lua_State *L) {\n  lua_Number max = 0;\n  luaL_checktype(L, 1, LUA_TTABLE);\n  lua_pushnil(L);  /* first key */\n  while (lua_next(L, 1)) {\n    lua_pop(L, 1);  /* remove value */\n    if (lua_type(L, -1) == LUA_TNUMBER) {\n      lua_Number v = lua_tonumber(L, -1);\n      if (v > max) max = v;\n    }\n  }\n  lua_pushnumber(L, max);\n  return 1;\n}\n\n\nstatic int getn (lua_State *L) {\n  lua_pushinteger(L, aux_getn(L, 1));\n  return 1;\n}\n\n\nstatic int setn (lua_State *L) {\n  luaL_checktype(L, 1, LUA_TTABLE);\n#ifndef luaL_setn\n  luaL_setn(L, 1, luaL_checkint(L, 2));\n#else\n  luaL_error(L, LUA_QL(\"setn\") \" is obsolete\");\n#endif\n  lua_pushvalue(L, 1);\n  return 1;\n}\n\n\nstatic int tinsert (lua_State *L) {\n  int e = aux_getn(L, 1) + 1;  /* first empty element */\n  int pos;  /* where to insert new element */\n  switch (lua_gettop(L)) {\n    case 2: {  /* called with only 2 arguments */\n      pos = e;  /* insert new element at the end */\n      break;\n    }\n    case 3: {\n      int i;\n      pos = luaL_checkint(L, 2);  /* 2nd argument is the position */\n      if (pos > e) e = pos;  /* `grow' array if necessary */\n      for (i = e; i > pos; i--) {  /* move up elements */\n        lua_rawgeti(L, 1, i-1);\n        lua_rawseti(L, 1, i);  /* t[i] = t[i-1] */\n      }\n      break;\n    }\n    default: {\n      return luaL_error(L, \"wrong number of arguments to \" LUA_QL(\"insert\"));\n    }\n  }\n  luaL_setn(L, 1, e);  /* new size */\n  lua_rawseti(L, 1, pos);  /* t[pos] = v */\n  return 0;\n}\n\n\nstatic int tremove (lua_State *L) {\n  int e = aux_getn(L, 1);\n  int pos = luaL_optint(L, 2, e);\n  if (!(1 <= pos && pos <= e))  /* position is outside bounds? */\n   return 0;  /* nothing to remove */\n  luaL_setn(L, 1, e - 1);  /* t.n = n-1 */\n  lua_rawgeti(L, 1, pos);  /* result = t[pos] */\n  for ( ;pos<e; pos++) {\n    lua_rawgeti(L, 1, pos+1);\n    lua_rawseti(L, 1, pos);  /* t[pos] = t[pos+1] */\n  }\n  lua_pushnil(L);\n  lua_rawseti(L, 1, e);  /* t[e] = nil */\n  return 1;\n}\n\n\nstatic void addfield (lua_State *L, luaL_Buffer *b, int i) {\n  lua_rawgeti(L, 1, i);\n  if (!lua_isstring(L, -1))\n    luaL_error(L, \"invalid value (%s) at index %d in table for \"\n                  LUA_QL(\"concat\"), luaL_typename(L, -1), i);\n    luaL_addvalue(b);\n}\n\n\nstatic int tconcat (lua_State *L) {\n  luaL_Buffer b;\n  size_t lsep;\n  int i, last;\n  const char *sep = luaL_optlstring(L, 2, \"\", &lsep);\n  luaL_checktype(L, 1, LUA_TTABLE);\n  i = luaL_optint(L, 3, 1);\n  last = luaL_opt(L, luaL_checkint, 4, luaL_getn(L, 1));\n  luaL_buffinit(L, &b);\n  for (; i < last; i++) {\n    addfield(L, &b, i);\n    luaL_addlstring(&b, sep, lsep);\n  }\n  if (i == last)  /* add last value (if interval was not empty) */\n    addfield(L, &b, i);\n  luaL_pushresult(&b);\n  return 1;\n}\n\n\n\n/*\n** {======================================================\n** Quicksort\n** (based on `Algorithms in MODULA-3', Robert Sedgewick;\n**  Addison-Wesley, 1993.)\n*/\n\n\nstatic void set2 (lua_State *L, int i, int j) {\n  lua_rawseti(L, 1, i);\n  lua_rawseti(L, 1, j);\n}\n\nstatic int sort_comp (lua_State *L, int a, int b) {\n  if (!lua_isnil(L, 2)) {  /* function? */\n    int res;\n    lua_pushvalue(L, 2);\n    lua_pushvalue(L, a-1);  /* -1 to compensate function */\n    lua_pushvalue(L, b-2);  /* -2 to compensate function and `a' */\n    lua_call(L, 2, 1);\n    res = lua_toboolean(L, -1);\n    lua_pop(L, 1);\n    return res;\n  }\n  else  /* a < b? */\n    return lua_lessthan(L, a, b);\n}\n\nstatic void auxsort (lua_State *L, int l, int u) {\n  while (l < u) {  /* for tail recursion */\n    int i, j;\n    /* sort elements a[l], a[(l+u)/2] and a[u] */\n    lua_rawgeti(L, 1, l);\n    lua_rawgeti(L, 1, u);\n    if (sort_comp(L, -1, -2))  /* a[u] < a[l]? */\n      set2(L, l, u);  /* swap a[l] - a[u] */\n    else\n      lua_pop(L, 2);\n    if (u-l == 1) break;  /* only 2 elements */\n    i = (l+u)/2;\n    lua_rawgeti(L, 1, i);\n    lua_rawgeti(L, 1, l);\n    if (sort_comp(L, -2, -1))  /* a[i]<a[l]? */\n      set2(L, i, l);\n    else {\n      lua_pop(L, 1);  /* remove a[l] */\n      lua_rawgeti(L, 1, u);\n      if (sort_comp(L, -1, -2))  /* a[u]<a[i]? */\n        set2(L, i, u);\n      else\n        lua_pop(L, 2);\n    }\n    if (u-l == 2) break;  /* only 3 elements */\n    lua_rawgeti(L, 1, i);  /* Pivot */\n    lua_pushvalue(L, -1);\n    lua_rawgeti(L, 1, u-1);\n    set2(L, i, u-1);\n    /* a[l] <= P == a[u-1] <= a[u], only need to sort from l+1 to u-2 */\n    i = l; j = u-1;\n    for (;;) {  /* invariant: a[l..i] <= P <= a[j..u] */\n      /* repeat ++i until a[i] >= P */\n      while (lua_rawgeti(L, 1, ++i), sort_comp(L, -1, -2)) {\n        if (i>u) luaL_error(L, \"invalid order function for sorting\");\n        lua_pop(L, 1);  /* remove a[i] */\n      }\n      /* repeat --j until a[j] <= P */\n      while (lua_rawgeti(L, 1, --j), sort_comp(L, -3, -1)) {\n        if (j<l) luaL_error(L, \"invalid order function for sorting\");\n        lua_pop(L, 1);  /* remove a[j] */\n      }\n      if (j<i) {\n        lua_pop(L, 3);  /* pop pivot, a[i], a[j] */\n        break;\n      }\n      set2(L, i, j);\n    }\n    lua_rawgeti(L, 1, u-1);\n    lua_rawgeti(L, 1, i);\n    set2(L, u-1, i);  /* swap pivot (a[u-1]) with a[i] */\n    /* a[l..i-1] <= a[i] == P <= a[i+1..u] */\n    /* adjust so that smaller half is in [j..i] and larger one in [l..u] */\n    if (i-l < u-i) {\n      j=l; i=i-1; l=i+2;\n    }\n    else {\n      j=i+1; i=u; u=j-2;\n    }\n    auxsort(L, j, i);  /* call recursively the smaller one */\n  }  /* repeat the routine for the larger one */\n}\n\nstatic int sort (lua_State *L) {\n  int n = aux_getn(L, 1);\n  luaL_checkstack(L, 40, \"\");  /* assume array is smaller than 2^40 */\n  if (!lua_isnoneornil(L, 2))  /* is there a 2nd argument? */\n    luaL_checktype(L, 2, LUA_TFUNCTION);\n  lua_settop(L, 2);  /* make sure there is two arguments */\n  auxsort(L, 1, n);\n  return 0;\n}\n\n/* }====================================================== */\n\n\nstatic const luaL_Reg tab_funcs[] = {\n  {\"concat\", tconcat},\n  {\"foreach\", foreach},\n  {\"foreachi\", foreachi},\n  {\"getn\", getn},\n  {\"maxn\", maxn},\n  {\"insert\", tinsert},\n  {\"remove\", tremove},\n  {\"setn\", setn},\n  {\"sort\", sort},\n  {NULL, NULL}\n};\n\n\nLUALIB_API int luaopen_table (lua_State *L) {\n  luaL_register(L, LUA_TABLIBNAME, tab_funcs);\n  return 1;\n}\n\n"
  },
  {
    "path": "deps/lua/src/ltm.c",
    "content": "/*\n** $Id: ltm.c,v 2.8.1.1 2007/12/27 13:02:25 roberto Exp $\n** Tag methods\n** See Copyright Notice in lua.h\n*/\n\n\n#include <string.h>\n\n#define ltm_c\n#define LUA_CORE\n\n#include \"lua.h\"\n\n#include \"lobject.h\"\n#include \"lstate.h\"\n#include \"lstring.h\"\n#include \"ltable.h\"\n#include \"ltm.h\"\n\n\n\nconst char *const luaT_typenames[] = {\n  \"nil\", \"boolean\", \"userdata\", \"number\",\n  \"string\", \"table\", \"function\", \"userdata\", \"thread\",\n  \"proto\", \"upval\"\n};\n\n\nvoid luaT_init (lua_State *L) {\n  static const char *const luaT_eventname[] = {  /* ORDER TM */\n    \"__index\", \"__newindex\",\n    \"__gc\", \"__mode\", \"__eq\",\n    \"__add\", \"__sub\", \"__mul\", \"__div\", \"__mod\",\n    \"__pow\", \"__unm\", \"__len\", \"__lt\", \"__le\",\n    \"__concat\", \"__call\"\n  };\n  int i;\n  for (i=0; i<TM_N; i++) {\n    G(L)->tmname[i] = luaS_new(L, luaT_eventname[i]);\n    luaS_fix(G(L)->tmname[i]);  /* never collect these names */\n  }\n}\n\n\n/*\n** function to be used with macro \"fasttm\": optimized for absence of\n** tag methods\n*/\nconst TValue *luaT_gettm (Table *events, TMS event, TString *ename) {\n  const TValue *tm = luaH_getstr(events, ename);\n  lua_assert(event <= TM_EQ);\n  if (ttisnil(tm)) {  /* no tag method? */\n    events->flags |= cast_byte(1u<<event);  /* cache this fact */\n    return NULL;\n  }\n  else return tm;\n}\n\n\nconst TValue *luaT_gettmbyobj (lua_State *L, const TValue *o, TMS event) {\n  Table *mt;\n  switch (ttype(o)) {\n    case LUA_TTABLE:\n      mt = hvalue(o)->metatable;\n      break;\n    case LUA_TUSERDATA:\n      mt = uvalue(o)->metatable;\n      break;\n    default:\n      mt = G(L)->mt[ttype(o)];\n  }\n  return (mt ? luaH_getstr(mt, G(L)->tmname[event]) : luaO_nilobject);\n}\n\n"
  },
  {
    "path": "deps/lua/src/ltm.h",
    "content": "/*\n** $Id: ltm.h,v 2.6.1.1 2007/12/27 13:02:25 roberto Exp $\n** Tag methods\n** See Copyright Notice in lua.h\n*/\n\n#ifndef ltm_h\n#define ltm_h\n\n\n#include \"lobject.h\"\n\n\n/*\n* WARNING: if you change the order of this enumeration,\n* grep \"ORDER TM\"\n*/\ntypedef enum {\n  TM_INDEX,\n  TM_NEWINDEX,\n  TM_GC,\n  TM_MODE,\n  TM_EQ,  /* last tag method with `fast' access */\n  TM_ADD,\n  TM_SUB,\n  TM_MUL,\n  TM_DIV,\n  TM_MOD,\n  TM_POW,\n  TM_UNM,\n  TM_LEN,\n  TM_LT,\n  TM_LE,\n  TM_CONCAT,\n  TM_CALL,\n  TM_N\t\t/* number of elements in the enum */\n} TMS;\n\n\n\n#define gfasttm(g,et,e) ((et) == NULL ? NULL : \\\n  ((et)->flags & (1u<<(e))) ? NULL : luaT_gettm(et, e, (g)->tmname[e]))\n\n#define fasttm(l,et,e)\tgfasttm(G(l), et, e)\n\nLUAI_DATA const char *const luaT_typenames[];\n\n\nLUAI_FUNC const TValue *luaT_gettm (Table *events, TMS event, TString *ename);\nLUAI_FUNC const TValue *luaT_gettmbyobj (lua_State *L, const TValue *o,\n                                                       TMS event);\nLUAI_FUNC void luaT_init (lua_State *L);\n\n#endif\n"
  },
  {
    "path": "deps/lua/src/lua.c",
    "content": "/*\n** $Id: lua.c,v 1.160.1.2 2007/12/28 15:32:23 roberto Exp $\n** Lua stand-alone interpreter\n** See Copyright Notice in lua.h\n*/\n\n\n#include <signal.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#define lua_c\n\n#include \"lua.h\"\n\n#include \"lauxlib.h\"\n#include \"lualib.h\"\n\n\n\nstatic lua_State *globalL = NULL;\n\nstatic const char *progname = LUA_PROGNAME;\n\n\n\nstatic void lstop (lua_State *L, lua_Debug *ar) {\n  (void)ar;  /* unused arg. */\n  lua_sethook(L, NULL, 0, 0);\n  luaL_error(L, \"interrupted!\");\n}\n\n\nstatic void laction (int i) {\n  signal(i, SIG_DFL); /* if another SIGINT happens before lstop,\n                              terminate process (default action) */\n  lua_sethook(globalL, lstop, LUA_MASKCALL | LUA_MASKRET | LUA_MASKCOUNT, 1);\n}\n\n\nstatic void print_usage (void) {\n  fprintf(stderr,\n  \"usage: %s [options] [script [args]].\\n\"\n  \"Available options are:\\n\"\n  \"  -e stat  execute string \" LUA_QL(\"stat\") \"\\n\"\n  \"  -l name  require library \" LUA_QL(\"name\") \"\\n\"\n  \"  -i       enter interactive mode after executing \" LUA_QL(\"script\") \"\\n\"\n  \"  -v       show version information\\n\"\n  \"  --       stop handling options\\n\"\n  \"  -        execute stdin and stop handling options\\n\"\n  ,\n  progname);\n  fflush(stderr);\n}\n\n\nstatic void l_message (const char *pname, const char *msg) {\n  if (pname) fprintf(stderr, \"%s: \", pname);\n  fprintf(stderr, \"%s\\n\", msg);\n  fflush(stderr);\n}\n\n\nstatic int report (lua_State *L, int status) {\n  if (status && !lua_isnil(L, -1)) {\n    const char *msg = lua_tostring(L, -1);\n    if (msg == NULL) msg = \"(error object is not a string)\";\n    l_message(progname, msg);\n    lua_pop(L, 1);\n  }\n  return status;\n}\n\n\nstatic int traceback (lua_State *L) {\n  if (!lua_isstring(L, 1))  /* 'message' not a string? */\n    return 1;  /* keep it intact */\n  lua_getfield(L, LUA_GLOBALSINDEX, \"debug\");\n  if (!lua_istable(L, -1)) {\n    lua_pop(L, 1);\n    return 1;\n  }\n  lua_getfield(L, -1, \"traceback\");\n  if (!lua_isfunction(L, -1)) {\n    lua_pop(L, 2);\n    return 1;\n  }\n  lua_pushvalue(L, 1);  /* pass error message */\n  lua_pushinteger(L, 2);  /* skip this function and traceback */\n  lua_call(L, 2, 1);  /* call debug.traceback */\n  return 1;\n}\n\n\nstatic int docall (lua_State *L, int narg, int clear) {\n  int status;\n  int base = lua_gettop(L) - narg;  /* function index */\n  lua_pushcfunction(L, traceback);  /* push traceback function */\n  lua_insert(L, base);  /* put it under chunk and args */\n  signal(SIGINT, laction);\n  status = lua_pcall(L, narg, (clear ? 0 : LUA_MULTRET), base);\n  signal(SIGINT, SIG_DFL);\n  lua_remove(L, base);  /* remove traceback function */\n  /* force a complete garbage collection in case of errors */\n  if (status != 0) lua_gc(L, LUA_GCCOLLECT, 0);\n  return status;\n}\n\n\nstatic void print_version (void) {\n  l_message(NULL, LUA_RELEASE \"  \" LUA_COPYRIGHT);\n}\n\n\nstatic int getargs (lua_State *L, char **argv, int n) {\n  int narg;\n  int i;\n  int argc = 0;\n  while (argv[argc]) argc++;  /* count total number of arguments */\n  narg = argc - (n + 1);  /* number of arguments to the script */\n  luaL_checkstack(L, narg + 3, \"too many arguments to script\");\n  for (i=n+1; i < argc; i++)\n    lua_pushstring(L, argv[i]);\n  lua_createtable(L, narg, n + 1);\n  for (i=0; i < argc; i++) {\n    lua_pushstring(L, argv[i]);\n    lua_rawseti(L, -2, i - n);\n  }\n  return narg;\n}\n\n\nstatic int dofile (lua_State *L, const char *name) {\n  int status = luaL_loadfile(L, name) || docall(L, 0, 1);\n  return report(L, status);\n}\n\n\nstatic int dostring (lua_State *L, const char *s, const char *name) {\n  int status = luaL_loadbuffer(L, s, strlen(s), name) || docall(L, 0, 1);\n  return report(L, status);\n}\n\n\nstatic int dolibrary (lua_State *L, const char *name) {\n  lua_getglobal(L, \"require\");\n  lua_pushstring(L, name);\n  return report(L, docall(L, 1, 1));\n}\n\n\nstatic const char *get_prompt (lua_State *L, int firstline) {\n  const char *p;\n  lua_getfield(L, LUA_GLOBALSINDEX, firstline ? \"_PROMPT\" : \"_PROMPT2\");\n  p = lua_tostring(L, -1);\n  if (p == NULL) p = (firstline ? LUA_PROMPT : LUA_PROMPT2);\n  lua_pop(L, 1);  /* remove global */\n  return p;\n}\n\n\nstatic int incomplete (lua_State *L, int status) {\n  if (status == LUA_ERRSYNTAX) {\n    size_t lmsg;\n    const char *msg = lua_tolstring(L, -1, &lmsg);\n    const char *tp = msg + lmsg - (sizeof(LUA_QL(\"<eof>\")) - 1);\n    if (strstr(msg, LUA_QL(\"<eof>\")) == tp) {\n      lua_pop(L, 1);\n      return 1;\n    }\n  }\n  return 0;  /* else... */\n}\n\n\nstatic int pushline (lua_State *L, int firstline) {\n  char buffer[LUA_MAXINPUT];\n  char *b = buffer;\n  size_t l;\n  const char *prmt = get_prompt(L, firstline);\n  if (lua_readline(L, b, prmt) == 0)\n    return 0;  /* no input */\n  l = strlen(b);\n  if (l > 0 && b[l-1] == '\\n')  /* line ends with newline? */\n    b[l-1] = '\\0';  /* remove it */\n  if (firstline && b[0] == '=')  /* first line starts with `=' ? */\n    lua_pushfstring(L, \"return %s\", b+1);  /* change it to `return' */\n  else\n    lua_pushstring(L, b);\n  lua_freeline(L, b);\n  return 1;\n}\n\n\nstatic int loadline (lua_State *L) {\n  int status;\n  lua_settop(L, 0);\n  if (!pushline(L, 1))\n    return -1;  /* no input */\n  for (;;) {  /* repeat until gets a complete line */\n    status = luaL_loadbuffer(L, lua_tostring(L, 1), lua_strlen(L, 1), \"=stdin\");\n    if (!incomplete(L, status)) break;  /* cannot try to add lines? */\n    if (!pushline(L, 0))  /* no more input? */\n      return -1;\n    lua_pushliteral(L, \"\\n\");  /* add a new line... */\n    lua_insert(L, -2);  /* ...between the two lines */\n    lua_concat(L, 3);  /* join them */\n  }\n  lua_saveline(L, 1);\n  lua_remove(L, 1);  /* remove line */\n  return status;\n}\n\n\nstatic void dotty (lua_State *L) {\n  int status;\n  const char *oldprogname = progname;\n  progname = NULL;\n  while ((status = loadline(L)) != -1) {\n    if (status == 0) status = docall(L, 0, 0);\n    report(L, status);\n    if (status == 0 && lua_gettop(L) > 0) {  /* any result to print? */\n      lua_getglobal(L, \"print\");\n      lua_insert(L, 1);\n      if (lua_pcall(L, lua_gettop(L)-1, 0, 0) != 0)\n        l_message(progname, lua_pushfstring(L,\n                               \"error calling \" LUA_QL(\"print\") \" (%s)\",\n                               lua_tostring(L, -1)));\n    }\n  }\n  lua_settop(L, 0);  /* clear stack */\n  fputs(\"\\n\", stdout);\n  fflush(stdout);\n  progname = oldprogname;\n}\n\n\nstatic int handle_script (lua_State *L, char **argv, int n) {\n  int status;\n  const char *fname;\n  int narg = getargs(L, argv, n);  /* collect arguments */\n  lua_setglobal(L, \"arg\");\n  fname = argv[n];\n  if (strcmp(fname, \"-\") == 0 && strcmp(argv[n-1], \"--\") != 0) \n    fname = NULL;  /* stdin */\n  status = luaL_loadfile(L, fname);\n  lua_insert(L, -(narg+1));\n  if (status == 0)\n    status = docall(L, narg, 0);\n  else\n    lua_pop(L, narg);      \n  return report(L, status);\n}\n\n\n/* check that argument has no extra characters at the end */\n#define notail(x)\t{if ((x)[2] != '\\0') return -1;}\n\n\nstatic int collectargs (char **argv, int *pi, int *pv, int *pe) {\n  int i;\n  for (i = 1; argv[i] != NULL; i++) {\n    if (argv[i][0] != '-')  /* not an option? */\n        return i;\n    switch (argv[i][1]) {  /* option */\n      case '-':\n        notail(argv[i]);\n        return (argv[i+1] != NULL ? i+1 : 0);\n      case '\\0':\n        return i;\n      case 'i':\n        notail(argv[i]);\n        *pi = 1;  /* go through */\n      case 'v':\n        notail(argv[i]);\n        *pv = 1;\n        break;\n      case 'e':\n        *pe = 1;  /* go through */\n      case 'l':\n        if (argv[i][2] == '\\0') {\n          i++;\n          if (argv[i] == NULL) return -1;\n        }\n        break;\n      default: return -1;  /* invalid option */\n    }\n  }\n  return 0;\n}\n\n\nstatic int runargs (lua_State *L, char **argv, int n) {\n  int i;\n  for (i = 1; i < n; i++) {\n    if (argv[i] == NULL) continue;\n    lua_assert(argv[i][0] == '-');\n    switch (argv[i][1]) {  /* option */\n      case 'e': {\n        const char *chunk = argv[i] + 2;\n        if (*chunk == '\\0') chunk = argv[++i];\n        lua_assert(chunk != NULL);\n        if (dostring(L, chunk, \"=(command line)\") != 0)\n          return 1;\n        break;\n      }\n      case 'l': {\n        const char *filename = argv[i] + 2;\n        if (*filename == '\\0') filename = argv[++i];\n        lua_assert(filename != NULL);\n        if (dolibrary(L, filename))\n          return 1;  /* stop if file fails */\n        break;\n      }\n      default: break;\n    }\n  }\n  return 0;\n}\n\n\nstatic int handle_luainit (lua_State *L) {\n  const char *init = getenv(LUA_INIT);\n  if (init == NULL) return 0;  /* status OK */\n  else if (init[0] == '@')\n    return dofile(L, init+1);\n  else\n    return dostring(L, init, \"=\" LUA_INIT);\n}\n\n\nstruct Smain {\n  int argc;\n  char **argv;\n  int status;\n};\n\n\nstatic int pmain (lua_State *L) {\n  struct Smain *s = (struct Smain *)lua_touserdata(L, 1);\n  char **argv = s->argv;\n  int script;\n  int has_i = 0, has_v = 0, has_e = 0;\n  globalL = L;\n  if (argv[0] && argv[0][0]) progname = argv[0];\n  lua_gc(L, LUA_GCSTOP, 0);  /* stop collector during initialization */\n  luaL_openlibs(L);  /* open libraries */\n  lua_gc(L, LUA_GCRESTART, 0);\n  s->status = handle_luainit(L);\n  if (s->status != 0) return 0;\n  script = collectargs(argv, &has_i, &has_v, &has_e);\n  if (script < 0) {  /* invalid args? */\n    print_usage();\n    s->status = 1;\n    return 0;\n  }\n  if (has_v) print_version();\n  s->status = runargs(L, argv, (script > 0) ? script : s->argc);\n  if (s->status != 0) return 0;\n  if (script)\n    s->status = handle_script(L, argv, script);\n  if (s->status != 0) return 0;\n  if (has_i)\n    dotty(L);\n  else if (script == 0 && !has_e && !has_v) {\n    if (lua_stdin_is_tty()) {\n      print_version();\n      dotty(L);\n    }\n    else dofile(L, NULL);  /* executes stdin as a file */\n  }\n  return 0;\n}\n\n\nint main (int argc, char **argv) {\n  int status;\n  struct Smain s;\n  lua_State *L = lua_open();  /* create state */\n  if (L == NULL) {\n    l_message(argv[0], \"cannot create state: not enough memory\");\n    return EXIT_FAILURE;\n  }\n  s.argc = argc;\n  s.argv = argv;\n  status = lua_cpcall(L, &pmain, &s);\n  report(L, status);\n  lua_close(L);\n  return (status || s.status) ? EXIT_FAILURE : EXIT_SUCCESS;\n}\n\n"
  },
  {
    "path": "deps/lua/src/lua.h",
    "content": "/*\n** $Id: lua.h,v 1.218.1.7 2012/01/13 20:36:20 roberto Exp $\n** Lua - An Extensible Extension Language\n** Lua.org, PUC-Rio, Brazil (http://www.lua.org)\n** See Copyright Notice at the end of this file\n*/\n\n\n#ifndef lua_h\n#define lua_h\n\n#include <stdarg.h>\n#include <stddef.h>\n\n\n#include \"luaconf.h\"\n\n\n#define LUA_VERSION\t\"Lua 5.1\"\n#define LUA_RELEASE\t\"Lua 5.1.5\"\n#define LUA_VERSION_NUM\t501\n#define LUA_COPYRIGHT\t\"Copyright (C) 1994-2012 Lua.org, PUC-Rio\"\n#define LUA_AUTHORS \t\"R. Ierusalimschy, L. H. de Figueiredo & W. Celes\"\n\n\n/* mark for precompiled code (`<esc>Lua') */\n#define\tLUA_SIGNATURE\t\"\\033Lua\"\n\n/* option for multiple returns in `lua_pcall' and `lua_call' */\n#define LUA_MULTRET\t(-1)\n\n\n/*\n** pseudo-indices\n*/\n#define LUA_REGISTRYINDEX\t(-10000)\n#define LUA_ENVIRONINDEX\t(-10001)\n#define LUA_GLOBALSINDEX\t(-10002)\n#define lua_upvalueindex(i)\t(LUA_GLOBALSINDEX-(i))\n\n\n/* thread status; 0 is OK */\n#define LUA_YIELD\t1\n#define LUA_ERRRUN\t2\n#define LUA_ERRSYNTAX\t3\n#define LUA_ERRMEM\t4\n#define LUA_ERRERR\t5\n\n\ntypedef struct lua_State lua_State;\n\ntypedef int (*lua_CFunction) (lua_State *L);\n\n\n/*\n** functions that read/write blocks when loading/dumping Lua chunks\n*/\ntypedef const char * (*lua_Reader) (lua_State *L, void *ud, size_t *sz);\n\ntypedef int (*lua_Writer) (lua_State *L, const void* p, size_t sz, void* ud);\n\n\n/*\n** prototype for memory-allocation functions\n*/\ntypedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize);\n\n\n/*\n** basic types\n*/\n#define LUA_TNONE\t\t(-1)\n\n#define LUA_TNIL\t\t0\n#define LUA_TBOOLEAN\t\t1\n#define LUA_TLIGHTUSERDATA\t2\n#define LUA_TNUMBER\t\t3\n#define LUA_TSTRING\t\t4\n#define LUA_TTABLE\t\t5\n#define LUA_TFUNCTION\t\t6\n#define LUA_TUSERDATA\t\t7\n#define LUA_TTHREAD\t\t8\n\n\n\n/* minimum Lua stack available to a C function */\n#define LUA_MINSTACK\t20\n\n\n/*\n** generic extra include file\n*/\n#if defined(LUA_USER_H)\n#include LUA_USER_H\n#endif\n\n\n/* type of numbers in Lua */\ntypedef LUA_NUMBER lua_Number;\n\n\n/* type for integer functions */\ntypedef LUA_INTEGER lua_Integer;\n\n\n\n/*\n** state manipulation\n*/\nLUA_API lua_State *(lua_newstate) (lua_Alloc f, void *ud);\nLUA_API void       (lua_close) (lua_State *L);\nLUA_API lua_State *(lua_newthread) (lua_State *L);\n\nLUA_API lua_CFunction (lua_atpanic) (lua_State *L, lua_CFunction panicf);\n\n\n/*\n** basic stack manipulation\n*/\nLUA_API int   (lua_gettop) (lua_State *L);\nLUA_API void  (lua_settop) (lua_State *L, int idx);\nLUA_API void  (lua_pushvalue) (lua_State *L, int idx);\nLUA_API void  (lua_remove) (lua_State *L, int idx);\nLUA_API void  (lua_insert) (lua_State *L, int idx);\nLUA_API void  (lua_replace) (lua_State *L, int idx);\nLUA_API int   (lua_checkstack) (lua_State *L, int sz);\n\nLUA_API void  (lua_xmove) (lua_State *from, lua_State *to, int n);\n\n\n/*\n** access functions (stack -> C)\n*/\n\nLUA_API int             (lua_isnumber) (lua_State *L, int idx);\nLUA_API int             (lua_isstring) (lua_State *L, int idx);\nLUA_API int             (lua_iscfunction) (lua_State *L, int idx);\nLUA_API int             (lua_isuserdata) (lua_State *L, int idx);\nLUA_API int             (lua_type) (lua_State *L, int idx);\nLUA_API const char     *(lua_typename) (lua_State *L, int tp);\n\nLUA_API int            (lua_equal) (lua_State *L, int idx1, int idx2);\nLUA_API int            (lua_rawequal) (lua_State *L, int idx1, int idx2);\nLUA_API int            (lua_lessthan) (lua_State *L, int idx1, int idx2);\n\nLUA_API lua_Number      (lua_tonumber) (lua_State *L, int idx);\nLUA_API lua_Integer     (lua_tointeger) (lua_State *L, int idx);\nLUA_API int             (lua_toboolean) (lua_State *L, int idx);\nLUA_API const char     *(lua_tolstring) (lua_State *L, int idx, size_t *len);\nLUA_API size_t          (lua_objlen) (lua_State *L, int idx);\nLUA_API lua_CFunction   (lua_tocfunction) (lua_State *L, int idx);\nLUA_API void\t       *(lua_touserdata) (lua_State *L, int idx);\nLUA_API lua_State      *(lua_tothread) (lua_State *L, int idx);\nLUA_API const void     *(lua_topointer) (lua_State *L, int idx);\n\n\n/*\n** push functions (C -> stack)\n*/\nLUA_API void  (lua_pushnil) (lua_State *L);\nLUA_API void  (lua_pushnumber) (lua_State *L, lua_Number n);\nLUA_API void  (lua_pushinteger) (lua_State *L, lua_Integer n);\nLUA_API void  (lua_pushlstring) (lua_State *L, const char *s, size_t l);\nLUA_API void  (lua_pushstring) (lua_State *L, const char *s);\nLUA_API const char *(lua_pushvfstring) (lua_State *L, const char *fmt,\n                                                      va_list argp);\nLUA_API const char *(lua_pushfstring) (lua_State *L, const char *fmt, ...);\nLUA_API void  (lua_pushcclosure) (lua_State *L, lua_CFunction fn, int n);\nLUA_API void  (lua_pushboolean) (lua_State *L, int b);\nLUA_API void  (lua_pushlightuserdata) (lua_State *L, void *p);\nLUA_API int   (lua_pushthread) (lua_State *L);\n\n\n/*\n** get functions (Lua -> stack)\n*/\nLUA_API void  (lua_gettable) (lua_State *L, int idx);\nLUA_API void  (lua_getfield) (lua_State *L, int idx, const char *k);\nLUA_API void  (lua_rawget) (lua_State *L, int idx);\nLUA_API void  (lua_rawgeti) (lua_State *L, int idx, int n);\nLUA_API void  (lua_createtable) (lua_State *L, int narr, int nrec);\nLUA_API void *(lua_newuserdata) (lua_State *L, size_t sz);\nLUA_API int   (lua_getmetatable) (lua_State *L, int objindex);\nLUA_API void  (lua_getfenv) (lua_State *L, int idx);\n\n\n/*\n** set functions (stack -> Lua)\n*/\nLUA_API void  (lua_settable) (lua_State *L, int idx);\nLUA_API void  (lua_setfield) (lua_State *L, int idx, const char *k);\nLUA_API void  (lua_rawset) (lua_State *L, int idx);\nLUA_API void  (lua_rawseti) (lua_State *L, int idx, int n);\nLUA_API int   (lua_setmetatable) (lua_State *L, int objindex);\nLUA_API int   (lua_setfenv) (lua_State *L, int idx);\n\n\n/*\n** `load' and `call' functions (load and run Lua code)\n*/\nLUA_API void  (lua_call) (lua_State *L, int nargs, int nresults);\nLUA_API int   (lua_pcall) (lua_State *L, int nargs, int nresults, int errfunc);\nLUA_API int   (lua_cpcall) (lua_State *L, lua_CFunction func, void *ud);\nLUA_API int   (lua_load) (lua_State *L, lua_Reader reader, void *dt,\n                                        const char *chunkname);\n\nLUA_API int (lua_dump) (lua_State *L, lua_Writer writer, void *data);\n\n\n/*\n** coroutine functions\n*/\nLUA_API int  (lua_yield) (lua_State *L, int nresults);\nLUA_API int  (lua_resume) (lua_State *L, int narg);\nLUA_API int  (lua_status) (lua_State *L);\n\n/*\n** garbage-collection function and options\n*/\n\n#define LUA_GCSTOP\t\t0\n#define LUA_GCRESTART\t\t1\n#define LUA_GCCOLLECT\t\t2\n#define LUA_GCCOUNT\t\t3\n#define LUA_GCCOUNTB\t\t4\n#define LUA_GCSTEP\t\t5\n#define LUA_GCSETPAUSE\t\t6\n#define LUA_GCSETSTEPMUL\t7\n\nLUA_API int (lua_gc) (lua_State *L, int what, int data);\n\n\n/*\n** miscellaneous functions\n*/\n\nLUA_API int   (lua_error) (lua_State *L);\n\nLUA_API int   (lua_next) (lua_State *L, int idx);\n\nLUA_API void  (lua_concat) (lua_State *L, int n);\n\nLUA_API lua_Alloc (lua_getallocf) (lua_State *L, void **ud);\nLUA_API void lua_setallocf (lua_State *L, lua_Alloc f, void *ud);\n\n\n\n/* \n** ===============================================================\n** some useful macros\n** ===============================================================\n*/\n\n#define lua_pop(L,n)\t\tlua_settop(L, -(n)-1)\n\n#define lua_newtable(L)\t\tlua_createtable(L, 0, 0)\n\n#define lua_register(L,n,f) (lua_pushcfunction(L, (f)), lua_setglobal(L, (n)))\n\n#define lua_pushcfunction(L,f)\tlua_pushcclosure(L, (f), 0)\n\n#define lua_strlen(L,i)\t\tlua_objlen(L, (i))\n\n#define lua_isfunction(L,n)\t(lua_type(L, (n)) == LUA_TFUNCTION)\n#define lua_istable(L,n)\t(lua_type(L, (n)) == LUA_TTABLE)\n#define lua_islightuserdata(L,n)\t(lua_type(L, (n)) == LUA_TLIGHTUSERDATA)\n#define lua_isnil(L,n)\t\t(lua_type(L, (n)) == LUA_TNIL)\n#define lua_isboolean(L,n)\t(lua_type(L, (n)) == LUA_TBOOLEAN)\n#define lua_isthread(L,n)\t(lua_type(L, (n)) == LUA_TTHREAD)\n#define lua_isnone(L,n)\t\t(lua_type(L, (n)) == LUA_TNONE)\n#define lua_isnoneornil(L, n)\t(lua_type(L, (n)) <= 0)\n\n#define lua_pushliteral(L, s)\t\\\n\tlua_pushlstring(L, \"\" s, (sizeof(s)/sizeof(char))-1)\n\n#define lua_setglobal(L,s)\tlua_setfield(L, LUA_GLOBALSINDEX, (s))\n#define lua_getglobal(L,s)\tlua_getfield(L, LUA_GLOBALSINDEX, (s))\n\n#define lua_tostring(L,i)\tlua_tolstring(L, (i), NULL)\n\n\n\n/*\n** compatibility macros and functions\n*/\n\n#define lua_open()\tluaL_newstate()\n\n#define lua_getregistry(L)\tlua_pushvalue(L, LUA_REGISTRYINDEX)\n\n#define lua_getgccount(L)\tlua_gc(L, LUA_GCCOUNT, 0)\n\n#define lua_Chunkreader\t\tlua_Reader\n#define lua_Chunkwriter\t\tlua_Writer\n\n\n/* hack */\nLUA_API void lua_setlevel\t(lua_State *from, lua_State *to);\n\n\n/*\n** {======================================================================\n** Debug API\n** =======================================================================\n*/\n\n\n/*\n** Event codes\n*/\n#define LUA_HOOKCALL\t0\n#define LUA_HOOKRET\t1\n#define LUA_HOOKLINE\t2\n#define LUA_HOOKCOUNT\t3\n#define LUA_HOOKTAILRET 4\n\n\n/*\n** Event masks\n*/\n#define LUA_MASKCALL\t(1 << LUA_HOOKCALL)\n#define LUA_MASKRET\t(1 << LUA_HOOKRET)\n#define LUA_MASKLINE\t(1 << LUA_HOOKLINE)\n#define LUA_MASKCOUNT\t(1 << LUA_HOOKCOUNT)\n\ntypedef struct lua_Debug lua_Debug;  /* activation record */\n\n\n/* Functions to be called by the debuger in specific events */\ntypedef void (*lua_Hook) (lua_State *L, lua_Debug *ar);\n\n\nLUA_API int lua_getstack (lua_State *L, int level, lua_Debug *ar);\nLUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar);\nLUA_API const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n);\nLUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n);\nLUA_API const char *lua_getupvalue (lua_State *L, int funcindex, int n);\nLUA_API const char *lua_setupvalue (lua_State *L, int funcindex, int n);\n\nLUA_API int lua_sethook (lua_State *L, lua_Hook func, int mask, int count);\nLUA_API lua_Hook lua_gethook (lua_State *L);\nLUA_API int lua_gethookmask (lua_State *L);\nLUA_API int lua_gethookcount (lua_State *L);\n\n\nstruct lua_Debug {\n  int event;\n  const char *name;\t/* (n) */\n  const char *namewhat;\t/* (n) `global', `local', `field', `method' */\n  const char *what;\t/* (S) `Lua', `C', `main', `tail' */\n  const char *source;\t/* (S) */\n  int currentline;\t/* (l) */\n  int nups;\t\t/* (u) number of upvalues */\n  int linedefined;\t/* (S) */\n  int lastlinedefined;\t/* (S) */\n  char short_src[LUA_IDSIZE]; /* (S) */\n  /* private part */\n  int i_ci;  /* active function */\n};\n\n/* }====================================================================== */\n\n\n/******************************************************************************\n* Copyright (C) 1994-2012 Lua.org, PUC-Rio.  All rights reserved.\n*\n* Permission is hereby granted, free of charge, to any person obtaining\n* a copy of this software and associated documentation files (the\n* \"Software\"), to deal in the Software without restriction, including\n* without limitation the rights to use, copy, modify, merge, publish,\n* distribute, sublicense, and/or sell copies of the Software, and to\n* permit persons to whom the Software is furnished to do so, subject to\n* the following conditions:\n*\n* The above copyright notice and this permission notice shall be\n* included in all copies or substantial portions of the Software.\n*\n* THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n******************************************************************************/\n\n\n#endif\n"
  },
  {
    "path": "deps/lua/src/lua_cjson.c",
    "content": "#define VERSION \"1.0.3\"\n\n/* CJSON - JSON support for Lua\n *\n * Copyright (c) 2010-2011  Mark Pulford <mark@kyne.com.au>\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/* Caveats:\n * - JSON \"null\" values are represented as lightuserdata since Lua\n *   tables cannot contain \"nil\". Compare with cjson.null.\n * - Invalid UTF-8 characters are not detected and will be passed\n *   untouched. If required, UTF-8 error checking should be done\n *   outside this library.\n * - Javascript comments are not part of the JSON spec, and are not\n *   currently supported.\n *\n * Note: Decoding is slower than encoding. Lua spends significant\n *       time (30%) managing tables when parsing JSON since it is\n *       difficult to know object/array sizes ahead of time.\n */\n\n#include <assert.h>\n#include <string.h>\n#include <math.h>\n#include \"lua.h\"\n#include \"lauxlib.h\"\n\n#include \"strbuf.h\"\n\n#ifdef MISSING_ISINF\n#define isinf(x) (!isnan(x) && isnan((x) - (x)))\n#endif\n\n#define DEFAULT_SPARSE_CONVERT 0\n#define DEFAULT_SPARSE_RATIO 2\n#define DEFAULT_SPARSE_SAFE 10\n#define DEFAULT_MAX_DEPTH 20\n#define DEFAULT_ENCODE_REFUSE_BADNUM 1\n#define DEFAULT_DECODE_REFUSE_BADNUM 0\n#define DEFAULT_ENCODE_KEEP_BUFFER 1\n\ntypedef enum {\n    T_OBJ_BEGIN,\n    T_OBJ_END,\n    T_ARR_BEGIN,\n    T_ARR_END,\n    T_STRING,\n    T_NUMBER,\n    T_BOOLEAN,\n    T_NULL,\n    T_COLON,\n    T_COMMA,\n    T_END,\n    T_WHITESPACE,\n    T_ERROR,\n    T_UNKNOWN\n} json_token_type_t;\n\nstatic const char *json_token_type_name[] = {\n    \"T_OBJ_BEGIN\",\n    \"T_OBJ_END\",\n    \"T_ARR_BEGIN\",\n    \"T_ARR_END\",\n    \"T_STRING\",\n    \"T_NUMBER\",\n    \"T_BOOLEAN\",\n    \"T_NULL\",\n    \"T_COLON\",\n    \"T_COMMA\",\n    \"T_END\",\n    \"T_WHITESPACE\",\n    \"T_ERROR\",\n    \"T_UNKNOWN\",\n    NULL\n};\n\ntypedef struct {\n    json_token_type_t ch2token[256];\n    char escape2char[256];  /* Decoding */\n#if 0\n    char escapes[35][8];    /* Pre-generated escape string buffer */\n    char *char2escape[256]; /* Encoding */\n#endif\n    strbuf_t encode_buf;\n    char number_fmt[8];     /* \"%.XXg\\0\" */\n    int current_depth;\n\n    int encode_sparse_convert;\n    int encode_sparse_ratio;\n    int encode_sparse_safe;\n    int encode_max_depth;\n    int encode_refuse_badnum;\n    int decode_refuse_badnum;\n    int encode_keep_buffer;\n    int encode_number_precision;\n} json_config_t;\n\ntypedef struct {\n    const char *data;\n    int index;\n    strbuf_t *tmp;    /* Temporary storage for strings */\n    json_config_t *cfg;\n} json_parse_t;\n\ntypedef struct {\n    json_token_type_t type;\n    int index;\n    union {\n        const char *string;\n        double number;\n        int boolean;\n    } value;\n    int string_len;\n} json_token_t;\n\nstatic const char *char2escape[256] = {\n    \"\\\\u0000\", \"\\\\u0001\", \"\\\\u0002\", \"\\\\u0003\",\n    \"\\\\u0004\", \"\\\\u0005\", \"\\\\u0006\", \"\\\\u0007\",\n    \"\\\\b\", \"\\\\t\", \"\\\\n\", \"\\\\u000b\",\n    \"\\\\f\", \"\\\\r\", \"\\\\u000e\", \"\\\\u000f\",\n    \"\\\\u0010\", \"\\\\u0011\", \"\\\\u0012\", \"\\\\u0013\",\n    \"\\\\u0014\", \"\\\\u0015\", \"\\\\u0016\", \"\\\\u0017\",\n    \"\\\\u0018\", \"\\\\u0019\", \"\\\\u001a\", \"\\\\u001b\",\n    \"\\\\u001c\", \"\\\\u001d\", \"\\\\u001e\", \"\\\\u001f\",\n    NULL, NULL, \"\\\\\\\"\", NULL, NULL, NULL, NULL, NULL,\n    NULL, NULL, NULL, NULL, NULL, NULL, NULL, \"\\\\/\",\n    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,\n    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,\n    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,\n    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,\n    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,\n    NULL, NULL, NULL, NULL, \"\\\\\\\\\", NULL, NULL, NULL,\n    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,\n    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,\n    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,\n    NULL, NULL, NULL, NULL, NULL, NULL, NULL, \"\\\\u007f\",\n    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,\n    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,\n    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,\n    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,\n    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,\n    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,\n    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,\n    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,\n    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,\n    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,\n    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,\n    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,\n    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,\n    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,\n    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,\n    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,\n};\n\nstatic int json_config_key;\n\n/* ===== CONFIGURATION ===== */\n\nstatic json_config_t *json_fetch_config(lua_State *l)\n{\n    json_config_t *cfg;\n\n    lua_pushlightuserdata(l, &json_config_key);\n    lua_gettable(l, LUA_REGISTRYINDEX);\n    cfg = lua_touserdata(l, -1);\n    if (!cfg)\n        luaL_error(l, \"BUG: Unable to fetch CJSON configuration\");\n\n    lua_pop(l, 1);\n\n    return cfg;\n}\n\nstatic void json_verify_arg_count(lua_State *l, int args)\n{\n    luaL_argcheck(l, lua_gettop(l) <= args, args + 1,\n                  \"found too many arguments\");\n}\n\n/* Configures handling of extremely sparse arrays:\n * convert: Convert extremely sparse arrays into objects? Otherwise error.\n * ratio: 0: always allow sparse; 1: never allow sparse; >1: use ratio\n * safe: Always use an array when the max index <= safe */\nstatic int json_cfg_encode_sparse_array(lua_State *l)\n{\n    json_config_t *cfg;\n    int val;\n\n    json_verify_arg_count(l, 3);\n    cfg = json_fetch_config(l);\n\n    switch (lua_gettop(l)) {\n    case 3:\n        val = luaL_checkinteger(l, 3);\n        luaL_argcheck(l, val >= 0, 3, \"expected integer >= 0\");\n        cfg->encode_sparse_safe = val;\n    case 2:\n        val = luaL_checkinteger(l, 2);\n        luaL_argcheck(l, val >= 0, 2, \"expected integer >= 0\");\n        cfg->encode_sparse_ratio = val;\n    case 1:\n        luaL_argcheck(l, lua_isboolean(l, 1), 1, \"expected boolean\");\n        cfg->encode_sparse_convert = lua_toboolean(l, 1);\n    }\n\n    lua_pushboolean(l, cfg->encode_sparse_convert);\n    lua_pushinteger(l, cfg->encode_sparse_ratio);\n    lua_pushinteger(l, cfg->encode_sparse_safe);\n\n    return 3;\n}\n\n/* Configures the maximum number of nested arrays/objects allowed when\n * encoding */\nstatic int json_cfg_encode_max_depth(lua_State *l)\n{\n    json_config_t *cfg;\n    int depth;\n\n    json_verify_arg_count(l, 1);\n    cfg = json_fetch_config(l);\n\n    if (lua_gettop(l)) {\n        depth = luaL_checkinteger(l, 1);\n        luaL_argcheck(l, depth > 0, 1, \"expected positive integer\");\n        cfg->encode_max_depth = depth;\n    }\n\n    lua_pushinteger(l, cfg->encode_max_depth);\n\n    return 1;\n}\n\nstatic void json_set_number_precision(json_config_t *cfg, int prec)\n{\n    cfg->encode_number_precision = prec;\n    sprintf(cfg->number_fmt, \"%%.%dg\", prec);\n}\n\n/* Configures number precision when converting doubles to text */\nstatic int json_cfg_encode_number_precision(lua_State *l)\n{\n    json_config_t *cfg;\n    int precision;\n\n    json_verify_arg_count(l, 1);\n    cfg = json_fetch_config(l);\n\n    if (lua_gettop(l)) {\n        precision = luaL_checkinteger(l, 1);\n        luaL_argcheck(l, 1 <= precision && precision <= 14, 1,\n                      \"expected integer between 1 and 14\");\n        json_set_number_precision(cfg, precision);\n    }\n\n    lua_pushinteger(l, cfg->encode_number_precision);\n\n    return 1;\n}\n\n/* Configures JSON encoding buffer persistence */\nstatic int json_cfg_encode_keep_buffer(lua_State *l)\n{\n    json_config_t *cfg;\n\n    json_verify_arg_count(l, 1);\n    cfg = json_fetch_config(l);\n\n    if (lua_gettop(l)) {\n        luaL_checktype(l, 1, LUA_TBOOLEAN);\n        cfg->encode_keep_buffer = lua_toboolean(l, 1);\n    }\n\n    lua_pushboolean(l, cfg->encode_keep_buffer);\n\n    return 1;\n}\n\n/* On argument: decode enum and set config variables\n * **options must point to a NULL terminated array of 4 enums\n * Returns: current enum value */\nstatic void json_enum_option(lua_State *l, const char **options,\n                             int *opt1, int *opt2)\n{\n    int setting;\n\n    if (lua_gettop(l)) {\n        if (lua_isboolean(l, 1))\n            setting = lua_toboolean(l, 1) * 3;\n        else\n            setting = luaL_checkoption(l, 1, NULL, options);\n\n        *opt1 = setting & 1 ? 1 : 0;\n        *opt2 = setting & 2 ? 1 : 0;\n    } else {\n        setting = *opt1 | (*opt2 << 1);\n    }\n\n    if (setting)\n        lua_pushstring(l, options[setting]);\n    else\n        lua_pushboolean(l, 0);\n}\n\n\n/* When enabled, rejects: NaN, Infinity, hexidecimal numbers */\nstatic int json_cfg_refuse_invalid_numbers(lua_State *l)\n{\n    static const char *options_enc_dec[] = { \"none\", \"encode\", \"decode\",\n                                             \"both\", NULL };\n    json_config_t *cfg;\n\n    json_verify_arg_count(l, 1);\n    cfg = json_fetch_config(l);\n\n    json_enum_option(l, options_enc_dec,\n                     &cfg->encode_refuse_badnum,\n                     &cfg->decode_refuse_badnum);\n\n    return 1;\n}\n\nstatic int json_destroy_config(lua_State *l)\n{\n    json_config_t *cfg;\n\n    cfg = lua_touserdata(l, 1);\n    if (cfg)\n        strbuf_free(&cfg->encode_buf);\n    cfg = NULL;\n\n    return 0;\n}\n\nstatic void json_create_config(lua_State *l)\n{\n    json_config_t *cfg;\n    int i;\n\n    cfg = lua_newuserdata(l, sizeof(*cfg));\n\n    /* Create GC method to clean up strbuf */\n    lua_newtable(l);\n    lua_pushcfunction(l, json_destroy_config);\n    lua_setfield(l, -2, \"__gc\");\n    lua_setmetatable(l, -2);\n\n    strbuf_init(&cfg->encode_buf, 0);\n\n    cfg->encode_sparse_convert = DEFAULT_SPARSE_CONVERT;\n    cfg->encode_sparse_ratio = DEFAULT_SPARSE_RATIO;\n    cfg->encode_sparse_safe = DEFAULT_SPARSE_SAFE;\n    cfg->encode_max_depth = DEFAULT_MAX_DEPTH;\n    cfg->encode_refuse_badnum = DEFAULT_ENCODE_REFUSE_BADNUM;\n    cfg->decode_refuse_badnum = DEFAULT_DECODE_REFUSE_BADNUM;\n    cfg->encode_keep_buffer = DEFAULT_ENCODE_KEEP_BUFFER;\n    json_set_number_precision(cfg, 14);\n\n    /* Decoding init */\n\n    /* Tag all characters as an error */\n    for (i = 0; i < 256; i++)\n        cfg->ch2token[i] = T_ERROR;\n\n    /* Set tokens that require no further processing */\n    cfg->ch2token['{'] = T_OBJ_BEGIN;\n    cfg->ch2token['}'] = T_OBJ_END;\n    cfg->ch2token['['] = T_ARR_BEGIN;\n    cfg->ch2token[']'] = T_ARR_END;\n    cfg->ch2token[','] = T_COMMA;\n    cfg->ch2token[':'] = T_COLON;\n    cfg->ch2token['\\0'] = T_END;\n    cfg->ch2token[' '] = T_WHITESPACE;\n    cfg->ch2token['\\t'] = T_WHITESPACE;\n    cfg->ch2token['\\n'] = T_WHITESPACE;\n    cfg->ch2token['\\r'] = T_WHITESPACE;\n\n    /* Update characters that require further processing */\n    cfg->ch2token['f'] = T_UNKNOWN;     /* false? */\n    cfg->ch2token['i'] = T_UNKNOWN;     /* inf, ininity? */\n    cfg->ch2token['I'] = T_UNKNOWN;\n    cfg->ch2token['n'] = T_UNKNOWN;     /* null, nan? */\n    cfg->ch2token['N'] = T_UNKNOWN;\n    cfg->ch2token['t'] = T_UNKNOWN;     /* true? */\n    cfg->ch2token['\"'] = T_UNKNOWN;     /* string? */\n    cfg->ch2token['+'] = T_UNKNOWN;     /* number? */\n    cfg->ch2token['-'] = T_UNKNOWN;\n    for (i = 0; i < 10; i++)\n        cfg->ch2token['0' + i] = T_UNKNOWN;\n\n    /* Lookup table for parsing escape characters */\n    for (i = 0; i < 256; i++)\n        cfg->escape2char[i] = 0;          /* String error */\n    cfg->escape2char['\"'] = '\"';\n    cfg->escape2char['\\\\'] = '\\\\';\n    cfg->escape2char['/'] = '/';\n    cfg->escape2char['b'] = '\\b';\n    cfg->escape2char['t'] = '\\t';\n    cfg->escape2char['n'] = '\\n';\n    cfg->escape2char['f'] = '\\f';\n    cfg->escape2char['r'] = '\\r';\n    cfg->escape2char['u'] = 'u';          /* Unicode parsing required */\n\n\n#if 0\n    /* Initialise separate storage for pre-generated escape codes.\n     * Escapes 0-31 map directly, 34, 92, 127 follow afterwards to\n     * save memory. */\n    for (i = 0 ; i < 32; i++)\n        sprintf(cfg->escapes[i], \"\\\\u%04x\", i);\n    strcpy(cfg->escapes[8], \"\\b\");              /* Override simpler escapes */\n    strcpy(cfg->escapes[9], \"\\t\");\n    strcpy(cfg->escapes[10], \"\\n\");\n    strcpy(cfg->escapes[12], \"\\f\");\n    strcpy(cfg->escapes[13], \"\\r\");\n    strcpy(cfg->escapes[32], \"\\\\\\\"\");           /* chr(34) */\n    strcpy(cfg->escapes[33], \"\\\\\\\\\");           /* chr(92) */\n    sprintf(cfg->escapes[34], \"\\\\u%04x\", 127);  /* char(127) */\n\n    /* Initialise encoding escape lookup table */\n    for (i = 0; i < 32; i++)\n        cfg->char2escape[i] = cfg->escapes[i];\n    for (i = 32; i < 256; i++)\n        cfg->char2escape[i] = NULL;\n    cfg->char2escape[34] = cfg->escapes[32];\n    cfg->char2escape[92] = cfg->escapes[33];\n    cfg->char2escape[127] = cfg->escapes[34];\n#endif\n}\n\n/* ===== ENCODING ===== */\n\nstatic void json_encode_exception(lua_State *l, json_config_t *cfg, int lindex,\n                                  const char *reason)\n{\n    if (!cfg->encode_keep_buffer)\n        strbuf_free(&cfg->encode_buf);\n    luaL_error(l, \"Cannot serialise %s: %s\",\n                  lua_typename(l, lua_type(l, lindex)), reason);\n}\n\n/* json_append_string args:\n * - lua_State\n * - JSON strbuf\n * - String (Lua stack index)\n *\n * Returns nothing. Doesn't remove string from Lua stack */\nstatic void json_append_string(lua_State *l, strbuf_t *json, int lindex)\n{\n    const char *escstr;\n    int i;\n    const char *str;\n    size_t len;\n\n    str = lua_tolstring(l, lindex, &len);\n\n    /* Worst case is len * 6 (all unicode escapes).\n     * This buffer is reused constantly for small strings\n     * If there are any excess pages, they won't be hit anyway.\n     * This gains ~5% speedup. */\n    strbuf_ensure_empty_length(json, len * 6 + 2);\n\n    strbuf_append_char_unsafe(json, '\\\"');\n    for (i = 0; i < len; i++) {\n        escstr = char2escape[(unsigned char)str[i]];\n        if (escstr)\n            strbuf_append_string(json, escstr);\n        else\n            strbuf_append_char_unsafe(json, str[i]);\n    }\n    strbuf_append_char_unsafe(json, '\\\"');\n}\n\n/* Find the size of the array on the top of the Lua stack\n * -1   object (not a pure array)\n * >=0  elements in array\n */\nstatic int lua_array_length(lua_State *l, json_config_t *cfg)\n{\n    double k;\n    int max;\n    int items;\n\n    max = 0;\n    items = 0;\n\n    lua_pushnil(l);\n    /* table, startkey */\n    while (lua_next(l, -2) != 0) {\n        /* table, key, value */\n        if (lua_type(l, -2) == LUA_TNUMBER &&\n            (k = lua_tonumber(l, -2))) {\n            /* Integer >= 1 ? */\n            if (floor(k) == k && k >= 1) {\n                if (k > max)\n                    max = k;\n                items++;\n                lua_pop(l, 1);\n                continue;\n            }\n        }\n\n        /* Must not be an array (non integer key) */\n        lua_pop(l, 2);\n        return -1;\n    }\n\n    /* Encode excessively sparse arrays as objects (if enabled) */\n    if (cfg->encode_sparse_ratio > 0 &&\n        max > items * cfg->encode_sparse_ratio &&\n        max > cfg->encode_sparse_safe) {\n        if (!cfg->encode_sparse_convert)\n            json_encode_exception(l, cfg, -1, \"excessively sparse array\");\n\n        return -1;\n    }\n\n    return max;\n}\n\nstatic void json_encode_descend(lua_State *l, json_config_t *cfg)\n{\n    cfg->current_depth++;\n\n    if (cfg->current_depth > cfg->encode_max_depth) {\n        if (!cfg->encode_keep_buffer)\n            strbuf_free(&cfg->encode_buf);\n        luaL_error(l, \"Cannot serialise, excessive nesting (%d)\",\n                   cfg->current_depth);\n    }\n}\n\nstatic void json_append_data(lua_State *l, json_config_t *cfg, strbuf_t *json);\n\n/* json_append_array args:\n * - lua_State\n * - JSON strbuf\n * - Size of passwd Lua array (top of stack) */\nstatic void json_append_array(lua_State *l, json_config_t *cfg, strbuf_t *json,\n                              int array_length)\n{\n    int comma, i;\n\n    json_encode_descend(l, cfg);\n\n    strbuf_append_char(json, '[');\n\n    comma = 0;\n    for (i = 1; i <= array_length; i++) {\n        if (comma)\n            strbuf_append_char(json, ',');\n        else\n            comma = 1;\n\n        lua_rawgeti(l, -1, i);\n        json_append_data(l, cfg, json);\n        lua_pop(l, 1);\n    }\n\n    strbuf_append_char(json, ']');\n\n    cfg->current_depth--;\n}\n\nstatic void json_append_number(lua_State *l, strbuf_t *json, int index,\n                               json_config_t *cfg)\n{\n    double num = lua_tonumber(l, index);\n\n    if (cfg->encode_refuse_badnum && (isinf(num) || isnan(num)))\n        json_encode_exception(l, cfg, index, \"must not be NaN or Inf\");\n\n    /* Lowest double printed with %.14g is 21 characters long:\n     * -1.7976931348623e+308\n     *\n     * Use 32 to include the \\0, and a few extra just in case..\n     */\n    strbuf_append_fmt(json, 32, cfg->number_fmt, num);\n}\n\nstatic void json_append_object(lua_State *l, json_config_t *cfg,\n                               strbuf_t *json)\n{\n    int comma, keytype;\n\n    json_encode_descend(l, cfg);\n\n    /* Object */\n    strbuf_append_char(json, '{');\n\n    lua_pushnil(l);\n    /* table, startkey */\n    comma = 0;\n    while (lua_next(l, -2) != 0) {\n        if (comma)\n            strbuf_append_char(json, ',');\n        else\n            comma = 1;\n\n        /* table, key, value */\n        keytype = lua_type(l, -2);\n        if (keytype == LUA_TNUMBER) {\n            strbuf_append_char(json, '\"');\n            json_append_number(l, json, -2, cfg);\n            strbuf_append_mem(json, \"\\\":\", 2);\n        } else if (keytype == LUA_TSTRING) {\n            json_append_string(l, json, -2);\n            strbuf_append_char(json, ':');\n        } else {\n            json_encode_exception(l, cfg, -2,\n                                  \"table key must be a number or string\");\n            /* never returns */\n        }\n\n        /* table, key, value */\n        json_append_data(l, cfg, json);\n        lua_pop(l, 1);\n        /* table, key */\n    }\n\n    strbuf_append_char(json, '}');\n\n    cfg->current_depth--;\n}\n\n/* Serialise Lua data into JSON string. */\nstatic void json_append_data(lua_State *l, json_config_t *cfg, strbuf_t *json)\n{\n    int len;\n\n    switch (lua_type(l, -1)) {\n    case LUA_TSTRING:\n        json_append_string(l, json, -1);\n        break;\n    case LUA_TNUMBER:\n        json_append_number(l, json, -1, cfg);\n        break;\n    case LUA_TBOOLEAN:\n        if (lua_toboolean(l, -1))\n            strbuf_append_mem(json, \"true\", 4);\n        else\n            strbuf_append_mem(json, \"false\", 5);\n        break;\n    case LUA_TTABLE:\n        len = lua_array_length(l, cfg);\n        if (len > 0)\n            json_append_array(l, cfg, json, len);\n        else\n            json_append_object(l, cfg, json);\n        break;\n    case LUA_TNIL:\n        strbuf_append_mem(json, \"null\", 4);\n        break;\n    case LUA_TLIGHTUSERDATA:\n        if (lua_touserdata(l, -1) == NULL) {\n            strbuf_append_mem(json, \"null\", 4);\n            break;\n        }\n    default:\n        /* Remaining types (LUA_TFUNCTION, LUA_TUSERDATA, LUA_TTHREAD,\n         * and LUA_TLIGHTUSERDATA) cannot be serialised */\n        json_encode_exception(l, cfg, -1, \"type not supported\");\n        /* never returns */\n    }\n}\n\nstatic int json_encode(lua_State *l)\n{\n    json_config_t *cfg;\n    char *json;\n    int len;\n\n    /* Can't use json_verify_arg_count() since we need to ensure\n     * there is only 1 argument */\n    luaL_argcheck(l, lua_gettop(l) == 1, 1, \"expected 1 argument\");\n\n    cfg = json_fetch_config(l);\n    cfg->current_depth = 0;\n\n    /* Reset the persistent buffer if it exists.\n     * Otherwise allocate a new buffer. */\n    if (strbuf_allocated(&cfg->encode_buf))\n        strbuf_reset(&cfg->encode_buf);\n    else\n        strbuf_init(&cfg->encode_buf, 0);\n\n    json_append_data(l, cfg, &cfg->encode_buf);\n    json = strbuf_string(&cfg->encode_buf, &len);\n\n    lua_pushlstring(l, json, len);\n\n    if (!cfg->encode_keep_buffer)\n        strbuf_free(&cfg->encode_buf);\n\n    return 1;\n}\n\n/* ===== DECODING ===== */\n\nstatic void json_process_value(lua_State *l, json_parse_t *json,\n                               json_token_t *token);\n\nstatic int hexdigit2int(char hex)\n{\n    if ('0' <= hex  && hex <= '9')\n        return hex - '0';\n\n    /* Force lowercase */\n    hex |= 0x20;\n    if ('a' <= hex && hex <= 'f')\n        return 10 + hex - 'a';\n\n    return -1;\n}\n\nstatic int decode_hex4(const char *hex)\n{\n    int digit[4];\n    int i;\n\n    /* Convert ASCII hex digit to numeric digit\n     * Note: this returns an error for invalid hex digits, including\n     *       NULL */\n    for (i = 0; i < 4; i++) {\n        digit[i] = hexdigit2int(hex[i]);\n        if (digit[i] < 0) {\n            return -1;\n        }\n    }\n\n    return (digit[0] << 12) +\n           (digit[1] << 8) +\n           (digit[2] << 4) +\n            digit[3];\n}\n\n/* Converts a Unicode codepoint to UTF-8.\n * Returns UTF-8 string length, and up to 4 bytes in *utf8 */\nstatic int codepoint_to_utf8(char *utf8, int codepoint)\n{\n    /* 0xxxxxxx */\n    if (codepoint <= 0x7F) {\n        utf8[0] = codepoint;\n        return 1;\n    }\n\n    /* 110xxxxx 10xxxxxx */\n    if (codepoint <= 0x7FF) {\n        utf8[0] = (codepoint >> 6) | 0xC0;\n        utf8[1] = (codepoint & 0x3F) | 0x80;\n        return 2;\n    }\n\n    /* 1110xxxx 10xxxxxx 10xxxxxx */\n    if (codepoint <= 0xFFFF) {\n        utf8[0] = (codepoint >> 12) | 0xE0;\n        utf8[1] = ((codepoint >> 6) & 0x3F) | 0x80;\n        utf8[2] = (codepoint & 0x3F) | 0x80;\n        return 3;\n    }\n\n    /* 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */\n    if (codepoint <= 0x1FFFFF) {\n        utf8[0] = (codepoint >> 18) | 0xF0;\n        utf8[1] = ((codepoint >> 12) & 0x3F) | 0x80;\n        utf8[2] = ((codepoint >> 6) & 0x3F) | 0x80;\n        utf8[3] = (codepoint & 0x3F) | 0x80;\n        return 4;\n    }\n\n    return 0;\n}\n\n\n/* Called when index pointing to beginning of UTF-16 code escape: \\uXXXX\n * \\u is guaranteed to exist, but the remaining hex characters may be\n * missing.\n * Translate to UTF-8 and append to temporary token string.\n * Must advance index to the next character to be processed.\n * Returns: 0   success\n *          -1  error\n */\nstatic int json_append_unicode_escape(json_parse_t *json)\n{\n    char utf8[4];       /* Surrogate pairs require 4 UTF-8 bytes */\n    int codepoint;\n    int surrogate_low;\n    int len;\n    int escape_len = 6;\n\n    /* Fetch UTF-16 code unit */\n    codepoint = decode_hex4(&json->data[json->index + 2]);\n    if (codepoint < 0)\n        return -1;\n\n    /* UTF-16 surrogate pairs take the following 2 byte form:\n     *      11011 x yyyyyyyyyy\n     * When x = 0: y is the high 10 bits of the codepoint\n     *      x = 1: y is the low 10 bits of the codepoint\n     *\n     * Check for a surrogate pair (high or low) */\n    if ((codepoint & 0xF800) == 0xD800) {\n        /* Error if the 1st surrogate is not high */\n        if (codepoint & 0x400)\n            return -1;\n\n        /* Ensure the next code is a unicode escape */\n        if (json->data[json->index + escape_len] != '\\\\' ||\n            json->data[json->index + escape_len + 1] != 'u') {\n            return -1;\n        }\n\n        /* Fetch the next codepoint */\n        surrogate_low = decode_hex4(&json->data[json->index + 2 + escape_len]);\n        if (surrogate_low < 0)\n            return -1;\n\n        /* Error if the 2nd code is not a low surrogate */\n        if ((surrogate_low & 0xFC00) != 0xDC00)\n            return -1;\n\n        /* Calculate Unicode codepoint */\n        codepoint = (codepoint & 0x3FF) << 10;\n        surrogate_low &= 0x3FF;\n        codepoint = (codepoint | surrogate_low) + 0x10000;\n        escape_len = 12;\n    }\n\n    /* Convert codepoint to UTF-8 */\n    len = codepoint_to_utf8(utf8, codepoint);\n    if (!len)\n        return -1;\n\n    /* Append bytes and advance parse index */\n    strbuf_append_mem_unsafe(json->tmp, utf8, len);\n    json->index += escape_len;\n\n    return 0;\n}\n\nstatic void json_set_token_error(json_token_t *token, json_parse_t *json,\n                                 const char *errtype)\n{\n    token->type = T_ERROR;\n    token->index = json->index;\n    token->value.string = errtype;\n}\n\nstatic void json_next_string_token(json_parse_t *json, json_token_t *token)\n{\n    char *escape2char = json->cfg->escape2char;\n    char ch;\n\n    /* Caller must ensure a string is next */\n    assert(json->data[json->index] == '\"');\n\n    /* Skip \" */\n    json->index++;\n\n    /* json->tmp is the temporary strbuf used to accumulate the\n     * decoded string value. */\n    strbuf_reset(json->tmp);\n    while ((ch = json->data[json->index]) != '\"') {\n        if (!ch) {\n            /* Premature end of the string */\n            json_set_token_error(token, json, \"unexpected end of string\");\n            return;\n        }\n\n        /* Handle escapes */\n        if (ch == '\\\\') {\n            /* Fetch escape character */\n            ch = json->data[json->index + 1];\n\n            /* Translate escape code and append to tmp string */\n            ch = escape2char[(unsigned char)ch];\n            if (ch == 'u') {\n                if (json_append_unicode_escape(json) == 0)\n                    continue;\n\n                json_set_token_error(token, json,\n                                     \"invalid unicode escape code\");\n                return;\n            }\n            if (!ch) {\n                json_set_token_error(token, json, \"invalid escape code\");\n                return;\n            }\n\n            /* Skip '\\' */\n            json->index++;\n        }\n        /* Append normal character or translated single character\n         * Unicode escapes are handled above */\n        strbuf_append_char_unsafe(json->tmp, ch);\n        json->index++;\n    }\n    json->index++;  /* Eat final quote (\") */\n\n    strbuf_ensure_null(json->tmp);\n\n    token->type = T_STRING;\n    token->value.string = strbuf_string(json->tmp, &token->string_len);\n}\n\n/* JSON numbers should take the following form:\n *      -?(0|[1-9]|[1-9][0-9]+)(.[0-9]+)?([eE][-+]?[0-9]+)?\n *\n * json_next_number_token() uses strtod() which allows other forms:\n * - numbers starting with '+'\n * - NaN, -NaN, infinity, -infinity\n * - hexidecimal numbers\n * - numbers with leading zeros\n *\n * json_is_invalid_number() detects \"numbers\" which may pass strtod()'s\n * error checking, but should not be allowed with strict JSON.\n *\n * json_is_invalid_number() may pass numbers which cause strtod()\n * to generate an error.\n */\nstatic int json_is_invalid_number(json_parse_t *json)\n{\n    int i = json->index;\n\n    /* Reject numbers starting with + */\n    if (json->data[i] == '+')\n        return 1;\n\n    /* Skip minus sign if it exists */\n    if (json->data[i] == '-')\n        i++;\n\n    /* Reject numbers starting with 0x, or leading zeros */\n    if (json->data[i] == '0') {\n        int ch2 = json->data[i + 1];\n\n        if ((ch2 | 0x20) == 'x' ||          /* Hex */\n            ('0' <= ch2 && ch2 <= '9'))     /* Leading zero */\n            return 1;\n\n        return 0;\n    } else if (json->data[i] <= '9') {\n        return 0;                           /* Ordinary number */\n    }\n\n\n    /* Reject inf/nan */\n    if (!strncasecmp(&json->data[i], \"inf\", 3))\n        return 1;\n    if (!strncasecmp(&json->data[i], \"nan\", 3))\n        return 1;\n\n    /* Pass all other numbers which may still be invalid, but\n     * strtod() will catch them. */\n    return 0;\n}\n\nstatic void json_next_number_token(json_parse_t *json, json_token_t *token)\n{\n    const char *startptr;\n    char *endptr;\n\n    token->type = T_NUMBER;\n    startptr = &json->data[json->index];\n    token->value.number = strtod(&json->data[json->index], &endptr);\n    if (startptr == endptr)\n        json_set_token_error(token, json, \"invalid number\");\n    else\n        json->index += endptr - startptr;   /* Skip the processed number */\n\n    return;\n}\n\n/* Fills in the token struct.\n * T_STRING will return a pointer to the json_parse_t temporary string\n * T_ERROR will leave the json->index pointer at the error.\n */\nstatic void json_next_token(json_parse_t *json, json_token_t *token)\n{\n    json_token_type_t *ch2token = json->cfg->ch2token;\n    int ch;\n\n    /* Eat whitespace. FIXME: UGLY */\n    token->type = ch2token[(unsigned char)json->data[json->index]];\n    while (token->type == T_WHITESPACE)\n        token->type = ch2token[(unsigned char)json->data[++json->index]];\n\n    token->index = json->index;\n\n    /* Don't advance the pointer for an error or the end */\n    if (token->type == T_ERROR) {\n        json_set_token_error(token, json, \"invalid token\");\n        return;\n    }\n\n    if (token->type == T_END) {\n        return;\n    }\n\n    /* Found a known single character token, advance index and return */\n    if (token->type != T_UNKNOWN) {\n        json->index++;\n        return;\n    }\n\n    /* Process characters which triggered T_UNKNOWN */\n    ch = json->data[json->index];\n\n    /* Must use strncmp() to match the front of the JSON string.\n     * JSON identifier must be lowercase.\n     * When strict_numbers if disabled, either case is allowed for\n     * Infinity/NaN (since we are no longer following the spec..) */\n    if (ch == '\"') {\n        json_next_string_token(json, token);\n        return;\n    } else if (ch == '-' || ('0' <= ch && ch <= '9')) {\n        if (json->cfg->decode_refuse_badnum && json_is_invalid_number(json)) {\n            json_set_token_error(token, json, \"invalid number\");\n            return;\n        }\n        json_next_number_token(json, token);\n        return;\n    } else if (!strncmp(&json->data[json->index], \"true\", 4)) {\n        token->type = T_BOOLEAN;\n        token->value.boolean = 1;\n        json->index += 4;\n        return;\n    } else if (!strncmp(&json->data[json->index], \"false\", 5)) {\n        token->type = T_BOOLEAN;\n        token->value.boolean = 0;\n        json->index += 5;\n        return;\n    } else if (!strncmp(&json->data[json->index], \"null\", 4)) {\n        token->type = T_NULL;\n        json->index += 4;\n        return;\n    } else if (!json->cfg->decode_refuse_badnum &&\n               json_is_invalid_number(json)) {\n        /* When refuse_badnum is disabled, only attempt to process\n         * numbers we know are invalid JSON (Inf, NaN, hex)\n         * This is required to generate an appropriate token error,\n         * otherwise all bad tokens will register as \"invalid number\"\n         */\n        json_next_number_token(json, token);\n        return;\n    }\n\n    /* Token starts with t/f/n but isn't recognised above. */\n    json_set_token_error(token, json, \"invalid token\");\n}\n\n/* This function does not return.\n * DO NOT CALL WITH DYNAMIC MEMORY ALLOCATED.\n * The only supported exception is the temporary parser string\n * json->tmp struct.\n * json and token should exist on the stack somewhere.\n * luaL_error() will long_jmp and release the stack */\nstatic void json_throw_parse_error(lua_State *l, json_parse_t *json,\n                                   const char *exp, json_token_t *token)\n{\n    const char *found;\n\n    strbuf_free(json->tmp);\n\n    if (token->type == T_ERROR)\n        found = token->value.string;\n    else\n        found = json_token_type_name[token->type];\n\n    /* Note: token->index is 0 based, display starting from 1 */\n    luaL_error(l, \"Expected %s but found %s at character %d\",\n               exp, found, token->index + 1);\n}\n\nstatic void json_decode_checkstack(lua_State *l, json_parse_t *json, int n)\n{\n    if (lua_checkstack(l, n))\n        return;\n\n    strbuf_free(json->tmp);\n    luaL_error(l, \"Too many nested data structures\");\n}\n\nstatic void json_parse_object_context(lua_State *l, json_parse_t *json)\n{\n    json_token_t token;\n\n    /* 3 slots required:\n     * .., table, key, value */\n    json_decode_checkstack(l, json, 3);\n\n    lua_newtable(l);\n\n    json_next_token(json, &token);\n\n    /* Handle empty objects */\n    if (token.type == T_OBJ_END) {\n        return;\n    }\n\n    while (1) {\n        if (token.type != T_STRING)\n            json_throw_parse_error(l, json, \"object key string\", &token);\n\n        /* Push key */\n        lua_pushlstring(l, token.value.string, token.string_len);\n\n        json_next_token(json, &token);\n        if (token.type != T_COLON)\n            json_throw_parse_error(l, json, \"colon\", &token);\n\n        /* Fetch value */\n        json_next_token(json, &token);\n        json_process_value(l, json, &token);\n\n        /* Set key = value */\n        lua_rawset(l, -3);\n\n        json_next_token(json, &token);\n\n        if (token.type == T_OBJ_END)\n            return;\n\n        if (token.type != T_COMMA)\n            json_throw_parse_error(l, json, \"comma or object end\", &token);\n\n        json_next_token(json, &token);\n    }\n}\n\n/* Handle the array context */\nstatic void json_parse_array_context(lua_State *l, json_parse_t *json)\n{\n    json_token_t token;\n    int i;\n\n    /* 2 slots required:\n     * .., table, value */\n    json_decode_checkstack(l, json, 2);\n\n    lua_newtable(l);\n\n    json_next_token(json, &token);\n\n    /* Handle empty arrays */\n    if (token.type == T_ARR_END)\n        return;\n\n    for (i = 1; ; i++) {\n        json_process_value(l, json, &token);\n        lua_rawseti(l, -2, i);            /* arr[i] = value */\n\n        json_next_token(json, &token);\n\n        if (token.type == T_ARR_END)\n            return;\n\n        if (token.type != T_COMMA)\n            json_throw_parse_error(l, json, \"comma or array end\", &token);\n\n        json_next_token(json, &token);\n    }\n}\n\n/* Handle the \"value\" context */\nstatic void json_process_value(lua_State *l, json_parse_t *json,\n                               json_token_t *token)\n{\n    switch (token->type) {\n    case T_STRING:\n        lua_pushlstring(l, token->value.string, token->string_len);\n        break;;\n    case T_NUMBER:\n        lua_pushnumber(l, token->value.number);\n        break;;\n    case T_BOOLEAN:\n        lua_pushboolean(l, token->value.boolean);\n        break;;\n    case T_OBJ_BEGIN:\n        json_parse_object_context(l, json);\n        break;;\n    case T_ARR_BEGIN:\n        json_parse_array_context(l, json);\n        break;;\n    case T_NULL:\n        /* In Lua, setting \"t[k] = nil\" will delete k from the table.\n         * Hence a NULL pointer lightuserdata object is used instead */\n        lua_pushlightuserdata(l, NULL);\n        break;;\n    default:\n        json_throw_parse_error(l, json, \"value\", token);\n    }\n}\n\n/* json_text must be null terminated string */\nstatic void lua_json_decode(lua_State *l, const char *json_text, int json_len)\n{\n    json_parse_t json;\n    json_token_t token;\n\n    json.cfg = json_fetch_config(l);\n    json.data = json_text;\n    json.index = 0;\n\n    /* Ensure the temporary buffer can hold the entire string.\n     * This means we no longer need to do length checks since the decoded\n     * string must be smaller than the entire json string */\n    json.tmp = strbuf_new(json_len);\n\n    json_next_token(&json, &token);\n    json_process_value(l, &json, &token);\n\n    /* Ensure there is no more input left */\n    json_next_token(&json, &token);\n\n    if (token.type != T_END)\n        json_throw_parse_error(l, &json, \"the end\", &token);\n\n    strbuf_free(json.tmp);\n}\n\nstatic int json_decode(lua_State *l)\n{\n    const char *json;\n    size_t len;\n\n    json_verify_arg_count(l, 1);\n\n    json = luaL_checklstring(l, 1, &len);\n\n    /* Detect Unicode other than UTF-8 (see RFC 4627, Sec 3)\n     *\n     * CJSON can support any simple data type, hence only the first\n     * character is guaranteed to be ASCII (at worst: '\"'). This is\n     * still enough to detect whether the wrong encoding is in use. */\n    if (len >= 2 && (!json[0] || !json[1]))\n        luaL_error(l, \"JSON parser does not support UTF-16 or UTF-32\");\n\n    lua_json_decode(l, json, len);\n\n    return 1;\n}\n\n/* ===== INITIALISATION ===== */\n\nint luaopen_cjson(lua_State *l)\n{\n    luaL_Reg reg[] = {\n        { \"encode\", json_encode },\n        { \"decode\", json_decode },\n        { \"encode_sparse_array\", json_cfg_encode_sparse_array },\n        { \"encode_max_depth\", json_cfg_encode_max_depth },\n        { \"encode_number_precision\", json_cfg_encode_number_precision },\n        { \"encode_keep_buffer\", json_cfg_encode_keep_buffer },\n        { \"refuse_invalid_numbers\", json_cfg_refuse_invalid_numbers },\n        { NULL, NULL }\n    };\n\n    /* Use json_fetch_config as a pointer.\n     * It's faster than using a config string, and more unique */\n    lua_pushlightuserdata(l, &json_config_key);\n    json_create_config(l);\n    lua_settable(l, LUA_REGISTRYINDEX);\n\n    luaL_register(l, \"cjson\", reg);\n\n    /* Set cjson.null */\n    lua_pushlightuserdata(l, NULL);\n    lua_setfield(l, -2, \"null\");\n\n    /* Set cjson.version */\n    lua_pushliteral(l, VERSION);\n    lua_setfield(l, -2, \"version\");\n\n    /* Return cjson table */\n    return 1;\n}\n\n/* vi:ai et sw=4 ts=4:\n */\n"
  },
  {
    "path": "deps/lua/src/lua_cmsgpack.c",
    "content": "#include <math.h>\n#include <stdlib.h>\n#include <stdint.h>\n#include <string.h>\n#include <assert.h>\n\n#include \"lua.h\"\n#include \"lauxlib.h\"\n\n#define LUACMSGPACK_VERSION     \"lua-cmsgpack 0.3.0\"\n#define LUACMSGPACK_COPYRIGHT   \"Copyright (C) 2012, Salvatore Sanfilippo\"\n#define LUACMSGPACK_DESCRIPTION \"MessagePack C implementation for Lua\"\n\n#define LUACMSGPACK_MAX_NESTING  16 /* Max tables nesting. */\n\n/* ==============================================================================\n * MessagePack implementation and bindings for Lua 5.1.\n * Copyright(C) 2012 Salvatore Sanfilippo <antirez@gmail.com>\n *\n * http://github.com/antirez/lua-cmsgpack\n *\n * For MessagePack specification check the following web site:\n * http://wiki.msgpack.org/display/MSGPACK/Format+specification\n *\n * See Copyright Notice at the end of this file.\n *\n * CHANGELOG:\n * 19-Feb-2012 (ver 0.1.0): Initial release.\n * 20-Feb-2012 (ver 0.2.0): Tables encoding improved.\n * 20-Feb-2012 (ver 0.2.1): Minor bug fixing.\n * 20-Feb-2012 (ver 0.3.0): Module renamed lua-cmsgpack (was lua-msgpack).\n * ============================================================================ */\n\n/* --------------------------- Endian conversion --------------------------------\n * We use it only for floats and doubles, all the other conversions are performed\n * in an endian independent fashion. So the only thing we need is a function\n * that swaps a binary string if the arch is little endian (and left it untouched\n * otherwise). */\n\n/* Reverse memory bytes if arch is little endian. Given the conceptual\n * simplicity of the Lua build system we prefer to check for endianess at runtime.\n * The performance difference should be acceptable. */\nstatic void memrevifle(void *ptr, size_t len) {\n    unsigned char *p = ptr, *e = p+len-1, aux;\n    int test = 1;\n    unsigned char *testp = (unsigned char*) &test;\n\n    if (testp[0] == 0) return; /* Big endian, nothign to do. */\n    len /= 2;\n    while(len--) {\n        aux = *p;\n        *p = *e;\n        *e = aux;\n        p++;\n        e--;\n    }\n}\n\n/* ----------------------------- String buffer ----------------------------------\n * This is a simple implementation of string buffers. The only opereation\n * supported is creating empty buffers and appending bytes to it.\n * The string buffer uses 2x preallocation on every realloc for O(N) append\n * behavior.  */\n\ntypedef struct mp_buf {\n    unsigned char *b;\n    size_t len, free;\n} mp_buf;\n\nstatic mp_buf *mp_buf_new(void) {\n    mp_buf *buf = malloc(sizeof(*buf));\n    \n    buf->b = NULL;\n    buf->len = buf->free = 0;\n    return buf;\n}\n\nvoid mp_buf_append(mp_buf *buf, const unsigned char *s, size_t len) {\n    if (buf->free < len) {\n        size_t newlen = buf->len+len;\n\n        buf->b = realloc(buf->b,newlen*2);\n        buf->free = newlen;\n    }\n    memcpy(buf->b+buf->len,s,len);\n    buf->len += len;\n    buf->free -= len;\n}\n\nvoid mp_buf_free(mp_buf *buf) {\n    free(buf->b);\n    free(buf);\n}\n\n/* ------------------------------ String cursor ----------------------------------\n * This simple data structure is used for parsing. Basically you create a cursor\n * using a string pointer and a length, then it is possible to access the\n * current string position with cursor->p, check the remaining length\n * in cursor->left, and finally consume more string using\n * mp_cur_consume(cursor,len), to advance 'p' and subtract 'left'.\n * An additional field cursor->error is set to zero on initialization and can\n * be used to report errors. */\n\n#define MP_CUR_ERROR_NONE   0\n#define MP_CUR_ERROR_EOF    1   /* Not enough data to complete the opereation. */\n#define MP_CUR_ERROR_BADFMT 2   /* Bad data format */\n\ntypedef struct mp_cur {\n    const unsigned char *p;\n    size_t left;\n    int err;\n} mp_cur;\n\nstatic mp_cur *mp_cur_new(const unsigned char *s, size_t len) {\n    mp_cur *cursor = malloc(sizeof(*cursor));\n\n    cursor->p = s;\n    cursor->left = len;\n    cursor->err = MP_CUR_ERROR_NONE;\n    return cursor;\n}\n\nstatic void mp_cur_free(mp_cur *cursor) {\n    free(cursor);\n}\n\n#define mp_cur_consume(_c,_len) do { _c->p += _len; _c->left -= _len; } while(0)\n\n/* When there is not enough room we set an error in the cursor and return, this\n * is very common across the code so we have a macro to make the code look\n * a bit simpler. */\n#define mp_cur_need(_c,_len) do { \\\n    if (_c->left < _len) { \\\n        _c->err = MP_CUR_ERROR_EOF; \\\n        return; \\\n    } \\\n} while(0)\n\n/* --------------------------- Low level MP encoding -------------------------- */\n\nstatic void mp_encode_bytes(mp_buf *buf, const unsigned char *s, size_t len) {\n    unsigned char hdr[5];\n    int hdrlen;\n\n    if (len < 32) {\n        hdr[0] = 0xa0 | (len&0xff); /* fix raw */\n        hdrlen = 1;\n    } else if (len <= 0xffff) {\n        hdr[0] = 0xda;\n        hdr[1] = (len&0xff00)>>8;\n        hdr[2] = len&0xff;\n        hdrlen = 3;\n    } else {\n        hdr[0] = 0xdb;\n        hdr[1] = (len&0xff000000)>>24;\n        hdr[2] = (len&0xff0000)>>16;\n        hdr[3] = (len&0xff00)>>8;\n        hdr[4] = len&0xff;\n        hdrlen = 5;\n    }\n    mp_buf_append(buf,hdr,hdrlen);\n    mp_buf_append(buf,s,len);\n}\n\n/* we assume IEEE 754 internal format for single and double precision floats. */\nstatic void mp_encode_double(mp_buf *buf, double d) {\n    unsigned char b[9];\n    float f = d;\n\n    assert(sizeof(f) == 4 && sizeof(d) == 8);\n    if (d == (double)f) {\n        b[0] = 0xca;    /* float IEEE 754 */\n        memcpy(b+1,&f,4);\n        memrevifle(b+1,4);\n        mp_buf_append(buf,b,5);\n    } else if (sizeof(d) == 8) {\n        b[0] = 0xcb;    /* double IEEE 754 */\n        memcpy(b+1,&d,8);\n        memrevifle(b+1,8);\n        mp_buf_append(buf,b,9);\n    }\n}\n\nstatic void mp_encode_int(mp_buf *buf, int64_t n) {\n    unsigned char b[9];\n    int enclen;\n\n    if (n >= 0) {\n        if (n <= 127) {\n            b[0] = n & 0x7f;    /* positive fixnum */\n            enclen = 1;\n        } else if (n <= 0xff) {\n            b[0] = 0xcc;        /* uint 8 */\n            b[1] = n & 0xff;\n            enclen = 2;\n        } else if (n <= 0xffff) {\n            b[0] = 0xcd;        /* uint 16 */\n            b[1] = (n & 0xff00) >> 8;\n            b[2] = n & 0xff;\n            enclen = 3;\n        } else if (n <= 0xffffffffLL) {\n            b[0] = 0xce;        /* uint 32 */\n            b[1] = (n & 0xff000000) >> 24;\n            b[2] = (n & 0xff0000) >> 16;\n            b[3] = (n & 0xff00) >> 8;\n            b[4] = n & 0xff;\n            enclen = 5;\n        } else {\n            b[0] = 0xcf;        /* uint 64 */\n            b[1] = (n & 0xff00000000000000LL) >> 56;\n            b[2] = (n & 0xff000000000000LL) >> 48;\n            b[3] = (n & 0xff0000000000LL) >> 40;\n            b[4] = (n & 0xff00000000LL) >> 32;\n            b[5] = (n & 0xff000000) >> 24;\n            b[6] = (n & 0xff0000) >> 16;\n            b[7] = (n & 0xff00) >> 8;\n            b[8] = n & 0xff;\n            enclen = 9;\n        }\n    } else {\n        if (n >= -32) {\n            b[0] = ((char)n);   /* negative fixnum */\n            enclen = 1;\n        } else if (n >= -128) {\n            b[0] = 0xd0;        /* int 8 */\n            b[1] = n & 0xff;\n            enclen = 2;\n        } else if (n >= -32768) {\n            b[0] = 0xd1;        /* int 16 */\n            b[1] = (n & 0xff00) >> 8;\n            b[2] = n & 0xff;\n            enclen = 3;\n        } else if (n >= -2147483648LL) {\n            b[0] = 0xd2;        /* int 32 */\n            b[1] = (n & 0xff000000) >> 24;\n            b[2] = (n & 0xff0000) >> 16;\n            b[3] = (n & 0xff00) >> 8;\n            b[4] = n & 0xff;\n            enclen = 5;\n        } else {\n            b[0] = 0xd3;        /* int 64 */\n            b[1] = (n & 0xff00000000000000LL) >> 56;\n            b[2] = (n & 0xff000000000000LL) >> 48;\n            b[3] = (n & 0xff0000000000LL) >> 40;\n            b[4] = (n & 0xff00000000LL) >> 32;\n            b[5] = (n & 0xff000000) >> 24;\n            b[6] = (n & 0xff0000) >> 16;\n            b[7] = (n & 0xff00) >> 8;\n            b[8] = n & 0xff;\n            enclen = 9;\n        }\n    }\n    mp_buf_append(buf,b,enclen);\n}\n\nstatic void mp_encode_array(mp_buf *buf, int64_t n) {\n    unsigned char b[5];\n    int enclen;\n\n    if (n <= 15) {\n        b[0] = 0x90 | (n & 0xf);    /* fix array */\n        enclen = 1;\n    } else if (n <= 65535) {\n        b[0] = 0xdc;                /* array 16 */\n        b[1] = (n & 0xff00) >> 8;\n        b[2] = n & 0xff;\n        enclen = 3;\n    } else {\n        b[0] = 0xdd;                /* array 32 */\n        b[1] = (n & 0xff000000) >> 24;\n        b[2] = (n & 0xff0000) >> 16;\n        b[3] = (n & 0xff00) >> 8;\n        b[4] = n & 0xff;\n        enclen = 5;\n    }\n    mp_buf_append(buf,b,enclen);\n}\n\nstatic void mp_encode_map(mp_buf *buf, int64_t n) {\n    unsigned char b[5];\n    int enclen;\n\n    if (n <= 15) {\n        b[0] = 0x80 | (n & 0xf);    /* fix map */\n        enclen = 1;\n    } else if (n <= 65535) {\n        b[0] = 0xde;                /* map 16 */\n        b[1] = (n & 0xff00) >> 8;\n        b[2] = n & 0xff;\n        enclen = 3;\n    } else {\n        b[0] = 0xdf;                /* map 32 */\n        b[1] = (n & 0xff000000) >> 24;\n        b[2] = (n & 0xff0000) >> 16;\n        b[3] = (n & 0xff00) >> 8;\n        b[4] = n & 0xff;\n        enclen = 5;\n    }\n    mp_buf_append(buf,b,enclen);\n}\n\n/* ----------------------------- Lua types encoding --------------------------- */\n\nstatic void mp_encode_lua_string(lua_State *L, mp_buf *buf) {\n    size_t len;\n    const char *s;\n\n    s = lua_tolstring(L,-1,&len);\n    mp_encode_bytes(buf,(const unsigned char*)s,len);\n}\n\nstatic void mp_encode_lua_bool(lua_State *L, mp_buf *buf) {\n    unsigned char b = lua_toboolean(L,-1) ? 0xc3 : 0xc2;\n    mp_buf_append(buf,&b,1);\n}\n\nstatic void mp_encode_lua_number(lua_State *L, mp_buf *buf) {\n    lua_Number n = lua_tonumber(L,-1);\n\n    if (floor(n) != n) {\n        mp_encode_double(buf,(double)n);\n    } else {\n        mp_encode_int(buf,(int64_t)n);\n    }\n}\n\nstatic void mp_encode_lua_type(lua_State *L, mp_buf *buf, int level);\n\n/* Convert a lua table into a message pack list. */\nstatic void mp_encode_lua_table_as_array(lua_State *L, mp_buf *buf, int level) {\n    size_t len = lua_objlen(L,-1), j;\n\n    mp_encode_array(buf,len);\n    for (j = 1; j <= len; j++) {\n        lua_pushnumber(L,j);\n        lua_gettable(L,-2);\n        mp_encode_lua_type(L,buf,level+1);\n    }\n}\n\n/* Convert a lua table into a message pack key-value map. */\nstatic void mp_encode_lua_table_as_map(lua_State *L, mp_buf *buf, int level) {\n    size_t len = 0;\n\n    /* First step: count keys into table. No other way to do it with the\n     * Lua API, we need to iterate a first time. Note that an alternative\n     * would be to do a single run, and then hack the buffer to insert the\n     * map opcodes for message pack. Too hachish for this lib. */\n    lua_pushnil(L);\n    while(lua_next(L,-2)) {\n        lua_pop(L,1); /* remove value, keep key for next iteration. */\n        len++;\n    }\n\n    /* Step two: actually encoding of the map. */\n    mp_encode_map(buf,len);\n    lua_pushnil(L);\n    while(lua_next(L,-2)) {\n        /* Stack: ... key value */\n        lua_pushvalue(L,-2); /* Stack: ... key value key */\n        mp_encode_lua_type(L,buf,level+1); /* encode key */\n        mp_encode_lua_type(L,buf,level+1); /* encode val */\n    }\n}\n\n/* Returns true if the Lua table on top of the stack is exclusively composed\n * of keys from numerical keys from 1 up to N, with N being the total number\n * of elements, without any hole in the middle. */\nstatic int table_is_an_array(lua_State *L) {\n    long count = 0, max = 0, idx = 0;\n    lua_Number n;\n\n    lua_pushnil(L);\n    while(lua_next(L,-2)) {\n        /* Stack: ... key value */\n        lua_pop(L,1); /* Stack: ... key */\n        if (lua_type(L,-1) != LUA_TNUMBER) goto not_array;\n        n = lua_tonumber(L,-1);\n        idx = n;\n        if (idx != n || idx < 1) goto not_array;\n        count++;\n        max = idx;\n    }\n    /* We have the total number of elements in \"count\". Also we have\n     * the max index encountered in \"idx\". We can't reach this code\n     * if there are indexes <= 0. If you also note that there can not be\n     * repeated keys into a table, you have that if idx==count you are sure\n     * that there are all the keys form 1 to count (both included). */\n    return idx == count;\n\nnot_array:\n    lua_pop(L,1);\n    return 0;\n}\n\n/* If the length operator returns non-zero, that is, there is at least\n * an object at key '1', we serialize to message pack list. Otherwise\n * we use a map. */\nstatic void mp_encode_lua_table(lua_State *L, mp_buf *buf, int level) {\n    if (table_is_an_array(L))\n        mp_encode_lua_table_as_array(L,buf,level);\n    else\n        mp_encode_lua_table_as_map(L,buf,level);\n}\n\nstatic void mp_encode_lua_null(lua_State *L, mp_buf *buf) {\n    unsigned char b[1];\n\n    b[0] = 0xc0;\n    mp_buf_append(buf,b,1);\n}\n\nstatic void mp_encode_lua_type(lua_State *L, mp_buf *buf, int level) {\n    int t = lua_type(L,-1);\n\n    /* Limit the encoding of nested tables to a specfiied maximum depth, so that\n     * we survive when called against circular references in tables. */\n    if (t == LUA_TTABLE && level == LUACMSGPACK_MAX_NESTING) t = LUA_TNIL;\n    switch(t) {\n    case LUA_TSTRING: mp_encode_lua_string(L,buf); break;\n    case LUA_TBOOLEAN: mp_encode_lua_bool(L,buf); break;\n    case LUA_TNUMBER: mp_encode_lua_number(L,buf); break;\n    case LUA_TTABLE: mp_encode_lua_table(L,buf,level); break;\n    default: mp_encode_lua_null(L,buf); break;\n    }\n    lua_pop(L,1);\n}\n\nstatic int mp_pack(lua_State *L) {\n    mp_buf *buf = mp_buf_new();\n\n    mp_encode_lua_type(L,buf,0);\n    lua_pushlstring(L,(char*)buf->b,buf->len);\n    mp_buf_free(buf);\n    return 1;\n}\n\n/* --------------------------------- Decoding --------------------------------- */\n\nvoid mp_decode_to_lua_type(lua_State *L, mp_cur *c);\n\nvoid mp_decode_to_lua_array(lua_State *L, mp_cur *c, size_t len) {\n    int index = 1;\n\n    lua_newtable(L);\n    while(len--) {\n        lua_pushnumber(L,index++);\n        mp_decode_to_lua_type(L,c);\n        if (c->err) return;\n        lua_settable(L,-3);\n    }\n}\n\nvoid mp_decode_to_lua_hash(lua_State *L, mp_cur *c, size_t len) {\n    lua_newtable(L);\n    while(len--) {\n        mp_decode_to_lua_type(L,c); /* key */\n        if (c->err) return;\n        mp_decode_to_lua_type(L,c); /* value */\n        if (c->err) return;\n        lua_settable(L,-3);\n    }\n}\n\n/* Decode a Message Pack raw object pointed by the string cursor 'c' to\n * a Lua type, that is left as the only result on the stack. */\nvoid mp_decode_to_lua_type(lua_State *L, mp_cur *c) {\n    mp_cur_need(c,1);\n    switch(c->p[0]) {\n    case 0xcc:  /* uint 8 */\n        mp_cur_need(c,2);\n        lua_pushnumber(L,c->p[1]);\n        mp_cur_consume(c,2);\n        break;\n    case 0xd0:  /* int 8 */\n        mp_cur_need(c,2);\n        lua_pushnumber(L,(char)c->p[1]);\n        mp_cur_consume(c,2);\n        break;\n    case 0xcd:  /* uint 16 */\n        mp_cur_need(c,3);\n        lua_pushnumber(L,\n            (c->p[1] << 8) |\n             c->p[2]);\n        mp_cur_consume(c,3);\n        break;\n    case 0xd1:  /* int 16 */\n        mp_cur_need(c,3);\n        lua_pushnumber(L,(int16_t)\n            (c->p[1] << 8) |\n             c->p[2]);\n        mp_cur_consume(c,3);\n        break;\n    case 0xce:  /* uint 32 */\n        mp_cur_need(c,5);\n        lua_pushnumber(L,\n            ((uint32_t)c->p[1] << 24) |\n            ((uint32_t)c->p[2] << 16) |\n            ((uint32_t)c->p[3] << 8) |\n             (uint32_t)c->p[4]);\n        mp_cur_consume(c,5);\n        break;\n    case 0xd2:  /* int 32 */\n        mp_cur_need(c,5);\n        lua_pushnumber(L,\n            ((int32_t)c->p[1] << 24) |\n            ((int32_t)c->p[2] << 16) |\n            ((int32_t)c->p[3] << 8) |\n             (int32_t)c->p[4]);\n        mp_cur_consume(c,5);\n        break;\n    case 0xcf:  /* uint 64 */\n        mp_cur_need(c,9);\n        lua_pushnumber(L,\n            ((uint64_t)c->p[1] << 56) |\n            ((uint64_t)c->p[2] << 48) |\n            ((uint64_t)c->p[3] << 40) |\n            ((uint64_t)c->p[4] << 32) |\n            ((uint64_t)c->p[5] << 24) |\n            ((uint64_t)c->p[6] << 16) |\n            ((uint64_t)c->p[7] << 8) |\n             (uint64_t)c->p[8]);\n        mp_cur_consume(c,9);\n        break;\n    case 0xd3:  /* int 64 */\n        mp_cur_need(c,9);\n        lua_pushnumber(L,\n            ((int64_t)c->p[1] << 56) |\n            ((int64_t)c->p[2] << 48) |\n            ((int64_t)c->p[3] << 40) |\n            ((int64_t)c->p[4] << 32) |\n            ((int64_t)c->p[5] << 24) |\n            ((int64_t)c->p[6] << 16) |\n            ((int64_t)c->p[7] << 8) |\n             (int64_t)c->p[8]);\n        mp_cur_consume(c,9);\n        break;\n    case 0xc0:  /* nil */\n        lua_pushnil(L);\n        mp_cur_consume(c,1);\n        break;\n    case 0xc3:  /* true */\n        lua_pushboolean(L,1);\n        mp_cur_consume(c,1);\n        break;\n    case 0xc2:  /* false */\n        lua_pushboolean(L,0);\n        mp_cur_consume(c,1);\n        break;\n    case 0xca:  /* float */\n        mp_cur_need(c,5);\n        assert(sizeof(float) == 4);\n        {\n            float f;\n            memcpy(&f,c->p+1,4);\n            memrevifle(&f,4);\n            lua_pushnumber(L,f);\n            mp_cur_consume(c,5);\n        }\n        break;\n    case 0xcb:  /* double */\n        mp_cur_need(c,9);\n        assert(sizeof(double) == 8);\n        {\n            double d;\n            memcpy(&d,c->p+1,8);\n            memrevifle(&d,8);\n            lua_pushnumber(L,d);\n            mp_cur_consume(c,9);\n        }\n        break;\n    case 0xda:  /* raw 16 */\n        mp_cur_need(c,3);\n        {\n            size_t l = (c->p[1] << 8) | c->p[2];\n            mp_cur_need(c,3+l);\n            lua_pushlstring(L,(char*)c->p+3,l);\n            mp_cur_consume(c,3+l);\n        }\n        break;\n    case 0xdb:  /* raw 32 */\n        mp_cur_need(c,5);\n        {\n            size_t l = (c->p[1] << 24) |\n                       (c->p[2] << 16) |\n                       (c->p[3] << 8) |\n                       c->p[4];\n            mp_cur_need(c,5+l);\n            lua_pushlstring(L,(char*)c->p+5,l);\n            mp_cur_consume(c,5+l);\n        }\n        break;\n    case 0xdc:  /* array 16 */\n        mp_cur_need(c,3);\n        {\n            size_t l = (c->p[1] << 8) | c->p[2];\n            mp_cur_consume(c,3);\n            mp_decode_to_lua_array(L,c,l);\n        }\n        break;\n    case 0xdd:  /* array 32 */\n        mp_cur_need(c,5);\n        {\n            size_t l = (c->p[1] << 24) |\n                       (c->p[2] << 16) |\n                       (c->p[3] << 8) |\n                       c->p[4];\n            mp_cur_consume(c,5);\n            mp_decode_to_lua_array(L,c,l);\n        }\n        break;\n    case 0xde:  /* map 16 */\n        mp_cur_need(c,3);\n        {\n            size_t l = (c->p[1] << 8) | c->p[2];\n            mp_cur_consume(c,3);\n            mp_decode_to_lua_hash(L,c,l);\n        }\n        break;\n    case 0xdf:  /* map 32 */\n        mp_cur_need(c,5);\n        {\n            size_t l = (c->p[1] << 24) |\n                       (c->p[2] << 16) |\n                       (c->p[3] << 8) |\n                       c->p[4];\n            mp_cur_consume(c,5);\n            mp_decode_to_lua_hash(L,c,l);\n        }\n        break;\n    default:    /* types that can't be idenitified by first byte value. */\n        if ((c->p[0] & 0x80) == 0) {   /* positive fixnum */\n            lua_pushnumber(L,c->p[0]);\n            mp_cur_consume(c,1);\n        } else if ((c->p[0] & 0xe0) == 0xe0) {  /* negative fixnum */\n            lua_pushnumber(L,(signed char)c->p[0]);\n            mp_cur_consume(c,1);\n        } else if ((c->p[0] & 0xe0) == 0xa0) {  /* fix raw */\n            size_t l = c->p[0] & 0x1f;\n            mp_cur_need(c,1+l);\n            lua_pushlstring(L,(char*)c->p+1,l);\n            mp_cur_consume(c,1+l);\n        } else if ((c->p[0] & 0xf0) == 0x90) {  /* fix map */\n            size_t l = c->p[0] & 0xf;\n            mp_cur_consume(c,1);\n            mp_decode_to_lua_array(L,c,l);\n        } else if ((c->p[0] & 0xf0) == 0x80) {  /* fix map */\n            size_t l = c->p[0] & 0xf;\n            mp_cur_consume(c,1);\n            mp_decode_to_lua_hash(L,c,l);\n        } else {\n            c->err = MP_CUR_ERROR_BADFMT;\n        }\n    }\n}\n\nstatic int mp_unpack(lua_State *L) {\n    size_t len;\n    const unsigned char *s;\n    mp_cur *c;\n\n    if (!lua_isstring(L,-1)) {\n        lua_pushstring(L,\"MessagePack decoding needs a string as input.\");\n        lua_error(L);\n    }\n\n    s = (const unsigned char*) lua_tolstring(L,-1,&len);\n    c = mp_cur_new(s,len);\n    mp_decode_to_lua_type(L,c);\n    \n    if (c->err == MP_CUR_ERROR_EOF) {\n        mp_cur_free(c);\n        lua_pushstring(L,\"Missing bytes in input.\");\n        lua_error(L);\n    } else if (c->err == MP_CUR_ERROR_BADFMT) {\n        mp_cur_free(c);\n        lua_pushstring(L,\"Bad data format in input.\");\n        lua_error(L);\n    } else if (c->left != 0) {\n        mp_cur_free(c);\n        lua_pushstring(L,\"Extra bytes in input.\");\n        lua_error(L);\n    }\n    mp_cur_free(c);\n    return 1;\n}\n\n/* ---------------------------------------------------------------------------- */\n\nstatic const struct luaL_reg thislib[] = {\n    {\"pack\", mp_pack},\n    {\"unpack\", mp_unpack},\n    {NULL, NULL}\n};\n\nLUALIB_API int luaopen_cmsgpack (lua_State *L) {\n    luaL_register(L, \"cmsgpack\", thislib);\n\n    lua_pushliteral(L, LUACMSGPACK_VERSION);\n    lua_setfield(L, -2, \"_VERSION\");\n    lua_pushliteral(L, LUACMSGPACK_COPYRIGHT);\n    lua_setfield(L, -2, \"_COPYRIGHT\");\n    lua_pushliteral(L, LUACMSGPACK_DESCRIPTION);\n    lua_setfield(L, -2, \"_DESCRIPTION\"); \n    return 1;\n}\n\n/******************************************************************************\n* Copyright (C) 2012 Salvatore Sanfilippo.  All rights reserved.\n*\n* Permission is hereby granted, free of charge, to any person obtaining\n* a copy of this software and associated documentation files (the\n* \"Software\"), to deal in the Software without restriction, including\n* without limitation the rights to use, copy, modify, merge, publish,\n* distribute, sublicense, and/or sell copies of the Software, and to\n* permit persons to whom the Software is furnished to do so, subject to\n* the following conditions:\n*\n* The above copyright notice and this permission notice shall be\n* included in all copies or substantial portions of the Software.\n*\n* THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n******************************************************************************/\n"
  },
  {
    "path": "deps/lua/src/lua_struct.c",
    "content": "/*\n** {======================================================\n** Library for packing/unpacking structures.\n** $Id: struct.c,v 1.4 2012/07/04 18:54:29 roberto Exp $\n** See Copyright Notice at the end of this file\n** =======================================================\n*/\n/*\n** Valid formats:\n** > - big endian\n** < - little endian\n** ![num] - alignment\n** x - pading\n** b/B - signed/unsigned byte\n** h/H - signed/unsigned short\n** l/L - signed/unsigned long\n** T   - size_t\n** i/In - signed/unsigned integer with size `n' (default is size of int)\n** cn - sequence of `n' chars (from/to a string); when packing, n==0 means\n        the whole string; when unpacking, n==0 means use the previous\n        read number as the string length\n** s - zero-terminated string\n** f - float\n** d - double\n** ' ' - ignored\n*/\n\n\n#include <assert.h>\n#include <ctype.h>\n#include <limits.h>\n#include <stddef.h>\n#include <string.h>\n\n\n#include \"lua.h\"\n#include \"lauxlib.h\"\n\n\n#if (LUA_VERSION_NUM >= 502)\n\n#define luaL_register(L,n,f)\tluaL_newlib(L,f)\n\n#endif\n\n\n/* basic integer type */\n#if !defined(STRUCT_INT)\n#define STRUCT_INT\tlong\n#endif\n\ntypedef STRUCT_INT Inttype;\n\n/* corresponding unsigned version */\ntypedef unsigned STRUCT_INT Uinttype;\n\n\n/* maximum size (in bytes) for integral types */\n#define MAXINTSIZE\t32\n\n/* is 'x' a power of 2? */\n#define isp2(x)\t\t((x) > 0 && ((x) & ((x) - 1)) == 0)\n\n/* dummy structure to get alignment requirements */\nstruct cD {\n  char c;\n  double d;\n};\n\n\n#define PADDING\t\t(sizeof(struct cD) - sizeof(double))\n#define MAXALIGN  \t(PADDING > sizeof(int) ? PADDING : sizeof(int))\n\n\n/* endian options */\n#define BIG\t0\n#define LITTLE\t1\n\n\nstatic union {\n  int dummy;\n  char endian;\n} const native = {1};\n\n\ntypedef struct Header {\n  int endian;\n  int align;\n} Header;\n\n\nstatic int getnum (const char **fmt, int df) {\n  if (!isdigit(**fmt))  /* no number? */\n    return df;  /* return default value */\n  else {\n    int a = 0;\n    do {\n      a = a*10 + *((*fmt)++) - '0';\n    } while (isdigit(**fmt));\n    return a;\n  }\n}\n\n\n#define defaultoptions(h)\t((h)->endian = native.endian, (h)->align = 1)\n\n\n\nstatic size_t optsize (lua_State *L, char opt, const char **fmt) {\n  switch (opt) {\n    case 'B': case 'b': return sizeof(char);\n    case 'H': case 'h': return sizeof(short);\n    case 'L': case 'l': return sizeof(long);\n    case 'T': return sizeof(size_t);\n    case 'f':  return sizeof(float);\n    case 'd':  return sizeof(double);\n    case 'x': return 1;\n    case 'c': return getnum(fmt, 1);\n    case 'i': case 'I': {\n      int sz = getnum(fmt, sizeof(int));\n      if (sz > MAXINTSIZE)\n        luaL_error(L, \"integral size %d is larger than limit of %d\",\n                       sz, MAXINTSIZE);\n      return sz;\n    }\n    default: return 0;  /* other cases do not need alignment */\n  }\n}\n\n\n/*\n** return number of bytes needed to align an element of size 'size'\n** at current position 'len'\n*/\nstatic int gettoalign (size_t len, Header *h, int opt, size_t size) {\n  if (size == 0 || opt == 'c') return 0;\n  if (size > (size_t)h->align)\n    size = h->align;  /* respect max. alignment */\n  return (size - (len & (size - 1))) & (size - 1);\n}\n\n\n/*\n** options to control endianess and alignment\n*/\nstatic void controloptions (lua_State *L, int opt, const char **fmt,\n                            Header *h) {\n  switch (opt) {\n    case  ' ': return;  /* ignore white spaces */\n    case '>': h->endian = BIG; return;\n    case '<': h->endian = LITTLE; return;\n    case '!': {\n      int a = getnum(fmt, MAXALIGN);\n      if (!isp2(a))\n        luaL_error(L, \"alignment %d is not a power of 2\", a);\n      h->align = a;\n      return;\n    }\n    default: {\n      const char *msg = lua_pushfstring(L, \"invalid format option '%c'\", opt);\n      luaL_argerror(L, 1, msg);\n    }\n  }\n}\n\n\nstatic void putinteger (lua_State *L, luaL_Buffer *b, int arg, int endian,\n                        int size) {\n  lua_Number n = luaL_checknumber(L, arg);\n  Uinttype value;\n  char buff[MAXINTSIZE];\n  if (n < 0)\n    value = (Uinttype)(Inttype)n;\n  else\n    value = (Uinttype)n;\n  if (endian == LITTLE) {\n    int i;\n    for (i = 0; i < size; i++) {\n      buff[i] = (value & 0xff);\n      value >>= 8;\n    }\n  }\n  else {\n    int i;\n    for (i = size - 1; i >= 0; i--) {\n      buff[i] = (value & 0xff);\n      value >>= 8;\n    }\n  }\n  luaL_addlstring(b, buff, size);\n}\n\n\nstatic void correctbytes (char *b, int size, int endian) {\n  if (endian != native.endian) {\n    int i = 0;\n    while (i < --size) {\n      char temp = b[i];\n      b[i++] = b[size];\n      b[size] = temp;\n    }\n  }\n}\n\n\nstatic int b_pack (lua_State *L) {\n  luaL_Buffer b;\n  const char *fmt = luaL_checkstring(L, 1);\n  Header h;\n  int arg = 2;\n  size_t totalsize = 0;\n  defaultoptions(&h);\n  lua_pushnil(L);  /* mark to separate arguments from string buffer */\n  luaL_buffinit(L, &b);\n  while (*fmt != '\\0') {\n    int opt = *fmt++;\n    size_t size = optsize(L, opt, &fmt);\n    int toalign = gettoalign(totalsize, &h, opt, size);\n    totalsize += toalign;\n    while (toalign-- > 0) luaL_addchar(&b, '\\0');\n    switch (opt) {\n      case 'b': case 'B': case 'h': case 'H':\n      case 'l': case 'L': case 'T': case 'i': case 'I': {  /* integer types */\n        putinteger(L, &b, arg++, h.endian, size);\n        break;\n      }\n      case 'x': {\n        luaL_addchar(&b, '\\0');\n        break;\n      }\n      case 'f': {\n        float f = (float)luaL_checknumber(L, arg++);\n        correctbytes((char *)&f, size, h.endian);\n        luaL_addlstring(&b, (char *)&f, size);\n        break;\n      }\n      case 'd': {\n        double d = luaL_checknumber(L, arg++);\n        correctbytes((char *)&d, size, h.endian);\n        luaL_addlstring(&b, (char *)&d, size);\n        break;\n      }\n      case 'c': case 's': {\n        size_t l;\n        const char *s = luaL_checklstring(L, arg++, &l);\n        if (size == 0) size = l;\n        luaL_argcheck(L, l >= (size_t)size, arg, \"string too short\");\n        luaL_addlstring(&b, s, size);\n        if (opt == 's') {\n          luaL_addchar(&b, '\\0');  /* add zero at the end */\n          size++;\n        }\n        break;\n      }\n      default: controloptions(L, opt, &fmt, &h);\n    }\n    totalsize += size;\n  }\n  luaL_pushresult(&b);\n  return 1;\n}\n\n\nstatic lua_Number getinteger (const char *buff, int endian,\n                        int issigned, int size) {\n  Uinttype l = 0;\n  int i;\n  if (endian == BIG) {\n    for (i = 0; i < size; i++) {\n      l <<= 8;\n      l |= (Uinttype)(unsigned char)buff[i];\n    }\n  }\n  else {\n    for (i = size - 1; i >= 0; i--) {\n      l <<= 8;\n      l |= (Uinttype)(unsigned char)buff[i];\n    }\n  }\n  if (!issigned)\n    return (lua_Number)l;\n  else {  /* signed format */\n    Uinttype mask = (Uinttype)(~((Uinttype)0)) << (size*8 - 1);\n    if (l & mask)  /* negative value? */\n      l |= mask;  /* signal extension */\n    return (lua_Number)(Inttype)l;\n  }\n}\n\n\nstatic int b_unpack (lua_State *L) {\n  Header h;\n  const char *fmt = luaL_checkstring(L, 1);\n  size_t ld;\n  const char *data = luaL_checklstring(L, 2, &ld);\n  size_t pos = luaL_optinteger(L, 3, 1) - 1;\n  defaultoptions(&h);\n  lua_settop(L, 2);\n  while (*fmt) {\n    int opt = *fmt++;\n    size_t size = optsize(L, opt, &fmt);\n    pos += gettoalign(pos, &h, opt, size);\n    luaL_argcheck(L, pos+size <= ld, 2, \"data string too short\");\n    luaL_checkstack(L, 1, \"too many results\");\n    switch (opt) {\n      case 'b': case 'B': case 'h': case 'H':\n      case 'l': case 'L': case 'T': case 'i':  case 'I': {  /* integer types */\n        int issigned = islower(opt);\n        lua_Number res = getinteger(data+pos, h.endian, issigned, size);\n        lua_pushnumber(L, res);\n        break;\n      }\n      case 'x': {\n        break;\n      }\n      case 'f': {\n        float f;\n        memcpy(&f, data+pos, size);\n        correctbytes((char *)&f, sizeof(f), h.endian);\n        lua_pushnumber(L, f);\n        break;\n      }\n      case 'd': {\n        double d;\n        memcpy(&d, data+pos, size);\n        correctbytes((char *)&d, sizeof(d), h.endian);\n        lua_pushnumber(L, d);\n        break;\n      }\n      case 'c': {\n        if (size == 0) {\n          if (!lua_isnumber(L, -1))\n            luaL_error(L, \"format `c0' needs a previous size\");\n          size = lua_tonumber(L, -1);\n          lua_pop(L, 1);\n          luaL_argcheck(L, pos+size <= ld, 2, \"data string too short\");\n        }\n        lua_pushlstring(L, data+pos, size);\n        break;\n      }\n      case 's': {\n        const char *e = (const char *)memchr(data+pos, '\\0', ld - pos);\n        if (e == NULL)\n          luaL_error(L, \"unfinished string in data\");\n        size = (e - (data+pos)) + 1;\n        lua_pushlstring(L, data+pos, size - 1);\n        break;\n      }\n      default: controloptions(L, opt, &fmt, &h);\n    }\n    pos += size;\n  }\n  lua_pushinteger(L, pos + 1);\n  return lua_gettop(L) - 2;\n}\n\n\nstatic int b_size (lua_State *L) {\n  Header h;\n  const char *fmt = luaL_checkstring(L, 1);\n  size_t pos = 0;\n  defaultoptions(&h);\n  while (*fmt) {\n    int opt = *fmt++;\n    size_t size = optsize(L, opt, &fmt);\n    pos += gettoalign(pos, &h, opt, size);\n    if (opt == 's')\n      luaL_argerror(L, 1, \"option 's' has no fixed size\");\n    else if (opt == 'c' && size == 0)\n      luaL_argerror(L, 1, \"option 'c0' has no fixed size\");\n    if (!isalnum(opt))\n      controloptions(L, opt, &fmt, &h);\n    pos += size;\n  }\n  lua_pushinteger(L, pos);\n  return 1;\n}\n\n/* }====================================================== */\n\n\n\nstatic const struct luaL_Reg thislib[] = {\n  {\"pack\", b_pack},\n  {\"unpack\", b_unpack},\n  {\"size\", b_size},\n  {NULL, NULL}\n};\n\n\nLUALIB_API int luaopen_struct (lua_State *L);\n\nLUALIB_API int luaopen_struct (lua_State *L) {\n  luaL_register(L, \"struct\", thislib);\n  return 1;\n}\n\n\n/******************************************************************************\n* Copyright (C) 2010-2012 Lua.org, PUC-Rio.  All rights reserved.\n*\n* Permission is hereby granted, free of charge, to any person obtaining\n* a copy of this software and associated documentation files (the\n* \"Software\"), to deal in the Software without restriction, including\n* without limitation the rights to use, copy, modify, merge, publish,\n* distribute, sublicense, and/or sell copies of the Software, and to\n* permit persons to whom the Software is furnished to do so, subject to\n* the following conditions:\n*\n* The above copyright notice and this permission notice shall be\n* included in all copies or substantial portions of the Software.\n*\n* THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n******************************************************************************/\n\n"
  },
  {
    "path": "deps/lua/src/luac.c",
    "content": "/*\n** $Id: luac.c,v 1.54 2006/06/02 17:37:11 lhf Exp $\n** Lua compiler (saves bytecodes to files; also list bytecodes)\n** See Copyright Notice in lua.h\n*/\n\n#include <errno.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#define luac_c\n#define LUA_CORE\n\n#include \"lua.h\"\n#include \"lauxlib.h\"\n\n#include \"ldo.h\"\n#include \"lfunc.h\"\n#include \"lmem.h\"\n#include \"lobject.h\"\n#include \"lopcodes.h\"\n#include \"lstring.h\"\n#include \"lundump.h\"\n\n#define PROGNAME\t\"luac\"\t\t/* default program name */\n#define\tOUTPUT\t\tPROGNAME \".out\"\t/* default output file */\n\nstatic int listing=0;\t\t\t/* list bytecodes? */\nstatic int dumping=1;\t\t\t/* dump bytecodes? */\nstatic int stripping=0;\t\t\t/* strip debug information? */\nstatic char Output[]={ OUTPUT };\t/* default output file name */\nstatic const char* output=Output;\t/* actual output file name */\nstatic const char* progname=PROGNAME;\t/* actual program name */\n\nstatic void fatal(const char* message)\n{\n fprintf(stderr,\"%s: %s\\n\",progname,message);\n exit(EXIT_FAILURE);\n}\n\nstatic void cannot(const char* what)\n{\n fprintf(stderr,\"%s: cannot %s %s: %s\\n\",progname,what,output,strerror(errno));\n exit(EXIT_FAILURE);\n}\n\nstatic void usage(const char* message)\n{\n if (*message=='-')\n  fprintf(stderr,\"%s: unrecognized option \" LUA_QS \"\\n\",progname,message);\n else\n  fprintf(stderr,\"%s: %s\\n\",progname,message);\n fprintf(stderr,\n \"usage: %s [options] [filenames].\\n\"\n \"Available options are:\\n\"\n \"  -        process stdin\\n\"\n \"  -l       list\\n\"\n \"  -o name  output to file \" LUA_QL(\"name\") \" (default is \\\"%s\\\")\\n\"\n \"  -p       parse only\\n\"\n \"  -s       strip debug information\\n\"\n \"  -v       show version information\\n\"\n \"  --       stop handling options\\n\",\n progname,Output);\n exit(EXIT_FAILURE);\n}\n\n#define\tIS(s)\t(strcmp(argv[i],s)==0)\n\nstatic int doargs(int argc, char* argv[])\n{\n int i;\n int version=0;\n if (argv[0]!=NULL && *argv[0]!=0) progname=argv[0];\n for (i=1; i<argc; i++)\n {\n  if (*argv[i]!='-')\t\t\t/* end of options; keep it */\n   break;\n  else if (IS(\"--\"))\t\t\t/* end of options; skip it */\n  {\n   ++i;\n   if (version) ++version;\n   break;\n  }\n  else if (IS(\"-\"))\t\t\t/* end of options; use stdin */\n   break;\n  else if (IS(\"-l\"))\t\t\t/* list */\n   ++listing;\n  else if (IS(\"-o\"))\t\t\t/* output file */\n  {\n   output=argv[++i];\n   if (output==NULL || *output==0) usage(LUA_QL(\"-o\") \" needs argument\");\n   if (IS(\"-\")) output=NULL;\n  }\n  else if (IS(\"-p\"))\t\t\t/* parse only */\n   dumping=0;\n  else if (IS(\"-s\"))\t\t\t/* strip debug information */\n   stripping=1;\n  else if (IS(\"-v\"))\t\t\t/* show version */\n   ++version;\n  else\t\t\t\t\t/* unknown option */\n   usage(argv[i]);\n }\n if (i==argc && (listing || !dumping))\n {\n  dumping=0;\n  argv[--i]=Output;\n }\n if (version)\n {\n  printf(\"%s  %s\\n\",LUA_RELEASE,LUA_COPYRIGHT);\n  if (version==argc-1) exit(EXIT_SUCCESS);\n }\n return i;\n}\n\n#define toproto(L,i) (clvalue(L->top+(i))->l.p)\n\nstatic const Proto* combine(lua_State* L, int n)\n{\n if (n==1)\n  return toproto(L,-1);\n else\n {\n  int i,pc;\n  Proto* f=luaF_newproto(L);\n  setptvalue2s(L,L->top,f); incr_top(L);\n  f->source=luaS_newliteral(L,\"=(\" PROGNAME \")\");\n  f->maxstacksize=1;\n  pc=2*n+1;\n  f->code=luaM_newvector(L,pc,Instruction);\n  f->sizecode=pc;\n  f->p=luaM_newvector(L,n,Proto*);\n  f->sizep=n;\n  pc=0;\n  for (i=0; i<n; i++)\n  {\n   f->p[i]=toproto(L,i-n-1);\n   f->code[pc++]=CREATE_ABx(OP_CLOSURE,0,i);\n   f->code[pc++]=CREATE_ABC(OP_CALL,0,1,1);\n  }\n  f->code[pc++]=CREATE_ABC(OP_RETURN,0,1,0);\n  return f;\n }\n}\n\nstatic int writer(lua_State* L, const void* p, size_t size, void* u)\n{\n UNUSED(L);\n return (fwrite(p,size,1,(FILE*)u)!=1) && (size!=0);\n}\n\nstruct Smain {\n int argc;\n char** argv;\n};\n\nstatic int pmain(lua_State* L)\n{\n struct Smain* s = (struct Smain*)lua_touserdata(L, 1);\n int argc=s->argc;\n char** argv=s->argv;\n const Proto* f;\n int i;\n if (!lua_checkstack(L,argc)) fatal(\"too many input files\");\n for (i=0; i<argc; i++)\n {\n  const char* filename=IS(\"-\") ? NULL : argv[i];\n  if (luaL_loadfile(L,filename)!=0) fatal(lua_tostring(L,-1));\n }\n f=combine(L,argc);\n if (listing) luaU_print(f,listing>1);\n if (dumping)\n {\n  FILE* D= (output==NULL) ? stdout : fopen(output,\"wb\");\n  if (D==NULL) cannot(\"open\");\n  lua_lock(L);\n  luaU_dump(L,f,writer,D,stripping);\n  lua_unlock(L);\n  if (ferror(D)) cannot(\"write\");\n  if (fclose(D)) cannot(\"close\");\n }\n return 0;\n}\n\nint main(int argc, char* argv[])\n{\n lua_State* L;\n struct Smain s;\n int i=doargs(argc,argv);\n argc-=i; argv+=i;\n if (argc<=0) usage(\"no input files given\");\n L=lua_open();\n if (L==NULL) fatal(\"not enough memory for state\");\n s.argc=argc;\n s.argv=argv;\n if (lua_cpcall(L,pmain,&s)!=0) fatal(lua_tostring(L,-1));\n lua_close(L);\n return EXIT_SUCCESS;\n}\n"
  },
  {
    "path": "deps/lua/src/luaconf.h",
    "content": "/*\n** $Id: luaconf.h,v 1.82.1.7 2008/02/11 16:25:08 roberto Exp $\n** Configuration file for Lua\n** See Copyright Notice in lua.h\n*/\n\n\n#ifndef lconfig_h\n#define lconfig_h\n\n#include <limits.h>\n#include <stddef.h>\n\n\n/*\n** ==================================================================\n** Search for \"@@\" to find all configurable definitions.\n** ===================================================================\n*/\n\n\n/*\n@@ LUA_ANSI controls the use of non-ansi features.\n** CHANGE it (define it) if you want Lua to avoid the use of any\n** non-ansi feature or library.\n*/\n#if defined(__STRICT_ANSI__)\n#define LUA_ANSI\n#endif\n\n\n#if !defined(LUA_ANSI) && defined(_WIN32)\n#define LUA_WIN\n#endif\n\n#if defined(LUA_USE_LINUX)\n#define LUA_USE_POSIX\n#define LUA_USE_DLOPEN\t\t/* needs an extra library: -ldl */\n#define LUA_USE_READLINE\t/* needs some extra libraries */\n#endif\n\n#if defined(LUA_USE_MACOSX)\n#define LUA_USE_POSIX\n#define LUA_DL_DYLD\t\t/* does not need extra library */\n#endif\n\n\n\n/*\n@@ LUA_USE_POSIX includes all functionallity listed as X/Open System\n@* Interfaces Extension (XSI).\n** CHANGE it (define it) if your system is XSI compatible.\n*/\n#if defined(LUA_USE_POSIX)\n#define LUA_USE_MKSTEMP\n#define LUA_USE_ISATTY\n#define LUA_USE_POPEN\n#define LUA_USE_ULONGJMP\n#endif\n\n\n/*\n@@ LUA_PATH and LUA_CPATH are the names of the environment variables that\n@* Lua check to set its paths.\n@@ LUA_INIT is the name of the environment variable that Lua\n@* checks for initialization code.\n** CHANGE them if you want different names.\n*/\n#define LUA_PATH        \"LUA_PATH\"\n#define LUA_CPATH       \"LUA_CPATH\"\n#define LUA_INIT\t\"LUA_INIT\"\n\n\n/*\n@@ LUA_PATH_DEFAULT is the default path that Lua uses to look for\n@* Lua libraries.\n@@ LUA_CPATH_DEFAULT is the default path that Lua uses to look for\n@* C libraries.\n** CHANGE them if your machine has a non-conventional directory\n** hierarchy or if you want to install your libraries in\n** non-conventional directories.\n*/\n#if defined(_WIN32)\n/*\n** In Windows, any exclamation mark ('!') in the path is replaced by the\n** path of the directory of the executable file of the current process.\n*/\n#define LUA_LDIR\t\"!\\\\lua\\\\\"\n#define LUA_CDIR\t\"!\\\\\"\n#define LUA_PATH_DEFAULT  \\\n\t\t\".\\\\?.lua;\"  LUA_LDIR\"?.lua;\"  LUA_LDIR\"?\\\\init.lua;\" \\\n\t\t             LUA_CDIR\"?.lua;\"  LUA_CDIR\"?\\\\init.lua\"\n#define LUA_CPATH_DEFAULT \\\n\t\".\\\\?.dll;\"  LUA_CDIR\"?.dll;\" LUA_CDIR\"loadall.dll\"\n\n#else\n#define LUA_ROOT\t\"/usr/local/\"\n#define LUA_LDIR\tLUA_ROOT \"share/lua/5.1/\"\n#define LUA_CDIR\tLUA_ROOT \"lib/lua/5.1/\"\n#define LUA_PATH_DEFAULT  \\\n\t\t\"./?.lua;\"  LUA_LDIR\"?.lua;\"  LUA_LDIR\"?/init.lua;\" \\\n\t\t            LUA_CDIR\"?.lua;\"  LUA_CDIR\"?/init.lua\"\n#define LUA_CPATH_DEFAULT \\\n\t\"./?.so;\"  LUA_CDIR\"?.so;\" LUA_CDIR\"loadall.so\"\n#endif\n\n\n/*\n@@ LUA_DIRSEP is the directory separator (for submodules).\n** CHANGE it if your machine does not use \"/\" as the directory separator\n** and is not Windows. (On Windows Lua automatically uses \"\\\".)\n*/\n#if defined(_WIN32)\n#define LUA_DIRSEP\t\"\\\\\"\n#else\n#define LUA_DIRSEP\t\"/\"\n#endif\n\n\n/*\n@@ LUA_PATHSEP is the character that separates templates in a path.\n@@ LUA_PATH_MARK is the string that marks the substitution points in a\n@* template.\n@@ LUA_EXECDIR in a Windows path is replaced by the executable's\n@* directory.\n@@ LUA_IGMARK is a mark to ignore all before it when bulding the\n@* luaopen_ function name.\n** CHANGE them if for some reason your system cannot use those\n** characters. (E.g., if one of those characters is a common character\n** in file/directory names.) Probably you do not need to change them.\n*/\n#define LUA_PATHSEP\t\";\"\n#define LUA_PATH_MARK\t\"?\"\n#define LUA_EXECDIR\t\"!\"\n#define LUA_IGMARK\t\"-\"\n\n\n/*\n@@ LUA_INTEGER is the integral type used by lua_pushinteger/lua_tointeger.\n** CHANGE that if ptrdiff_t is not adequate on your machine. (On most\n** machines, ptrdiff_t gives a good choice between int or long.)\n*/\n#define LUA_INTEGER\tptrdiff_t\n\n\n/*\n@@ LUA_API is a mark for all core API functions.\n@@ LUALIB_API is a mark for all standard library functions.\n** CHANGE them if you need to define those functions in some special way.\n** For instance, if you want to create one Windows DLL with the core and\n** the libraries, you may want to use the following definition (define\n** LUA_BUILD_AS_DLL to get it).\n*/\n#if defined(LUA_BUILD_AS_DLL)\n\n#if defined(LUA_CORE) || defined(LUA_LIB)\n#define LUA_API __declspec(dllexport)\n#else\n#define LUA_API __declspec(dllimport)\n#endif\n\n#else\n\n#define LUA_API\t\textern\n\n#endif\n\n/* more often than not the libs go together with the core */\n#define LUALIB_API\tLUA_API\n\n\n/*\n@@ LUAI_FUNC is a mark for all extern functions that are not to be\n@* exported to outside modules.\n@@ LUAI_DATA is a mark for all extern (const) variables that are not to\n@* be exported to outside modules.\n** CHANGE them if you need to mark them in some special way. Elf/gcc\n** (versions 3.2 and later) mark them as \"hidden\" to optimize access\n** when Lua is compiled as a shared library.\n*/\n#if defined(luaall_c)\n#define LUAI_FUNC\tstatic\n#define LUAI_DATA\t/* empty */\n\n#elif defined(__GNUC__) && ((__GNUC__*100 + __GNUC_MINOR__) >= 302) && \\\n      defined(__ELF__)\n#define LUAI_FUNC\t__attribute__((visibility(\"hidden\"))) extern\n#define LUAI_DATA\tLUAI_FUNC\n\n#else\n#define LUAI_FUNC\textern\n#define LUAI_DATA\textern\n#endif\n\n\n\n/*\n@@ LUA_QL describes how error messages quote program elements.\n** CHANGE it if you want a different appearance.\n*/\n#define LUA_QL(x)\t\"'\" x \"'\"\n#define LUA_QS\t\tLUA_QL(\"%s\")\n\n\n/*\n@@ LUA_IDSIZE gives the maximum size for the description of the source\n@* of a function in debug information.\n** CHANGE it if you want a different size.\n*/\n#define LUA_IDSIZE\t60\n\n\n/*\n** {==================================================================\n** Stand-alone configuration\n** ===================================================================\n*/\n\n#if defined(lua_c) || defined(luaall_c)\n\n/*\n@@ lua_stdin_is_tty detects whether the standard input is a 'tty' (that\n@* is, whether we're running lua interactively).\n** CHANGE it if you have a better definition for non-POSIX/non-Windows\n** systems.\n*/\n#if defined(LUA_USE_ISATTY)\n#include <unistd.h>\n#define lua_stdin_is_tty()\tisatty(0)\n#elif defined(LUA_WIN)\n#include <io.h>\n#include <stdio.h>\n#define lua_stdin_is_tty()\t_isatty(_fileno(stdin))\n#else\n#define lua_stdin_is_tty()\t1  /* assume stdin is a tty */\n#endif\n\n\n/*\n@@ LUA_PROMPT is the default prompt used by stand-alone Lua.\n@@ LUA_PROMPT2 is the default continuation prompt used by stand-alone Lua.\n** CHANGE them if you want different prompts. (You can also change the\n** prompts dynamically, assigning to globals _PROMPT/_PROMPT2.)\n*/\n#define LUA_PROMPT\t\t\"> \"\n#define LUA_PROMPT2\t\t\">> \"\n\n\n/*\n@@ LUA_PROGNAME is the default name for the stand-alone Lua program.\n** CHANGE it if your stand-alone interpreter has a different name and\n** your system is not able to detect that name automatically.\n*/\n#define LUA_PROGNAME\t\t\"lua\"\n\n\n/*\n@@ LUA_MAXINPUT is the maximum length for an input line in the\n@* stand-alone interpreter.\n** CHANGE it if you need longer lines.\n*/\n#define LUA_MAXINPUT\t512\n\n\n/*\n@@ lua_readline defines how to show a prompt and then read a line from\n@* the standard input.\n@@ lua_saveline defines how to \"save\" a read line in a \"history\".\n@@ lua_freeline defines how to free a line read by lua_readline.\n** CHANGE them if you want to improve this functionality (e.g., by using\n** GNU readline and history facilities).\n*/\n#if defined(LUA_USE_READLINE)\n#include <stdio.h>\n#include <readline/readline.h>\n#include <readline/history.h>\n#define lua_readline(L,b,p)\t((void)L, ((b)=readline(p)) != NULL)\n#define lua_saveline(L,idx) \\\n\tif (lua_strlen(L,idx) > 0)  /* non-empty line? */ \\\n\t  add_history(lua_tostring(L, idx));  /* add it to history */\n#define lua_freeline(L,b)\t((void)L, free(b))\n#else\n#define lua_readline(L,b,p)\t\\\n\t((void)L, fputs(p, stdout), fflush(stdout),  /* show prompt */ \\\n\tfgets(b, LUA_MAXINPUT, stdin) != NULL)  /* get line */\n#define lua_saveline(L,idx)\t{ (void)L; (void)idx; }\n#define lua_freeline(L,b)\t{ (void)L; (void)b; }\n#endif\n\n#endif\n\n/* }================================================================== */\n\n\n/*\n@@ LUAI_GCPAUSE defines the default pause between garbage-collector cycles\n@* as a percentage.\n** CHANGE it if you want the GC to run faster or slower (higher values\n** mean larger pauses which mean slower collection.) You can also change\n** this value dynamically.\n*/\n#define LUAI_GCPAUSE\t200  /* 200% (wait memory to double before next GC) */\n\n\n/*\n@@ LUAI_GCMUL defines the default speed of garbage collection relative to\n@* memory allocation as a percentage.\n** CHANGE it if you want to change the granularity of the garbage\n** collection. (Higher values mean coarser collections. 0 represents\n** infinity, where each step performs a full collection.) You can also\n** change this value dynamically.\n*/\n#define LUAI_GCMUL\t200 /* GC runs 'twice the speed' of memory allocation */\n\n\n\n/*\n@@ LUA_COMPAT_GETN controls compatibility with old getn behavior.\n** CHANGE it (define it) if you want exact compatibility with the\n** behavior of setn/getn in Lua 5.0.\n*/\n#undef LUA_COMPAT_GETN\n\n/*\n@@ LUA_COMPAT_LOADLIB controls compatibility about global loadlib.\n** CHANGE it to undefined as soon as you do not need a global 'loadlib'\n** function (the function is still available as 'package.loadlib').\n*/\n#undef LUA_COMPAT_LOADLIB\n\n/*\n@@ LUA_COMPAT_VARARG controls compatibility with old vararg feature.\n** CHANGE it to undefined as soon as your programs use only '...' to\n** access vararg parameters (instead of the old 'arg' table).\n*/\n#define LUA_COMPAT_VARARG\n\n/*\n@@ LUA_COMPAT_MOD controls compatibility with old math.mod function.\n** CHANGE it to undefined as soon as your programs use 'math.fmod' or\n** the new '%' operator instead of 'math.mod'.\n*/\n#define LUA_COMPAT_MOD\n\n/*\n@@ LUA_COMPAT_LSTR controls compatibility with old long string nesting\n@* facility.\n** CHANGE it to 2 if you want the old behaviour, or undefine it to turn\n** off the advisory error when nesting [[...]].\n*/\n#define LUA_COMPAT_LSTR\t\t1\n\n/*\n@@ LUA_COMPAT_GFIND controls compatibility with old 'string.gfind' name.\n** CHANGE it to undefined as soon as you rename 'string.gfind' to\n** 'string.gmatch'.\n*/\n#define LUA_COMPAT_GFIND\n\n/*\n@@ LUA_COMPAT_OPENLIB controls compatibility with old 'luaL_openlib'\n@* behavior.\n** CHANGE it to undefined as soon as you replace to 'luaL_register'\n** your uses of 'luaL_openlib'\n*/\n#define LUA_COMPAT_OPENLIB\n\n\n\n/*\n@@ luai_apicheck is the assert macro used by the Lua-C API.\n** CHANGE luai_apicheck if you want Lua to perform some checks in the\n** parameters it gets from API calls. This may slow down the interpreter\n** a bit, but may be quite useful when debugging C code that interfaces\n** with Lua. A useful redefinition is to use assert.h.\n*/\n#if defined(LUA_USE_APICHECK)\n#include <assert.h>\n#define luai_apicheck(L,o)\t{ (void)L; assert(o); }\n#else\n#define luai_apicheck(L,o)\t{ (void)L; }\n#endif\n\n\n/*\n@@ LUAI_BITSINT defines the number of bits in an int.\n** CHANGE here if Lua cannot automatically detect the number of bits of\n** your machine. Probably you do not need to change this.\n*/\n/* avoid overflows in comparison */\n#if INT_MAX-20 < 32760\n#define LUAI_BITSINT\t16\n#elif INT_MAX > 2147483640L\n/* int has at least 32 bits */\n#define LUAI_BITSINT\t32\n#else\n#error \"you must define LUA_BITSINT with number of bits in an integer\"\n#endif\n\n\n/*\n@@ LUAI_UINT32 is an unsigned integer with at least 32 bits.\n@@ LUAI_INT32 is an signed integer with at least 32 bits.\n@@ LUAI_UMEM is an unsigned integer big enough to count the total\n@* memory used by Lua.\n@@ LUAI_MEM is a signed integer big enough to count the total memory\n@* used by Lua.\n** CHANGE here if for some weird reason the default definitions are not\n** good enough for your machine. (The definitions in the 'else'\n** part always works, but may waste space on machines with 64-bit\n** longs.) Probably you do not need to change this.\n*/\n#if LUAI_BITSINT >= 32\n#define LUAI_UINT32\tunsigned int\n#define LUAI_INT32\tint\n#define LUAI_MAXINT32\tINT_MAX\n#define LUAI_UMEM\tsize_t\n#define LUAI_MEM\tptrdiff_t\n#else\n/* 16-bit ints */\n#define LUAI_UINT32\tunsigned long\n#define LUAI_INT32\tlong\n#define LUAI_MAXINT32\tLONG_MAX\n#define LUAI_UMEM\tunsigned long\n#define LUAI_MEM\tlong\n#endif\n\n\n/*\n@@ LUAI_MAXCALLS limits the number of nested calls.\n** CHANGE it if you need really deep recursive calls. This limit is\n** arbitrary; its only purpose is to stop infinite recursion before\n** exhausting memory.\n*/\n#define LUAI_MAXCALLS\t20000\n\n\n/*\n@@ LUAI_MAXCSTACK limits the number of Lua stack slots that a C function\n@* can use.\n** CHANGE it if you need lots of (Lua) stack space for your C\n** functions. This limit is arbitrary; its only purpose is to stop C\n** functions to consume unlimited stack space. (must be smaller than\n** -LUA_REGISTRYINDEX)\n*/\n#define LUAI_MAXCSTACK\t8000\n\n\n\n/*\n** {==================================================================\n** CHANGE (to smaller values) the following definitions if your system\n** has a small C stack. (Or you may want to change them to larger\n** values if your system has a large C stack and these limits are\n** too rigid for you.) Some of these constants control the size of\n** stack-allocated arrays used by the compiler or the interpreter, while\n** others limit the maximum number of recursive calls that the compiler\n** or the interpreter can perform. Values too large may cause a C stack\n** overflow for some forms of deep constructs.\n** ===================================================================\n*/\n\n\n/*\n@@ LUAI_MAXCCALLS is the maximum depth for nested C calls (short) and\n@* syntactical nested non-terminals in a program.\n*/\n#define LUAI_MAXCCALLS\t\t200\n\n\n/*\n@@ LUAI_MAXVARS is the maximum number of local variables per function\n@* (must be smaller than 250).\n*/\n#define LUAI_MAXVARS\t\t200\n\n\n/*\n@@ LUAI_MAXUPVALUES is the maximum number of upvalues per function\n@* (must be smaller than 250).\n*/\n#define LUAI_MAXUPVALUES\t60\n\n\n/*\n@@ LUAL_BUFFERSIZE is the buffer size used by the lauxlib buffer system.\n*/\n#define LUAL_BUFFERSIZE\t\tBUFSIZ\n\n/* }================================================================== */\n\n\n\n\n/*\n** {==================================================================\n@@ LUA_NUMBER is the type of numbers in Lua.\n** CHANGE the following definitions only if you want to build Lua\n** with a number type different from double. You may also need to\n** change lua_number2int & lua_number2integer.\n** ===================================================================\n*/\n\n#define LUA_NUMBER_DOUBLE\n#define LUA_NUMBER\tdouble\n\n/*\n@@ LUAI_UACNUMBER is the result of an 'usual argument conversion'\n@* over a number.\n*/\n#define LUAI_UACNUMBER\tdouble\n\n\n/*\n@@ LUA_NUMBER_SCAN is the format for reading numbers.\n@@ LUA_NUMBER_FMT is the format for writing numbers.\n@@ lua_number2str converts a number to a string.\n@@ LUAI_MAXNUMBER2STR is maximum size of previous conversion.\n@@ lua_str2number converts a string to a number.\n*/\n#define LUA_NUMBER_SCAN\t\t\"%lf\"\n#define LUA_NUMBER_FMT\t\t\"%.14g\"\n#define lua_number2str(s,n)\tsprintf((s), LUA_NUMBER_FMT, (n))\n#define LUAI_MAXNUMBER2STR\t32 /* 16 digits, sign, point, and \\0 */\n#define lua_str2number(s,p)\tstrtod((s), (p))\n\n\n/*\n@@ The luai_num* macros define the primitive operations over numbers.\n*/\n#if defined(LUA_CORE)\n#include <math.h>\n#define luai_numadd(a,b)\t((a)+(b))\n#define luai_numsub(a,b)\t((a)-(b))\n#define luai_nummul(a,b)\t((a)*(b))\n#define luai_numdiv(a,b)\t((a)/(b))\n#define luai_nummod(a,b)\t((a) - floor((a)/(b))*(b))\n#define luai_numpow(a,b)\t(pow(a,b))\n#define luai_numunm(a)\t\t(-(a))\n#define luai_numeq(a,b)\t\t((a)==(b))\n#define luai_numlt(a,b)\t\t((a)<(b))\n#define luai_numle(a,b)\t\t((a)<=(b))\n#define luai_numisnan(a)\t(!luai_numeq((a), (a)))\n#endif\n\n\n/*\n@@ lua_number2int is a macro to convert lua_Number to int.\n@@ lua_number2integer is a macro to convert lua_Number to lua_Integer.\n** CHANGE them if you know a faster way to convert a lua_Number to\n** int (with any rounding method and without throwing errors) in your\n** system. In Pentium machines, a naive typecast from double to int\n** in C is extremely slow, so any alternative is worth trying.\n*/\n\n/* On a Pentium, resort to a trick */\n#if defined(LUA_NUMBER_DOUBLE) && !defined(LUA_ANSI) && !defined(__SSE2__) && \\\n    (defined(__i386) || defined (_M_IX86) || defined(__i386__))\n\n/* On a Microsoft compiler, use assembler */\n#if defined(_MSC_VER)\n\n#define lua_number2int(i,d)   __asm fld d   __asm fistp i\n#define lua_number2integer(i,n)\t\tlua_number2int(i, n)\n\n/* the next trick should work on any Pentium, but sometimes clashes\n   with a DirectX idiosyncrasy */\n#else\n\nunion luai_Cast { double l_d; long l_l; };\n#define lua_number2int(i,d) \\\n  { volatile union luai_Cast u; u.l_d = (d) + 6755399441055744.0; (i) = u.l_l; }\n#define lua_number2integer(i,n)\t\tlua_number2int(i, n)\n\n#endif\n\n\n/* this option always works, but may be slow */\n#else\n#define lua_number2int(i,d)\t((i)=(int)(d))\n#define lua_number2integer(i,d)\t((i)=(lua_Integer)(d))\n\n#endif\n\n/* }================================================================== */\n\n\n/*\n@@ LUAI_USER_ALIGNMENT_T is a type that requires maximum alignment.\n** CHANGE it if your system requires alignments larger than double. (For\n** instance, if your system supports long doubles and they must be\n** aligned in 16-byte boundaries, then you should add long double in the\n** union.) Probably you do not need to change this.\n*/\n#define LUAI_USER_ALIGNMENT_T\tunion { double u; void *s; long l; }\n\n\n/*\n@@ LUAI_THROW/LUAI_TRY define how Lua does exception handling.\n** CHANGE them if you prefer to use longjmp/setjmp even with C++\n** or if want/don't to use _longjmp/_setjmp instead of regular\n** longjmp/setjmp. By default, Lua handles errors with exceptions when\n** compiling as C++ code, with _longjmp/_setjmp when asked to use them,\n** and with longjmp/setjmp otherwise.\n*/\n#if defined(__cplusplus)\n/* C++ exceptions */\n#define LUAI_THROW(L,c)\tthrow(c)\n#define LUAI_TRY(L,c,a)\ttry { a } catch(...) \\\n\t{ if ((c)->status == 0) (c)->status = -1; }\n#define luai_jmpbuf\tint  /* dummy variable */\n\n#elif defined(LUA_USE_ULONGJMP)\n/* in Unix, try _longjmp/_setjmp (more efficient) */\n#define LUAI_THROW(L,c)\t_longjmp((c)->b, 1)\n#define LUAI_TRY(L,c,a)\tif (_setjmp((c)->b) == 0) { a }\n#define luai_jmpbuf\tjmp_buf\n\n#else\n/* default handling with long jumps */\n#define LUAI_THROW(L,c)\tlongjmp((c)->b, 1)\n#define LUAI_TRY(L,c,a)\tif (setjmp((c)->b) == 0) { a }\n#define luai_jmpbuf\tjmp_buf\n\n#endif\n\n\n/*\n@@ LUA_MAXCAPTURES is the maximum number of captures that a pattern\n@* can do during pattern-matching.\n** CHANGE it if you need more captures. This limit is arbitrary.\n*/\n#define LUA_MAXCAPTURES\t\t32\n\n\n/*\n@@ lua_tmpnam is the function that the OS library uses to create a\n@* temporary name.\n@@ LUA_TMPNAMBUFSIZE is the maximum size of a name created by lua_tmpnam.\n** CHANGE them if you have an alternative to tmpnam (which is considered\n** insecure) or if you want the original tmpnam anyway.  By default, Lua\n** uses tmpnam except when POSIX is available, where it uses mkstemp.\n*/\n#if defined(loslib_c) || defined(luaall_c)\n\n#if defined(LUA_USE_MKSTEMP)\n#include <unistd.h>\n#define LUA_TMPNAMBUFSIZE\t32\n#define lua_tmpnam(b,e)\t{ \\\n\tstrcpy(b, \"/tmp/lua_XXXXXX\"); \\\n\te = mkstemp(b); \\\n\tif (e != -1) close(e); \\\n\te = (e == -1); }\n\n#else\n#define LUA_TMPNAMBUFSIZE\tL_tmpnam\n#define lua_tmpnam(b,e)\t\t{ e = (tmpnam(b) == NULL); }\n#endif\n\n#endif\n\n\n/*\n@@ lua_popen spawns a new process connected to the current one through\n@* the file streams.\n** CHANGE it if you have a way to implement it in your system.\n*/\n#if defined(LUA_USE_POPEN)\n\n#define lua_popen(L,c,m)\t((void)L, fflush(NULL), popen(c,m))\n#define lua_pclose(L,file)\t((void)L, (pclose(file) != -1))\n\n#elif defined(LUA_WIN)\n\n#define lua_popen(L,c,m)\t((void)L, _popen(c,m))\n#define lua_pclose(L,file)\t((void)L, (_pclose(file) != -1))\n\n#else\n\n#define lua_popen(L,c,m)\t((void)((void)c, m),  \\\n\t\tluaL_error(L, LUA_QL(\"popen\") \" not supported\"), (FILE*)0)\n#define lua_pclose(L,file)\t\t((void)((void)L, file), 0)\n\n#endif\n\n/*\n@@ LUA_DL_* define which dynamic-library system Lua should use.\n** CHANGE here if Lua has problems choosing the appropriate\n** dynamic-library system for your platform (either Windows' DLL, Mac's\n** dyld, or Unix's dlopen). If your system is some kind of Unix, there\n** is a good chance that it has dlopen, so LUA_DL_DLOPEN will work for\n** it.  To use dlopen you also need to adapt the src/Makefile (probably\n** adding -ldl to the linker options), so Lua does not select it\n** automatically.  (When you change the makefile to add -ldl, you must\n** also add -DLUA_USE_DLOPEN.)\n** If you do not want any kind of dynamic library, undefine all these\n** options.\n** By default, _WIN32 gets LUA_DL_DLL and MAC OS X gets LUA_DL_DYLD.\n*/\n#if defined(LUA_USE_DLOPEN)\n#define LUA_DL_DLOPEN\n#endif\n\n#if defined(LUA_WIN)\n#define LUA_DL_DLL\n#endif\n\n\n/*\n@@ LUAI_EXTRASPACE allows you to add user-specific data in a lua_State\n@* (the data goes just *before* the lua_State pointer).\n** CHANGE (define) this if you really need that. This value must be\n** a multiple of the maximum alignment required for your machine.\n*/\n#define LUAI_EXTRASPACE\t\t0\n\n\n/*\n@@ luai_userstate* allow user-specific actions on threads.\n** CHANGE them if you defined LUAI_EXTRASPACE and need to do something\n** extra when a thread is created/deleted/resumed/yielded.\n*/\n#define luai_userstateopen(L)\t\t((void)L)\n#define luai_userstateclose(L)\t\t((void)L)\n#define luai_userstatethread(L,L1)\t((void)L)\n#define luai_userstatefree(L)\t\t((void)L)\n#define luai_userstateresume(L,n)\t((void)L)\n#define luai_userstateyield(L,n)\t((void)L)\n\n\n/*\n@@ LUA_INTFRMLEN is the length modifier for integer conversions\n@* in 'string.format'.\n@@ LUA_INTFRM_T is the integer type correspoding to the previous length\n@* modifier.\n** CHANGE them if your system supports long long or does not support long.\n*/\n\n#if defined(LUA_USELONGLONG)\n\n#define LUA_INTFRMLEN\t\t\"ll\"\n#define LUA_INTFRM_T\t\tlong long\n\n#else\n\n#define LUA_INTFRMLEN\t\t\"l\"\n#define LUA_INTFRM_T\t\tlong\n\n#endif\n\n\n\n/* =================================================================== */\n\n/*\n** Local configuration. You can use this space to add your redefinitions\n** without modifying the main part of the file.\n*/\n\n\n\n#endif\n\n"
  },
  {
    "path": "deps/lua/src/lualib.h",
    "content": "/*\n** $Id: lualib.h,v 1.36.1.1 2007/12/27 13:02:25 roberto Exp $\n** Lua standard libraries\n** See Copyright Notice in lua.h\n*/\n\n\n#ifndef lualib_h\n#define lualib_h\n\n#include \"lua.h\"\n\n\n/* Key to file-handle type */\n#define LUA_FILEHANDLE\t\t\"FILE*\"\n\n\n#define LUA_COLIBNAME\t\"coroutine\"\nLUALIB_API int (luaopen_base) (lua_State *L);\n\n#define LUA_TABLIBNAME\t\"table\"\nLUALIB_API int (luaopen_table) (lua_State *L);\n\n#define LUA_IOLIBNAME\t\"io\"\nLUALIB_API int (luaopen_io) (lua_State *L);\n\n#define LUA_OSLIBNAME\t\"os\"\nLUALIB_API int (luaopen_os) (lua_State *L);\n\n#define LUA_STRLIBNAME\t\"string\"\nLUALIB_API int (luaopen_string) (lua_State *L);\n\n#define LUA_MATHLIBNAME\t\"math\"\nLUALIB_API int (luaopen_math) (lua_State *L);\n\n#define LUA_DBLIBNAME\t\"debug\"\nLUALIB_API int (luaopen_debug) (lua_State *L);\n\n#define LUA_LOADLIBNAME\t\"package\"\nLUALIB_API int (luaopen_package) (lua_State *L);\n\n\n/* open all previous libraries */\nLUALIB_API void (luaL_openlibs) (lua_State *L); \n\n\n\n#ifndef lua_assert\n#define lua_assert(x)\t((void)0)\n#endif\n\n\n#endif\n"
  },
  {
    "path": "deps/lua/src/lundump.c",
    "content": "/*\n** $Id: lundump.c,v 2.7.1.4 2008/04/04 19:51:41 roberto Exp $\n** load precompiled Lua chunks\n** See Copyright Notice in lua.h\n*/\n\n#include <string.h>\n\n#define lundump_c\n#define LUA_CORE\n\n#include \"lua.h\"\n\n#include \"ldebug.h\"\n#include \"ldo.h\"\n#include \"lfunc.h\"\n#include \"lmem.h\"\n#include \"lobject.h\"\n#include \"lstring.h\"\n#include \"lundump.h\"\n#include \"lzio.h\"\n\ntypedef struct {\n lua_State* L;\n ZIO* Z;\n Mbuffer* b;\n const char* name;\n} LoadState;\n\n#ifdef LUAC_TRUST_BINARIES\n#define IF(c,s)\n#define error(S,s)\n#else\n#define IF(c,s)\t\tif (c) error(S,s)\n\nstatic void error(LoadState* S, const char* why)\n{\n luaO_pushfstring(S->L,\"%s: %s in precompiled chunk\",S->name,why);\n luaD_throw(S->L,LUA_ERRSYNTAX);\n}\n#endif\n\n#define LoadMem(S,b,n,size)\tLoadBlock(S,b,(n)*(size))\n#define\tLoadByte(S)\t\t(lu_byte)LoadChar(S)\n#define LoadVar(S,x)\t\tLoadMem(S,&x,1,sizeof(x))\n#define LoadVector(S,b,n,size)\tLoadMem(S,b,n,size)\n\nstatic void LoadBlock(LoadState* S, void* b, size_t size)\n{\n size_t r=luaZ_read(S->Z,b,size);\n IF (r!=0, \"unexpected end\");\n}\n\nstatic int LoadChar(LoadState* S)\n{\n char x;\n LoadVar(S,x);\n return x;\n}\n\nstatic int LoadInt(LoadState* S)\n{\n int x;\n LoadVar(S,x);\n IF (x<0, \"bad integer\");\n return x;\n}\n\nstatic lua_Number LoadNumber(LoadState* S)\n{\n lua_Number x;\n LoadVar(S,x);\n return x;\n}\n\nstatic TString* LoadString(LoadState* S)\n{\n size_t size;\n LoadVar(S,size);\n if (size==0)\n  return NULL;\n else\n {\n  char* s=luaZ_openspace(S->L,S->b,size);\n  LoadBlock(S,s,size);\n  return luaS_newlstr(S->L,s,size-1);\t\t/* remove trailing '\\0' */\n }\n}\n\nstatic void LoadCode(LoadState* S, Proto* f)\n{\n int n=LoadInt(S);\n f->code=luaM_newvector(S->L,n,Instruction);\n f->sizecode=n;\n LoadVector(S,f->code,n,sizeof(Instruction));\n}\n\nstatic Proto* LoadFunction(LoadState* S, TString* p);\n\nstatic void LoadConstants(LoadState* S, Proto* f)\n{\n int i,n;\n n=LoadInt(S);\n f->k=luaM_newvector(S->L,n,TValue);\n f->sizek=n;\n for (i=0; i<n; i++) setnilvalue(&f->k[i]);\n for (i=0; i<n; i++)\n {\n  TValue* o=&f->k[i];\n  int t=LoadChar(S);\n  switch (t)\n  {\n   case LUA_TNIL:\n   \tsetnilvalue(o);\n\tbreak;\n   case LUA_TBOOLEAN:\n   \tsetbvalue(o,LoadChar(S)!=0);\n\tbreak;\n   case LUA_TNUMBER:\n\tsetnvalue(o,LoadNumber(S));\n\tbreak;\n   case LUA_TSTRING:\n\tsetsvalue2n(S->L,o,LoadString(S));\n\tbreak;\n   default:\n\terror(S,\"bad constant\");\n\tbreak;\n  }\n }\n n=LoadInt(S);\n f->p=luaM_newvector(S->L,n,Proto*);\n f->sizep=n;\n for (i=0; i<n; i++) f->p[i]=NULL;\n for (i=0; i<n; i++) f->p[i]=LoadFunction(S,f->source);\n}\n\nstatic void LoadDebug(LoadState* S, Proto* f)\n{\n int i,n;\n n=LoadInt(S);\n f->lineinfo=luaM_newvector(S->L,n,int);\n f->sizelineinfo=n;\n LoadVector(S,f->lineinfo,n,sizeof(int));\n n=LoadInt(S);\n f->locvars=luaM_newvector(S->L,n,LocVar);\n f->sizelocvars=n;\n for (i=0; i<n; i++) f->locvars[i].varname=NULL;\n for (i=0; i<n; i++)\n {\n  f->locvars[i].varname=LoadString(S);\n  f->locvars[i].startpc=LoadInt(S);\n  f->locvars[i].endpc=LoadInt(S);\n }\n n=LoadInt(S);\n f->upvalues=luaM_newvector(S->L,n,TString*);\n f->sizeupvalues=n;\n for (i=0; i<n; i++) f->upvalues[i]=NULL;\n for (i=0; i<n; i++) f->upvalues[i]=LoadString(S);\n}\n\nstatic Proto* LoadFunction(LoadState* S, TString* p)\n{\n Proto* f;\n if (++S->L->nCcalls > LUAI_MAXCCALLS) error(S,\"code too deep\");\n f=luaF_newproto(S->L);\n setptvalue2s(S->L,S->L->top,f); incr_top(S->L);\n f->source=LoadString(S); if (f->source==NULL) f->source=p;\n f->linedefined=LoadInt(S);\n f->lastlinedefined=LoadInt(S);\n f->nups=LoadByte(S);\n f->numparams=LoadByte(S);\n f->is_vararg=LoadByte(S);\n f->maxstacksize=LoadByte(S);\n LoadCode(S,f);\n LoadConstants(S,f);\n LoadDebug(S,f);\n IF (!luaG_checkcode(f), \"bad code\");\n S->L->top--;\n S->L->nCcalls--;\n return f;\n}\n\nstatic void LoadHeader(LoadState* S)\n{\n char h[LUAC_HEADERSIZE];\n char s[LUAC_HEADERSIZE];\n luaU_header(h);\n LoadBlock(S,s,LUAC_HEADERSIZE);\n IF (memcmp(h,s,LUAC_HEADERSIZE)!=0, \"bad header\");\n}\n\n/*\n** load precompiled chunk\n*/\nProto* luaU_undump (lua_State* L, ZIO* Z, Mbuffer* buff, const char* name)\n{\n LoadState S;\n if (*name=='@' || *name=='=')\n  S.name=name+1;\n else if (*name==LUA_SIGNATURE[0])\n  S.name=\"binary string\";\n else\n  S.name=name;\n S.L=L;\n S.Z=Z;\n S.b=buff;\n LoadHeader(&S);\n return LoadFunction(&S,luaS_newliteral(L,\"=?\"));\n}\n\n/*\n* make header\n*/\nvoid luaU_header (char* h)\n{\n int x=1;\n memcpy(h,LUA_SIGNATURE,sizeof(LUA_SIGNATURE)-1);\n h+=sizeof(LUA_SIGNATURE)-1;\n *h++=(char)LUAC_VERSION;\n *h++=(char)LUAC_FORMAT;\n *h++=(char)*(char*)&x;\t\t\t\t/* endianness */\n *h++=(char)sizeof(int);\n *h++=(char)sizeof(size_t);\n *h++=(char)sizeof(Instruction);\n *h++=(char)sizeof(lua_Number);\n *h++=(char)(((lua_Number)0.5)==0);\t\t/* is lua_Number integral? */\n}\n"
  },
  {
    "path": "deps/lua/src/lundump.h",
    "content": "/*\n** $Id: lundump.h,v 1.37.1.1 2007/12/27 13:02:25 roberto Exp $\n** load precompiled Lua chunks\n** See Copyright Notice in lua.h\n*/\n\n#ifndef lundump_h\n#define lundump_h\n\n#include \"lobject.h\"\n#include \"lzio.h\"\n\n/* load one chunk; from lundump.c */\nLUAI_FUNC Proto* luaU_undump (lua_State* L, ZIO* Z, Mbuffer* buff, const char* name);\n\n/* make header; from lundump.c */\nLUAI_FUNC void luaU_header (char* h);\n\n/* dump one chunk; from ldump.c */\nLUAI_FUNC int luaU_dump (lua_State* L, const Proto* f, lua_Writer w, void* data, int strip);\n\n#ifdef luac_c\n/* print one chunk; from print.c */\nLUAI_FUNC void luaU_print (const Proto* f, int full);\n#endif\n\n/* for header of binary files -- this is Lua 5.1 */\n#define LUAC_VERSION\t\t0x51\n\n/* for header of binary files -- this is the official format */\n#define LUAC_FORMAT\t\t0\n\n/* size of header of binary files */\n#define LUAC_HEADERSIZE\t\t12\n\n#endif\n"
  },
  {
    "path": "deps/lua/src/lvm.c",
    "content": "/*\n** $Id: lvm.c,v 2.63.1.5 2011/08/17 20:43:11 roberto Exp $\n** Lua virtual machine\n** See Copyright Notice in lua.h\n*/\n\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#define lvm_c\n#define LUA_CORE\n\n#include \"lua.h\"\n\n#include \"ldebug.h\"\n#include \"ldo.h\"\n#include \"lfunc.h\"\n#include \"lgc.h\"\n#include \"lobject.h\"\n#include \"lopcodes.h\"\n#include \"lstate.h\"\n#include \"lstring.h\"\n#include \"ltable.h\"\n#include \"ltm.h\"\n#include \"lvm.h\"\n\n\n\n/* limit for table tag-method chains (to avoid loops) */\n#define MAXTAGLOOP\t100\n\n\nconst TValue *luaV_tonumber (const TValue *obj, TValue *n) {\n  lua_Number num;\n  if (ttisnumber(obj)) return obj;\n  if (ttisstring(obj) && luaO_str2d(svalue(obj), &num)) {\n    setnvalue(n, num);\n    return n;\n  }\n  else\n    return NULL;\n}\n\n\nint luaV_tostring (lua_State *L, StkId obj) {\n  if (!ttisnumber(obj))\n    return 0;\n  else {\n    char s[LUAI_MAXNUMBER2STR];\n    lua_Number n = nvalue(obj);\n    lua_number2str(s, n);\n    setsvalue2s(L, obj, luaS_new(L, s));\n    return 1;\n  }\n}\n\n\nstatic void traceexec (lua_State *L, const Instruction *pc) {\n  lu_byte mask = L->hookmask;\n  const Instruction *oldpc = L->savedpc;\n  L->savedpc = pc;\n  if ((mask & LUA_MASKCOUNT) && L->hookcount == 0) {\n    resethookcount(L);\n    luaD_callhook(L, LUA_HOOKCOUNT, -1);\n  }\n  if (mask & LUA_MASKLINE) {\n    Proto *p = ci_func(L->ci)->l.p;\n    int npc = pcRel(pc, p);\n    int newline = getline(p, npc);\n    /* call linehook when enter a new function, when jump back (loop),\n       or when enter a new line */\n    if (npc == 0 || pc <= oldpc || newline != getline(p, pcRel(oldpc, p)))\n      luaD_callhook(L, LUA_HOOKLINE, newline);\n  }\n}\n\n\nstatic void callTMres (lua_State *L, StkId res, const TValue *f,\n                        const TValue *p1, const TValue *p2) {\n  ptrdiff_t result = savestack(L, res);\n  setobj2s(L, L->top, f);  /* push function */\n  setobj2s(L, L->top+1, p1);  /* 1st argument */\n  setobj2s(L, L->top+2, p2);  /* 2nd argument */\n  luaD_checkstack(L, 3);\n  L->top += 3;\n  luaD_call(L, L->top - 3, 1);\n  res = restorestack(L, result);\n  L->top--;\n  setobjs2s(L, res, L->top);\n}\n\n\n\nstatic void callTM (lua_State *L, const TValue *f, const TValue *p1,\n                    const TValue *p2, const TValue *p3) {\n  setobj2s(L, L->top, f);  /* push function */\n  setobj2s(L, L->top+1, p1);  /* 1st argument */\n  setobj2s(L, L->top+2, p2);  /* 2nd argument */\n  setobj2s(L, L->top+3, p3);  /* 3th argument */\n  luaD_checkstack(L, 4);\n  L->top += 4;\n  luaD_call(L, L->top - 4, 0);\n}\n\n\nvoid luaV_gettable (lua_State *L, const TValue *t, TValue *key, StkId val) {\n  int loop;\n  for (loop = 0; loop < MAXTAGLOOP; loop++) {\n    const TValue *tm;\n    if (ttistable(t)) {  /* `t' is a table? */\n      Table *h = hvalue(t);\n      const TValue *res = luaH_get(h, key); /* do a primitive get */\n      if (!ttisnil(res) ||  /* result is no nil? */\n          (tm = fasttm(L, h->metatable, TM_INDEX)) == NULL) { /* or no TM? */\n        setobj2s(L, val, res);\n        return;\n      }\n      /* else will try the tag method */\n    }\n    else if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_INDEX)))\n      luaG_typeerror(L, t, \"index\");\n    if (ttisfunction(tm)) {\n      callTMres(L, val, tm, t, key);\n      return;\n    }\n    t = tm;  /* else repeat with `tm' */ \n  }\n  luaG_runerror(L, \"loop in gettable\");\n}\n\n\nvoid luaV_settable (lua_State *L, const TValue *t, TValue *key, StkId val) {\n  int loop;\n  TValue temp;\n  for (loop = 0; loop < MAXTAGLOOP; loop++) {\n    const TValue *tm;\n    if (ttistable(t)) {  /* `t' is a table? */\n      Table *h = hvalue(t);\n      TValue *oldval = luaH_set(L, h, key); /* do a primitive set */\n      if (!ttisnil(oldval) ||  /* result is no nil? */\n          (tm = fasttm(L, h->metatable, TM_NEWINDEX)) == NULL) { /* or no TM? */\n        setobj2t(L, oldval, val);\n        h->flags = 0;\n        luaC_barriert(L, h, val);\n        return;\n      }\n      /* else will try the tag method */\n    }\n    else if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_NEWINDEX)))\n      luaG_typeerror(L, t, \"index\");\n    if (ttisfunction(tm)) {\n      callTM(L, tm, t, key, val);\n      return;\n    }\n    /* else repeat with `tm' */\n    setobj(L, &temp, tm);  /* avoid pointing inside table (may rehash) */\n    t = &temp;\n  }\n  luaG_runerror(L, \"loop in settable\");\n}\n\n\nstatic int call_binTM (lua_State *L, const TValue *p1, const TValue *p2,\n                       StkId res, TMS event) {\n  const TValue *tm = luaT_gettmbyobj(L, p1, event);  /* try first operand */\n  if (ttisnil(tm))\n    tm = luaT_gettmbyobj(L, p2, event);  /* try second operand */\n  if (ttisnil(tm)) return 0;\n  callTMres(L, res, tm, p1, p2);\n  return 1;\n}\n\n\nstatic const TValue *get_compTM (lua_State *L, Table *mt1, Table *mt2,\n                                  TMS event) {\n  const TValue *tm1 = fasttm(L, mt1, event);\n  const TValue *tm2;\n  if (tm1 == NULL) return NULL;  /* no metamethod */\n  if (mt1 == mt2) return tm1;  /* same metatables => same metamethods */\n  tm2 = fasttm(L, mt2, event);\n  if (tm2 == NULL) return NULL;  /* no metamethod */\n  if (luaO_rawequalObj(tm1, tm2))  /* same metamethods? */\n    return tm1;\n  return NULL;\n}\n\n\nstatic int call_orderTM (lua_State *L, const TValue *p1, const TValue *p2,\n                         TMS event) {\n  const TValue *tm1 = luaT_gettmbyobj(L, p1, event);\n  const TValue *tm2;\n  if (ttisnil(tm1)) return -1;  /* no metamethod? */\n  tm2 = luaT_gettmbyobj(L, p2, event);\n  if (!luaO_rawequalObj(tm1, tm2))  /* different metamethods? */\n    return -1;\n  callTMres(L, L->top, tm1, p1, p2);\n  return !l_isfalse(L->top);\n}\n\n\nstatic int l_strcmp (const TString *ls, const TString *rs) {\n  const char *l = getstr(ls);\n  size_t ll = ls->tsv.len;\n  const char *r = getstr(rs);\n  size_t lr = rs->tsv.len;\n  for (;;) {\n    int temp = strcoll(l, r);\n    if (temp != 0) return temp;\n    else {  /* strings are equal up to a `\\0' */\n      size_t len = strlen(l);  /* index of first `\\0' in both strings */\n      if (len == lr)  /* r is finished? */\n        return (len == ll) ? 0 : 1;\n      else if (len == ll)  /* l is finished? */\n        return -1;  /* l is smaller than r (because r is not finished) */\n      /* both strings longer than `len'; go on comparing (after the `\\0') */\n      len++;\n      l += len; ll -= len; r += len; lr -= len;\n    }\n  }\n}\n\n\nint luaV_lessthan (lua_State *L, const TValue *l, const TValue *r) {\n  int res;\n  if (ttype(l) != ttype(r))\n    return luaG_ordererror(L, l, r);\n  else if (ttisnumber(l))\n    return luai_numlt(nvalue(l), nvalue(r));\n  else if (ttisstring(l))\n    return l_strcmp(rawtsvalue(l), rawtsvalue(r)) < 0;\n  else if ((res = call_orderTM(L, l, r, TM_LT)) != -1)\n    return res;\n  return luaG_ordererror(L, l, r);\n}\n\n\nstatic int lessequal (lua_State *L, const TValue *l, const TValue *r) {\n  int res;\n  if (ttype(l) != ttype(r))\n    return luaG_ordererror(L, l, r);\n  else if (ttisnumber(l))\n    return luai_numle(nvalue(l), nvalue(r));\n  else if (ttisstring(l))\n    return l_strcmp(rawtsvalue(l), rawtsvalue(r)) <= 0;\n  else if ((res = call_orderTM(L, l, r, TM_LE)) != -1)  /* first try `le' */\n    return res;\n  else if ((res = call_orderTM(L, r, l, TM_LT)) != -1)  /* else try `lt' */\n    return !res;\n  return luaG_ordererror(L, l, r);\n}\n\n\nint luaV_equalval (lua_State *L, const TValue *t1, const TValue *t2) {\n  const TValue *tm;\n  lua_assert(ttype(t1) == ttype(t2));\n  switch (ttype(t1)) {\n    case LUA_TNIL: return 1;\n    case LUA_TNUMBER: return luai_numeq(nvalue(t1), nvalue(t2));\n    case LUA_TBOOLEAN: return bvalue(t1) == bvalue(t2);  /* true must be 1 !! */\n    case LUA_TLIGHTUSERDATA: return pvalue(t1) == pvalue(t2);\n    case LUA_TUSERDATA: {\n      if (uvalue(t1) == uvalue(t2)) return 1;\n      tm = get_compTM(L, uvalue(t1)->metatable, uvalue(t2)->metatable,\n                         TM_EQ);\n      break;  /* will try TM */\n    }\n    case LUA_TTABLE: {\n      if (hvalue(t1) == hvalue(t2)) return 1;\n      tm = get_compTM(L, hvalue(t1)->metatable, hvalue(t2)->metatable, TM_EQ);\n      break;  /* will try TM */\n    }\n    default: return gcvalue(t1) == gcvalue(t2);\n  }\n  if (tm == NULL) return 0;  /* no TM? */\n  callTMres(L, L->top, tm, t1, t2);  /* call TM */\n  return !l_isfalse(L->top);\n}\n\n\nvoid luaV_concat (lua_State *L, int total, int last) {\n  do {\n    StkId top = L->base + last + 1;\n    int n = 2;  /* number of elements handled in this pass (at least 2) */\n    if (!(ttisstring(top-2) || ttisnumber(top-2)) || !tostring(L, top-1)) {\n      if (!call_binTM(L, top-2, top-1, top-2, TM_CONCAT))\n        luaG_concaterror(L, top-2, top-1);\n    } else if (tsvalue(top-1)->len == 0)  /* second op is empty? */\n      (void)tostring(L, top - 2);  /* result is first op (as string) */\n    else {\n      /* at least two string values; get as many as possible */\n      size_t tl = tsvalue(top-1)->len;\n      char *buffer;\n      int i;\n      /* collect total length */\n      for (n = 1; n < total && tostring(L, top-n-1); n++) {\n        size_t l = tsvalue(top-n-1)->len;\n        if (l >= MAX_SIZET - tl) luaG_runerror(L, \"string length overflow\");\n        tl += l;\n      }\n      buffer = luaZ_openspace(L, &G(L)->buff, tl);\n      tl = 0;\n      for (i=n; i>0; i--) {  /* concat all strings */\n        size_t l = tsvalue(top-i)->len;\n        memcpy(buffer+tl, svalue(top-i), l);\n        tl += l;\n      }\n      setsvalue2s(L, top-n, luaS_newlstr(L, buffer, tl));\n    }\n    total -= n-1;  /* got `n' strings to create 1 new */\n    last -= n-1;\n  } while (total > 1);  /* repeat until only 1 result left */\n}\n\n\nstatic void Arith (lua_State *L, StkId ra, const TValue *rb,\n                   const TValue *rc, TMS op) {\n  TValue tempb, tempc;\n  const TValue *b, *c;\n  if ((b = luaV_tonumber(rb, &tempb)) != NULL &&\n      (c = luaV_tonumber(rc, &tempc)) != NULL) {\n    lua_Number nb = nvalue(b), nc = nvalue(c);\n    switch (op) {\n      case TM_ADD: setnvalue(ra, luai_numadd(nb, nc)); break;\n      case TM_SUB: setnvalue(ra, luai_numsub(nb, nc)); break;\n      case TM_MUL: setnvalue(ra, luai_nummul(nb, nc)); break;\n      case TM_DIV: setnvalue(ra, luai_numdiv(nb, nc)); break;\n      case TM_MOD: setnvalue(ra, luai_nummod(nb, nc)); break;\n      case TM_POW: setnvalue(ra, luai_numpow(nb, nc)); break;\n      case TM_UNM: setnvalue(ra, luai_numunm(nb)); break;\n      default: lua_assert(0); break;\n    }\n  }\n  else if (!call_binTM(L, rb, rc, ra, op))\n    luaG_aritherror(L, rb, rc);\n}\n\n\n\n/*\n** some macros for common tasks in `luaV_execute'\n*/\n\n#define runtime_check(L, c)\t{ if (!(c)) break; }\n\n#define RA(i)\t(base+GETARG_A(i))\n/* to be used after possible stack reallocation */\n#define RB(i)\tcheck_exp(getBMode(GET_OPCODE(i)) == OpArgR, base+GETARG_B(i))\n#define RC(i)\tcheck_exp(getCMode(GET_OPCODE(i)) == OpArgR, base+GETARG_C(i))\n#define RKB(i)\tcheck_exp(getBMode(GET_OPCODE(i)) == OpArgK, \\\n\tISK(GETARG_B(i)) ? k+INDEXK(GETARG_B(i)) : base+GETARG_B(i))\n#define RKC(i)\tcheck_exp(getCMode(GET_OPCODE(i)) == OpArgK, \\\n\tISK(GETARG_C(i)) ? k+INDEXK(GETARG_C(i)) : base+GETARG_C(i))\n#define KBx(i)\tcheck_exp(getBMode(GET_OPCODE(i)) == OpArgK, k+GETARG_Bx(i))\n\n\n#define dojump(L,pc,i)\t{(pc) += (i); luai_threadyield(L);}\n\n\n#define Protect(x)\t{ L->savedpc = pc; {x;}; base = L->base; }\n\n\n#define arith_op(op,tm) { \\\n        TValue *rb = RKB(i); \\\n        TValue *rc = RKC(i); \\\n        if (ttisnumber(rb) && ttisnumber(rc)) { \\\n          lua_Number nb = nvalue(rb), nc = nvalue(rc); \\\n          setnvalue(ra, op(nb, nc)); \\\n        } \\\n        else \\\n          Protect(Arith(L, ra, rb, rc, tm)); \\\n      }\n\n\n\nvoid luaV_execute (lua_State *L, int nexeccalls) {\n  LClosure *cl;\n  StkId base;\n  TValue *k;\n  const Instruction *pc;\n reentry:  /* entry point */\n  lua_assert(isLua(L->ci));\n  pc = L->savedpc;\n  cl = &clvalue(L->ci->func)->l;\n  base = L->base;\n  k = cl->p->k;\n  /* main loop of interpreter */\n  for (;;) {\n    const Instruction i = *pc++;\n    StkId ra;\n    if ((L->hookmask & (LUA_MASKLINE | LUA_MASKCOUNT)) &&\n        (--L->hookcount == 0 || L->hookmask & LUA_MASKLINE)) {\n      traceexec(L, pc);\n      if (L->status == LUA_YIELD) {  /* did hook yield? */\n        L->savedpc = pc - 1;\n        return;\n      }\n      base = L->base;\n    }\n    /* warning!! several calls may realloc the stack and invalidate `ra' */\n    ra = RA(i);\n    lua_assert(base == L->base && L->base == L->ci->base);\n    lua_assert(base <= L->top && L->top <= L->stack + L->stacksize);\n    lua_assert(L->top == L->ci->top || luaG_checkopenop(i));\n    switch (GET_OPCODE(i)) {\n      case OP_MOVE: {\n        setobjs2s(L, ra, RB(i));\n        continue;\n      }\n      case OP_LOADK: {\n        setobj2s(L, ra, KBx(i));\n        continue;\n      }\n      case OP_LOADBOOL: {\n        setbvalue(ra, GETARG_B(i));\n        if (GETARG_C(i)) pc++;  /* skip next instruction (if C) */\n        continue;\n      }\n      case OP_LOADNIL: {\n        TValue *rb = RB(i);\n        do {\n          setnilvalue(rb--);\n        } while (rb >= ra);\n        continue;\n      }\n      case OP_GETUPVAL: {\n        int b = GETARG_B(i);\n        setobj2s(L, ra, cl->upvals[b]->v);\n        continue;\n      }\n      case OP_GETGLOBAL: {\n        TValue g;\n        TValue *rb = KBx(i);\n        sethvalue(L, &g, cl->env);\n        lua_assert(ttisstring(rb));\n        Protect(luaV_gettable(L, &g, rb, ra));\n        continue;\n      }\n      case OP_GETTABLE: {\n        Protect(luaV_gettable(L, RB(i), RKC(i), ra));\n        continue;\n      }\n      case OP_SETGLOBAL: {\n        TValue g;\n        sethvalue(L, &g, cl->env);\n        lua_assert(ttisstring(KBx(i)));\n        Protect(luaV_settable(L, &g, KBx(i), ra));\n        continue;\n      }\n      case OP_SETUPVAL: {\n        UpVal *uv = cl->upvals[GETARG_B(i)];\n        setobj(L, uv->v, ra);\n        luaC_barrier(L, uv, ra);\n        continue;\n      }\n      case OP_SETTABLE: {\n        Protect(luaV_settable(L, ra, RKB(i), RKC(i)));\n        continue;\n      }\n      case OP_NEWTABLE: {\n        int b = GETARG_B(i);\n        int c = GETARG_C(i);\n        sethvalue(L, ra, luaH_new(L, luaO_fb2int(b), luaO_fb2int(c)));\n        Protect(luaC_checkGC(L));\n        continue;\n      }\n      case OP_SELF: {\n        StkId rb = RB(i);\n        setobjs2s(L, ra+1, rb);\n        Protect(luaV_gettable(L, rb, RKC(i), ra));\n        continue;\n      }\n      case OP_ADD: {\n        arith_op(luai_numadd, TM_ADD);\n        continue;\n      }\n      case OP_SUB: {\n        arith_op(luai_numsub, TM_SUB);\n        continue;\n      }\n      case OP_MUL: {\n        arith_op(luai_nummul, TM_MUL);\n        continue;\n      }\n      case OP_DIV: {\n        arith_op(luai_numdiv, TM_DIV);\n        continue;\n      }\n      case OP_MOD: {\n        arith_op(luai_nummod, TM_MOD);\n        continue;\n      }\n      case OP_POW: {\n        arith_op(luai_numpow, TM_POW);\n        continue;\n      }\n      case OP_UNM: {\n        TValue *rb = RB(i);\n        if (ttisnumber(rb)) {\n          lua_Number nb = nvalue(rb);\n          setnvalue(ra, luai_numunm(nb));\n        }\n        else {\n          Protect(Arith(L, ra, rb, rb, TM_UNM));\n        }\n        continue;\n      }\n      case OP_NOT: {\n        int res = l_isfalse(RB(i));  /* next assignment may change this value */\n        setbvalue(ra, res);\n        continue;\n      }\n      case OP_LEN: {\n        const TValue *rb = RB(i);\n        switch (ttype(rb)) {\n          case LUA_TTABLE: {\n            setnvalue(ra, cast_num(luaH_getn(hvalue(rb))));\n            break;\n          }\n          case LUA_TSTRING: {\n            setnvalue(ra, cast_num(tsvalue(rb)->len));\n            break;\n          }\n          default: {  /* try metamethod */\n            Protect(\n              if (!call_binTM(L, rb, luaO_nilobject, ra, TM_LEN))\n                luaG_typeerror(L, rb, \"get length of\");\n            )\n          }\n        }\n        continue;\n      }\n      case OP_CONCAT: {\n        int b = GETARG_B(i);\n        int c = GETARG_C(i);\n        Protect(luaV_concat(L, c-b+1, c); luaC_checkGC(L));\n        setobjs2s(L, RA(i), base+b);\n        continue;\n      }\n      case OP_JMP: {\n        dojump(L, pc, GETARG_sBx(i));\n        continue;\n      }\n      case OP_EQ: {\n        TValue *rb = RKB(i);\n        TValue *rc = RKC(i);\n        Protect(\n          if (equalobj(L, rb, rc) == GETARG_A(i))\n            dojump(L, pc, GETARG_sBx(*pc));\n        )\n        pc++;\n        continue;\n      }\n      case OP_LT: {\n        Protect(\n          if (luaV_lessthan(L, RKB(i), RKC(i)) == GETARG_A(i))\n            dojump(L, pc, GETARG_sBx(*pc));\n        )\n        pc++;\n        continue;\n      }\n      case OP_LE: {\n        Protect(\n          if (lessequal(L, RKB(i), RKC(i)) == GETARG_A(i))\n            dojump(L, pc, GETARG_sBx(*pc));\n        )\n        pc++;\n        continue;\n      }\n      case OP_TEST: {\n        if (l_isfalse(ra) != GETARG_C(i))\n          dojump(L, pc, GETARG_sBx(*pc));\n        pc++;\n        continue;\n      }\n      case OP_TESTSET: {\n        TValue *rb = RB(i);\n        if (l_isfalse(rb) != GETARG_C(i)) {\n          setobjs2s(L, ra, rb);\n          dojump(L, pc, GETARG_sBx(*pc));\n        }\n        pc++;\n        continue;\n      }\n      case OP_CALL: {\n        int b = GETARG_B(i);\n        int nresults = GETARG_C(i) - 1;\n        if (b != 0) L->top = ra+b;  /* else previous instruction set top */\n        L->savedpc = pc;\n        switch (luaD_precall(L, ra, nresults)) {\n          case PCRLUA: {\n            nexeccalls++;\n            goto reentry;  /* restart luaV_execute over new Lua function */\n          }\n          case PCRC: {\n            /* it was a C function (`precall' called it); adjust results */\n            if (nresults >= 0) L->top = L->ci->top;\n            base = L->base;\n            continue;\n          }\n          default: {\n            return;  /* yield */\n          }\n        }\n      }\n      case OP_TAILCALL: {\n        int b = GETARG_B(i);\n        if (b != 0) L->top = ra+b;  /* else previous instruction set top */\n        L->savedpc = pc;\n        lua_assert(GETARG_C(i) - 1 == LUA_MULTRET);\n        switch (luaD_precall(L, ra, LUA_MULTRET)) {\n          case PCRLUA: {\n            /* tail call: put new frame in place of previous one */\n            CallInfo *ci = L->ci - 1;  /* previous frame */\n            int aux;\n            StkId func = ci->func;\n            StkId pfunc = (ci+1)->func;  /* previous function index */\n            if (L->openupval) luaF_close(L, ci->base);\n            L->base = ci->base = ci->func + ((ci+1)->base - pfunc);\n            for (aux = 0; pfunc+aux < L->top; aux++)  /* move frame down */\n              setobjs2s(L, func+aux, pfunc+aux);\n            ci->top = L->top = func+aux;  /* correct top */\n            lua_assert(L->top == L->base + clvalue(func)->l.p->maxstacksize);\n            ci->savedpc = L->savedpc;\n            ci->tailcalls++;  /* one more call lost */\n            L->ci--;  /* remove new frame */\n            goto reentry;\n          }\n          case PCRC: {  /* it was a C function (`precall' called it) */\n            base = L->base;\n            continue;\n          }\n          default: {\n            return;  /* yield */\n          }\n        }\n      }\n      case OP_RETURN: {\n        int b = GETARG_B(i);\n        if (b != 0) L->top = ra+b-1;\n        if (L->openupval) luaF_close(L, base);\n        L->savedpc = pc;\n        b = luaD_poscall(L, ra);\n        if (--nexeccalls == 0)  /* was previous function running `here'? */\n          return;  /* no: return */\n        else {  /* yes: continue its execution */\n          if (b) L->top = L->ci->top;\n          lua_assert(isLua(L->ci));\n          lua_assert(GET_OPCODE(*((L->ci)->savedpc - 1)) == OP_CALL);\n          goto reentry;\n        }\n      }\n      case OP_FORLOOP: {\n        lua_Number step = nvalue(ra+2);\n        lua_Number idx = luai_numadd(nvalue(ra), step); /* increment index */\n        lua_Number limit = nvalue(ra+1);\n        if (luai_numlt(0, step) ? luai_numle(idx, limit)\n                                : luai_numle(limit, idx)) {\n          dojump(L, pc, GETARG_sBx(i));  /* jump back */\n          setnvalue(ra, idx);  /* update internal index... */\n          setnvalue(ra+3, idx);  /* ...and external index */\n        }\n        continue;\n      }\n      case OP_FORPREP: {\n        const TValue *init = ra;\n        const TValue *plimit = ra+1;\n        const TValue *pstep = ra+2;\n        L->savedpc = pc;  /* next steps may throw errors */\n        if (!tonumber(init, ra))\n          luaG_runerror(L, LUA_QL(\"for\") \" initial value must be a number\");\n        else if (!tonumber(plimit, ra+1))\n          luaG_runerror(L, LUA_QL(\"for\") \" limit must be a number\");\n        else if (!tonumber(pstep, ra+2))\n          luaG_runerror(L, LUA_QL(\"for\") \" step must be a number\");\n        setnvalue(ra, luai_numsub(nvalue(ra), nvalue(pstep)));\n        dojump(L, pc, GETARG_sBx(i));\n        continue;\n      }\n      case OP_TFORLOOP: {\n        StkId cb = ra + 3;  /* call base */\n        setobjs2s(L, cb+2, ra+2);\n        setobjs2s(L, cb+1, ra+1);\n        setobjs2s(L, cb, ra);\n        L->top = cb+3;  /* func. + 2 args (state and index) */\n        Protect(luaD_call(L, cb, GETARG_C(i)));\n        L->top = L->ci->top;\n        cb = RA(i) + 3;  /* previous call may change the stack */\n        if (!ttisnil(cb)) {  /* continue loop? */\n          setobjs2s(L, cb-1, cb);  /* save control variable */\n          dojump(L, pc, GETARG_sBx(*pc));  /* jump back */\n        }\n        pc++;\n        continue;\n      }\n      case OP_SETLIST: {\n        int n = GETARG_B(i);\n        int c = GETARG_C(i);\n        int last;\n        Table *h;\n        if (n == 0) {\n          n = cast_int(L->top - ra) - 1;\n          L->top = L->ci->top;\n        }\n        if (c == 0) c = cast_int(*pc++);\n        runtime_check(L, ttistable(ra));\n        h = hvalue(ra);\n        last = ((c-1)*LFIELDS_PER_FLUSH) + n;\n        if (last > h->sizearray)  /* needs more space? */\n          luaH_resizearray(L, h, last);  /* pre-alloc it at once */\n        for (; n > 0; n--) {\n          TValue *val = ra+n;\n          setobj2t(L, luaH_setnum(L, h, last--), val);\n          luaC_barriert(L, h, val);\n        }\n        continue;\n      }\n      case OP_CLOSE: {\n        luaF_close(L, ra);\n        continue;\n      }\n      case OP_CLOSURE: {\n        Proto *p;\n        Closure *ncl;\n        int nup, j;\n        p = cl->p->p[GETARG_Bx(i)];\n        nup = p->nups;\n        ncl = luaF_newLclosure(L, nup, cl->env);\n        ncl->l.p = p;\n        for (j=0; j<nup; j++, pc++) {\n          if (GET_OPCODE(*pc) == OP_GETUPVAL)\n            ncl->l.upvals[j] = cl->upvals[GETARG_B(*pc)];\n          else {\n            lua_assert(GET_OPCODE(*pc) == OP_MOVE);\n            ncl->l.upvals[j] = luaF_findupval(L, base + GETARG_B(*pc));\n          }\n        }\n        setclvalue(L, ra, ncl);\n        Protect(luaC_checkGC(L));\n        continue;\n      }\n      case OP_VARARG: {\n        int b = GETARG_B(i) - 1;\n        int j;\n        CallInfo *ci = L->ci;\n        int n = cast_int(ci->base - ci->func) - cl->p->numparams - 1;\n        if (b == LUA_MULTRET) {\n          Protect(luaD_checkstack(L, n));\n          ra = RA(i);  /* previous call may change the stack */\n          b = n;\n          L->top = ra + n;\n        }\n        for (j = 0; j < b; j++) {\n          if (j < n) {\n            setobjs2s(L, ra + j, ci->base - n + j);\n          }\n          else {\n            setnilvalue(ra + j);\n          }\n        }\n        continue;\n      }\n    }\n  }\n}\n\n"
  },
  {
    "path": "deps/lua/src/lvm.h",
    "content": "/*\n** $Id: lvm.h,v 2.5.1.1 2007/12/27 13:02:25 roberto Exp $\n** Lua virtual machine\n** See Copyright Notice in lua.h\n*/\n\n#ifndef lvm_h\n#define lvm_h\n\n\n#include \"ldo.h\"\n#include \"lobject.h\"\n#include \"ltm.h\"\n\n\n#define tostring(L,o) ((ttype(o) == LUA_TSTRING) || (luaV_tostring(L, o)))\n\n#define tonumber(o,n)\t(ttype(o) == LUA_TNUMBER || \\\n                         (((o) = luaV_tonumber(o,n)) != NULL))\n\n#define equalobj(L,o1,o2) \\\n\t(ttype(o1) == ttype(o2) && luaV_equalval(L, o1, o2))\n\n\nLUAI_FUNC int luaV_lessthan (lua_State *L, const TValue *l, const TValue *r);\nLUAI_FUNC int luaV_equalval (lua_State *L, const TValue *t1, const TValue *t2);\nLUAI_FUNC const TValue *luaV_tonumber (const TValue *obj, TValue *n);\nLUAI_FUNC int luaV_tostring (lua_State *L, StkId obj);\nLUAI_FUNC void luaV_gettable (lua_State *L, const TValue *t, TValue *key,\n                                            StkId val);\nLUAI_FUNC void luaV_settable (lua_State *L, const TValue *t, TValue *key,\n                                            StkId val);\nLUAI_FUNC void luaV_execute (lua_State *L, int nexeccalls);\nLUAI_FUNC void luaV_concat (lua_State *L, int total, int last);\n\n#endif\n"
  },
  {
    "path": "deps/lua/src/lzio.c",
    "content": "/*\n** $Id: lzio.c,v 1.31.1.1 2007/12/27 13:02:25 roberto Exp $\n** a generic input stream interface\n** See Copyright Notice in lua.h\n*/\n\n\n#include <string.h>\n\n#define lzio_c\n#define LUA_CORE\n\n#include \"lua.h\"\n\n#include \"llimits.h\"\n#include \"lmem.h\"\n#include \"lstate.h\"\n#include \"lzio.h\"\n\n\nint luaZ_fill (ZIO *z) {\n  size_t size;\n  lua_State *L = z->L;\n  const char *buff;\n  lua_unlock(L);\n  buff = z->reader(L, z->data, &size);\n  lua_lock(L);\n  if (buff == NULL || size == 0) return EOZ;\n  z->n = size - 1;\n  z->p = buff;\n  return char2int(*(z->p++));\n}\n\n\nint luaZ_lookahead (ZIO *z) {\n  if (z->n == 0) {\n    if (luaZ_fill(z) == EOZ)\n      return EOZ;\n    else {\n      z->n++;  /* luaZ_fill removed first byte; put back it */\n      z->p--;\n    }\n  }\n  return char2int(*z->p);\n}\n\n\nvoid luaZ_init (lua_State *L, ZIO *z, lua_Reader reader, void *data) {\n  z->L = L;\n  z->reader = reader;\n  z->data = data;\n  z->n = 0;\n  z->p = NULL;\n}\n\n\n/* --------------------------------------------------------------- read --- */\nsize_t luaZ_read (ZIO *z, void *b, size_t n) {\n  while (n) {\n    size_t m;\n    if (luaZ_lookahead(z) == EOZ)\n      return n;  /* return number of missing bytes */\n    m = (n <= z->n) ? n : z->n;  /* min. between n and z->n */\n    memcpy(b, z->p, m);\n    z->n -= m;\n    z->p += m;\n    b = (char *)b + m;\n    n -= m;\n  }\n  return 0;\n}\n\n/* ------------------------------------------------------------------------ */\nchar *luaZ_openspace (lua_State *L, Mbuffer *buff, size_t n) {\n  if (n > buff->buffsize) {\n    if (n < LUA_MINBUFFER) n = LUA_MINBUFFER;\n    luaZ_resizebuffer(L, buff, n);\n  }\n  return buff->buffer;\n}\n\n\n"
  },
  {
    "path": "deps/lua/src/lzio.h",
    "content": "/*\n** $Id: lzio.h,v 1.21.1.1 2007/12/27 13:02:25 roberto Exp $\n** Buffered streams\n** See Copyright Notice in lua.h\n*/\n\n\n#ifndef lzio_h\n#define lzio_h\n\n#include \"lua.h\"\n\n#include \"lmem.h\"\n\n\n#define EOZ\t(-1)\t\t\t/* end of stream */\n\ntypedef struct Zio ZIO;\n\n#define char2int(c)\tcast(int, cast(unsigned char, (c)))\n\n#define zgetc(z)  (((z)->n--)>0 ?  char2int(*(z)->p++) : luaZ_fill(z))\n\ntypedef struct Mbuffer {\n  char *buffer;\n  size_t n;\n  size_t buffsize;\n} Mbuffer;\n\n#define luaZ_initbuffer(L, buff) ((buff)->buffer = NULL, (buff)->buffsize = 0)\n\n#define luaZ_buffer(buff)\t((buff)->buffer)\n#define luaZ_sizebuffer(buff)\t((buff)->buffsize)\n#define luaZ_bufflen(buff)\t((buff)->n)\n\n#define luaZ_resetbuffer(buff) ((buff)->n = 0)\n\n\n#define luaZ_resizebuffer(L, buff, size) \\\n\t(luaM_reallocvector(L, (buff)->buffer, (buff)->buffsize, size, char), \\\n\t(buff)->buffsize = size)\n\n#define luaZ_freebuffer(L, buff)\tluaZ_resizebuffer(L, buff, 0)\n\n\nLUAI_FUNC char *luaZ_openspace (lua_State *L, Mbuffer *buff, size_t n);\nLUAI_FUNC void luaZ_init (lua_State *L, ZIO *z, lua_Reader reader,\n                                        void *data);\nLUAI_FUNC size_t luaZ_read (ZIO* z, void* b, size_t n);\t/* read next n bytes */\nLUAI_FUNC int luaZ_lookahead (ZIO *z);\n\n\n\n/* --------- Private Part ------------------ */\n\nstruct Zio {\n  size_t n;\t\t\t/* bytes still unread */\n  const char *p;\t\t/* current position in buffer */\n  lua_Reader reader;\n  void* data;\t\t\t/* additional data */\n  lua_State *L;\t\t\t/* Lua state (for reader) */\n};\n\n\nLUAI_FUNC int luaZ_fill (ZIO *z);\n\n#endif\n"
  },
  {
    "path": "deps/lua/src/print.c",
    "content": "/*\n** $Id: print.c,v 1.55a 2006/05/31 13:30:05 lhf Exp $\n** print bytecodes\n** See Copyright Notice in lua.h\n*/\n\n#include <ctype.h>\n#include <stdio.h>\n\n#define luac_c\n#define LUA_CORE\n\n#include \"ldebug.h\"\n#include \"lobject.h\"\n#include \"lopcodes.h\"\n#include \"lundump.h\"\n\n#define PrintFunction\tluaU_print\n\n#define Sizeof(x)\t((int)sizeof(x))\n#define VOID(p)\t\t((const void*)(p))\n\nstatic void PrintString(const TString* ts)\n{\n const char* s=getstr(ts);\n size_t i,n=ts->tsv.len;\n putchar('\"');\n for (i=0; i<n; i++)\n {\n  int c=s[i];\n  switch (c)\n  {\n   case '\"': printf(\"\\\\\\\"\"); break;\n   case '\\\\': printf(\"\\\\\\\\\"); break;\n   case '\\a': printf(\"\\\\a\"); break;\n   case '\\b': printf(\"\\\\b\"); break;\n   case '\\f': printf(\"\\\\f\"); break;\n   case '\\n': printf(\"\\\\n\"); break;\n   case '\\r': printf(\"\\\\r\"); break;\n   case '\\t': printf(\"\\\\t\"); break;\n   case '\\v': printf(\"\\\\v\"); break;\n   default:\tif (isprint((unsigned char)c))\n   \t\t\tputchar(c);\n\t\telse\n\t\t\tprintf(\"\\\\%03u\",(unsigned char)c);\n  }\n }\n putchar('\"');\n}\n\nstatic void PrintConstant(const Proto* f, int i)\n{\n const TValue* o=&f->k[i];\n switch (ttype(o))\n {\n  case LUA_TNIL:\n\tprintf(\"nil\");\n\tbreak;\n  case LUA_TBOOLEAN:\n\tprintf(bvalue(o) ? \"true\" : \"false\");\n\tbreak;\n  case LUA_TNUMBER:\n\tprintf(LUA_NUMBER_FMT,nvalue(o));\n\tbreak;\n  case LUA_TSTRING:\n\tPrintString(rawtsvalue(o));\n\tbreak;\n  default:\t\t\t\t/* cannot happen */\n\tprintf(\"? type=%d\",ttype(o));\n\tbreak;\n }\n}\n\nstatic void PrintCode(const Proto* f)\n{\n const Instruction* code=f->code;\n int pc,n=f->sizecode;\n for (pc=0; pc<n; pc++)\n {\n  Instruction i=code[pc];\n  OpCode o=GET_OPCODE(i);\n  int a=GETARG_A(i);\n  int b=GETARG_B(i);\n  int c=GETARG_C(i);\n  int bx=GETARG_Bx(i);\n  int sbx=GETARG_sBx(i);\n  int line=getline(f,pc);\n  printf(\"\\t%d\\t\",pc+1);\n  if (line>0) printf(\"[%d]\\t\",line); else printf(\"[-]\\t\");\n  printf(\"%-9s\\t\",luaP_opnames[o]);\n  switch (getOpMode(o))\n  {\n   case iABC:\n    printf(\"%d\",a);\n    if (getBMode(o)!=OpArgN) printf(\" %d\",ISK(b) ? (-1-INDEXK(b)) : b);\n    if (getCMode(o)!=OpArgN) printf(\" %d\",ISK(c) ? (-1-INDEXK(c)) : c);\n    break;\n   case iABx:\n    if (getBMode(o)==OpArgK) printf(\"%d %d\",a,-1-bx); else printf(\"%d %d\",a,bx);\n    break;\n   case iAsBx:\n    if (o==OP_JMP) printf(\"%d\",sbx); else printf(\"%d %d\",a,sbx);\n    break;\n  }\n  switch (o)\n  {\n   case OP_LOADK:\n    printf(\"\\t; \"); PrintConstant(f,bx);\n    break;\n   case OP_GETUPVAL:\n   case OP_SETUPVAL:\n    printf(\"\\t; %s\", (f->sizeupvalues>0) ? getstr(f->upvalues[b]) : \"-\");\n    break;\n   case OP_GETGLOBAL:\n   case OP_SETGLOBAL:\n    printf(\"\\t; %s\",svalue(&f->k[bx]));\n    break;\n   case OP_GETTABLE:\n   case OP_SELF:\n    if (ISK(c)) { printf(\"\\t; \"); PrintConstant(f,INDEXK(c)); }\n    break;\n   case OP_SETTABLE:\n   case OP_ADD:\n   case OP_SUB:\n   case OP_MUL:\n   case OP_DIV:\n   case OP_POW:\n   case OP_EQ:\n   case OP_LT:\n   case OP_LE:\n    if (ISK(b) || ISK(c))\n    {\n     printf(\"\\t; \");\n     if (ISK(b)) PrintConstant(f,INDEXK(b)); else printf(\"-\");\n     printf(\" \");\n     if (ISK(c)) PrintConstant(f,INDEXK(c)); else printf(\"-\");\n    }\n    break;\n   case OP_JMP:\n   case OP_FORLOOP:\n   case OP_FORPREP:\n    printf(\"\\t; to %d\",sbx+pc+2);\n    break;\n   case OP_CLOSURE:\n    printf(\"\\t; %p\",VOID(f->p[bx]));\n    break;\n   case OP_SETLIST:\n    if (c==0) printf(\"\\t; %d\",(int)code[++pc]);\n    else printf(\"\\t; %d\",c);\n    break;\n   default:\n    break;\n  }\n  printf(\"\\n\");\n }\n}\n\n#define SS(x)\t(x==1)?\"\":\"s\"\n#define S(x)\tx,SS(x)\n\nstatic void PrintHeader(const Proto* f)\n{\n const char* s=getstr(f->source);\n if (*s=='@' || *s=='=')\n  s++;\n else if (*s==LUA_SIGNATURE[0])\n  s=\"(bstring)\";\n else\n  s=\"(string)\";\n printf(\"\\n%s <%s:%d,%d> (%d instruction%s, %d bytes at %p)\\n\",\n \t(f->linedefined==0)?\"main\":\"function\",s,\n\tf->linedefined,f->lastlinedefined,\n\tS(f->sizecode),f->sizecode*Sizeof(Instruction),VOID(f));\n printf(\"%d%s param%s, %d slot%s, %d upvalue%s, \",\n\tf->numparams,f->is_vararg?\"+\":\"\",SS(f->numparams),\n\tS(f->maxstacksize),S(f->nups));\n printf(\"%d local%s, %d constant%s, %d function%s\\n\",\n\tS(f->sizelocvars),S(f->sizek),S(f->sizep));\n}\n\nstatic void PrintConstants(const Proto* f)\n{\n int i,n=f->sizek;\n printf(\"constants (%d) for %p:\\n\",n,VOID(f));\n for (i=0; i<n; i++)\n {\n  printf(\"\\t%d\\t\",i+1);\n  PrintConstant(f,i);\n  printf(\"\\n\");\n }\n}\n\nstatic void PrintLocals(const Proto* f)\n{\n int i,n=f->sizelocvars;\n printf(\"locals (%d) for %p:\\n\",n,VOID(f));\n for (i=0; i<n; i++)\n {\n  printf(\"\\t%d\\t%s\\t%d\\t%d\\n\",\n  i,getstr(f->locvars[i].varname),f->locvars[i].startpc+1,f->locvars[i].endpc+1);\n }\n}\n\nstatic void PrintUpvalues(const Proto* f)\n{\n int i,n=f->sizeupvalues;\n printf(\"upvalues (%d) for %p:\\n\",n,VOID(f));\n if (f->upvalues==NULL) return;\n for (i=0; i<n; i++)\n {\n  printf(\"\\t%d\\t%s\\n\",i,getstr(f->upvalues[i]));\n }\n}\n\nvoid PrintFunction(const Proto* f, int full)\n{\n int i,n=f->sizep;\n PrintHeader(f);\n PrintCode(f);\n if (full)\n {\n  PrintConstants(f);\n  PrintLocals(f);\n  PrintUpvalues(f);\n }\n for (i=0; i<n; i++) PrintFunction(f->p[i],full);\n}\n"
  },
  {
    "path": "deps/lua/src/strbuf.c",
    "content": "/* strbuf - string buffer routines\n *\n * Copyright (c) 2010-2011  Mark Pulford <mark@kyne.com.au>\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <stdarg.h>\n#include <string.h>\n\n#include \"strbuf.h\"\n\nvoid die(const char *fmt, ...)\n{\n    va_list arg;\n\n    va_start(arg, fmt);\n    vfprintf(stderr, fmt, arg);\n    va_end(arg);\n    fprintf(stderr, \"\\n\");\n\n    exit(-1);\n}\n\nvoid strbuf_init(strbuf_t *s, int len)\n{\n    int size;\n\n    if (len <= 0)\n        size = STRBUF_DEFAULT_SIZE;\n    else\n        size = len + 1;         /* \\0 terminator */\n\n    s->buf = NULL;\n    s->size = size;\n    s->length = 0;\n    s->increment = STRBUF_DEFAULT_INCREMENT;\n    s->dynamic = 0;\n    s->reallocs = 0;\n    s->debug = 0;\n\n    s->buf = malloc(size);\n    if (!s->buf)\n        die(\"Out of memory\");\n\n    strbuf_ensure_null(s);\n}\n\nstrbuf_t *strbuf_new(int len)\n{\n    strbuf_t *s;\n\n    s = malloc(sizeof(strbuf_t));\n    if (!s)\n        die(\"Out of memory\");\n\n    strbuf_init(s, len);\n\n    /* Dynamic strbuf allocation / deallocation */\n    s->dynamic = 1;\n\n    return s;\n}\n\nvoid strbuf_set_increment(strbuf_t *s, int increment)\n{\n    /* Increment > 0:  Linear buffer growth rate\n     * Increment < -1: Exponential buffer growth rate */\n    if (increment == 0 || increment == -1)\n        die(\"BUG: Invalid string increment\");\n\n    s->increment = increment;\n}\n\nstatic inline void debug_stats(strbuf_t *s)\n{\n    if (s->debug) {\n        fprintf(stderr, \"strbuf(%lx) reallocs: %d, length: %d, size: %d\\n\",\n                (long)s, s->reallocs, s->length, s->size);\n    }\n}\n\n/* If strbuf_t has not been dynamically allocated, strbuf_free() can\n * be called any number of times strbuf_init() */\nvoid strbuf_free(strbuf_t *s)\n{\n    debug_stats(s);\n\n    if (s->buf) {\n        free(s->buf);\n        s->buf = NULL;\n    }\n    if (s->dynamic)\n        free(s);\n}\n\nchar *strbuf_free_to_string(strbuf_t *s, int *len)\n{\n    char *buf;\n\n    debug_stats(s);\n\n    strbuf_ensure_null(s);\n\n    buf = s->buf;\n    if (len)\n        *len = s->length;\n\n    if (s->dynamic)\n        free(s);\n\n    return buf;\n}\n\nstatic int calculate_new_size(strbuf_t *s, int len)\n{\n    int reqsize, newsize;\n\n    if (len <= 0)\n        die(\"BUG: Invalid strbuf length requested\");\n\n    /* Ensure there is room for optional NULL termination */\n    reqsize = len + 1;\n\n    /* If the user has requested to shrink the buffer, do it exactly */\n    if (s->size > reqsize)\n        return reqsize;\n\n    newsize = s->size;\n    if (s->increment < 0) {\n        /* Exponential sizing */\n        while (newsize < reqsize)\n            newsize *= -s->increment;\n    } else {\n        /* Linear sizing */\n        newsize = ((newsize + s->increment - 1) / s->increment) * s->increment;\n    }\n\n    return newsize;\n}\n\n\n/* Ensure strbuf can handle a string length bytes long (ignoring NULL\n * optional termination). */\nvoid strbuf_resize(strbuf_t *s, int len)\n{\n    int newsize;\n\n    newsize = calculate_new_size(s, len);\n\n    if (s->debug > 1) {\n        fprintf(stderr, \"strbuf(%lx) resize: %d => %d\\n\",\n                (long)s, s->size, newsize);\n    }\n\n    s->size = newsize;\n    s->buf = realloc(s->buf, s->size);\n    if (!s->buf)\n        die(\"Out of memory\");\n    s->reallocs++;\n}\n\nvoid strbuf_append_string(strbuf_t *s, const char *str)\n{\n    int space, i;\n\n    space = strbuf_empty_length(s);\n\n    for (i = 0; str[i]; i++) {\n        if (space < 1) {\n            strbuf_resize(s, s->length + 1);\n            space = strbuf_empty_length(s);\n        }\n\n        s->buf[s->length] = str[i];\n        s->length++;\n        space--;\n    }\n}\n\n/* strbuf_append_fmt() should only be used when an upper bound\n * is known for the output string. */\nvoid strbuf_append_fmt(strbuf_t *s, int len, const char *fmt, ...)\n{\n    va_list arg;\n    int fmt_len;\n\n    strbuf_ensure_empty_length(s, len);\n\n    va_start(arg, fmt);\n    fmt_len = vsnprintf(s->buf + s->length, len, fmt, arg);\n    va_end(arg);\n\n    if (fmt_len < 0)\n        die(\"BUG: Unable to convert number\");  /* This should never happen.. */\n\n    s->length += fmt_len;\n}\n\n/* strbuf_append_fmt_retry() can be used when the there is no known\n * upper bound for the output string. */\nvoid strbuf_append_fmt_retry(strbuf_t *s, const char *fmt, ...)\n{\n    va_list arg;\n    int fmt_len, try;\n    int empty_len;\n\n    /* If the first attempt to append fails, resize the buffer appropriately\n     * and try again */\n    for (try = 0; ; try++) {\n        va_start(arg, fmt);\n        /* Append the new formatted string */\n        /* fmt_len is the length of the string required, excluding the\n         * trailing NULL */\n        empty_len = strbuf_empty_length(s);\n        /* Add 1 since there is also space to store the terminating NULL. */\n        fmt_len = vsnprintf(s->buf + s->length, empty_len + 1, fmt, arg);\n        va_end(arg);\n\n        if (fmt_len <= empty_len)\n            break;  /* SUCCESS */\n        if (try > 0)\n            die(\"BUG: length of formatted string changed\");\n\n        strbuf_resize(s, s->length + fmt_len);\n    }\n\n    s->length += fmt_len;\n}\n\n/* vi:ai et sw=4 ts=4:\n */\n"
  },
  {
    "path": "deps/lua/src/strbuf.h",
    "content": "/* strbuf - String buffer routines\n *\n * Copyright (c) 2010-2011  Mark Pulford <mark@kyne.com.au>\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#include <stdlib.h>\n#include <stdarg.h>\n\n/* Size: Total bytes allocated to *buf\n * Length: String length, excluding optional NULL terminator.\n * Increment: Allocation increments when resizing the string buffer.\n * Dynamic: True if created via strbuf_new()\n */\n\ntypedef struct {\n    char *buf;\n    int size;\n    int length;\n    int increment;\n    int dynamic;\n    int reallocs;\n    int debug;\n} strbuf_t;\n\n#ifndef STRBUF_DEFAULT_SIZE\n#define STRBUF_DEFAULT_SIZE 1023\n#endif\n#ifndef STRBUF_DEFAULT_INCREMENT\n#define STRBUF_DEFAULT_INCREMENT -2\n#endif\n\n/* Initialise */\nextern strbuf_t *strbuf_new(int len);\nextern void strbuf_init(strbuf_t *s, int len);\nextern void strbuf_set_increment(strbuf_t *s, int increment);\n\n/* Release */\nextern void strbuf_free(strbuf_t *s);\nextern char *strbuf_free_to_string(strbuf_t *s, int *len);\n\n/* Management */\nextern void strbuf_resize(strbuf_t *s, int len);\nstatic int strbuf_empty_length(strbuf_t *s);\nstatic int strbuf_length(strbuf_t *s);\nstatic char *strbuf_string(strbuf_t *s, int *len);\nstatic  void strbuf_ensure_empty_length(strbuf_t *s, int len);\n\n/* Update */\nextern void strbuf_append_fmt(strbuf_t *s, int len, const char *fmt, ...);\nextern void strbuf_append_fmt_retry(strbuf_t *s, const char *format, ...);\nstatic void strbuf_append_mem(strbuf_t *s, const char *c, int len);\nextern void strbuf_append_string(strbuf_t *s, const char *str);\nstatic void strbuf_append_char(strbuf_t *s, const char c);\nstatic void strbuf_ensure_null(strbuf_t *s);\n\n/* Reset string for before use */\nstatic inline void strbuf_reset(strbuf_t *s)\n{\n    s->length = 0;\n}\n\nstatic inline int strbuf_allocated(strbuf_t *s)\n{\n    return s->buf != NULL;\n}\n\n/* Return bytes remaining in the string buffer\n * Ensure there is space for a NULL terminator. */\nstatic inline int strbuf_empty_length(strbuf_t *s)\n{\n    return s->size - s->length - 1;\n}\n\nstatic inline void strbuf_ensure_empty_length(strbuf_t *s, int len)\n{\n    if (len > strbuf_empty_length(s))\n        strbuf_resize(s, s->length + len);\n}\n\nstatic inline int strbuf_length(strbuf_t *s)\n{\n    return s->length;\n}\n\nstatic inline void strbuf_append_char(strbuf_t *s, const char c)\n{\n    strbuf_ensure_empty_length(s, 1);\n    s->buf[s->length++] = c;\n}\n\nstatic inline void strbuf_append_char_unsafe(strbuf_t *s, const char c)\n{\n    s->buf[s->length++] = c;\n}\n\nstatic inline void strbuf_append_mem(strbuf_t *s, const char *c, int len)\n{\n    strbuf_ensure_empty_length(s, len);\n    memcpy(s->buf + s->length, c, len);\n    s->length += len;\n}\n\nstatic inline void strbuf_append_mem_unsafe(strbuf_t *s, const char *c, int len)\n{\n    memcpy(s->buf + s->length, c, len);\n    s->length += len;\n}\n\nstatic inline void strbuf_ensure_null(strbuf_t *s)\n{\n    s->buf[s->length] = 0;\n}\n\nstatic inline char *strbuf_string(strbuf_t *s, int *len)\n{\n    if (len)\n        *len = s->length;\n\n    return s->buf;\n}\n\n/* vi:ai et sw=4 ts=4:\n */\n"
  },
  {
    "path": "deps/lua/test/README",
    "content": "These are simple tests for Lua.  Some of them contain useful code.\nThey are meant to be run to make sure Lua is built correctly and also\nto be read, to see how Lua programs look.\n\nHere is a one-line summary of each program:\n\n   bisect.lua\t\tbisection method for solving non-linear equations\n   cf.lua\t\ttemperature conversion table (celsius to farenheit)\n   echo.lua             echo command line arguments\n   env.lua              environment variables as automatic global variables\n   factorial.lua\tfactorial without recursion\n   fib.lua\t\tfibonacci function with cache\n   fibfor.lua\t\tfibonacci numbers with coroutines and generators\n   globals.lua\t\treport global variable usage\n   hello.lua\t\tthe first program in every language\n   life.lua\t\tConway's Game of Life\n   luac.lua\t \tbare-bones luac\n   printf.lua\t\tan implementation of printf\n   readonly.lua\t\tmake global variables readonly\n   sieve.lua\t\tthe sieve of of Eratosthenes programmed with coroutines\n   sort.lua\t\ttwo implementations of a sort function\n   table.lua\t\tmake table, grouping all data for the same item\n   trace-calls.lua\ttrace calls\n   trace-globals.lua\ttrace assigments to global variables\n   xd.lua\t\thex dump\n\n"
  },
  {
    "path": "deps/lua/test/bisect.lua",
    "content": "-- bisection method for solving non-linear equations\n\ndelta=1e-6\t-- tolerance\n\nfunction bisect(f,a,b,fa,fb)\n local c=(a+b)/2\n io.write(n,\" c=\",c,\" a=\",a,\" b=\",b,\"\\n\")\n if c==a or c==b or math.abs(a-b)<delta then return c,b-a end\n n=n+1\n local fc=f(c)\n if fa*fc<0 then return bisect(f,a,c,fa,fc) else return bisect(f,c,b,fc,fb) end\nend\n\n-- find root of f in the inverval [a,b]. needs f(a)*f(b)<0\nfunction solve(f,a,b)\n n=0\n local z,e=bisect(f,a,b,f(a),f(b))\n io.write(string.format(\"after %d steps, root is %.17g with error %.1e, f=%.1e\\n\",n,z,e,f(z)))\nend\n\n-- our function\nfunction f(x)\n return x*x*x-x-1\nend\n\n-- find zero in [1,2]\nsolve(f,1,2)\n"
  },
  {
    "path": "deps/lua/test/cf.lua",
    "content": "-- temperature conversion table (celsius to farenheit)\n\nfor c0=-20,50-1,10 do\n\tio.write(\"C \")\n\tfor c=c0,c0+10-1 do\n\t\tio.write(string.format(\"%3.0f \",c))\n\tend\n\tio.write(\"\\n\")\n\t\n\tio.write(\"F \")\n\tfor c=c0,c0+10-1 do\n\t\tf=(9/5)*c+32\n\t\tio.write(string.format(\"%3.0f \",f))\n\tend\n\tio.write(\"\\n\\n\")\nend\n"
  },
  {
    "path": "deps/lua/test/echo.lua",
    "content": "-- echo command line arguments\n\nfor i=0,table.getn(arg) do\n print(i,arg[i])\nend\n"
  },
  {
    "path": "deps/lua/test/env.lua",
    "content": "-- read environment variables as if they were global variables\n\nlocal f=function (t,i) return os.getenv(i) end\nsetmetatable(getfenv(),{__index=f})\n\n-- an example\nprint(a,USER,PATH)\n"
  },
  {
    "path": "deps/lua/test/factorial.lua",
    "content": "-- function closures are powerful\n\n-- traditional fixed-point operator from functional programming\nY = function (g)\n      local a = function (f) return f(f) end\n      return a(function (f)\n                 return g(function (x)\n                             local c=f(f)\n                             return c(x)\n                           end)\n               end)\nend\n\n\n-- factorial without recursion\nF = function (f)\n      return function (n)\n               if n == 0 then return 1\n               else return n*f(n-1) end\n             end\n    end\n\nfactorial = Y(F)   -- factorial is the fixed point of F\n\n-- now test it\nfunction test(x)\n\tio.write(x,\"! = \",factorial(x),\"\\n\")\nend\n\nfor n=0,16 do\n\ttest(n)\nend\n"
  },
  {
    "path": "deps/lua/test/fib.lua",
    "content": "-- fibonacci function with cache\n\n-- very inefficient fibonacci function\nfunction fib(n)\n\tN=N+1\n\tif n<2 then\n\t\treturn n\n\telse\n\t\treturn fib(n-1)+fib(n-2)\n\tend\nend\n\n-- a general-purpose value cache\nfunction cache(f)\n\tlocal c={}\n\treturn function (x)\n\t\tlocal y=c[x]\n\t\tif not y then\n\t\t\ty=f(x)\n\t\t\tc[x]=y\n\t\tend\n\t\treturn y\n\tend\nend\n\n-- run and time it\nfunction test(s,f)\n\tN=0\n\tlocal c=os.clock()\n\tlocal v=f(n)\n\tlocal t=os.clock()-c\n\tprint(s,n,v,t,N)\nend\n\nn=arg[1] or 24\t\t-- for other values, do lua fib.lua XX\nn=tonumber(n)\nprint(\"\",\"n\",\"value\",\"time\",\"evals\")\ntest(\"plain\",fib)\nfib=cache(fib)\ntest(\"cached\",fib)\n"
  },
  {
    "path": "deps/lua/test/fibfor.lua",
    "content": "-- example of for with generator functions\n\nfunction generatefib (n)\n  return coroutine.wrap(function ()\n    local a,b = 1, 1\n    while a <= n do\n      coroutine.yield(a)\n      a, b = b, a+b\n    end\n  end)\nend\n\nfor i in generatefib(1000) do print(i) end\n"
  },
  {
    "path": "deps/lua/test/globals.lua",
    "content": "-- reads luac listings and reports global variable usage\n-- lines where a global is written to are marked with \"*\"\n-- typical usage: luac -p -l file.lua | lua globals.lua | sort | lua table.lua\n\nwhile 1 do\n local s=io.read()\n if s==nil then break end\n local ok,_,l,op,g=string.find(s,\"%[%-?(%d*)%]%s*([GS])ETGLOBAL.-;%s+(.*)$\")\n if ok then\n  if op==\"S\" then op=\"*\" else op=\"\" end\n  io.write(g,\"\\t\",l,op,\"\\n\")\n end\nend\n"
  },
  {
    "path": "deps/lua/test/hello.lua",
    "content": "-- the first program in every language\n\nio.write(\"Hello world, from \",_VERSION,\"!\\n\")\n"
  },
  {
    "path": "deps/lua/test/life.lua",
    "content": "-- life.lua\n-- original by Dave Bollinger <DBollinger@compuserve.com> posted to lua-l\n-- modified to use ANSI terminal escape sequences\n-- modified to use for instead of while\n\nlocal write=io.write\n\nALIVE=\"\"\tDEAD=\"\"\nALIVE=\"O\"\tDEAD=\"-\"\n\nfunction delay() -- NOTE: SYSTEM-DEPENDENT, adjust as necessary\n  for i=1,10000 do end\n  -- local i=os.clock()+1 while(os.clock()<i) do end\nend\n\nfunction ARRAY2D(w,h)\n  local t = {w=w,h=h}\n  for y=1,h do\n    t[y] = {}\n    for x=1,w do\n      t[y][x]=0\n    end\n  end\n  return t\nend\n\n_CELLS = {}\n\n-- give birth to a \"shape\" within the cell array\nfunction _CELLS:spawn(shape,left,top)\n  for y=0,shape.h-1 do\n    for x=0,shape.w-1 do\n      self[top+y][left+x] = shape[y*shape.w+x+1]\n    end\n  end\nend\n\n-- run the CA and produce the next generation\nfunction _CELLS:evolve(next)\n  local ym1,y,yp1,yi=self.h-1,self.h,1,self.h\n  while yi > 0 do\n    local xm1,x,xp1,xi=self.w-1,self.w,1,self.w\n    while xi > 0 do\n      local sum = self[ym1][xm1] + self[ym1][x] + self[ym1][xp1] +\n                  self[y][xm1] + self[y][xp1] +\n                  self[yp1][xm1] + self[yp1][x] + self[yp1][xp1]\n      next[y][x] = ((sum==2) and self[y][x]) or ((sum==3) and 1) or 0\n      xm1,x,xp1,xi = x,xp1,xp1+1,xi-1\n    end\n    ym1,y,yp1,yi = y,yp1,yp1+1,yi-1\n  end\nend\n\n-- output the array to screen\nfunction _CELLS:draw()\n  local out=\"\" -- accumulate to reduce flicker\n  for y=1,self.h do\n   for x=1,self.w do\n      out=out..(((self[y][x]>0) and ALIVE) or DEAD)\n    end\n    out=out..\"\\n\"\n  end\n  write(out)\nend\n\n-- constructor\nfunction CELLS(w,h)\n  local c = ARRAY2D(w,h)\n  c.spawn = _CELLS.spawn\n  c.evolve = _CELLS.evolve\n  c.draw = _CELLS.draw\n  return c\nend\n\n--\n-- shapes suitable for use with spawn() above\n--\nHEART = { 1,0,1,1,0,1,1,1,1; w=3,h=3 }\nGLIDER = { 0,0,1,1,0,1,0,1,1; w=3,h=3 }\nEXPLODE = { 0,1,0,1,1,1,1,0,1,0,1,0; w=3,h=4 }\nFISH = { 0,1,1,1,1,1,0,0,0,1,0,0,0,0,1,1,0,0,1,0; w=5,h=4 }\nBUTTERFLY = { 1,0,0,0,1,0,1,1,1,0,1,0,0,0,1,1,0,1,0,1,1,0,0,0,1; w=5,h=5 }\n\n-- the main routine\nfunction LIFE(w,h)\n  -- create two arrays\n  local thisgen = CELLS(w,h)\n  local nextgen = CELLS(w,h)\n\n  -- create some life\n  -- about 1000 generations of fun, then a glider steady-state\n  thisgen:spawn(GLIDER,5,4)\n  thisgen:spawn(EXPLODE,25,10)\n  thisgen:spawn(FISH,4,12)\n\n  -- run until break\n  local gen=1\n  write(\"\\027[2J\")\t-- ANSI clear screen\n  while 1 do\n    thisgen:evolve(nextgen)\n    thisgen,nextgen = nextgen,thisgen\n    write(\"\\027[H\")\t-- ANSI home cursor\n    thisgen:draw()\n    write(\"Life - generation \",gen,\"\\n\")\n    gen=gen+1\n    if gen>2000 then break end\n    --delay()\t\t-- no delay\n  end\nend\n\nLIFE(40,20)\n"
  },
  {
    "path": "deps/lua/test/luac.lua",
    "content": "-- bare-bones luac in Lua\n-- usage: lua luac.lua file.lua\n\nassert(arg[1]~=nil and arg[2]==nil,\"usage: lua luac.lua file.lua\")\nf=assert(io.open(\"luac.out\",\"wb\"))\nassert(f:write(string.dump(assert(loadfile(arg[1])))))\nassert(f:close())\n"
  },
  {
    "path": "deps/lua/test/printf.lua",
    "content": "-- an implementation of printf\n\nfunction printf(...)\n io.write(string.format(...))\nend\n\nprintf(\"Hello %s from %s on %s\\n\",os.getenv\"USER\" or \"there\",_VERSION,os.date())\n"
  },
  {
    "path": "deps/lua/test/readonly.lua",
    "content": "-- make global variables readonly\n\nlocal f=function (t,i) error(\"cannot redefine global variable `\"..i..\"'\",2) end\nlocal g={}\nlocal G=getfenv()\nsetmetatable(g,{__index=G,__newindex=f})\nsetfenv(1,g)\n\n-- an example\nrawset(g,\"x\",3)\nx=2\ny=1\t-- cannot redefine `y'\n"
  },
  {
    "path": "deps/lua/test/sieve.lua",
    "content": "-- the sieve of of Eratosthenes programmed with coroutines\n-- typical usage: lua -e N=1000 sieve.lua | column\n\n-- generate all the numbers from 2 to n\nfunction gen (n)\n  return coroutine.wrap(function ()\n    for i=2,n do coroutine.yield(i) end\n  end)\nend\n\n-- filter the numbers generated by `g', removing multiples of `p'\nfunction filter (p, g)\n  return coroutine.wrap(function ()\n    while 1 do\n      local n = g()\n      if n == nil then return end\n      if math.mod(n, p) ~= 0 then coroutine.yield(n) end\n    end\n  end)\nend\n\nN=N or 1000\t\t-- from command line\nx = gen(N)\t\t-- generate primes up to N\nwhile 1 do\n  local n = x()\t\t-- pick a number until done\n  if n == nil then break end\n  print(n)\t\t-- must be a prime number\n  x = filter(n, x)\t-- now remove its multiples\nend\n"
  },
  {
    "path": "deps/lua/test/sort.lua",
    "content": "-- two implementations of a sort function\n-- this is an example only. Lua has now a built-in function \"sort\"\n\n-- extracted from Programming Pearls, page 110\nfunction qsort(x,l,u,f)\n if l<u then\n  local m=math.random(u-(l-1))+l-1\t-- choose a random pivot in range l..u\n  x[l],x[m]=x[m],x[l]\t\t\t-- swap pivot to first position\n  local t=x[l]\t\t\t\t-- pivot value\n  m=l\n  local i=l+1\n  while i<=u do\n    -- invariant: x[l+1..m] < t <= x[m+1..i-1]\n    if f(x[i],t) then\n      m=m+1\n      x[m],x[i]=x[i],x[m]\t\t-- swap x[i] and x[m]\n    end\n    i=i+1\n  end\n  x[l],x[m]=x[m],x[l]\t\t\t-- swap pivot to a valid place\n  -- x[l+1..m-1] < x[m] <= x[m+1..u]\n  qsort(x,l,m-1,f)\n  qsort(x,m+1,u,f)\n end\nend\n\nfunction selectionsort(x,n,f)\n local i=1\n while i<=n do\n  local m,j=i,i+1\n  while j<=n do\n   if f(x[j],x[m]) then m=j end\n   j=j+1\n  end\n x[i],x[m]=x[m],x[i]\t\t\t-- swap x[i] and x[m]\n i=i+1\n end\nend\n\nfunction show(m,x)\n io.write(m,\"\\n\\t\")\n local i=1\n while x[i] do\n  io.write(x[i])\n  i=i+1\n  if x[i] then io.write(\",\") end\n end\n io.write(\"\\n\")\nend\n\nfunction testsorts(x)\n local n=1\n while x[n] do n=n+1 end; n=n-1\t\t-- count elements\n show(\"original\",x)\n qsort(x,1,n,function (x,y) return x<y end)\n show(\"after quicksort\",x)\n selectionsort(x,n,function (x,y) return x>y end)\n show(\"after reverse selection sort\",x)\n qsort(x,1,n,function (x,y) return x<y end)\n show(\"after quicksort again\",x)\nend\n\n-- array to be sorted\nx={\"Jan\",\"Feb\",\"Mar\",\"Apr\",\"May\",\"Jun\",\"Jul\",\"Aug\",\"Sep\",\"Oct\",\"Nov\",\"Dec\"}\n\ntestsorts(x)\n"
  },
  {
    "path": "deps/lua/test/table.lua",
    "content": "-- make table, grouping all data for the same item\n-- input is 2 columns (item, data)\n\nlocal A\nwhile 1 do\n local l=io.read()\n if l==nil then break end\n local _,_,a,b=string.find(l,'\"?([_%w]+)\"?%s*(.*)$')\n if a~=A then A=a io.write(\"\\n\",a,\":\") end\n io.write(\" \",b)\nend\nio.write(\"\\n\")\n"
  },
  {
    "path": "deps/lua/test/trace-calls.lua",
    "content": "-- trace calls\n-- example: lua -ltrace-calls bisect.lua\n\nlocal level=0\n\nlocal function hook(event)\n local t=debug.getinfo(3)\n io.write(level,\" >>> \",string.rep(\" \",level))\n if t~=nil and t.currentline>=0 then io.write(t.short_src,\":\",t.currentline,\" \") end\n t=debug.getinfo(2)\n if event==\"call\" then\n  level=level+1\n else\n  level=level-1 if level<0 then level=0 end\n end\n if t.what==\"main\" then\n  if event==\"call\" then\n   io.write(\"begin \",t.short_src)\n  else\n   io.write(\"end \",t.short_src)\n  end\n elseif t.what==\"Lua\" then\n-- table.foreach(t,print)\n  io.write(event,\" \",t.name or \"(Lua)\",\" <\",t.linedefined,\":\",t.short_src,\">\")\n else\n io.write(event,\" \",t.name or \"(C)\",\" [\",t.what,\"] \")\n end\n io.write(\"\\n\")\nend\n\ndebug.sethook(hook,\"cr\")\nlevel=0\n"
  },
  {
    "path": "deps/lua/test/trace-globals.lua",
    "content": "-- trace assigments to global variables\n\ndo\n -- a tostring that quotes strings. note the use of the original tostring.\n local _tostring=tostring\n local tostring=function(a)\n  if type(a)==\"string\" then\n   return string.format(\"%q\",a)\n  else\n   return _tostring(a)\n  end\n end\n\n local log=function (name,old,new)\n  local t=debug.getinfo(3,\"Sl\")\n  local line=t.currentline\n  io.write(t.short_src)\n  if line>=0 then io.write(\":\",line) end\n  io.write(\": \",name,\" is now \",tostring(new),\" (was \",tostring(old),\")\",\"\\n\")\n end\n\n local g={}\n local set=function (t,name,value)\n  log(name,g[name],value)\n  g[name]=value\n end\n setmetatable(getfenv(),{__index=g,__newindex=set})\nend\n\n-- an example\n\na=1\nb=2\na=10\nb=20\nb=nil\nb=200\nprint(a,b,c)\n"
  },
  {
    "path": "deps/lua/test/xd.lua",
    "content": "-- hex dump\n-- usage: lua xd.lua < file\n\nlocal offset=0\nwhile true do\n local s=io.read(16)\n if s==nil then return end\n io.write(string.format(\"%08X  \",offset))\n string.gsub(s,\"(.)\",\n\tfunction (c) io.write(string.format(\"%02X \",string.byte(c))) end)\n io.write(string.rep(\" \",3*(16-string.len(s))))\n io.write(\" \",string.gsub(s,\"%c\",\".\"),\"\\n\") \n offset=offset+16\nend\n"
  },
  {
    "path": "redis.conf",
    "content": "# Redis configuration file example\n\n# Note on units: when memory size is needed, it is possible to specify\n# it in the usual form of 1k 5GB 4M and so forth:\n#\n# 1k => 1000 bytes\n# 1kb => 1024 bytes\n# 1m => 1000000 bytes\n# 1mb => 1024*1024 bytes\n# 1g => 1000000000 bytes\n# 1gb => 1024*1024*1024 bytes\n#\n# units are case insensitive so 1GB 1Gb 1gB are all the same.\n\n################################## INCLUDES ###################################\n\n# Include one or more other config files here.  This is useful if you\n# have a standard template that goes to all Redis server but also need\n# to customize a few per-server settings.  Include files can include\n# other files, so use this wisely.\n#\n# Notice option \"include\" won't be rewritten by command \"CONFIG REWRITE\"\n# from admin or Redis Sentinel. Since Redis always uses the last processed\n# line as value of a configuration directive, you'd better put includes\n# at the beginning of this file to avoid overwriting config change at runtime.\n#\n# If instead you are interested in using includes to override configuration\n# options, it is better to use include as the last line.\n#\n# include /path/to/local.conf\n# include /path/to/other.conf\n\n################################ GENERAL  #####################################\n\n# By default Redis does not run as a daemon. Use 'yes' if you need it.\n# Note that Redis will write a pid file in /var/run/redis.pid when daemonized.\ndaemonize no\n\n# When running daemonized, Redis writes a pid file in /var/run/redis.pid by\n# default. You can specify a custom pid file location here.\npidfile /var/run/redis.pid\n\n# Accept connections on the specified port, default is 6379.\n# If port 0 is specified Redis will not listen on a TCP socket.\nport 6379\n\n# TCP listen() backlog.\n#\n# In high requests-per-second environments you need an high backlog in order\n# to avoid slow clients connections issues. Note that the Linux kernel\n# will silently truncate it to the value of /proc/sys/net/core/somaxconn so\n# make sure to raise both the value of somaxconn and tcp_max_syn_backlog\n# in order to get the desired effect.\ntcp-backlog 511\n\n# By default Redis listens for connections from all the network interfaces\n# available on the server. It is possible to listen to just one or multiple\n# interfaces using the \"bind\" configuration directive, followed by one or\n# more IP addresses.\n#\n# Examples:\n#\n# bind 192.168.1.100 10.0.0.1\n# bind 127.0.0.1\n\n# Specify the path for the Unix socket that will be used to listen for\n# incoming connections. There is no default, so Redis will not listen\n# on a unix socket when not specified.\n#\n# unixsocket /tmp/redis.sock\n# unixsocketperm 755\n\n# Close the connection after a client is idle for N seconds (0 to disable)\ntimeout 0\n\n# TCP keepalive.\n#\n# If non-zero, use SO_KEEPALIVE to send TCP ACKs to clients in absence\n# of communication. This is useful for two reasons:\n#\n# 1) Detect dead peers.\n# 2) Take the connection alive from the point of view of network\n#    equipment in the middle.\n#\n# On Linux, the specified value (in seconds) is the period used to send ACKs.\n# Note that to close the connection the double of the time is needed.\n# On other kernels the period depends on the kernel configuration.\n#\n# A reasonable value for this option is 60 seconds.\ntcp-keepalive 0\n\n# Specify the server verbosity level.\n# This can be one of:\n# debug (a lot of information, useful for development/testing)\n# verbose (many rarely useful info, but not a mess like the debug level)\n# notice (moderately verbose, what you want in production probably)\n# warning (only very important / critical messages are logged)\nloglevel notice\n\n# Specify the log file name. Also the empty string can be used to force\n# Redis to log on the standard output. Note that if you use standard\n# output for logging but daemonize, logs will be sent to /dev/null\nlogfile \"\"\n\n# To enable logging to the system logger, just set 'syslog-enabled' to yes,\n# and optionally update the other syslog parameters to suit your needs.\n# syslog-enabled no\n\n# Specify the syslog identity.\n# syslog-ident redis\n\n# Specify the syslog facility. Must be USER or between LOCAL0-LOCAL7.\n# syslog-facility local0\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 16\n\n################################ SNAPSHOTTING  ################################\n#\n# Save the DB on disk:\n#\n#   save <seconds> <changes>\n#\n#   Will save the DB if both the given number of seconds and the given\n#   number of write operations against the DB occurred.\n#\n#   In the example below the behaviour will be to save:\n#   after 900 sec (15 min) if at least 1 key changed\n#   after 300 sec (5 min) if at least 10 keys changed\n#   after 60 sec if at least 10000 keys changed\n#\n#   Note: you can disable saving at all commenting all the \"save\" lines.\n#\n#   It is also possible to remove all the previously configured save\n#   points by adding a save directive with a single empty string argument\n#   like in the following example:\n#\n#   save \"\"\n\nsave 900 1\nsave 300 10\nsave 60 10000\n\n# By default Redis will stop accepting writes if RDB snapshots are enabled\n# (at least one save point) and the latest background save failed.\n# This will make the user aware (in a hard way) that data is not persisting\n# on disk properly, otherwise chances are that no one will notice and some\n# disaster will happen.\n#\n# If the background saving process will start working again Redis will\n# automatically allow writes again.\n#\n# However if you have setup your proper monitoring of the Redis server\n# and persistence, you may want to disable this feature so that Redis will\n# continue to work as usual even if there are problems with disk,\n# permissions, and so forth.\nstop-writes-on-bgsave-error yes\n\n# Compress string objects using LZF when dump .rdb databases?\n# For default that's set to 'yes' as it's almost always a win.\n# If you want to save some CPU in the saving child set it to 'no' but\n# the dataset will likely be bigger if you have compressible values or keys.\nrdbcompression yes\n\n# Since version 5 of RDB a CRC64 checksum is placed at the end of the file.\n# This makes the format more resistant to corruption but there is a performance\n# hit to pay (around 10%) when saving and loading RDB files, so you can disable it\n# for maximum performances.\n#\n# RDB files created with checksum disabled have a checksum of zero that will\n# tell the loading code to skip the check.\nrdbchecksum yes\n\n# The filename where to dump the DB\ndbfilename dump.rdb\n\n# The working directory.\n#\n# The DB will be written inside this directory, with the filename specified\n# above using the 'dbfilename' configuration directive.\n# \n# The Append Only File will also be created inside this directory.\n# \n# Note that you must specify a directory here, not a file name.\ndir ./\n\n################################# REPLICATION #################################\n\n# Master-Slave replication. Use slaveof to make a Redis instance a copy of\n# another Redis server. Note that the configuration is local to the slave\n# so for example it is possible to configure the slave to save the DB with a\n# different interval, or to listen to another port, and so on.\n#\n# slaveof <masterip> <masterport>\n\n# If the master is password protected (using the \"requirepass\" configuration\n# directive below) it is possible to tell the slave to authenticate before\n# starting the replication synchronization process, otherwise the master will\n# refuse the slave request.\n#\n# masterauth <master-password>\n\n# When a slave loses its connection with the master, or when the replication\n# is still in progress, the slave can act in two different ways:\n#\n# 1) if slave-serve-stale-data is set to 'yes' (the default) the slave will\n#    still reply to client requests, possibly with out of date data, or the\n#    data set may just be empty if this is the first synchronization.\n#\n# 2) if slave-serve-stale-data is set to 'no' the slave will reply with\n#    an error \"SYNC with master in progress\" to all the kind of commands\n#    but to INFO and SLAVEOF.\n#\nslave-serve-stale-data yes\n\n# You can configure a slave instance to accept writes or not. Writing against\n# a slave instance may be useful to store some ephemeral data (because data\n# written on a slave will be easily deleted after resync with the master) but\n# may also cause problems if clients are writing to it because of a\n# misconfiguration.\n#\n# Since Redis 2.6 by default slaves are read-only.\n#\n# Note: read only slaves are not designed to be exposed to untrusted clients\n# on the internet. It's just a protection layer against misuse of the instance.\n# Still a read only slave exports by default all the administrative commands\n# such as CONFIG, DEBUG, and so forth. To a limited extent you can improve\n# security of read only slaves using 'rename-command' to shadow all the\n# administrative / dangerous commands.\nslave-read-only yes\n\n# Slaves send PINGs to server in a predefined interval. It's possible to change\n# this interval with the repl_ping_slave_period option. The default value is 10\n# seconds.\n#\n# repl-ping-slave-period 10\n\n# The following option sets the replication timeout for:\n#\n# 1) Bulk transfer I/O during SYNC, from the point of view of slave.\n# 2) Master timeout from the point of view of slaves (data, pings).\n# 3) Slave timeout from the point of view of masters (REPLCONF ACK pings).\n#\n# It is important to make sure that this value is greater than the value\n# specified for repl-ping-slave-period otherwise a timeout will be detected\n# every time there is low traffic between the master and the slave.\n#\n# repl-timeout 60\n\n# Disable TCP_NODELAY on the slave socket after SYNC?\n#\n# If you select \"yes\" Redis will use a smaller number of TCP packets and\n# less bandwidth to send data to slaves. But this can add a delay for\n# the data to appear on the slave side, up to 40 milliseconds with\n# Linux kernels using a default configuration.\n#\n# If you select \"no\" the delay for data to appear on the slave side will\n# be reduced but more bandwidth will be used for replication.\n#\n# By default we optimize for low latency, but in very high traffic conditions\n# or when the master and slaves are many hops away, turning this to \"yes\" may\n# be a good idea.\nrepl-disable-tcp-nodelay no\n\n# Set the replication backlog size. The backlog is a buffer that accumulates\n# slave data when slaves are disconnected for some time, so that when a slave\n# wants to reconnect again, often a full resync is not needed, but a partial\n# resync is enough, just passing the portion of data the slave missed while\n# disconnected.\n#\n# The biggest the replication backlog, the longer the time the slave can be\n# disconnected and later be able to perform a partial resynchronization.\n#\n# The backlog is only allocated once there is at least a slave connected.\n#\n# repl-backlog-size 1mb\n\n# After a master has no longer connected slaves for some time, the backlog\n# will be freed. The following option configures the amount of seconds that\n# need to elapse, starting from the time the last slave disconnected, for\n# the backlog buffer to be freed.\n#\n# A value of 0 means to never release the backlog.\n#\n# repl-backlog-ttl 3600\n\n# The slave priority is an integer number published by Redis in the INFO output.\n# It is used by Redis Sentinel in order to select a slave to promote into a\n# master if the master is no longer working correctly.\n#\n# A slave with a low priority number is considered better for promotion, so\n# for instance if there are three slaves with priority 10, 100, 25 Sentinel will\n# pick the one with priority 10, that is the lowest.\n#\n# However a special priority of 0 marks the slave as not able to perform the\n# role of master, so a slave with priority of 0 will never be selected by\n# Redis Sentinel for promotion.\n#\n# By default the priority is 100.\nslave-priority 100\n\n# It is possible for a master to stop accepting writes if there are less than\n# N slaves connected, having a lag less or equal than M seconds.\n#\n# The N slaves need to be in \"online\" state.\n#\n# The lag in seconds, that must be <= the specified value, is calculated from\n# the last ping received from the slave, that is usually sent every second.\n#\n# This option does not GUARANTEES that N replicas will accept the write, but\n# will limit the window of exposure for lost writes in case not enough slaves\n# are available, to the specified number of seconds.\n#\n# For example to require at least 3 slaves with a lag <= 10 seconds use:\n#\n# min-slaves-to-write 3\n# min-slaves-max-lag 10\n#\n# Setting one or the other to 0 disables the feature.\n#\n# By default min-slaves-to-write is set to 0 (feature disabled) and\n# min-slaves-max-lag is set to 10.\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# Command renaming.\n#\n# It is possible to change the name of dangerous commands in a shared\n# environment. For instance the CONFIG command may be renamed into something\n# hard to guess so that it will still be available for internal-use tools\n# but not available for general clients.\n#\n# Example:\n#\n# rename-command CONFIG b840fc02d524045429941cc15f59e41cb7be6c52\n#\n# It is also possible to completely kill a command by renaming it into\n# an empty string:\n#\n# rename-command CONFIG \"\"\n#\n# Please note that changing the name of commands that are logged into the\n# AOF file or transmitted to slaves may cause problems.\n\n################################### LIMITS ####################################\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# Don't use more memory than the specified amount of bytes.\n# When the memory limit is reached Redis will try to remove keys\n# according to the eviction policy selected (see maxmemory-policy).\n#\n# If Redis can't remove keys according to the policy, or if the policy is\n# set to 'noeviction', Redis 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 Redis 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 Redis 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\n# allkeys-lru -> remove any key accordingly to the LRU algorithm\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, Redis will return an error on write\n#       operations, when there are not suitable keys for eviction.\n#\n#       At the date of writing this 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 Redis 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############################## APPEND ONLY MODE ###############################\n\n# By default Redis asynchronously dumps the dataset on disk. This mode is\n# good enough in many applications, but an issue with the Redis process or\n# a power outage may result into a few minutes of writes lost (depending on\n# the configured save points).\n#\n# The Append Only File is an alternative persistence mode that provides\n# much better durability. For instance using the default data fsync policy\n# (see later in the config file) Redis can lose just one second of writes in a\n# dramatic event like a server power outage, or a single write if something\n# wrong with the Redis process itself happens, but the operating system is\n# still running correctly.\n#\n# AOF and RDB persistence can be enabled at the same time without problems.\n# If the AOF is enabled on startup Redis will load the AOF, that is the file\n# with the better durability guarantees.\n#\n# Please check http://redis.io/topics/persistence for more information.\n\nappendonly no\n\n# The name of the append only file (default: \"appendonly.aof\")\n\nappendfilename \"appendonly.aof\"\n\n# The fsync() call tells the Operating System to actually write data on disk\n# instead to wait for more data in the output buffer. Some OS will really flush \n# data on disk, some other OS will just try to do it ASAP.\n#\n# Redis supports three different modes:\n#\n# no: don't fsync, just let the OS flush the data when it wants. Faster.\n# always: fsync after every write to the append only log . Slow, Safest.\n# everysec: fsync only one time every second. Compromise.\n#\n# The default is \"everysec\", as that's usually the right compromise between\n# speed and data safety. It's up to you to understand if you can relax this to\n# \"no\" that will let the operating system flush the output buffer when\n# it wants, for better performances (but if you can live with the idea of\n# some data loss consider the default persistence mode that's snapshotting),\n# or on the contrary, use \"always\" that's very slow but a bit safer than\n# everysec.\n#\n# More details please check the following article:\n# http://antirez.com/post/redis-persistence-demystified.html\n#\n# If unsure, use \"everysec\".\n\n# appendfsync always\nappendfsync everysec\n# appendfsync no\n\n# When the AOF fsync policy is set to always or everysec, and a background\n# saving process (a background save or AOF log background rewriting) is\n# performing a lot of I/O against the disk, in some Linux configurations\n# Redis may block too long on the fsync() call. Note that there is no fix for\n# this currently, as even performing fsync in a different thread will block\n# our synchronous write(2) call.\n#\n# In order to mitigate this problem it's possible to use the following option\n# that will prevent fsync() from being called in the main process while a\n# BGSAVE or BGREWRITEAOF is in progress.\n#\n# This means that while another child is saving, the durability of Redis is\n# the same as \"appendfsync none\". In practical terms, this means that it is\n# possible to lose up to 30 seconds of log in the worst scenario (with the\n# default Linux settings).\n# \n# If you have latency problems turn this to \"yes\". Otherwise leave it as\n# \"no\" that is the safest pick from the point of view of durability.\n\nno-appendfsync-on-rewrite no\n\n# Automatic rewrite of the append only file.\n# Redis is able to automatically rewrite the log file implicitly calling\n# BGREWRITEAOF when the AOF log size grows by the specified percentage.\n# \n# This is how it works: Redis remembers the size of the AOF file after the\n# latest rewrite (if no rewrite has happened since the restart, the size of\n# the AOF at startup is used).\n#\n# This base size is compared to the current size. If the current size is\n# bigger than the specified percentage, the rewrite is triggered. Also\n# you need to specify a minimal size for the AOF file to be rewritten, this\n# is useful to avoid rewriting the AOF file even if the percentage increase\n# is reached but it is still pretty small.\n#\n# Specify a percentage of zero in order to disable the automatic AOF\n# rewrite feature.\n\nauto-aof-rewrite-percentage 100\nauto-aof-rewrite-min-size 64mb\n\n################################ LUA SCRIPTING  ###############################\n\n# Max execution time of a Lua script in milliseconds.\n#\n# If the maximum execution time is reached Redis will log that a script is\n# still in execution after the maximum allowed time and will start to\n# reply to queries with an error.\n#\n# When a long running script exceed the maximum execution time only the\n# SCRIPT KILL and SHUTDOWN NOSAVE commands are available. The first can be\n# used to stop a script that did not yet called write commands. The second\n# is the only way to shut down the server in the case a write commands was\n# already issue by the script but the user don't want to wait for the natural\n# termination of the script.\n#\n# Set it to 0 or a negative value for unlimited execution without warnings.\nlua-time-limit 5000\n\n################################ REDIS CLUSTER  ###############################\n#\n# Normal Redis instances can't be part of a Redis Cluster; only nodes that are\n# started as cluster nodes can. In order to start a Redis instance as a\n# cluster node enable the cluster support uncommenting the following:\n#\n# cluster-enabled yes\n\n# Every cluster node has a cluster configuration file. This file is not\n# intended to be edited by hand. It is created and updated by Redis nodes.\n# Every Redis Cluster node requires a different cluster configuration file.\n# Make sure that instances running in the same system does not have\n# overlapping cluster configuration file names.\n#\n# cluster-config-file nodes-6379.conf\n\n# Cluster node timeout is the amount of milliseconds a node must be unreachable \n# for it to be considered in failure state.\n# Most other internal time limits are multiple of the node timeout.\n#\n# cluster-node-timeout 15000\n\n# Cluster slaves are able to migrate to orphaned masters, that are masters\n# that are left without working slaves. This improves the cluster ability\n# to resist to failures as otherwise an orphaned master can't be failed over\n# in case of failure if it has no working slaves.\n#\n# Slaves migrate to orphaned masters only if there are still at least a\n# given number of other working slaves for their old master. This number\n# is the \"migration barrier\". A migration barrier of 1 means that a slave\n# will migrate only if there is at least 1 other working slave for its master\n# and so forth. It usually reflects the number of slaves you want for every\n# master in your cluster.\n#\n# Default is 1 (slaves migrate only if their masters remain with at least\n# one slave). To disable migration just set it to a very large value.\n# A value of 0 can be set but is useful only for debugging and dangerous\n# in production.\n#\n# cluster-migration-barrier 1\n\n# In order to setup your cluster make sure to read the documentation\n# available at http://redis.io web site.\n\n################################## SLOW LOG ###################################\n\n# The Redis 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 Redis\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\n\n############################# Event notification ##############################\n\n# Redis can notify Pub/Sub clients about events happening in the key space.\n# This feature is documented at http://redis.io/topics/keyspace-events\n# \n# For instance if keyspace events notification is enabled, and a client\n# performs a DEL operation on key \"foo\" stored in the Database 0, two\n# messages will be published via Pub/Sub:\n#\n# PUBLISH __keyspace@0__:foo del\n# PUBLISH __keyevent@0__:del foo\n#\n# It is possible to select the events that Redis will notify among a set\n# of classes. Every class is identified by a single character:\n#\n#  K     Keyspace events, published with __keyspace@<db>__ prefix.\n#  E     Keyevent events, published with __keyevent@<db>__ prefix.\n#  g     Generic commands (non-type specific) like DEL, EXPIRE, RENAME, ...\n#  $     String commands\n#  l     List commands\n#  s     Set commands\n#  h     Hash commands\n#  z     Sorted set commands\n#  x     Expired events (events generated every time a key expires)\n#  e     Evicted events (events generated when a key is evicted for maxmemory)\n#  A     Alias for g$lshzxe, so that the \"AKE\" string means all the events.\n#\n#  The \"notify-keyspace-events\" takes as argument a string that is composed\n#  by zero or multiple characters. The empty string means that notifications\n#  are disabled at all.\n#\n#  Example: to enable list and generic events, from the point of view of the\n#           event name, use:\n#\n#  notify-keyspace-events Elg\n#\n#  Example 2: to get the stream of the expired keys subscribing to channel\n#             name __keyevent@0__:expired use:\n#\n#  notify-keyspace-events Ex\n#\n#  By default all notifications are disabled because most users don't need\n#  this feature and the feature has some overhead. Note that if you don't\n#  specify at least one of K or E, no events will be delivered.\nnotify-keyspace-events \"\"\n\n############################### ADVANCED CONFIG ###############################\n\n# Hashes are encoded using a memory efficient data structure when they have a\n# small number of entries, and the biggest entry does not exceed a given\n# threshold. These thresholds can be configured using the following directives.\nhash-max-ziplist-entries 512\nhash-max-ziplist-value 64\n\n# Similarly to hashes, small lists are also encoded in a special way in order\n# to save a lot of space. The special representation is only used when\n# you are under the following limits:\nlist-max-ziplist-entries 512\nlist-max-ziplist-value 64\n\n# Sets have a special encoding in just one case: when a set is composed\n# of just strings that happens to be integers in radix 10 in the range\n# of 64 bit signed integers.\n# The following configuration setting sets the limit in the size of the\n# set in order to use this special memory saving encoding.\nset-max-intset-entries 512\n\n# Similarly to hashes and lists, sorted sets are also specially encoded in\n# order to save a lot of space. This encoding is only used when the length and\n# elements of a sorted set are below the following limits:\nzset-max-ziplist-entries 128\nzset-max-ziplist-value 64\n\n# HyperLogLog sparse representation bytes limit. The limit includes the\n# 16 bytes header. When an HyperLogLog using the sparse representation crosses\n# this limit, it is converted into the dense representation.\n#\n# A value greater than 16000 is totally useless, since at that point the\n# dense representation is more memory efficient.\n# \n# The suggested value is ~ 3000 in order to have the benefits of\n# the space efficient encoding without slowing down too much PFADD,\n# which is O(N) with the sparse encoding. The value can be raised to\n# ~ 10000 when CPU is not a concern, but space is, and the data set is\n# composed of many HyperLogLogs with cardinality in the 0 - 15000 range.\nhll-sparse-max-bytes 3000\n\n# Active rehashing uses 1 millisecond every 100 milliseconds of CPU time in\n# order to help rehashing the main Redis hash table (the one mapping top-level\n# keys to values). The hash table implementation Redis uses (see dict.c)\n# performs a lazy rehashing: the more operation you run into a hash table\n# that is rehashing, the more rehashing \"steps\" are performed, so if the\n# server is idle the rehashing is never complete and some more memory is used\n# by the hash table.\n# \n# The default is to use this millisecond 10 times every second in order to\n# active rehashing the main dictionaries, freeing memory when possible.\n#\n# If unsure:\n# use \"activerehashing no\" if you have hard latency requirements and it is\n# not a good thing in your environment that Redis can reply form time to time\n# to queries with 2 milliseconds delay.\n#\n# use \"activerehashing yes\" if you don't have such hard requirements but\n# want to free memory asap when possible.\nactiverehashing yes\n\n# The client output buffer limits can be used to force disconnection of clients\n# that are not reading data from the server fast enough for some reason (a\n# common reason is that a Pub/Sub client can't consume messages as fast as the\n# publisher can produce them).\n#\n# The limit can be set differently for the three different classes of clients:\n#\n# normal -> normal clients\n# slave  -> slave clients and MONITOR clients\n# pubsub -> clients subscribed to at least one pubsub channel or pattern\n#\n# The syntax of every client-output-buffer-limit directive is the following:\n#\n# client-output-buffer-limit <class> <hard limit> <soft limit> <soft seconds>\n#\n# A client is immediately disconnected once the hard limit is reached, or if\n# the soft limit is reached and remains reached for the specified number of\n# seconds (continuously).\n# So for instance if the hard limit is 32 megabytes and the soft limit is\n# 16 megabytes / 10 seconds, the client will get disconnected immediately\n# if the size of the output buffers reach 32 megabytes, but will also get\n# disconnected if the client reaches 16 megabytes and continuously overcomes\n# the limit for 10 seconds.\n#\n# By default normal clients are not limited because they don't receive data\n# without asking (in a push way), but just after a request, so only\n# asynchronous clients may create a scenario where data is requested faster\n# than it can read.\n#\n# Instead there is a default limit for pubsub and slave clients, since\n# subscribers and slaves receive data in a push fashion.\n#\n# Both the hard or the soft limit can be disabled by setting them to zero.\nclient-output-buffer-limit normal 0 0 0\nclient-output-buffer-limit slave 256mb 64mb 60\nclient-output-buffer-limit pubsub 32mb 8mb 60\n\n# Redis calls an internal function to perform many background tasks, like\n# closing connections of clients in timeout, purging expired keys that are\n# never requested, and so forth.\n#\n# Not all tasks are performed with the same frequency, but Redis checks for\n# tasks to perform accordingly to the specified \"hz\" value.\n#\n# By default \"hz\" is set to 10. Raising the value will use more CPU when\n# Redis is idle, but at the same time will make Redis more responsive when\n# there are many keys expiring at the same time, and timeouts may be\n# handled with more precision.\n#\n# The range is between 1 and 500, however a value over 100 is usually not\n# a good idea. Most users should use the default of 10 and raise this up to\n# 100 only in environments where very low latency is required.\nhz 10\n\n# When a child rewrites the AOF file, if the following option is enabled\n# the file will be fsync-ed every 32 MB of data generated. This is useful\n# in order to commit the file to the disk more incrementally and avoid\n# big latency spikes.\naof-rewrite-incremental-fsync yes\n\n"
  },
  {
    "path": "runtest",
    "content": "#!/bin/sh\nTCL_VERSIONS=\"8.5 8.6\"\nTCLSH=\"\"\n\nfor VERSION in $TCL_VERSIONS; do\n\tTCL=`which tclsh$VERSION 2>/dev/null` && TCLSH=$TCL\ndone\n\nif [ -z $TCLSH ]\nthen\n    echo \"You need tcl 8.5 or newer in order to run the Redis test\"\n    exit 1\nfi\n$TCLSH tests/test_helper.tcl $*\n"
  },
  {
    "path": "runtest-cluster",
    "content": "#!/bin/sh\nTCL_VERSIONS=\"8.5 8.6\"\nTCLSH=\"\"\n\nfor VERSION in $TCL_VERSIONS; do\n\tTCL=`which tclsh$VERSION 2>/dev/null` && TCLSH=$TCL\ndone\n\nif [ -z $TCLSH ]\nthen\n    echo \"You need tcl 8.5 or newer in order to run the Redis Sentinel test\"\n    exit 1\nfi\n$TCLSH tests/cluster/run.tcl $*\n"
  },
  {
    "path": "runtest-sentinel",
    "content": "#!/bin/sh\nTCL_VERSIONS=\"8.5 8.6\"\nTCLSH=\"\"\n\nfor VERSION in $TCL_VERSIONS; do\n\tTCL=`which tclsh$VERSION 2>/dev/null` && TCLSH=$TCL\ndone\n\nif [ -z $TCLSH ]\nthen\n    echo \"You need tcl 8.5 or newer in order to run the Redis Sentinel test\"\n    exit 1\nfi\n$TCLSH tests/sentinel/run.tcl $*\n"
  },
  {
    "path": "sentinel.conf",
    "content": "# Example sentinel.conf\n\n# port <sentinel-port>\n# The port that this sentinel instance will run on\nport 26379\n\n# dir <working-directory>\n# Every long running process should have a well-defined working directory.\n# For Redis Sentinel to chdir to /tmp at startup is the simplest thing\n# for the process to don't interferer with administrative tasks such as\n# unmounting filesystems.\ndir /tmp\n\n# sentinel monitor <master-name> <ip> <redis-port> <quorum>\n#\n# Tells Sentinel to monitor this master, and to consider it in O_DOWN\n# (Objectively Down) state only if at least <quorum> sentinels agree.\n#\n# Note that whatever is the ODOWN quorum, a Sentinel will require to\n# be elected by the majority of the known Sentinels in order to\n# start a failover, so no failover can be performed in minority.\n#\n# Note: master name should not include special characters or spaces.\n# The valid charset is A-z 0-9 and the three characters \".-_\".\nsentinel monitor mymaster 127.0.0.1 6379 2\n\n# sentinel auth-pass <master-name> <password>\n#\n# Set the password to use to authenticate with the master and slaves.\n# Useful if there is a password set in the Redis instances to monitor.\n#\n# Note that the master password is also used for slaves, so it is not\n# possible to set a different password in masters and slaves instances\n# if you want to be able to monitor these instances with Sentinel.\n#\n# However you can have Redis instances without the authentication enabled\n# mixed with Redis instances requiring the authentication (as long as the\n# password set is the same for all the instances requiring the password) as\n# the AUTH command will have no effect in Redis instances with authentication\n# switched off.\n#\n# Example:\n#\n# sentinel auth-pass mymaster MySUPER--secret-0123passw0rd\n\n# sentinel down-after-milliseconds <master-name> <milliseconds>\n#\n# Number of milliseconds the master (or any attached slave or sentinel) should\n# be unreachable (as in, not acceptable reply to PING, continuously, for the\n# specified period) in order to consider it in S_DOWN state (Subjectively\n# Down).\n#\n# Default is 30 seconds.\nsentinel down-after-milliseconds mymaster 30000\n\n# sentinel parallel-syncs <master-name> <numslaves>\n#\n# How many slaves we can reconfigure to point to the new slave simultaneously\n# during the failover. Use a low number if you use the slaves to serve query\n# to avoid that all the slaves will be unreachable at about the same\n# time while performing the synchronization with the master.\nsentinel parallel-syncs mymaster 1\n\n# sentinel failover-timeout <master-name> <milliseconds>\n#\n# Specifies the failover timeout in milliseconds. It is used in many ways:\n#\n# - The time needed to re-start a failover after a previous failover was\n#   already tried against the same master by a given Sentinel, is two\n#   times the failover timeout.\n#\n# - The time needed for a slave replicating to a wrong master according\n#   to a Sentinel current configuration, to be forced to replicate\n#   with the right master, is exactly the failover timeout (counting since\n#   the moment a Sentinel detected the misconfiguration).\n#\n# - The time needed to cancel a failover that is already in progress but\n#   did not produced any configuration change (SLAVEOF NO ONE yet not\n#   acknowledged by the promoted slave).\n#\n# - The maximum time a failover in progress waits for all the slaves to be\n#   reconfigured as slaves of the new master. However even after this time\n#   the slaves will be reconfigured by the Sentinels anyway, but not with\n#   the exact parallel-syncs progression as specified.\n#\n# Default is 3 minutes.\nsentinel failover-timeout mymaster 180000\n\n# SCRIPTS EXECUTION\n#\n# sentinel notification-script and sentinel reconfig-script are used in order\n# to configure scripts that are called to notify the system administrator\n# or to reconfigure clients after a failover. The scripts are executed\n# with the following rules for error handling:\n#\n# If script exits with \"1\" the execution is retried later (up to a maximum\n# number of times currently set to 10).\n#\n# If script exits with \"2\" (or an higher value) the script execution is\n# not retried.\n#\n# If script terminates because it receives a signal the behavior is the same\n# as exit code 1.\n#\n# A script has a maximum running time of 60 seconds. After this limit is\n# reached the script is terminated with a SIGKILL and the execution retried.\n\n# NOTIFICATION SCRIPT\n#\n# sentinel notification-script <master-name> <script-path>\n# \n# Call the specified notification script for any sentinel event that is\n# generated in the WARNING level (for instance -sdown, -odown, and so forth).\n# This script should notify the system administrator via email, SMS, or any\n# other messaging system, that there is something wrong with the monitored\n# Redis systems.\n#\n# The script is called with just two arguments: the first is the event type\n# and the second the event description.\n#\n# The script must exist and be executable in order for sentinel to start if\n# this option is provided.\n#\n# Example:\n#\n# sentinel notification-script mymaster /var/redis/notify.sh\n\n# CLIENTS RECONFIGURATION SCRIPT\n#\n# sentinel client-reconfig-script <master-name> <script-path>\n#\n# When the master changed because of a failover a script can be called in\n# order to perform application-specific tasks to notify the clients that the\n# configuration has changed and the master is at a different address.\n# \n# The following arguments are passed to the script:\n#\n# <master-name> <role> <state> <from-ip> <from-port> <to-ip> <to-port>\n#\n# <state> is currently always \"failover\"\n# <role> is either \"leader\" or \"observer\"\n# \n# The arguments from-ip, from-port, to-ip, to-port are used to communicate\n# the old address of the master and the new address of the elected slave\n# (now a master).\n#\n# This script should be resistant to multiple invocations.\n#\n# Example:\n#\n# sentinel client-reconfig-script mymaster /var/redis/reconfig.sh\n\n\n"
  },
  {
    "path": "src/.gitignore",
    "content": "*.gcda\n*.gcno\n*.gcov\nredis.info\nlcov-html\n"
  },
  {
    "path": "src/Makefile",
    "content": "# Redis Makefile\n# Copyright (C) 2009 Salvatore Sanfilippo <antirez at gmail dot com>\n# This file is released under the BSD license, see the COPYING file\n#\n# The Makefile composes the final FINAL_CFLAGS and FINAL_LDFLAGS using\n# what is needed for Redis plus the standard CFLAGS and LDFLAGS passed.\n# However when building the dependencies (Jemalloc, Lua, Hiredis, ...)\n# CFLAGS and LDFLAGS are propagated to the dependencies, so to pass\n# flags only to be used when compiling / linking Redis itself REDIS_CFLAGS\n# and REDIS_LDFLAGS are used instead (this is the case of 'make gcov').\n#\n# Dependencies are stored in the Makefile.dep file. To rebuild this file\n# Just use 'make dep', but this is only needed by developers.\n\nrelease_hdr := $(shell sh -c './mkreleasehdr.sh')\nuname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')\nOPTIMIZATION?=-O2\nDEPENDENCY_TARGETS=hiredis linenoise lua\n\n# Default settings\nSTD=-std=c99 -pedantic\nWARN=-Wall\nOPT=$(OPTIMIZATION)\n\nPREFIX?=/usr/local\nINSTALL_BIN=$(PREFIX)/bin\nINSTALL=install\n\n# Default allocator\nifeq ($(uname_S),Linux)\n\tMALLOC=jemalloc\nelse\n\tMALLOC=libc\nendif\n\n# Backwards compatibility for selecting an allocator\nifeq ($(USE_TCMALLOC),yes)\n\tMALLOC=tcmalloc\nendif\n\nifeq ($(USE_TCMALLOC_MINIMAL),yes)\n\tMALLOC=tcmalloc_minimal\nendif\n\nifeq ($(USE_JEMALLOC),yes)\n\tMALLOC=jemalloc\nendif\n\n# Override default settings if possible\n-include .make-settings\n\nFINAL_CFLAGS=$(STD) $(WARN) $(OPT) $(DEBUG) $(CFLAGS) $(REDIS_CFLAGS)\nFINAL_LDFLAGS=$(LDFLAGS) $(REDIS_LDFLAGS) $(DEBUG)\nFINAL_LIBS=-lm\nDEBUG=-g -ggdb\n\nifeq ($(uname_S),SunOS)\n\t# SunOS\n\tINSTALL=cp -pf\n\tFINAL_CFLAGS+= -D__EXTENSIONS__ -D_XPG6\n\tFINAL_LIBS+= -ldl -lnsl -lsocket -lpthread\nelse\nifeq ($(uname_S),Darwin)\n\t# Darwin (nothing to do)\nelse\n\t# All the other OSes (notably Linux)\n\tFINAL_LDFLAGS+= -rdynamic\n\tFINAL_LIBS+= -pthread\nendif\nendif\n\n# Include paths to dependencies\nFINAL_CFLAGS+= -I../deps/hiredis -I../deps/linenoise -I../deps/lua/src\n\nifeq ($(MALLOC),tcmalloc)\n\tFINAL_CFLAGS+= -DUSE_TCMALLOC\n\tFINAL_LIBS+= -ltcmalloc\nendif\n\nifeq ($(MALLOC),tcmalloc_minimal)\n\tFINAL_CFLAGS+= -DUSE_TCMALLOC\n\tFINAL_LIBS+= -ltcmalloc_minimal\nendif\n\nifeq ($(MALLOC),jemalloc)\n\tDEPENDENCY_TARGETS+= jemalloc\n\tFINAL_CFLAGS+= -DUSE_JEMALLOC -I../deps/jemalloc/include\n\tFINAL_LIBS+= ../deps/jemalloc/lib/libjemalloc.a -ldl\nendif\n\nREDIS_CC=$(QUIET_CC)$(CC) $(FINAL_CFLAGS)\nREDIS_LD=$(QUIET_LINK)$(CC) $(FINAL_LDFLAGS)\nREDIS_INSTALL=$(QUIET_INSTALL)$(INSTALL)\n\nCCCOLOR=\"\\033[34m\"\nLINKCOLOR=\"\\033[34;1m\"\nSRCCOLOR=\"\\033[33m\"\nBINCOLOR=\"\\033[37;1m\"\nMAKECOLOR=\"\\033[32;1m\"\nENDCOLOR=\"\\033[0m\"\n\nifndef V\nQUIET_CC = @printf '    %b %b\\n' $(CCCOLOR)CC$(ENDCOLOR) $(SRCCOLOR)$@$(ENDCOLOR) 1>&2;\nQUIET_LINK = @printf '    %b %b\\n' $(LINKCOLOR)LINK$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR) 1>&2;\nQUIET_INSTALL = @printf '    %b %b\\n' $(LINKCOLOR)INSTALL$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR) 1>&2;\nendif\n\nREDIS_SERVER_NAME=redis-server\nREDIS_SENTINEL_NAME=redis-sentinel\nREDIS_SERVER_OBJ=adlist.o ae.o anet.o dict.o redis.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o ziplist.o release.o networking.o util.o object.o db.o replication.o rdb.o t_string.o t_list.o t_set.o t_zset.o t_hash.o config.o aof.o pubsub.o multi.o debug.o sort.o intset.o syncio.o cluster.o crc16.o endianconv.o slowlog.o scripting.o bio.o rio.o rand.o memtest.o crc64.o bitops.o sentinel.o notify.o setproctitle.o blocked.o hyperloglog.o\nREDIS_CLI_NAME=redis-cli\nREDIS_CLI_OBJ=anet.o sds.o adlist.o redis-cli.o zmalloc.o release.o anet.o ae.o crc64.o\nREDIS_BENCHMARK_NAME=redis-benchmark\nREDIS_BENCHMARK_OBJ=ae.o anet.o redis-benchmark.o sds.o adlist.o zmalloc.o redis-benchmark.o\nREDIS_CHECK_DUMP_NAME=redis-check-dump\nREDIS_CHECK_DUMP_OBJ=redis-check-dump.o lzf_c.o lzf_d.o crc64.o\nREDIS_CHECK_AOF_NAME=redis-check-aof\nREDIS_CHECK_AOF_OBJ=redis-check-aof.o\n\nall: $(REDIS_SERVER_NAME) $(REDIS_SENTINEL_NAME) $(REDIS_CLI_NAME) $(REDIS_BENCHMARK_NAME) $(REDIS_CHECK_DUMP_NAME) $(REDIS_CHECK_AOF_NAME)\n\t@echo \"\"\n\t@echo \"Hint: To run 'make test' is a good idea ;)\"\n\t@echo \"\"\n\n.PHONY: all\n\n# Deps (use make dep to generate this)\ninclude Makefile.dep\n\ndep:\n\t$(REDIS_CC) -MM *.c > Makefile.dep\n\n.PHONY: dep\n\npersist-settings: distclean\n\techo STD=$(STD) >> .make-settings\n\techo WARN=$(WARN) >> .make-settings\n\techo OPT=$(OPT) >> .make-settings\n\techo MALLOC=$(MALLOC) >> .make-settings\n\techo CFLAGS=$(CFLAGS) >> .make-settings\n\techo LDFLAGS=$(LDFLAGS) >> .make-settings\n\techo REDIS_CFLAGS=$(REDIS_CFLAGS) >> .make-settings\n\techo REDIS_LDFLAGS=$(REDIS_LDFLAGS) >> .make-settings\n\techo PREV_FINAL_CFLAGS=$(FINAL_CFLAGS) >> .make-settings\n\techo PREV_FINAL_LDFLAGS=$(FINAL_LDFLAGS) >> .make-settings\n\t-(cd ../deps && $(MAKE) $(DEPENDENCY_TARGETS))\n\n.PHONY: persist-settings\n\n# Prerequisites target\n.make-prerequisites:\n\t@touch $@\n\n# Clean everything, persist settings and build dependencies if anything changed\nifneq ($(strip $(PREV_FINAL_CFLAGS)), $(strip $(FINAL_CFLAGS)))\n.make-prerequisites: persist-settings\nendif\n\nifneq ($(strip $(PREV_FINAL_LDFLAGS)), $(strip $(FINAL_LDFLAGS)))\n.make-prerequisites: persist-settings\nendif\n\n# redis-server\n$(REDIS_SERVER_NAME): $(REDIS_SERVER_OBJ)\n\t$(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a ../deps/lua/src/liblua.a $(FINAL_LIBS)\n\n# redis-sentinel\n$(REDIS_SENTINEL_NAME): $(REDIS_SERVER_NAME)\n\t$(REDIS_INSTALL) $(REDIS_SERVER_NAME) $(REDIS_SENTINEL_NAME)\n\n# redis-cli\n$(REDIS_CLI_NAME): $(REDIS_CLI_OBJ)\n\t$(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a ../deps/linenoise/linenoise.o $(FINAL_LIBS)\n\n# redis-benchmark\n$(REDIS_BENCHMARK_NAME): $(REDIS_BENCHMARK_OBJ)\n\t$(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a $(FINAL_LIBS)\n\n# redis-check-dump\n$(REDIS_CHECK_DUMP_NAME): $(REDIS_CHECK_DUMP_OBJ)\n\t$(REDIS_LD) -o $@ $^ $(FINAL_LIBS)\n\n# redis-check-aof\n$(REDIS_CHECK_AOF_NAME): $(REDIS_CHECK_AOF_OBJ)\n\t$(REDIS_LD) -o $@ $^ $(FINAL_LIBS)\n\n# Because the jemalloc.h header is generated as a part of the jemalloc build,\n# building it should complete before building any other object. Instead of\n# depending on a single artifact, build all dependencies first.\n%.o: %.c .make-prerequisites\n\t$(REDIS_CC) -c $<\n\nclean:\n\trm -rf $(REDIS_SERVER_NAME) $(REDIS_SENTINEL_NAME) $(REDIS_CLI_NAME) $(REDIS_BENCHMARK_NAME) $(REDIS_CHECK_DUMP_NAME) $(REDIS_CHECK_AOF_NAME) *.o *.gcda *.gcno *.gcov redis.info lcov-html\n\n.PHONY: clean\n\ndistclean: clean\n\t-(cd ../deps && $(MAKE) distclean)\n\t-(rm -f .make-*)\n\n.PHONY: distclean\n\ntest: $(REDIS_SERVER_NAME) $(REDIS_CHECK_AOF_NAME)\n\t@(cd ..; ./runtest)\n\ntest-sentinel: $(REDIS_SENTINEL_NAME)\n\t@(cd ..; ./runtest-sentinel)\n\ncheck: test\n\nlcov:\n\t$(MAKE) gcov\n\t@(set -e; cd ..; ./runtest --clients 1)\n\t@geninfo -o redis.info .\n\t@genhtml --legend -o lcov-html redis.info\n\n.PHONY: lcov\n\nbench: $(REDIS_BENCHMARK_NAME)\n\t./$(REDIS_BENCHMARK_NAME)\n\n32bit:\n\t@echo \"\"\n\t@echo \"WARNING: if it fails under Linux you probably need to install libc6-dev-i386\"\n\t@echo \"\"\n\t$(MAKE) CFLAGS=\"-m32\" LDFLAGS=\"-m32\"\n\ngcov:\n\t$(MAKE) REDIS_CFLAGS=\"-fprofile-arcs -ftest-coverage -DCOVERAGE_TEST\" REDIS_LDFLAGS=\"-fprofile-arcs -ftest-coverage\"\n\nnoopt:\n\t$(MAKE) OPTIMIZATION=\"-O0\"\n\nvalgrind:\n\t$(MAKE) OPTIMIZATION=\"-O0\" MALLOC=\"libc\"\n\nsrc/help.h:\n\t@../utils/generate-command-help.rb > help.h\n\ninstall: all\n\t@mkdir -p $(INSTALL_BIN)\n\t$(REDIS_INSTALL) $(REDIS_SERVER_NAME) $(INSTALL_BIN)\n\t$(REDIS_INSTALL) $(REDIS_BENCHMARK_NAME) $(INSTALL_BIN)\n\t$(REDIS_INSTALL) $(REDIS_CLI_NAME) $(INSTALL_BIN)\n\t$(REDIS_INSTALL) $(REDIS_CHECK_DUMP_NAME) $(INSTALL_BIN)\n\t$(REDIS_INSTALL) $(REDIS_CHECK_AOF_NAME) $(INSTALL_BIN)\n"
  },
  {
    "path": "src/Makefile.dep",
    "content": "adlist.o: adlist.c adlist.h zmalloc.h\nae.o: ae.c ae.h zmalloc.h config.h ae_kqueue.c ae_epoll.c ae_select.c ae_evport.c\nae_epoll.o: ae_epoll.c\nae_evport.o: ae_evport.c\nae_kqueue.o: ae_kqueue.c\nae_select.o: ae_select.c\nanet.o: anet.c fmacros.h anet.h\naof.o: aof.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \\\n ../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \\\n ziplist.h intset.h version.h util.h rdb.h rio.h bio.h\nbio.o: bio.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \\\n ../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \\\n ziplist.h intset.h version.h util.h rdb.h rio.h bio.h\nbitops.o: bitops.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \\\n ../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \\\n ziplist.h intset.h version.h util.h rdb.h rio.h\nblocked.o: blocked.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \\\n ../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \\\n ziplist.h intset.h version.h util.h rdb.h rio.h\ncluster.o: cluster.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \\\n ../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \\\n ziplist.h intset.h version.h util.h rdb.h rio.h cluster.h endianconv.h\nconfig.o: config.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \\\n ../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \\\n ziplist.h intset.h version.h util.h rdb.h rio.h cluster.h\ncrc16.o: crc16.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \\\n ../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \\\n ziplist.h intset.h version.h util.h rdb.h rio.h\ncrc64.o: crc64.c\ndb.o: db.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \\\n ../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \\\n ziplist.h intset.h version.h util.h rdb.h rio.h cluster.h\ndebug.o: debug.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \\\n ../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \\\n ziplist.h intset.h version.h util.h rdb.h rio.h sha1.h crc64.h bio.h\ndict.o: dict.c fmacros.h dict.h zmalloc.h redisassert.h\nendianconv.o: endianconv.c\nhyperloglog.o: hyperloglog.c redis.h fmacros.h config.h \\\n ../deps/lua/src/lua.h ../deps/lua/src/luaconf.h ae.h sds.h dict.h \\\n adlist.h zmalloc.h anet.h ziplist.h intset.h version.h util.h rdb.h \\\n rio.h\nintset.o: intset.c intset.h zmalloc.h endianconv.h config.h\nlzf_c.o: lzf_c.c lzfP.h\nlzf_d.o: lzf_d.c lzfP.h\nmemtest.o: memtest.c config.h\nmulti.o: multi.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \\\n ../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \\\n ziplist.h intset.h version.h util.h rdb.h rio.h\nnetworking.o: networking.c redis.h fmacros.h config.h \\\n ../deps/lua/src/lua.h ../deps/lua/src/luaconf.h ae.h sds.h dict.h \\\n adlist.h zmalloc.h anet.h ziplist.h intset.h version.h util.h rdb.h \\\n rio.h\nnotify.o: notify.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \\\n ../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \\\n ziplist.h intset.h version.h util.h rdb.h rio.h\nobject.o: object.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \\\n ../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \\\n ziplist.h intset.h version.h util.h rdb.h rio.h\npqsort.o: pqsort.c\npubsub.o: pubsub.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \\\n ../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \\\n ziplist.h intset.h version.h util.h rdb.h rio.h\nrand.o: rand.c\nrdb.o: rdb.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \\\n ../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \\\n ziplist.h intset.h version.h util.h rdb.h rio.h lzf.h zipmap.h \\\n endianconv.h\nredis-benchmark.o: redis-benchmark.c fmacros.h ae.h \\\n ../deps/hiredis/hiredis.h sds.h adlist.h zmalloc.h\nredis-check-aof.o: redis-check-aof.c fmacros.h config.h\nredis-check-dump.o: redis-check-dump.c lzf.h crc64.h\nredis-cli.o: redis-cli.c fmacros.h version.h ../deps/hiredis/hiredis.h \\\n sds.h zmalloc.h ../deps/linenoise/linenoise.h help.h anet.h ae.h\nredis.o: redis.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \\\n ../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \\\n ziplist.h intset.h version.h util.h rdb.h rio.h cluster.h slowlog.h \\\n bio.h asciilogo.h\nrelease.o: release.c release.h version.h crc64.h\nreplication.o: replication.c redis.h fmacros.h config.h \\\n ../deps/lua/src/lua.h ../deps/lua/src/luaconf.h ae.h sds.h dict.h \\\n adlist.h zmalloc.h anet.h ziplist.h intset.h version.h util.h rdb.h \\\n rio.h\nrio.o: rio.c fmacros.h rio.h sds.h util.h crc64.h config.h redis.h \\\n ../deps/lua/src/lua.h ../deps/lua/src/luaconf.h ae.h dict.h adlist.h \\\n zmalloc.h anet.h ziplist.h intset.h version.h rdb.h\nscripting.o: scripting.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \\\n ../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \\\n ziplist.h intset.h version.h util.h rdb.h rio.h sha1.h rand.h \\\n ../deps/lua/src/lauxlib.h ../deps/lua/src/lua.h ../deps/lua/src/lualib.h\nsds.o: sds.c sds.h zmalloc.h\nsentinel.o: sentinel.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \\\n ../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \\\n ziplist.h intset.h version.h util.h rdb.h rio.h \\\n ../deps/hiredis/hiredis.h ../deps/hiredis/async.h \\\n ../deps/hiredis/hiredis.h\nsetproctitle.o: setproctitle.c\nsha1.o: sha1.c sha1.h config.h\nslowlog.o: slowlog.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \\\n ../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \\\n ziplist.h intset.h version.h util.h rdb.h rio.h slowlog.h\nsort.o: sort.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \\\n ../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \\\n ziplist.h intset.h version.h util.h rdb.h rio.h pqsort.h\nsyncio.o: syncio.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \\\n ../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \\\n ziplist.h intset.h version.h util.h rdb.h rio.h\nt_hash.o: t_hash.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \\\n ../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \\\n ziplist.h intset.h version.h util.h rdb.h rio.h\nt_list.o: t_list.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \\\n ../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \\\n ziplist.h intset.h version.h util.h rdb.h rio.h\nt_set.o: t_set.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \\\n ../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \\\n ziplist.h intset.h version.h util.h rdb.h rio.h\nt_string.o: t_string.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \\\n ../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \\\n ziplist.h intset.h version.h util.h rdb.h rio.h\nt_zset.o: t_zset.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \\\n ../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \\\n ziplist.h intset.h version.h util.h rdb.h rio.h\nutil.o: util.c fmacros.h util.h sds.h\nziplist.o: ziplist.c zmalloc.h util.h sds.h ziplist.h endianconv.h \\\n config.h redisassert.h\nzipmap.o: zipmap.c zmalloc.h endianconv.h config.h\nzmalloc.o: zmalloc.c config.h zmalloc.h\n"
  },
  {
    "path": "src/adlist.c",
    "content": "/* adlist.c - A generic doubly linked list implementation\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\n#include <stdlib.h>\n#include \"adlist.h\"\n#include \"zmalloc.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. */\n/*\n * 创建一个新的链表\n *\n * 创建成功返回链表，失败返回 NULL 。\n *\n * T = O(1)\n */\nlist *listCreate(void)\n{\n    struct list *list;\n\n    // 分配内存\n    if ((list = zmalloc(sizeof(*list))) == NULL)\n        return NULL;\n\n    // 初始化属性\n    list->head = list->tail = NULL;\n    list->len = 0;\n    list->dup = NULL;\n    list->free = NULL;\n    list->match = NULL;\n\n    return list;\n}\n\n/* Free the whole list.\n *\n * This function can't fail. */\n/*\n * 释放整个链表，以及链表中所有节点\n *\n * T = O(N)\n */\nvoid listRelease(list *list)\n{\n    unsigned long len;\n    listNode *current, *next;\n\n    // 指向头指针\n    current = list->head;\n    // 遍历整个链表\n    len = list->len;\n    while(len--) {\n        next = current->next;\n\n        // 如果有设置值释放函数，那么调用它\n        if (list->free) list->free(current->value);\n\n        // 释放节点结构\n        zfree(current);\n\n        current = next;\n    }\n\n    // 释放链表结构\n    zfree(list);\n}\n\n/* Add a new node to the list, to head, contaning 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. */\n/*\n * 将一个包含有给定值指针 value 的新节点添加到链表的表头\n *\n * 如果为新节点分配内存出错，那么不执行任何动作，仅返回 NULL\n *\n * 如果执行成功，返回传入的链表指针\n *\n * T = O(1)\n */\nlist *listAddNodeHead(list *list, void *value)\n{\n    listNode *node;\n\n    // 为节点分配内存\n    if ((node = zmalloc(sizeof(*node))) == NULL)\n        return NULL;\n\n    // 保存值指针\n    node->value = value;\n\n    // 添加节点到空链表\n    if (list->len == 0) {\n        list->head = list->tail = node;\n        node->prev = node->next = NULL;\n    // 添加节点到非空链表\n    } else {\n        node->prev = NULL;\n        node->next = list->head;\n        list->head->prev = node;\n        list->head = node;\n    }\n\n    // 更新链表节点数\n    list->len++;\n\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. */\n/*\n * 将一个包含有给定值指针 value 的新节点添加到链表的表尾\n *\n * 如果为新节点分配内存出错，那么不执行任何动作，仅返回 NULL\n *\n * 如果执行成功，返回传入的链表指针\n *\n * T = O(1)\n */\nlist *listAddNodeTail(list *list, void *value)\n{\n    listNode *node;\n\n    // 为新节点分配内存\n    if ((node = zmalloc(sizeof(*node))) == NULL)\n        return NULL;\n\n    // 保存值指针\n    node->value = value;\n\n    // 目标链表为空\n    if (list->len == 0) {\n        list->head = list->tail = node;\n        node->prev = node->next = NULL;\n    // 目标链表非空\n    } else {\n        node->prev = list->tail;\n        node->next = NULL;\n        list->tail->next = node;\n        list->tail = node;\n    }\n\n    // 更新链表节点数\n    list->len++;\n\n    return list;\n}\n\n/*\n * 创建一个包含值 value 的新节点，并将它插入到 old_node 的之前或之后\n *\n * 如果 after 为 0 ，将新节点插入到 old_node 之前。\n * 如果 after 为 1 ，将新节点插入到 old_node 之后。\n *\n * T = O(1)\n */\nlist *listInsertNode(list *list, listNode *old_node, void *value, int after) {\n    listNode *node;\n\n    // 创建新节点\n    if ((node = zmalloc(sizeof(*node))) == NULL)\n        return NULL;\n\n    // 保存值\n    node->value = value;\n\n    // 将新节点添加到给定节点之后\n    if (after) {\n        node->prev = old_node;\n        node->next = old_node->next;\n        // 给定节点是原表尾节点\n        if (list->tail == old_node) {\n            list->tail = node;\n        }\n    // 将新节点添加到给定节点之前\n    } else {\n        node->next = old_node;\n        node->prev = old_node->prev;\n        // 给定节点是原表头节点\n        if (list->head == old_node) {\n            list->head = node;\n        }\n    }\n\n    // 更新新节点的前置指针\n    if (node->prev != NULL) {\n        node->prev->next = node;\n    }\n    // 更新新节点的后置指针\n    if (node->next != NULL) {\n        node->next->prev = node;\n    }\n\n    // 更新链表节点数\n    list->len++;\n\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. */\n/*\n * 从链表 list 中删除给定节点 node \n * \n * 对节点私有值(private value of the node)的释放工作由调用者进行。\n *\n * T = O(1)\n */\nvoid listDelNode(list *list, listNode *node)\n{\n    // 调整前置节点的指针\n    if (node->prev)\n        node->prev->next = node->next;\n    else\n        list->head = node->next;\n\n    // 调整后置节点的指针\n    if (node->next)\n        node->next->prev = node->prev;\n    else\n        list->tail = node->prev;\n\n    // 释放值\n    if (list->free) list->free(node->value);\n\n    // 释放节点\n    zfree(node);\n\n    // 链表数减一\n    list->len--;\n}\n\n/* Returns a list iterator 'iter'. After the initialization every\n * call to listNext() will return the next element of the list.\n *\n * This function can't fail. */\n/*\n * 为给定链表创建一个迭代器，\n * 之后每次对这个迭代器调用 listNext 都返回被迭代到的链表节点\n *\n * direction 参数决定了迭代器的迭代方向：\n *  AL_START_HEAD ：从表头向表尾迭代\n *  AL_START_TAIL ：从表尾想表头迭代\n *\n * T = O(1)\n */\nlistIter *listGetIterator(list *list, int direction)\n{\n    // 为迭代器分配内存\n    listIter *iter;\n    if ((iter = zmalloc(sizeof(*iter))) == NULL) return NULL;\n\n    // 根据迭代方向，设置迭代器的起始节点\n    if (direction == AL_START_HEAD)\n        iter->next = list->head;\n    else\n        iter->next = list->tail;\n\n    // 记录迭代方向\n    iter->direction = direction;\n\n    return iter;\n}\n\n/* Release the iterator memory */\n/*\n * 释放迭代器\n *\n * T = O(1)\n */\nvoid listReleaseIterator(listIter *iter) {\n    zfree(iter);\n}\n\n/* Create an iterator in the list private iterator structure */\n/*\n * 将迭代器的方向设置为 AL_START_HEAD ，\n * 并将迭代指针重新指向表头节点。\n *\n * T = O(1)\n */\nvoid listRewind(list *list, listIter *li) {\n    li->next = list->head;\n    li->direction = AL_START_HEAD;\n}\n\n/*\n * 将迭代器的方向设置为 AL_START_TAIL ，\n * 并将迭代指针重新指向表尾节点。\n *\n * T = O(1)\n */\nvoid listRewindTail(list *list, listIter *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 * listDelNode(), 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 = listGetIterator(list,<direction>);\n * while ((node = listNext(iter)) != NULL) {\n *     doSomethingWith(listNodeValue(node));\n * }\n *\n * */\n/*\n * 返回迭代器当前所指向的节点。\n *\n * 删除当前节点是允许的，但不能修改链表里的其他节点。\n *\n * 函数要么返回一个节点，要么返回 NULL ，常见的用法是：\n *\n * iter = listGetIterator(list,<direction>);\n * while ((node = listNext(iter)) != NULL) {\n *     doSomethingWith(listNodeValue(node));\n * }\n *\n * T = O(1)\n */\nlistNode *listNext(listIter *iter)\n{\n    listNode *current = iter->next;\n\n    if (current != NULL) {\n        // 根据方向选择下一个节点\n        if (iter->direction == AL_START_HEAD)\n            // 保存下一个节点，防止当前节点被删除而造成指针丢失\n            iter->next = current->next;\n        else\n            // 保存下一个节点，防止当前节点被删除而造成指针丢失\n            iter->next = current->prev;\n    }\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. */\n/*\n * 复制整个链表。\n *\n * 复制成功返回输入链表的副本，\n * 如果因为内存不足而造成复制失败，返回 NULL 。\n *\n * 如果链表有设置值复制函数 dup ，那么对值的复制将使用复制函数进行，\n * 否则，新节点将和旧节点共享同一个指针。\n *\n * 无论复制是成功还是失败，输入节点都不会修改。\n *\n * T = O(N)\n */\nlist *listDup(list *orig)\n{\n    list *copy;\n    listIter *iter;\n    listNode *node;\n\n    // 创建新链表\n    if ((copy = listCreate()) == NULL)\n        return NULL;\n\n    // 设置节点值处理函数\n    copy->dup = orig->dup;\n    copy->free = orig->free;\n    copy->match = orig->match;\n\n    // 迭代整个输入链表\n    iter = listGetIterator(orig, AL_START_HEAD);\n    while((node = listNext(iter)) != NULL) {\n        void *value;\n\n        // 复制节点值到新节点\n        if (copy->dup) {\n            value = copy->dup(node->value);\n            if (value == NULL) {\n                listRelease(copy);\n                listReleaseIterator(iter);\n                return NULL;\n            }\n        } else\n            value = node->value;\n\n        // 将节点添加到链表\n        if (listAddNodeTail(copy, value) == NULL) {\n            listRelease(copy);\n            listReleaseIterator(iter);\n            return NULL;\n        }\n    }\n\n    // 释放迭代器\n    listReleaseIterator(iter);\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. */\n/* \n * 查找链表 list 中值和 key 匹配的节点。\n * \n * 对比操作由链表的 match 函数负责进行，\n * 如果没有设置 match 函数，\n * 那么直接通过对比值的指针来决定是否匹配。\n *\n * 如果匹配成功，那么第一个匹配的节点会被返回。\n * 如果没有匹配任何节点，那么返回 NULL 。\n *\n * T = O(N)\n */\nlistNode *listSearchKey(list *list, void *key)\n{\n    listIter *iter;\n    listNode *node;\n\n    // 迭代整个链表\n    iter = listGetIterator(list, AL_START_HEAD);\n    while((node = listNext(iter)) != NULL) {\n        \n        // 对比\n        if (list->match) {\n            if (list->match(node->value, key)) {\n                listReleaseIterator(iter);\n                // 找到\n                return node;\n            }\n        } else {\n            if (key == node->value) {\n                listReleaseIterator(iter);\n                // 找到\n                return node;\n            }\n        }\n    }\n    \n    listReleaseIterator(iter);\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. */\n/*\n * 返回链表在给定索引上的值。\n *\n * 索引以 0 为起始，也可以是负数， -1 表示链表最后一个节点，诸如此类。\n *\n * 如果索引超出范围（out of range），返回 NULL 。\n *\n * T = O(N)\n */\nlistNode *listIndex(list *list, long index) {\n    listNode *n;\n\n    // 如果索引为负数，从表尾开始查找\n    if (index < 0) {\n        index = (-index)-1;\n        n = list->tail;\n        while(index-- && n) n = n->prev;\n    // 如果索引为正数，从表头开始查找\n    } else {\n        n = list->head;\n        while(index-- && n) n = n->next;\n    }\n\n    return n;\n}\n\n/* Rotate the list removing the tail node and inserting it to the head. */\n/*\n * 取出链表的表尾节点，并将它移动到表头，成为新的表头节点。\n *\n * T = O(1)\n */\nvoid listRotate(list *list) {\n    listNode *tail = list->tail;\n\n    if (listLength(list) <= 1) return;\n\n    /* Detach current tail */\n    // 取出表尾节点\n    list->tail = tail->prev;\n    list->tail->next = NULL;\n\n    /* Move it as head */\n    // 插入到表头\n    list->head->prev = tail;\n    tail->prev = NULL;\n    tail->next = list->head;\n    list->head = tail;\n}\n"
  },
  {
    "path": "src/adlist.h",
    "content": "/* adlist.h - A generic doubly linked list implementation\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 __ADLIST_H__\n#define __ADLIST_H__\n\n/* Node, List, and Iterator are the only data structures used currently. */\n\n/*\n * 双端链表节点\n */\ntypedef struct listNode {\n\n    // 前置节点\n    struct listNode *prev;\n\n    // 后置节点\n    struct listNode *next;\n\n    // 节点的值\n    void *value;\n\n} listNode;\n\n/*\n * 双端链表迭代器\n */\ntypedef struct listIter {\n\n    // 当前迭代到的节点\n    listNode *next;\n\n    // 迭代的方向\n    int direction;\n\n} listIter;\n\n/*\n * 双端链表结构\n */\ntypedef struct list {\n\n    // 表头节点\n    listNode *head;\n\n    // 表尾节点\n    listNode *tail;\n\n    // 节点值复制函数\n    void *(*dup)(void *ptr);\n\n    // 节点值释放函数\n    void (*free)(void *ptr);\n\n    // 节点值对比函数\n    int (*match)(void *ptr, void *key);\n\n    // 链表所包含的节点数量\n    unsigned long len;\n\n} list;\n\n/* Functions implemented as macros */\n// 返回给定链表所包含的节点数量\n// T = O(1)\n#define listLength(l) ((l)->len)\n// 返回给定链表的表头节点\n// T = O(1)\n#define listFirst(l) ((l)->head)\n// 返回给定链表的表尾节点\n// T = O(1)\n#define listLast(l) ((l)->tail)\n// 返回给定节点的前置节点\n// T = O(1)\n#define listPrevNode(n) ((n)->prev)\n// 返回给定节点的后置节点\n// T = O(1)\n#define listNextNode(n) ((n)->next)\n// 返回给定节点的值\n// T = O(1)\n#define listNodeValue(n) ((n)->value)\n\n// 将链表 l 的值复制函数设置为 m\n// T = O(1)\n#define listSetDupMethod(l,m) ((l)->dup = (m))\n// 将链表 l 的值释放函数设置为 m\n// T = O(1)\n#define listSetFreeMethod(l,m) ((l)->free = (m))\n// 将链表的对比函数设置为 m\n// T = O(1)\n#define listSetMatchMethod(l,m) ((l)->match = (m))\n\n// 返回给定链表的值复制函数\n// T = O(1)\n#define listGetDupMethod(l) ((l)->dup)\n// 返回给定链表的值释放函数\n// T = O(1)\n#define listGetFree(l) ((l)->free)\n// 返回给定链表的值对比函数\n// T = O(1)\n#define listGetMatchMethod(l) ((l)->match)\n\n/* Prototypes */\nlist *listCreate(void);\nvoid listRelease(list *list);\nlist *listAddNodeHead(list *list, void *value);\nlist *listAddNodeTail(list *list, void *value);\nlist *listInsertNode(list *list, listNode *old_node, void *value, int after);\nvoid listDelNode(list *list, listNode *node);\nlistIter *listGetIterator(list *list, int direction);\nlistNode *listNext(listIter *iter);\nvoid listReleaseIterator(listIter *iter);\nlist *listDup(list *orig);\nlistNode *listSearchKey(list *list, void *key);\nlistNode *listIndex(list *list, long index);\nvoid listRewind(list *list, listIter *li);\nvoid listRewindTail(list *list, listIter *li);\nvoid listRotate(list *list);\n\n/* Directions for iterators \n *\n * 迭代器进行迭代的方向\n */\n// 从表头向表尾进行迭代\n#define AL_START_HEAD 0\n// 从表尾到表头进行迭代\n#define AL_START_TAIL 1\n\n#endif /* __ADLIST_H__ */\n"
  },
  {
    "path": "src/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 \"ae.h\"\n#include \"zmalloc.h\"\n#include \"config.h\"\n\n/* Include the best multiplexing layer supported by this system.\n * The following should be ordered by performances, descending. */\n#ifdef HAVE_EVPORT\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\n/*\n * 初始化事件处理器状态\n */\naeEventLoop *aeCreateEventLoop(int setsize) {\n    aeEventLoop *eventLoop;\n    int i;\n\n    // 创建事件状态结构\n    if ((eventLoop = zmalloc(sizeof(*eventLoop))) == NULL) goto err;\n\n    // 初始化文件事件结构和已就绪文件事件结构数组\n    eventLoop->events = zmalloc(sizeof(aeFileEvent)*setsize);\n    eventLoop->fired = zmalloc(sizeof(aeFiredEvent)*setsize);\n    if (eventLoop->events == NULL || eventLoop->fired == NULL) goto err;\n    // 设置数组大小\n    eventLoop->setsize = setsize;\n    // 初始化执行最近一次执行时间\n    eventLoop->lastTime = time(NULL);\n\n    // 初始化时间事件结构\n    eventLoop->timeEventHead = NULL;\n    eventLoop->timeEventNextId = 0;\n\n    eventLoop->stop = 0;\n    eventLoop->maxfd = -1;\n    eventLoop->beforesleep = NULL;\n    if (aeApiCreate(eventLoop) == -1) goto err;\n\n    /* Events with mask == AE_NONE are not set. So let's initialize the\n     * vector with it. */\n    // 初始化监听事件\n    for (i = 0; i < setsize; i++)\n        eventLoop->events[i].mask = AE_NONE;\n\n    // 返回事件循环\n    return eventLoop;\n\nerr:\n    if (eventLoop) {\n        zfree(eventLoop->events);\n        zfree(eventLoop->fired);\n        zfree(eventLoop);\n    }\n    return NULL;\n}\n\n/* Return the current set size. */\n// 返回当前事件槽大小\nint aeGetSetSize(aeEventLoop *eventLoop) {\n    return eventLoop->setsize;\n}\n\n/* Resize the maximum set size of the event loop.\n *\n * 调整事件槽的大小\n *\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 * 如果尝试调整大小为 setsize ，但是有 >= setsize 的文件描述符存在\n * 那么返回 AE_ERR ，不进行任何动作。\n *\n * Otherwise AE_OK is returned and the operation is successful. \n *\n * 否则，执行大小调整操作，并返回 AE_OK 。\n */\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 = zrealloc(eventLoop->events,sizeof(aeFileEvent)*setsize);\n    eventLoop->fired = zrealloc(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\n/*\n * 删除事件处理器\n */\nvoid aeDeleteEventLoop(aeEventLoop *eventLoop) {\n    aeApiFree(eventLoop);\n    zfree(eventLoop->events);\n    zfree(eventLoop->fired);\n    zfree(eventLoop);\n}\n\n/*\n * 停止事件处理器\n */\nvoid aeStop(aeEventLoop *eventLoop) {\n    eventLoop->stop = 1;\n}\n\n/*\n * 根据 mask 参数的值，监听 fd 文件的状态，\n * 当 fd 可用时，执行 proc 函数\n */\nint aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask,\n        aeFileProc *proc, void *clientData)\n{\n    if (fd >= eventLoop->setsize) {\n        errno = ERANGE;\n        return AE_ERR;\n    }\n\n    if (fd >= eventLoop->setsize) return AE_ERR;\n\n    // 取出文件事件结构\n    aeFileEvent *fe = &eventLoop->events[fd];\n\n    // 监听指定 fd 的指定事件\n    if (aeApiAddEvent(eventLoop, fd, mask) == -1)\n        return AE_ERR;\n\n    // 设置文件事件类型，以及事件的处理器\n    fe->mask |= mask;\n    if (mask & AE_READABLE) fe->rfileProc = proc;\n    if (mask & AE_WRITABLE) fe->wfileProc = proc;\n\n    // 私有数据\n    fe->clientData = clientData;\n\n    // 如果有需要，更新事件处理器的最大 fd\n    if (fd > eventLoop->maxfd)\n        eventLoop->maxfd = fd;\n\n    return AE_OK;\n}\n\n/*\n * 将 fd 从 mask 指定的监听队列中删除\n */\nvoid aeDeleteFileEvent(aeEventLoop *eventLoop, int fd, int mask)\n{\n    if (fd >= eventLoop->setsize) return;\n\n    // 取出文件事件结构\n    aeFileEvent *fe = &eventLoop->events[fd];\n\n    // 未设置监听的事件类型，直接返回\n    if (fe->mask == AE_NONE) return;\n\n    // 计算新掩码\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    // 取消对给定 fd 的给定事件的监视\n    aeApiDelEvent(eventLoop, fd, mask);\n}\n\n/*\n * 获取给定 fd 正在监听的事件类型\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\n/*\n * 取出当前时间的秒和毫秒，\n * 并分别将它们保存到 seconds 和 milliseconds 参数中\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\n/*\n * 在当前时间上加上 milliseconds 毫秒，\n * 并且将加上之后的秒数和毫秒数分别保存在 sec 和 ms 指针中。\n */\nstatic void aeAddMillisecondsToNow(long long milliseconds, long *sec, long *ms) {\n    long cur_sec, cur_ms, when_sec, when_ms;\n\n    // 获取当前时间\n    aeGetTime(&cur_sec, &cur_ms);\n\n    // 计算增加 milliseconds 之后的秒数和毫秒数\n    when_sec = cur_sec + milliseconds/1000;\n    when_ms = cur_ms + milliseconds%1000;\n\n    // 进位：\n    // 如果 when_ms 大于等于 1000\n    // 那么将 when_sec 增大一秒\n    if (when_ms >= 1000) {\n        when_sec ++;\n        when_ms -= 1000;\n    }\n\n    // 保存到指针中\n    *sec = when_sec;\n    *ms = when_ms;\n}\n\n/*\n * 创建时间事件\n */\nlong long aeCreateTimeEvent(aeEventLoop *eventLoop, long long milliseconds,\n        aeTimeProc *proc, void *clientData,\n        aeEventFinalizerProc *finalizerProc)\n{\n    // 更新时间计数器\n    long long id = eventLoop->timeEventNextId++;\n\n    // 创建时间事件结构\n    aeTimeEvent *te;\n\n    te = zmalloc(sizeof(*te));\n    if (te == NULL) return AE_ERR;\n\n    // 设置 ID\n    te->id = id;\n\n    // 设定处理事件的时间\n    aeAddMillisecondsToNow(milliseconds,&te->when_sec,&te->when_ms);\n    // 设置事件处理器\n    te->timeProc = proc;\n    te->finalizerProc = finalizerProc;\n    // 设置私有数据\n    te->clientData = clientData;\n\n    // 将新事件放入表头\n    te->next = eventLoop->timeEventHead;\n    eventLoop->timeEventHead = te;\n\n    return id;\n}\n\n/*\n * 删除给定 id 的时间事件\n */\nint aeDeleteTimeEvent(aeEventLoop *eventLoop, long long id)\n{\n    aeTimeEvent *te, *prev = NULL;\n\n    // 遍历链表\n    te = eventLoop->timeEventHead;\n    while(te) {\n\n        // 发现目标事件，删除\n        if (te->id == id) {\n\n            if (prev == NULL)\n                eventLoop->timeEventHead = te->next;\n            else\n                prev->next = te->next;\n\n            // 执行清理处理器\n            if (te->finalizerProc)\n                te->finalizerProc(eventLoop, te->clientData);\n\n            // 释放时间事件\n            zfree(te);\n\n            return AE_OK;\n        }\n        prev = te;\n        te = te->next;\n    }\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 */\n// 寻找里目前时间最近的时间事件\n// 因为链表是乱序的，所以查找复杂度为 O（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\n *\n * 处理所有已到达的时间事件\n */\nstatic int processTimeEvents(aeEventLoop *eventLoop) {\n    int processed = 0;\n    aeTimeEvent *te;\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    // 通过重置事件的运行时间，\n    // 防止因时间穿插（skew）而造成的事件处理混乱\n    if (now < eventLoop->lastTime) {\n        te = eventLoop->timeEventHead;\n        while(te) {\n            te->when_sec = 0;\n            te = te->next;\n        }\n    }\n    // 更新最后一次处理时间事件的时间\n    eventLoop->lastTime = now;\n\n    // 遍历链表\n    // 执行那些已经到达的事件\n    te = eventLoop->timeEventHead;\n    maxId = eventLoop->timeEventNextId-1;\n    while(te) {\n        long now_sec, now_ms;\n        long long id;\n\n        // 跳过无效事件\n        if (te->id > maxId) {\n            te = te->next;\n            continue;\n        }\n        \n        // 获取当前时间\n        aeGetTime(&now_sec, &now_ms);\n\n        // 如果当前时间等于或等于事件的执行时间，那么说明事件已到达，执行这个事件\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            // 执行事件处理器，并获取返回值\n            retval = te->timeProc(eventLoop, id, te->clientData);\n            processed++;\n            /* After an event is processed our time event list may\n             * no longer be the same, so we restart from head.\n             * Still we make sure to don't process events registered\n             * by event handlers itself in order to don't loop forever.\n             * To do so we saved the max ID we want to handle.\n             *\n             * FUTURE OPTIMIZATIONS:\n             * Note that this is NOT great algorithmically. Redis uses\n             * a single time event so it's not a problem but the right\n             * way to do this is to add the new elements on head, and\n             * to flag deleted elements in a special way for later\n             * deletion (putting references to the nodes to delete into\n             * another linked list). */\n\n            // 记录是否有需要循环执行这个事件时间\n            if (retval != AE_NOMORE) {\n                // 是的， retval 毫秒之后继续执行这个时间事件\n                aeAddMillisecondsToNow(retval,&te->when_sec,&te->when_ms);\n            } else {\n                // 不，将这个事件删除\n                aeDeleteTimeEvent(eventLoop, id);\n            }\n\n            // 因为执行事件之后，事件列表可能已经被改变了\n            // 因此需要将 te 放回表头，继续开始执行事件\n            te = eventLoop->timeEventHead;\n        } else {\n            te = te->next;\n        }\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 *\n * 处理所有已到达的时间事件，以及所有已就绪的文件事件。\n *\n * Without special flags the function sleeps until some file event\n * fires, or when the next time event occurs (if any).\n *\n * 如果不传入特殊 flags 的话，那么函数睡眠直到文件事件就绪，\n * 或者下个时间事件到达（如果有的话）。\n *\n * If flags is 0, the function does nothing and returns.\n * 如果 flags 为 0 ，那么函数不作动作，直接返回。\n *\n * if flags has AE_ALL_EVENTS set, all the kind of events are processed.\n * 如果 flags 包含 AE_ALL_EVENTS ，所有类型的事件都会被处理。\n *\n * if flags has AE_FILE_EVENTS set, file events are processed.\n * 如果 flags 包含 AE_FILE_EVENTS ，那么处理文件事件。\n *\n * if flags has AE_TIME_EVENTS set, time events are processed.\n * 如果 flags 包含 AE_TIME_EVENTS ，那么处理时间事件。\n *\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 * 如果 flags 包含 AE_DONT_WAIT ，\n * 那么函数在处理完所有不许阻塞的事件之后，即刻返回。\n *\n * The function returns the number of events processed. \n * 函数的返回值为已处理事件的数量\n */\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        // 获取最近的时间事件\n        if (flags & AE_TIME_EVENTS && !(flags & AE_DONT_WAIT))\n            shortest = aeSearchNearestTimer(eventLoop);\n        if (shortest) {\n            // 如果时间事件存在的话\n            // 那么根据最近可执行时间事件和现在时间的时间差来决定文件事件的阻塞时间\n            long now_sec, now_ms;\n\n            /* Calculate the time missing for the nearest\n             * timer to fire. */\n            // 计算距今最近的时间事件还要多久才能达到\n            // 并将该时间距保存在 tv 结构中\n            aeGetTime(&now_sec, &now_ms);\n            tvp = &tv;\n            tvp->tv_sec = shortest->when_sec - now_sec;\n            if (shortest->when_ms < now_ms) {\n                tvp->tv_usec = ((shortest->when_ms+1000) - now_ms)*1000;\n                tvp->tv_sec --;\n            } else {\n                tvp->tv_usec = (shortest->when_ms - now_ms)*1000;\n            }\n\n            // 时间差小于 0 ，说明事件已经可以执行了，将秒和毫秒设为 0 （不阻塞）\n            if (tvp->tv_sec < 0) tvp->tv_sec = 0;\n            if (tvp->tv_usec < 0) tvp->tv_usec = 0;\n        } else {\n            \n            // 执行到这一步，说明没有时间事件\n            // 那么根据 AE_DONT_WAIT 是否设置来决定是否阻塞，以及阻塞的时间长度\n\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                // 设置文件事件不阻塞\n                tv.tv_sec = tv.tv_usec = 0;\n                tvp = &tv;\n            } else {\n                /* Otherwise we can block */\n                // 文件事件可以阻塞直到有事件到达为止\n                tvp = NULL; /* wait forever */\n            }\n        }\n\n        // 处理文件事件，阻塞时间由 tvp 决定\n        numevents = aeApiPoll(eventLoop, tvp);\n        for (j = 0; j < numevents; j++) {\n            // 从已就绪数组中获取事件\n            aeFileEvent *fe = &eventLoop->events[eventLoop->fired[j].fd];\n\n            int mask = eventLoop->fired[j].mask;\n            int fd = eventLoop->fired[j].fd;\n            int rfired = 0;\n\n           /* 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            // 读事件\n            if (fe->mask & mask & AE_READABLE) {\n                // rfired 确保读/写事件只能执行其中一个\n                rfired = 1;\n                fe->rfileProc(eventLoop,fd,fe->clientData,mask);\n            }\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\n            processed++;\n        }\n    }\n\n    /* Check time events */\n    // 执行时间事件\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 \n *\n * 在给定毫秒内等待，直到 fd 变成可写、可读或异常\n */\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\n/*\n * 事件处理器的主循环\n */\nvoid aeMain(aeEventLoop *eventLoop) {\n\n    eventLoop->stop = 0;\n\n    while (!eventLoop->stop) {\n\n        // 如果有需要在事件处理前执行的函数，那么运行它\n        if (eventLoop->beforesleep != NULL)\n            eventLoop->beforesleep(eventLoop);\n\n        // 开始处理事件\n        aeProcessEvents(eventLoop, AE_ALL_EVENTS);\n    }\n}\n\n/*\n * 返回所使用的多路复用库的名称\n */\nchar *aeGetApiName(void) {\n    return aeApiName();\n}\n\n/*\n * 设置处理事件前需要被执行的函数\n */\nvoid aeSetBeforeSleepProc(aeEventLoop *eventLoop, aeBeforeSleepProc *beforesleep) {\n    eventLoop->beforesleep = beforesleep;\n}\n"
  },
  {
    "path": "src/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/*\n * 事件执行状态\n */\n// 成功\n#define AE_OK 0\n// 出错\n#define AE_ERR -1\n\n/*\n * 文件事件状态\n */\n// 未设置\n#define AE_NONE 0\n// 可读\n#define AE_READABLE 1\n// 可写\n#define AE_WRITABLE 2\n\n/*\n * 时间处理器的执行 flags\n */\n// 文件事件\n#define AE_FILE_EVENTS 1\n// 时间事件\n#define AE_TIME_EVENTS 2\n// 所有事件\n#define AE_ALL_EVENTS (AE_FILE_EVENTS|AE_TIME_EVENTS)\n// 不阻塞，也不进行等待\n#define AE_DONT_WAIT 4\n\n/*\n * 决定时间事件是否要持续执行的 flag\n */\n#define AE_NOMORE -1\n\n/* Macros */\n#define AE_NOTUSED(V) ((void) V)\n\n/*\n * 事件处理器状态\n */\nstruct aeEventLoop;\n\n/* Types and data structures \n *\n * 事件接口\n */\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);\n\n/* File event structure\n *\n * 文件事件结构\n */\ntypedef struct aeFileEvent {\n\n    // 监听事件类型掩码，\n    // 值可以是 AE_READABLE 或 AE_WRITABLE ，\n    // 或者 AE_READABLE | AE_WRITABLE\n    int mask; /* one of AE_(READABLE|WRITABLE) */\n\n    // 读事件处理器\n    aeFileProc *rfileProc;\n\n    // 写事件处理器\n    aeFileProc *wfileProc;\n\n    // 多路复用库的私有数据\n    void *clientData;\n\n} aeFileEvent;\n\n/* Time event structure\n *\n * 时间事件结构\n */\ntypedef struct aeTimeEvent {\n\n    // 时间事件的唯一标识符\n    long long id; /* time event identifier. */\n\n    // 事件的到达时间\n    long when_sec; /* seconds */\n    long when_ms; /* milliseconds */\n\n    // 事件处理函数\n    aeTimeProc *timeProc;\n\n    // 事件释放函数\n    aeEventFinalizerProc *finalizerProc;\n\n    // 多路复用库的私有数据\n    void *clientData;\n\n    // 指向下个时间事件结构，形成链表\n    struct aeTimeEvent *next;\n\n} aeTimeEvent;\n\n/* A fired event\n *\n * 已就绪事件\n */\ntypedef struct aeFiredEvent {\n\n    // 已就绪文件描述符\n    int fd;\n\n    // 事件类型掩码，\n    // 值可以是 AE_READABLE 或 AE_WRITABLE\n    // 或者是两者的或\n    int mask;\n\n} aeFiredEvent;\n\n/* State of an event based program \n *\n * 事件处理器的状态\n */\ntypedef struct aeEventLoop {\n\n    // 目前已注册的最大描述符\n    int maxfd;   /* highest file descriptor currently registered */\n\n    // 目前已追踪的最大描述符\n    int setsize; /* max number of file descriptors tracked */\n\n    // 用于生成时间事件 id\n    long long timeEventNextId;\n\n    // 最后一次执行时间事件的时间\n    time_t lastTime;     /* Used to detect system clock skew */\n\n    // 已注册的文件事件\n    aeFileEvent *events; /* Registered events */\n\n    // 已就绪的文件事件\n    aeFiredEvent *fired; /* Fired events */\n\n    // 时间事件\n    aeTimeEvent *timeEventHead;\n\n    // 事件处理器的开关\n    int stop;\n\n    // 多路复用库的私有数据\n    void *apidata; /* This is used for polling API specific data */\n\n    // 在处理事件前要执行的函数\n    aeBeforeSleepProc *beforesleep;\n\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);\nint aeGetSetSize(aeEventLoop *eventLoop);\nint aeResizeSetSize(aeEventLoop *eventLoop, int setsize);\n\n#endif\n"
  },
  {
    "path": "src/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\n/*\n * 事件状态\n */\ntypedef struct aeApiState {\n\n    // epoll_event 实例描述符\n    int epfd;\n\n    // 事件槽\n    struct epoll_event *events;\n\n} aeApiState;\n\n/*\n * 创建一个新的 epoll 实例，并将它赋值给 eventLoop\n */\nstatic int aeApiCreate(aeEventLoop *eventLoop) {\n\n    aeApiState *state = zmalloc(sizeof(aeApiState));\n\n    if (!state) return -1;\n\n    // 初始化事件槽空间\n    state->events = zmalloc(sizeof(struct epoll_event)*eventLoop->setsize);\n    if (!state->events) {\n        zfree(state);\n        return -1;\n    }\n\n    // 创建 epoll 实例\n    state->epfd = epoll_create(1024); /* 1024 is just a hint for the kernel */\n    if (state->epfd == -1) {\n        zfree(state->events);\n        zfree(state);\n        return -1;\n    }\n\n    // 赋值给 eventLoop\n    eventLoop->apidata = state;\n    return 0;\n}\n\n/*\n * 调整事件槽大小\n */\nstatic int aeApiResize(aeEventLoop *eventLoop, int setsize) {\n    aeApiState *state = eventLoop->apidata;\n\n    state->events = zrealloc(state->events, sizeof(struct epoll_event)*setsize);\n    return 0;\n}\n\n/*\n * 释放 epoll 实例和事件槽\n */\nstatic void aeApiFree(aeEventLoop *eventLoop) {\n    aeApiState *state = eventLoop->apidata;\n\n    close(state->epfd);\n    zfree(state->events);\n    zfree(state);\n}\n\n/*\n * 关联给定事件到 fd\n */\nstatic int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask) {\n    aeApiState *state = eventLoop->apidata;\n    struct epoll_event ee;\n\n    /* If the fd was already monitored for some event, we need a MOD\n     * operation. Otherwise we need an ADD operation. \n     *\n     * 如果 fd 没有关联任何事件，那么这是一个 ADD 操作。\n     *\n     * 如果已经关联了某个/某些事件，那么这是一个 MOD 操作。\n     */\n    int op = eventLoop->events[fd].mask == AE_NONE ?\n            EPOLL_CTL_ADD : EPOLL_CTL_MOD;\n\n    // 注册事件到 epoll\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.u64 = 0; /* avoid valgrind warning */\n    ee.data.fd = fd;\n\n    if (epoll_ctl(state->epfd,op,fd,&ee) == -1) return -1;\n\n    return 0;\n}\n\n/*\n * 从 fd 中删除给定事件\n */\nstatic void aeApiDelEvent(aeEventLoop *eventLoop, int fd, int delmask) {\n    aeApiState *state = eventLoop->apidata;\n    struct epoll_event ee;\n\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.u64 = 0; /* avoid valgrind warning */\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\n/*\n * 获取可执行事件\n */\nstatic int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp) {\n    aeApiState *state = eventLoop->apidata;\n    int retval, numevents = 0;\n\n    // 等待时间\n    retval = epoll_wait(state->epfd,state->events,eventLoop->setsize,\n            tvp ? (tvp->tv_sec*1000 + tvp->tv_usec/1000) : -1);\n\n    // 有至少一个事件就绪？\n    if (retval > 0) {\n        int j;\n\n        // 为已就绪事件设置相应的模式\n        // 并加入到 eventLoop 的 fired 数组中\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\n            eventLoop->fired[j].fd = e->data.fd;\n            eventLoop->fired[j].mask = mask;\n        }\n    }\n    \n    // 返回已就绪事件个数\n    return numevents;\n}\n\n/*\n * 返回当前正在使用的 poll 库的名字\n */\nstatic char *aeApiName(void) {\n    return \"epoll\";\n}\n"
  },
  {
    "path": "src/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 = zmalloc(sizeof(aeApiState));\n    if (!state) return -1;\n\n    state->portfd = port_create();\n    if (state->portfd == -1) {\n        zfree(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    zfree(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": "src/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 = zmalloc(sizeof(aeApiState));\n\n    if (!state) return -1;\n    state->events = zmalloc(sizeof(struct kevent)*eventLoop->setsize);\n    if (!state->events) {\n        zfree(state);\n        return -1;\n    }\n    state->kqfd = kqueue();\n    if (state->kqfd == -1) {\n        zfree(state->events);\n        zfree(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 = zrealloc(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    zfree(state->events);\n    zfree(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": "src/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 = zmalloc(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    zfree(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": "src/anet.c",
    "content": "/* anet.c -- Basic TCP socket stuff made a bit less boring\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#include \"fmacros.h\"\n\n#include <sys/types.h>\n#include <sys/socket.h>\n#include <sys/stat.h>\n#include <sys/un.h>\n#include <netinet/in.h>\n#include <netinet/tcp.h>\n#include <arpa/inet.h>\n#include <unistd.h>\n#include <fcntl.h>\n#include <string.h>\n#include <netdb.h>\n#include <errno.h>\n#include <stdarg.h>\n#include <stdio.h>\n\n#include \"anet.h\"\n\n/*\n * 打印错误信息\n */\nstatic void anetSetError(char *err, const char *fmt, ...)\n{\n    va_list ap;\n\n    if (!err) return;\n    va_start(ap, fmt);\n    vsnprintf(err, ANET_ERR_LEN, fmt, ap);\n    va_end(ap);\n}\n\n/*\n * 将 fd 设置为非阻塞模式（O_NONBLOCK）\n */\nint anetNonBlock(char *err, int fd)\n{\n    int flags;\n\n    /* Set the socket non-blocking.\n     * Note that fcntl(2) for F_GETFL and F_SETFL can't be\n     * interrupted by a signal. */\n    if ((flags = fcntl(fd, F_GETFL)) == -1) {\n        anetSetError(err, \"fcntl(F_GETFL): %s\", strerror(errno));\n        return ANET_ERR;\n    }\n    if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {\n        anetSetError(err, \"fcntl(F_SETFL,O_NONBLOCK): %s\", strerror(errno));\n        return ANET_ERR;\n    }\n    return ANET_OK;\n}\n\n/* Set TCP keep alive option to detect dead peers. The interval option\n * is only used for Linux as we are using Linux-specific APIs to set\n * the probe send time, interval, and count.\n *\n * 修改 TCP 连接的 keep alive 选项\n */\nint anetKeepAlive(char *err, int fd, int interval)\n{\n    int val = 1;\n\n    if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)) == -1)\n    {\n        anetSetError(err, \"setsockopt SO_KEEPALIVE: %s\", strerror(errno));\n        return ANET_ERR;\n    }\n\n#ifdef __linux__\n    /* Default settings are more or less garbage, with the keepalive time\n     * set to 7200 by default on Linux. Modify settings to make the feature\n     * actually useful. */\n\n    /* Send first probe after interval. */\n    val = interval;\n    if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &val, sizeof(val)) < 0) {\n        anetSetError(err, \"setsockopt TCP_KEEPIDLE: %s\\n\", strerror(errno));\n        return ANET_ERR;\n    }\n\n    /* Send next probes after the specified interval. Note that we set the\n     * delay as interval / 3, as we send three probes before detecting\n     * an error (see the next setsockopt call). */\n    val = interval/3;\n    if (val == 0) val = 1;\n    if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &val, sizeof(val)) < 0) {\n        anetSetError(err, \"setsockopt TCP_KEEPINTVL: %s\\n\", strerror(errno));\n        return ANET_ERR;\n    }\n\n    /* Consider the socket in error state after three we send three ACK\n     * probes without getting a reply. */\n    val = 3;\n    if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &val, sizeof(val)) < 0) {\n        anetSetError(err, \"setsockopt TCP_KEEPCNT: %s\\n\", strerror(errno));\n        return ANET_ERR;\n    }\n#endif\n\n    return ANET_OK;\n}\n\n/*\n * 打开或关闭 Nagle 算法\n */\nstatic int anetSetTcpNoDelay(char *err, int fd, int val)\n{\n    if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val)) == -1)\n    {\n        anetSetError(err, \"setsockopt TCP_NODELAY: %s\", strerror(errno));\n        return ANET_ERR;\n    }\n    return ANET_OK;\n}\n\n/*\n * 禁用 Nagle 算法\n */\nint anetEnableTcpNoDelay(char *err, int fd)\n{\n    return anetSetTcpNoDelay(err, fd, 1);\n}\n\n/*\n * 启用 Nagle 算法\n */\nint anetDisableTcpNoDelay(char *err, int fd) \n{\n    return anetSetTcpNoDelay(err, fd, 0);\n}\n\n/*\n * 设置 socket 的最大发送 buffer 字节数\n */\nint anetSetSendBuffer(char *err, int fd, int buffsize)\n{\n    if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &buffsize, sizeof(buffsize)) == -1)\n    {\n        anetSetError(err, \"setsockopt SO_SNDBUF: %s\", strerror(errno));\n        return ANET_ERR;\n    }\n    return ANET_OK;\n}\n\n/*\n * 开启 TCP 的 keep alive 选项\n */\nint anetTcpKeepAlive(char *err, int fd)\n{\n    int yes = 1;\n    if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &yes, sizeof(yes)) == -1) {\n        anetSetError(err, \"setsockopt SO_KEEPALIVE: %s\", strerror(errno));\n        return ANET_ERR;\n    }\n    return ANET_OK;\n}\n\n/* anetGenericResolve() is called by anetResolve() and anetResolveIP() to\n * do the actual work. It resolves the hostname \"host\" and set the string\n * representation of the IP address into the buffer pointed by \"ipbuf\".\n *\n * If flags is set to ANET_IP_ONLY the function only resolves hostnames\n * that are actually already IPv4 or IPv6 addresses. This turns the function\n * into a validating / normalizing function. */\n// 解释 host 的地址，并保存到 ipbuf 中\nint anetGenericResolve(char *err, char *host, char *ipbuf, size_t ipbuf_len,\n                       int flags)\n{\n    struct addrinfo hints, *info;\n    int rv;\n\n    memset(&hints,0,sizeof(hints));\n    if (flags & ANET_IP_ONLY) hints.ai_flags = AI_NUMERICHOST;\n    hints.ai_family = AF_UNSPEC;\n    hints.ai_socktype = SOCK_STREAM;  /* specify socktype to avoid dups */\n\n    if ((rv = getaddrinfo(host, NULL, &hints, &info)) != 0) {\n        anetSetError(err, \"%s\", gai_strerror(rv));\n        return ANET_ERR;\n    }\n    if (info->ai_family == AF_INET) {\n        struct sockaddr_in *sa = (struct sockaddr_in *)info->ai_addr;\n        inet_ntop(AF_INET, &(sa->sin_addr), ipbuf, ipbuf_len);\n    } else {\n        struct sockaddr_in6 *sa = (struct sockaddr_in6 *)info->ai_addr;\n        inet_ntop(AF_INET6, &(sa->sin6_addr), ipbuf, ipbuf_len);\n    }\n\n    freeaddrinfo(info);\n    return ANET_OK;\n}\n\nint anetResolve(char *err, char *host, char *ipbuf, size_t ipbuf_len) {\n    return anetGenericResolve(err,host,ipbuf,ipbuf_len,ANET_NONE);\n}\n\nint anetResolveIP(char *err, char *host, char *ipbuf, size_t ipbuf_len) {\n    return anetGenericResolve(err,host,ipbuf,ipbuf_len,ANET_IP_ONLY);\n}\n\n// 设置地址为可重用\nstatic int anetSetReuseAddr(char *err, int fd) {\n    int yes = 1;\n    /* Make sure connection-intensive things like the redis benckmark\n     * will be able to close/open sockets a zillion of times */\n    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) == -1) {\n        anetSetError(err, \"setsockopt SO_REUSEADDR: %s\", strerror(errno));\n        return ANET_ERR;\n    }\n    return ANET_OK;\n}\n\n/*\n * 创建并返回 socket\n */\nstatic int anetCreateSocket(char *err, int domain) {\n    int s;\n    if ((s = socket(domain, SOCK_STREAM, 0)) == -1) {\n        anetSetError(err, \"creating socket: %s\", strerror(errno));\n        return ANET_ERR;\n    }\n\n    /* Make sure connection-intensive things like the redis benchmark\n     * will be able to close/open sockets a zillion of times */\n    if (anetSetReuseAddr(err,s) == ANET_ERR) {\n        close(s);\n        return ANET_ERR;\n    }\n    return s;\n}\n\n// 通用连接创建函数，被其他高层函数所调用\n#define ANET_CONNECT_NONE 0\n#define ANET_CONNECT_NONBLOCK 1\nstatic int anetTcpGenericConnect(char *err, char *addr, int port,\n                                 char *source_addr, int flags)\n{\n    int s = ANET_ERR, rv;\n    char portstr[6];  /* strlen(\"65535\") + 1; */\n    struct addrinfo hints, *servinfo, *bservinfo, *p, *b;\n\n    snprintf(portstr,sizeof(portstr),\"%d\",port);\n    memset(&hints,0,sizeof(hints));\n    hints.ai_family = AF_UNSPEC;\n    hints.ai_socktype = SOCK_STREAM;\n\n    if ((rv = getaddrinfo(addr,portstr,&hints,&servinfo)) != 0) {\n        anetSetError(err, \"%s\", gai_strerror(rv));\n        return ANET_ERR;\n    }\n    for (p = servinfo; p != NULL; p = p->ai_next) {\n        /* Try to create the socket and to connect it.\n         * If we fail in the socket() call, or on connect(), we retry with\n         * the next entry in servinfo. */\n        if ((s = socket(p->ai_family,p->ai_socktype,p->ai_protocol)) == -1)\n            continue;\n        if (anetSetReuseAddr(err,s) == ANET_ERR) goto error;\n        if (flags & ANET_CONNECT_NONBLOCK && anetNonBlock(err,s) != ANET_OK)\n            goto error;\n        if (source_addr) {\n            int bound = 0;\n            /* Using getaddrinfo saves us from self-determining IPv4 vs IPv6 */\n            if ((rv = getaddrinfo(source_addr, NULL, &hints, &bservinfo)) != 0) {\n                anetSetError(err, \"%s\", gai_strerror(rv));\n                goto end;\n            }\n            for (b = bservinfo; b != NULL; b = b->ai_next) {\n                if (bind(s,b->ai_addr,b->ai_addrlen) != -1) {\n                    bound = 1;\n                    break;\n                }\n            }\n            if (!bound) {\n                anetSetError(err, \"bind: %s\", strerror(errno));\n                goto end;\n            }\n        }\n        if (connect(s,p->ai_addr,p->ai_addrlen) == -1) {\n            /* If the socket is non-blocking, it is ok for connect() to\n             * return an EINPROGRESS error here. */\n            if (errno == EINPROGRESS && flags & ANET_CONNECT_NONBLOCK)\n                goto end;\n            close(s);\n            s = ANET_ERR;\n            continue;\n        }\n\n        /* If we ended an iteration of the for loop without errors, we\n         * have a connected socket. Let's return to the caller. */\n        goto end;\n    }\n    if (p == NULL)\n        anetSetError(err, \"creating socket: %s\", strerror(errno));\n\nerror:\n    if (s != ANET_ERR) {\n        close(s);\n        s = ANET_ERR;\n    }\nend:\n    freeaddrinfo(servinfo);\n    return s;\n}\n\n/*\n * 创建阻塞 TCP 连接\n */\nint anetTcpConnect(char *err, char *addr, int port)\n{\n    return anetTcpGenericConnect(err,addr,port,NULL,ANET_CONNECT_NONE);\n}\n\n/*\n * 创建非阻塞 TCP 连接\n */\nint anetTcpNonBlockConnect(char *err, char *addr, int port)\n{\n    return anetTcpGenericConnect(err,addr,port,NULL,ANET_CONNECT_NONBLOCK);\n}\n\nint anetTcpNonBlockBindConnect(char *err, char *addr, int port, char *source_addr)\n{\n    return anetTcpGenericConnect(err,addr,port,source_addr,ANET_CONNECT_NONBLOCK);\n}\n\nint anetUnixGenericConnect(char *err, char *path, int flags)\n{\n    int s;\n    struct sockaddr_un sa;\n\n    if ((s = anetCreateSocket(err,AF_LOCAL)) == ANET_ERR)\n        return ANET_ERR;\n\n    sa.sun_family = AF_LOCAL;\n    strncpy(sa.sun_path,path,sizeof(sa.sun_path)-1);\n    if (flags & ANET_CONNECT_NONBLOCK) {\n        if (anetNonBlock(err,s) != ANET_OK)\n            return ANET_ERR;\n    }\n    if (connect(s,(struct sockaddr*)&sa,sizeof(sa)) == -1) {\n        if (errno == EINPROGRESS &&\n            flags & ANET_CONNECT_NONBLOCK)\n            return s;\n\n        anetSetError(err, \"connect: %s\", strerror(errno));\n        close(s);\n        return ANET_ERR;\n    }\n    return s;\n}\n\n/*\n * 创建阻塞本地连接\n */\nint anetUnixConnect(char *err, char *path)\n{\n    return anetUnixGenericConnect(err,path,ANET_CONNECT_NONE);\n}\n\n/*\n * 创建非阻塞本地连接\n */\nint anetUnixNonBlockConnect(char *err, char *path)\n{\n    return anetUnixGenericConnect(err,path,ANET_CONNECT_NONBLOCK);\n}\n\n/* Like read(2) but make sure 'count' is read before to return\n * (unless error or EOF condition is encountered) */\n/*\n * 带 short count 处理的读取函数\n */\nint anetRead(int fd, char *buf, int count)\n{\n    int nread, totlen = 0;\n    while(totlen != count) {\n        nread = read(fd,buf,count-totlen);\n        if (nread == 0) return totlen;\n        if (nread == -1) return -1;\n        totlen += nread;\n        buf += nread;\n    }\n    return totlen;\n}\n\n/* Like write(2) but make sure 'count' is read before to return\n * (unless error is encountered) */\n/*\n * 带 short count 处理的写入函数\n */\nint anetWrite(int fd, char *buf, int count)\n{\n    int nwritten, totlen = 0;\n    while(totlen != count) {\n        nwritten = write(fd,buf,count-totlen);\n        if (nwritten == 0) return totlen;\n        if (nwritten == -1) return -1;\n        totlen += nwritten;\n        buf += nwritten;\n    }\n    return totlen;\n}\n\n/*\n * 绑定并创建监听套接字\n */\nstatic int anetListen(char *err, int s, struct sockaddr *sa, socklen_t len, int backlog) {\n    if (bind(s,sa,len) == -1) {\n        anetSetError(err, \"bind: %s\", strerror(errno));\n        close(s);\n        return ANET_ERR;\n    }\n\n    if (listen(s, backlog) == -1) {\n        anetSetError(err, \"listen: %s\", strerror(errno));\n        close(s);\n        return ANET_ERR;\n    }\n    return ANET_OK;\n}\n\nstatic int anetV6Only(char *err, int s) {\n    int yes = 1;\n    if (setsockopt(s,IPPROTO_IPV6,IPV6_V6ONLY,&yes,sizeof(yes)) == -1) {\n        anetSetError(err, \"setsockopt: %s\", strerror(errno));\n        close(s);\n        return ANET_ERR;\n    }\n    return ANET_OK;\n}\n\nstatic int _anetTcpServer(char *err, int port, char *bindaddr, int af, int backlog)\n{\n    int s, rv;\n    char _port[6];  /* strlen(\"65535\") */\n    struct addrinfo hints, *servinfo, *p;\n\n    snprintf(_port,6,\"%d\",port);\n    memset(&hints,0,sizeof(hints));\n    hints.ai_family = af;\n    hints.ai_socktype = SOCK_STREAM;\n    hints.ai_flags = AI_PASSIVE;    /* No effect if bindaddr != NULL */\n\n    if ((rv = getaddrinfo(bindaddr,_port,&hints,&servinfo)) != 0) {\n        anetSetError(err, \"%s\", gai_strerror(rv));\n        return ANET_ERR;\n    }\n    for (p = servinfo; p != NULL; p = p->ai_next) {\n        if ((s = socket(p->ai_family,p->ai_socktype,p->ai_protocol)) == -1)\n            continue;\n\n        if (af == AF_INET6 && anetV6Only(err,s) == ANET_ERR) goto error;\n        if (anetSetReuseAddr(err,s) == ANET_ERR) goto error;\n        if (anetListen(err,s,p->ai_addr,p->ai_addrlen,backlog) == ANET_ERR) goto error;\n        goto end;\n    }\n    if (p == NULL) {\n        anetSetError(err, \"unable to bind socket\");\n        goto error;\n    }\n\nerror:\n    s = ANET_ERR;\nend:\n    freeaddrinfo(servinfo);\n    return s;\n}\n\nint anetTcpServer(char *err, int port, char *bindaddr, int backlog)\n{\n    return _anetTcpServer(err, port, bindaddr, AF_INET, backlog);\n}\n\nint anetTcp6Server(char *err, int port, char *bindaddr, int backlog)\n{\n    return _anetTcpServer(err, port, bindaddr, AF_INET6, backlog);\n}\n\n/*\n * 创建一个本地连接用的服务器监听套接字\n */\nint anetUnixServer(char *err, char *path, mode_t perm, int backlog)\n{\n    int s;\n    struct sockaddr_un sa;\n\n    if ((s = anetCreateSocket(err,AF_LOCAL)) == ANET_ERR)\n        return ANET_ERR;\n\n    memset(&sa,0,sizeof(sa));\n    sa.sun_family = AF_LOCAL;\n    strncpy(sa.sun_path,path,sizeof(sa.sun_path)-1);\n    if (anetListen(err,s,(struct sockaddr*)&sa,sizeof(sa),backlog) == ANET_ERR)\n        return ANET_ERR;\n    if (perm)\n        chmod(sa.sun_path, perm);\n    return s;\n}\n\nstatic int anetGenericAccept(char *err, int s, struct sockaddr *sa, socklen_t *len) {\n    int fd;\n    while(1) {\n        fd = accept(s,sa,len);\n        if (fd == -1) {\n            if (errno == EINTR)\n                continue;\n            else {\n                anetSetError(err, \"accept: %s\", strerror(errno));\n                return ANET_ERR;\n            }\n        }\n        break;\n    }\n    return fd;\n}\n\n/*\n * TCP 连接 accept 函数\n */\nint anetTcpAccept(char *err, int s, char *ip, size_t ip_len, int *port) {\n    int fd;\n    struct sockaddr_storage sa;\n    socklen_t salen = sizeof(sa);\n    if ((fd = anetGenericAccept(err,s,(struct sockaddr*)&sa,&salen)) == -1)\n        return ANET_ERR;\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 {\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    }\n    return fd;\n}\n\n/*\n * 本地连接 accept 函数\n */\nint anetUnixAccept(char *err, int s) {\n    int fd;\n    struct sockaddr_un sa;\n    socklen_t salen = sizeof(sa);\n    if ((fd = anetGenericAccept(err,s,(struct sockaddr*)&sa,&salen)) == -1)\n        return ANET_ERR;\n\n    return fd;\n}\n\n/*\n * 获取连接客户端的 IP 和端口号\n */\nint anetPeerToString(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) {\n        if (port) *port = 0;\n        ip[0] = '?';\n        ip[1] = '\\0';\n        return -1;\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 {\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    }\n    return 0;\n}\n\n/*\n * 获取服务器本机的 IP 和端口号\n */\nint anetSockName(int fd, char *ip, size_t ip_len, int *port) {\n    struct sockaddr_storage sa;\n    socklen_t salen = sizeof(sa);\n\n    if (getsockname(fd,(struct sockaddr*)&sa,&salen) == -1) {\n        if (port) *port = 0;\n        ip[0] = '?';\n        ip[1] = '\\0';\n        return -1;\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 {\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    }\n    return 0;\n}\n"
  },
  {
    "path": "src/anet.h",
    "content": "/* anet.c -- Basic TCP socket stuff made a bit less boring\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 ANET_H\n#define ANET_H\n\n#define ANET_OK 0\n#define ANET_ERR -1\n#define ANET_ERR_LEN 256\n\n/* Flags used with certain functions. */\n#define ANET_NONE 0\n#define ANET_IP_ONLY (1<<0)\n\n#if defined(__sun)\n#define AF_LOCAL AF_UNIX\n#endif\n\nint anetTcpConnect(char *err, char *addr, int port);\nint anetTcpNonBlockConnect(char *err, char *addr, int port);\nint anetTcpNonBlockBindConnect(char *err, char *addr, int port, char *source_addr);\nint anetUnixConnect(char *err, char *path);\nint anetUnixNonBlockConnect(char *err, char *path);\nint anetRead(int fd, char *buf, int count);\nint anetResolve(char *err, char *host, char *ipbuf, size_t ipbuf_len);\nint anetResolveIP(char *err, char *host, char *ipbuf, size_t ipbuf_len);\nint anetTcpServer(char *err, int port, char *bindaddr, int backlog);\nint anetTcp6Server(char *err, int port, char *bindaddr, int backlog);\nint anetUnixServer(char *err, char *path, mode_t perm, int backlog);\nint anetTcpAccept(char *err, int serversock, char *ip, size_t ip_len, int *port);\nint anetUnixAccept(char *err, int serversock);\nint anetWrite(int fd, char *buf, int count);\nint anetNonBlock(char *err, int fd);\nint anetEnableTcpNoDelay(char *err, int fd);\nint anetDisableTcpNoDelay(char *err, int fd);\nint anetTcpKeepAlive(char *err, int fd);\nint anetPeerToString(int fd, char *ip, size_t ip_len, int *port);\nint anetKeepAlive(char *err, int fd, int interval);\nint anetSockName(int fd, char *ip, size_t ip_len, int *port);\n\n#endif\n"
  },
  {
    "path": "src/aof.c",
    "content": "/*\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 \"redis.h\"\n#include \"bio.h\"\n#include \"rio.h\"\n\n#include <signal.h>\n#include <fcntl.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n#include <sys/time.h>\n#include <sys/resource.h>\n#include <sys/wait.h>\n\nvoid aofUpdateCurrentSize(void);\n\n/* ----------------------------------------------------------------------------\n * AOF rewrite buffer implementation.\n *\n * AOF 重写缓存的实现。\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 * 以下代码实现了一个简单的缓存，\n * 它可以在 BGREWRITEAOF 执行的过程中，累积所有修改数据集的命令。\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 * 程序需要不断对这个缓存执行 append 操作，\n * 因为分配一个非常大的空间并不总是可能的，\n * 也可能产生大量的复制工作，\n * 所以这里使用多个大小为 AOF_RW_BUF_BLOCK_SIZE 的空间来保存命令。\n *\n * ------------------------------------------------------------------------- */\n\n// 每个缓存块的大小\n#define AOF_RW_BUF_BLOCK_SIZE (1024*1024*10)    /* 10 MB per block */\n\ntypedef struct aofrwblock {\n    \n    // 缓存块已使用字节数和可用字节数\n    unsigned long used, free;\n\n    // 缓存块\n    char buf[AOF_RW_BUF_BLOCK_SIZE];\n\n} aofrwblock;\n\n/* This function free the old AOF rewrite buffer if needed, and initialize\n * a fresh new one. It tests for server.aof_rewrite_buf_blocks equal to NULL\n * so can be used for the first initialization as well. \n *\n * 释放旧的 AOF 重写缓存，并初始化一个新的 AOF 缓存。\n *\n * 这个函数也可以单纯地用于 AOF 重写缓存的初始化。\n */\nvoid aofRewriteBufferReset(void) {\n\n    // 释放旧有的缓存（链表）\n    if (server.aof_rewrite_buf_blocks)\n        listRelease(server.aof_rewrite_buf_blocks);\n\n    // 初始化新的缓存（链表）\n    server.aof_rewrite_buf_blocks = listCreate();\n    listSetFreeMethod(server.aof_rewrite_buf_blocks,zfree);\n}\n\n/* Return the current size of the AOF rerwite buffer. \n *\n * 返回 AOF 重写缓存当前的大小\n */\nunsigned long aofRewriteBufferSize(void) {\n\n    // 取出链表中最后的缓存块\n    listNode *ln = listLast(server.aof_rewrite_buf_blocks);\n    aofrwblock *block = ln ? ln->value : NULL;\n\n    // 没有缓存被使用\n    if (block == NULL) return 0;\n\n    // 总缓存大小 = （缓存块数量-1） * AOF_RW_BUF_BLOCK_SIZE + 最后一个缓存块的大小\n    unsigned long size =\n        (listLength(server.aof_rewrite_buf_blocks)-1) * AOF_RW_BUF_BLOCK_SIZE;\n    size += block->used;\n\n    return size;\n}\n\n/* Append data to the AOF rewrite buffer, allocating new blocks if needed. \n *\n * 将字符数组 s 追加到 AOF 缓存的末尾，\n * 如果有需要的话，分配一个新的缓存块。\n */\nvoid aofRewriteBufferAppend(unsigned char *s, unsigned long len) {\n\n    // 指向最后一个缓存块\n    listNode *ln = listLast(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         *\n         * 如果已经有至少一个缓存块，那么尝试将内容追加到这个缓存块里面\n         */\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        // 如果 block != NULL ，那么这里是创建另一个缓存块买容纳 block 装不下的内容\n        // 如果 block == NULL ，那么这里是创建缓存链表的第一个缓存块\n        if (len) { /* First block to allocate, or need another block. */\n            int numblocks;\n\n            // 分配缓存块\n            block = zmalloc(sizeof(*block));\n            block->free = AOF_RW_BUF_BLOCK_SIZE;\n            block->used = 0;\n\n            // 链接到链表末尾\n            listAddNodeTail(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             *\n             * 每次创建 10 个缓存块就打印一个日志，用作标记或者提醒\n             */\n            numblocks = listLength(server.aof_rewrite_buf_blocks);\n            if (((numblocks+1) % 10) == 0) {\n                int level = ((numblocks+1) % 100) == 0 ? REDIS_WARNING :\n                                                         REDIS_NOTICE;\n                redisLog(level,\"Background AOF buffer size: %lu MB\",\n                    aofRewriteBufferSize()/(1024*1024));\n            }\n        }\n    }\n}\n\n/* Write the buffer (possibly composed of multiple blocks) into the specified\n * fd. If a short write or any other error happens -1 is returned,\n * otherwise the number of bytes written is returned. \n *\n * 将重写缓存中的所有内容（可能由多个块组成）写入到给定 fd 中。\n *\n * 如果没有 short write 或者其他错误发生，那么返回写入的字节数量，\n * 否则，返回 -1 。\n */\nssize_t aofRewriteBufferWrite(int fd) {\n    listNode *ln;\n    listIter li;\n    ssize_t count = 0;\n\n    // 遍历所有缓存块\n    listRewind(server.aof_rewrite_buf_blocks,&li);\n    while((ln = listNext(&li))) {\n        aofrwblock *block = listNodeValue(ln);\n        ssize_t nwritten;\n\n        if (block->used) {\n\n            // 写入缓存块内容到 fd\n            nwritten = write(fd,block->buf,block->used);\n            if (nwritten != block->used) {\n                if (nwritten == 0) errno = EIO;\n                return -1;\n            }\n\n            // 积累写入字节\n            count += nwritten;\n        }\n    }\n\n    return count;\n}\n\n/* ----------------------------------------------------------------------------\n * AOF file implementation\n * ------------------------------------------------------------------------- */\n\n/* Starts a background task that performs fsync() against the specified\n * file descriptor (the one of the AOF file) in another thread. \n *\n * 在另一个线程中，对给定的描述符 fd （指向 AOF 文件）执行一个后台 fsync() 操作。\n */\nvoid aof_background_fsync(int fd) {\n    bioCreateBackgroundJob(REDIS_BIO_AOF_FSYNC,(void*)(long)fd,NULL,NULL);\n}\n\n/* Called when the user switches from \"appendonly yes\" to \"appendonly no\"\n * at runtime using the CONFIG command. \n *\n * 在用户通过 CONFIG 命令在运行时关闭 AOF 持久化时调用\n */\nvoid stopAppendOnly(void) {\n\n    // AOF 必须正在启用，才能调用这个函数\n    redisAssert(server.aof_state != REDIS_AOF_OFF);\n\n    // 将 AOF 缓存的内容写入并冲洗到 AOF 文件中\n    // 参数 1 表示强制模式\n    flushAppendOnlyFile(1);\n\n    // 冲洗 AOF 文件\n    aof_fsync(server.aof_fd);\n\n    // 关闭 AOF 文件\n    close(server.aof_fd);\n\n    // 清空 AOF 状态\n    server.aof_fd = -1;\n    server.aof_selected_db = -1;\n    server.aof_state = REDIS_AOF_OFF;\n\n    /* rewrite operation in progress? kill it, wait child exit \n    *\n    * 如果 BGREWRITEAOF 正在执行，那么杀死它\n    * 并等待子进程退出\n    */\n    if (server.aof_child_pid != -1) {\n        int statloc;\n\n        redisLog(REDIS_NOTICE,\"Killing running AOF rewrite child: %ld\",\n            (long) server.aof_child_pid);\n\n        // 杀死子进程\n        if (kill(server.aof_child_pid,SIGUSR1) != -1)\n            wait3(&statloc,0,NULL);\n\n        /* reset the buffer accumulating changes while the child saves \n         * 清理未完成的 AOF 重写留下来的缓存和临时文件\n         */\n        aofRewriteBufferReset();\n        aofRemoveTempFile(server.aof_child_pid);\n        server.aof_child_pid = -1;\n        server.aof_rewrite_time_start = -1;\n    }\n}\n\n/* Called when the user switches from \"appendonly no\" to \"appendonly yes\"\n * at runtime using the CONFIG command. \n *\n * 当用户在运行时使用 CONFIG 命令，\n * 从 appendonly no 切换到 appendonly yes 时执行\n */\nint startAppendOnly(void) {\n\n    // 将开始时间设为 AOF 最后一次 fsync 时间 \n    server.aof_last_fsync = server.unixtime;\n\n    // 打开 AOF 文件\n    server.aof_fd = open(server.aof_filename,O_WRONLY|O_APPEND|O_CREAT,0644);\n\n    redisAssert(server.aof_state == REDIS_AOF_OFF);\n\n    // 文件打开失败\n    if (server.aof_fd == -1) {\n        redisLog(REDIS_WARNING,\"Redis needs to enable the AOF but can't open the append only file: %s\",strerror(errno));\n        return REDIS_ERR;\n    }\n\n    if (rewriteAppendOnlyFileBackground() == REDIS_ERR) {\n        // AOF 后台重写失败，关闭 AOF 文件\n        close(server.aof_fd);\n        redisLog(REDIS_WARNING,\"Redis needs to enable the AOF but can't trigger a background AOF rewrite operation. Check the above logs for more info about the error.\");\n        return REDIS_ERR;\n    }\n\n    /* We correctly switched on AOF, now wait for the rerwite to be complete\n     * in order to append data on disk. \n     *\n     * 等待重写执行完毕\n     */\n    server.aof_state = REDIS_AOF_WAIT_REWRITE;\n\n    return REDIS_OK;\n}\n\n/* Write the append only file buffer on disk.\n *\n * 将 AOF 缓存写入到文件中。\n *\n * Since we are required to write the AOF before replying to the client,\n * and the only way the client socket can get a write is entering when the\n * the event loop, we accumulate all the AOF writes in a memory\n * buffer and write it on disk using this function just before entering\n * the event loop again.\n *\n * 因为程序需要在回复客户端之前对 AOF 执行写操作。\n * 而客户端能执行写操作的唯一机会就是在事件 loop 中，\n * 因此，程序将所有 AOF 写累积到缓存中，\n * 并在重新进入事件 loop 之前，将缓存写入到文件中。\n *\n * About the 'force' argument:\n *\n * 关于 force 参数：\n *\n * When the fsync policy is set to 'everysec' we may delay the flush if there\n * is still an fsync() going on in the background thread, since for instance\n * on Linux write(2) will be blocked by the background fsync anyway.\n *\n * 当 fsync 策略为每秒钟保存一次时，如果后台线程仍然有 fsync 在执行，\n * 那么我们可能会延迟执行冲洗（flush）操作，\n * 因为 Linux 上的 write(2) 会被后台的 fsync 阻塞。\n *\n * When this happens we remember that there is some aof buffer to be\n * flushed ASAP, and will try to do that in the serverCron() function.\n *\n * 当这种情况发生时，说明需要尽快冲洗 aof 缓存，\n * 程序会尝试在 serverCron() 函数中对缓存进行冲洗。\n *\n * However if force is set to 1 we'll write regardless of the background\n * fsync. \n *\n * 不过，如果 force 为 1 的话，那么不管后台是否正在 fsync ，\n * 程序都直接进行写入。\n */\n#define AOF_WRITE_LOG_ERROR_RATE 30 /* Seconds between errors logging. */\nvoid flushAppendOnlyFile(int force) {\n    ssize_t nwritten;\n    int sync_in_progress = 0;\n\n    // 缓冲区中没有任何内容，直接返回\n    if (sdslen(server.aof_buf) == 0) return;\n\n    // 策略为每秒 FSYNC \n    if (server.aof_fsync == AOF_FSYNC_EVERYSEC)\n        // 是否有 SYNC 正在后台进行？\n        sync_in_progress = bioPendingJobsOfType(REDIS_BIO_AOF_FSYNC) != 0;\n\n    // 每秒 fsync ，并且强制写入为假\n    if (server.aof_fsync == AOF_FSYNC_EVERYSEC && !force) {\n\n        /* With this append fsync policy we do background fsyncing.\n         *\n         * 当 fsync 策略为每秒钟一次时， fsync 在后台执行。\n         *\n         * If the fsync is still in progress we can try to delay\n         * the write for a couple of seconds. \n         *\n         * 如果后台仍在执行 FSYNC ，那么我们可以延迟写操作一两秒\n         * （如果强制执行 write 的话，服务器主线程将阻塞在 write 上面）\n         */\n        if (sync_in_progress) {\n\n            // 有 fsync 正在后台进行 。。。\n\n            if (server.aof_flush_postponed_start == 0) {\n                /* No previous write postponinig, remember that we are\n                 * postponing the flush and return. \n                 *\n                 * 前面没有推迟过 write 操作，这里将推迟写操作的时间记录下来\n                 * 然后就返回，不执行 write 或者 fsync\n                 */\n                server.aof_flush_postponed_start = server.unixtime;\n                return;\n\n            } else if (server.unixtime - server.aof_flush_postponed_start < 2) {\n                /* We were already waiting for fsync to finish, but for less\n                 * than two seconds this is still ok. Postpone again. \n                 *\n                 * 如果之前已经因为 fsync 而推迟了 write 操作\n                 * 但是推迟的时间不超过 2 秒，那么直接返回\n                 * 不执行 write 或者 fsync\n                 */\n                return;\n\n            }\n\n            /* Otherwise fall trough, and go write since we can't wait\n             * over two seconds. \n             *\n             * 如果后台还有 fsync 在执行，并且 write 已经推迟 >= 2 秒\n             * 那么执行写操作（write 将被阻塞）\n             */\n            server.aof_delayed_fsync++;\n            redisLog(REDIS_NOTICE,\"Asynchronous AOF fsync is taking too long (disk is busy?). Writing the AOF buffer without waiting for fsync to complete, this may slow down Redis.\");\n        }\n    }\n\n    /* If you are following this code path, then we are going to write so\n     * set reset the postponed flush sentinel to zero. \n     *\n     * 执行到这里，程序会对 AOF 文件进行写入。\n     *\n     * 清零延迟 write 的时间记录\n     */\n    server.aof_flush_postponed_start = 0;\n\n    /* We want to perform a single write. This should be guaranteed atomic\n     * at least if the filesystem we are writing is a real physical one.\n     *\n     * 执行单个 write 操作，如果写入设备是物理的话，那么这个操作应该是原子的\n     *\n     * While this will save us against the server being killed I don't think\n     * there is much to do about the whole server stopping for power problems\n     * or alike \n     *\n     * 当然，如果出现像电源中断这样的不可抗现象，那么 AOF 文件也是可能会出现问题的\n     * 这时就要用 redis-check-aof 程序来进行修复。\n     */\n    nwritten = write(server.aof_fd,server.aof_buf,sdslen(server.aof_buf));\n    if (nwritten != (signed)sdslen(server.aof_buf)) {\n\n        static time_t last_write_error_log = 0;\n        int can_log = 0;\n\n        /* Limit logging rate to 1 line per AOF_WRITE_LOG_ERROR_RATE seconds. */\n        // 将日志的记录频率限制在每行 AOF_WRITE_LOG_ERROR_RATE 秒\n        if ((server.unixtime - last_write_error_log) > AOF_WRITE_LOG_ERROR_RATE) {\n            can_log = 1;\n            last_write_error_log = server.unixtime;\n        }\n\n        /* Lof the AOF write error and record the error code. */\n        // 如果写入出错，那么尝试将该情况写入到日志里面\n        if (nwritten == -1) {\n            if (can_log) {\n                redisLog(REDIS_WARNING,\"Error writing to the AOF file: %s\",\n                    strerror(errno));\n                server.aof_last_write_errno = errno;\n            }\n        } else {\n            if (can_log) {\n                redisLog(REDIS_WARNING,\"Short write while writing to \"\n                                       \"the AOF file: (nwritten=%lld, \"\n                                       \"expected=%lld)\",\n                                       (long long)nwritten,\n                                       (long long)sdslen(server.aof_buf));\n            }\n\n            // 尝试移除新追加的不完整内容\n            if (ftruncate(server.aof_fd, server.aof_current_size) == -1) {\n                if (can_log) {\n                    redisLog(REDIS_WARNING, \"Could not remove short write \"\n                             \"from the append-only file.  Redis may refuse \"\n                             \"to load the AOF the next time it starts.  \"\n                             \"ftruncate: %s\", strerror(errno));\n                }\n            } else {\n                /* If the ftrunacate() succeeded we can set nwritten to\n                 * -1 since there is no longer partial data into the AOF. */\n                nwritten = -1;\n            }\n            server.aof_last_write_errno = ENOSPC;\n        }\n\n        /* Handle the AOF write error. */\n        // 处理写入 AOF 文件时出现的错误\n        if (server.aof_fsync == AOF_FSYNC_ALWAYS) {\n            /* We can't recover when the fsync policy is ALWAYS since the\n             * reply for the client is already in the output buffers, and we\n             * have the contract with the user that on acknowledged write data\n             * is synched on disk. */\n            redisLog(REDIS_WARNING,\"Can't recover from AOF write error when the AOF fsync policy is 'always'. Exiting...\");\n            exit(1);\n        } else {\n            /* Recover from failed write leaving data into the buffer. However\n             * set an error to stop accepting writes as long as the error\n             * condition is not cleared. */\n            server.aof_last_write_status = REDIS_ERR;\n\n            /* Trim the sds buffer if there was a partial write, and there\n             * was no way to undo it with ftruncate(2). */\n            if (nwritten > 0) {\n                server.aof_current_size += nwritten;\n                sdsrange(server.aof_buf,nwritten,-1);\n            }\n            return; /* We'll try again on the next call... */\n        }\n    } else {\n        /* Successful write(2). If AOF was in error state, restore the\n         * OK state and log the event. */\n        // 写入成功，更新最后写入状态\n        if (server.aof_last_write_status == REDIS_ERR) {\n            redisLog(REDIS_WARNING,\n                \"AOF write error looks solved, Redis can write again.\");\n            server.aof_last_write_status = REDIS_OK;\n        }\n    }\n\n    // 更新写入后的 AOF 文件大小\n    server.aof_current_size += nwritten;\n\n    /* Re-use AOF buffer when it is small enough. The maximum comes from the\n     * arena size of 4k minus some overhead (but is otherwise arbitrary). \n     *\n     * 如果 AOF 缓存的大小足够小的话，那么重用这个缓存，\n     * 否则的话，释放 AOF 缓存。\n     */\n    if ((sdslen(server.aof_buf)+sdsavail(server.aof_buf)) < 4000) {\n        // 清空缓存中的内容，等待重用\n        sdsclear(server.aof_buf);\n    } else {\n        // 释放缓存\n        sdsfree(server.aof_buf);\n        server.aof_buf = sdsempty();\n    }\n\n    /* Don't fsync if no-appendfsync-on-rewrite is set to yes and there are\n     * children doing I/O in the background. \n     *\n     * 如果 no-appendfsync-on-rewrite 选项为开启状态，\n     * 并且有 BGSAVE 或者 BGREWRITEAOF 正在进行的话，\n     * 那么不执行 fsync \n     */\n    if (server.aof_no_fsync_on_rewrite &&\n        (server.aof_child_pid != -1 || server.rdb_child_pid != -1))\n            return;\n\n    /* Perform the fsync if needed. */\n\n    // 总是执行 fsnyc\n    if (server.aof_fsync == AOF_FSYNC_ALWAYS) {\n        /* aof_fsync is defined as fdatasync() for Linux in order to avoid\n         * flushing metadata. */\n        aof_fsync(server.aof_fd); /* Let's try to get this data on the disk */\n\n        // 更新最后一次执行 fsnyc 的时间\n        server.aof_last_fsync = server.unixtime;\n\n    // 策略为每秒 fsnyc ，并且距离上次 fsync 已经超过 1 秒\n    } else if ((server.aof_fsync == AOF_FSYNC_EVERYSEC &&\n                server.unixtime > server.aof_last_fsync)) {\n        // 放到后台执行\n        if (!sync_in_progress) aof_background_fsync(server.aof_fd);\n        // 更新最后一次执行 fsync 的时间\n        server.aof_last_fsync = server.unixtime;\n    }\n\n    // 其实上面无论执行 if 部分还是 else 部分都要更新 fsync 的时间\n    // 可以将代码挪到下面来\n    // server.aof_last_fsync = server.unixtime;\n}\n\n/*\n * 根据传入的命令和命令参数，将它们还原成协议格式。\n */\nsds catAppendOnlyGenericCommand(sds dst, int argc, robj **argv) {\n    char buf[32];\n    int len, j;\n    robj *o;\n\n    // 重建命令的个数，格式为 *<count>\\r\\n\n    // 例如 *3\\r\\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    // 重建命令和命令参数，格式为 $<length>\\r\\n<content>\\r\\n\n    // 例如 $3\\r\\nSET\\r\\n$3\\r\\nKEY\\r\\n$5\\r\\nVALUE\\r\\n\n    for (j = 0; j < argc; j++) {\n        o = getDecodedObject(argv[j]);\n\n        // 组合 $<length>\\r\\n\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\n        // 组合 <content>\\r\\n\n        dst = sdscatlen(dst,o->ptr,sdslen(o->ptr));\n        dst = sdscatlen(dst,\"\\r\\n\",2);\n\n        decrRefCount(o);\n    }\n\n    // 返回重建后的协议内容\n    return dst;\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 * 创建 PEXPIREAT 命令的 sds 表示，\n * cmd 参数用于指定转换的源指令， seconds 为 TTL （剩余生存时间）。\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.\n *\n * 这个函数用于将 EXPIRE 、 PEXPIRE 和 EXPIREAT 转换为 PEXPIREAT \n * 从而在保证精确度不变的情况下，将过期时间从相对值转换为绝对值（一个 UNIX 时间戳）。\n *\n * （过期时间必须是绝对值，这样不管 AOF 文件何时被载入，该过期的 key 都会正确地过期。）\n */\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 strtol \n     *\n     * 取出过期值\n     */\n    seconds = getDecodedObject(seconds);\n    when = strtoll(seconds->ptr,NULL,10);\n\n    /* Convert argument into milliseconds for EXPIRE, SETEX, EXPIREAT \n     *\n     * 如果过期值的格式为秒，那么将它转换为毫秒\n     */\n    if (cmd->proc == expireCommand || cmd->proc == setexCommand ||\n        cmd->proc == expireatCommand)\n    {\n        when *= 1000;\n    }\n\n    /* Convert into absolute time for EXPIRE, PEXPIRE, SETEX, PSETEX \n     *\n     * 如果过期值的格式为相对值，那么将它转换为绝对值\n     */\n    if (cmd->proc == expireCommand || cmd->proc == pexpireCommand ||\n        cmd->proc == setexCommand || cmd->proc == psetexCommand)\n    {\n        when += mstime();\n    }\n\n    decrRefCount(seconds);\n\n    // 构建 PEXPIREAT 命令\n    argv[0] = createStringObject(\"PEXPIREAT\",9);\n    argv[1] = key;\n    argv[2] = createStringObjectFromLongLong(when);\n\n    // 追加到 AOF 缓存中\n    buf = catAppendOnlyGenericCommand(buf, 3, argv);\n\n    decrRefCount(argv[0]);\n    decrRefCount(argv[2]);\n\n    return buf;\n}\n\n/*\n * 将命令追加到 AOF 文件中，\n * 如果 AOF 重写正在进行，那么也将命令追加到 AOF 重写缓存中。\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 appendend. To issue a SELECT command is needed. \n     *\n     * 使用 SELECT 命令，显式设置数据库，确保之后的命令被设置到正确的数据库\n     */\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\n        server.aof_selected_db = dictid;\n    }\n\n    // EXPIRE 、 PEXPIRE 和 EXPIREAT 命令\n    if (cmd->proc == expireCommand || cmd->proc == pexpireCommand ||\n        cmd->proc == expireatCommand) {\n        /* Translate EXPIRE/PEXPIRE/EXPIREAT into PEXPIREAT \n         *\n         * 将 EXPIRE 、 PEXPIRE 和 EXPIREAT 都翻译成 PEXPIREAT\n         */\n        buf = catAppendOnlyExpireAtCommand(buf,cmd,argv[1],argv[2]);\n\n    // SETEX 和 PSETEX 命令\n    } else if (cmd->proc == setexCommand || cmd->proc == psetexCommand) {\n        /* Translate SETEX/PSETEX to SET and PEXPIREAT \n         *\n         * 将两个命令都翻译成 SET 和 PEXPIREAT\n         */\n\n        // SET\n        tmpargv[0] = createStringObject(\"SET\",3);\n        tmpargv[1] = argv[1];\n        tmpargv[2] = argv[3];\n        buf = catAppendOnlyGenericCommand(buf,3,tmpargv);\n\n        // PEXPIREAT\n        decrRefCount(tmpargv[0]);\n        buf = catAppendOnlyExpireAtCommand(buf,cmd,argv[1],argv[2]);\n\n    // 其他命令\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     *\n     * 将命令追加到 AOF 缓存中，\n     * 在重新进入事件循环之前，这些命令会被冲洗到磁盘上，\n     * 并向客户端返回一个回复。\n     */\n    if (server.aof_state == REDIS_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     *\n     * 如果 BGREWRITEAOF 正在进行，\n     * 那么我们还需要将命令追加到重写缓存中，\n     * 从而记录当前正在重写的 AOF 文件和数据库当前状态的差异。\n     */\n    if (server.aof_child_pid != -1)\n        aofRewriteBufferAppend((unsigned char*)buf,sdslen(buf));\n\n    // 释放\n    sdsfree(buf);\n}\n\n/* ----------------------------------------------------------------------------\n * AOF loading\n * ------------------------------------------------------------------------- */\n\n/* In Redis commands are always executed in the context of a client, so in\n * order to load the append only file we need to create a fake client. \n *\n * Redis 命令必须由客户端执行，\n * 所以 AOF 装载程序需要创建一个无网络连接的客户端来执行 AOF 文件中的命令。\n */\nstruct redisClient *createFakeClient(void) {\n    struct redisClient *c = zmalloc(sizeof(*c));\n\n    selectDb(c,0);\n\n    c->fd = -1;\n    c->name = NULL;\n    c->querybuf = sdsempty();\n    c->querybuf_peak = 0;\n    c->argc = 0;\n    c->argv = NULL;\n    c->bufpos = 0;\n    c->flags = 0;\n    c->btype = REDIS_BLOCKED_NONE;\n    /* We set the fake client as a slave waiting for the synchronization\n     * so that Redis will not try to send replies to this client. \n     *\n     * 将客户端设置为正在等待同步的附属节点，这样客户端就不会发送回复了。\n     */\n    c->replstate = REDIS_REPL_WAIT_BGSAVE_START;\n    c->reply = listCreate();\n    c->reply_bytes = 0;\n    c->obuf_soft_limit_reached_time = 0;\n    c->watched_keys = listCreate();\n    c->peerid = NULL;\n    listSetFreeMethod(c->reply,decrRefCountVoid);\n    listSetDupMethod(c->reply,dupClientReplyValue);\n    initClientMultiState(c);\n\n    return c;\n}\n\n/*\n * 释放伪客户端\n */\nvoid freeFakeClient(struct redisClient *c) {\n\n    // 释放查询缓存\n    sdsfree(c->querybuf);\n\n    // 释放回复缓存\n    listRelease(c->reply);\n\n    // 释放监视的键\n    listRelease(c->watched_keys);\n\n    // 释放事务状态\n    freeClientMultiState(c);\n\n    zfree(c);\n}\n\n/* Replay the append log file. On error REDIS_OK is returned. On non fatal\n * error (the append only file is zero-length) REDIS_ERR is returned. On\n * fatal error an error message is logged and the program exists.\n *\n * 执行 AOF 文件中的命令。\n *\n * 出错时返回 REDIS_OK 。\n *\n * 出现非执行错误（比如文件长度为 0 ）时返回 REDIS_ERR 。\n *\n * 出现致命错误时打印信息到日志，并且程序退出。\n */\nint loadAppendOnlyFile(char *filename) {\n\n    // 为客户端\n    struct redisClient *fakeClient;\n\n    // 打开 AOF 文件\n    FILE *fp = fopen(filename,\"r\");\n\n    struct redis_stat sb;\n    int old_aof_state = server.aof_state;\n    long loops = 0;\n\n    // 检查文件的正确性\n    if (fp && redis_fstat(fileno(fp),&sb) != -1 && sb.st_size == 0) {\n        server.aof_current_size = 0;\n        fclose(fp);\n        return REDIS_ERR;\n    }\n\n    // 检查文件是否正常打开\n    if (fp == NULL) {\n        redisLog(REDIS_WARNING,\"Fatal error: can't open the append log file for reading: %s\",strerror(errno));\n        exit(1);\n    }\n\n    /* Temporarily disable AOF, to prevent EXEC from feeding a MULTI\n     * to the same file we're about to read. \n     *\n     * 暂时性地关闭 AOF ，防止在执行 MULTI 时，\n     * EXEC 命令被传播到正在打开的 AOF 文件中。\n     */\n    server.aof_state = REDIS_AOF_OFF;\n\n    fakeClient = createFakeClient();\n\n    // 设置服务器的状态为：正在载入\n    // startLoading 定义于 rdb.c\n    startLoading(fp);\n\n    while(1) {\n        int argc, j;\n        unsigned long len;\n        robj **argv;\n        char buf[128];\n        sds argsds;\n        struct redisCommand *cmd;\n\n        /* Serve the clients from time to time \n         *\n         * 间隔性地处理客户端发送来的请求\n         * 因为服务器正处于载入状态，所以能正常执行的只有 PUBSUB 等模块\n         */\n        if (!(loops++ % 1000)) {\n            loadingProgress(ftello(fp));\n            processEventsWhileBlocked();\n        }\n\n        // 读入文件内容到缓存\n        if (fgets(buf,sizeof(buf),fp) == NULL) {\n            if (feof(fp))\n                // 文件已经读完，跳出\n                break;\n            else\n                goto readerr;\n        }\n\n        // 确认协议格式，比如 *3\\r\\n\n        if (buf[0] != '*') goto fmterr;\n        \n        // 取出命令参数，比如 *3\\r\\n 中的 3\n        argc = atoi(buf+1);\n\n        // 至少要有一个参数（被调用的命令）\n        if (argc < 1) goto fmterr;\n\n        // 从文本中创建字符串对象：包括命令，以及命令参数\n        // 例如 $3\\r\\nSET\\r\\n$3\\r\\nKEY\\r\\n$5\\r\\nVALUE\\r\\n\n        // 将创建三个包含以下内容的字符串对象：\n        // SET 、 KEY 、 VALUE\n        argv = zmalloc(sizeof(robj*)*argc);\n        for (j = 0; j < argc; j++) {\n            if (fgets(buf,sizeof(buf),fp) == NULL) goto readerr;\n\n            if (buf[0] != '$') goto fmterr;\n\n            // 读取参数值的长度\n            len = strtol(buf+1,NULL,10);\n            // 读取参数值\n            argsds = sdsnewlen(NULL,len);\n            if (len && fread(argsds,len,1,fp) == 0) goto fmterr;\n            // 为参数创建对象\n            argv[j] = createObject(REDIS_STRING,argsds);\n\n            if (fread(buf,2,1,fp) == 0) goto fmterr; /* discard CRLF */\n        }\n\n        /* Command lookup \n         *\n         * 查找命令\n         */\n        cmd = lookupCommand(argv[0]->ptr);\n        if (!cmd) {\n            redisLog(REDIS_WARNING,\"Unknown command '%s' reading the append only file\", (char*)argv[0]->ptr);\n            exit(1);\n        }\n\n        /* Run the command in the context of a fake client \n         *\n         * 调用伪客户端，执行命令\n         */\n        fakeClient->argc = argc;\n        fakeClient->argv = argv;\n        cmd->proc(fakeClient);\n\n        /* The fake client should not have a reply */\n        redisAssert(fakeClient->bufpos == 0 && listLength(fakeClient->reply) == 0);\n        /* The fake client should never get blocked */\n        redisAssert((fakeClient->flags & REDIS_BLOCKED) == 0);\n\n        /* Clean up. Command code may have changed argv/argc so we use the\n         * argv/argc of the client instead of the local variables. \n         *\n         * 清理命令和命令参数对象\n         */\n        for (j = 0; j < fakeClient->argc; j++)\n            decrRefCount(fakeClient->argv[j]);\n        zfree(fakeClient->argv);\n    }\n\n    /* This point can only be reached when EOF is reached without errors.\n     * If the client is in the middle of a MULTI/EXEC, log error and quit. \n     *\n     * 如果能执行到这里，说明 AOF 文件的全部内容都可以正确地读取，\n     * 但是，还要检查 AOF 是否包含未正确结束的事务\n     */\n    if (fakeClient->flags & REDIS_MULTI) goto readerr;\n\n    // 关闭 AOF 文件\n    fclose(fp);\n    // 释放伪客户端\n    freeFakeClient(fakeClient);\n    // 复原 AOF 状态\n    server.aof_state = old_aof_state;\n    // 停止载入\n    stopLoading();\n    // 更新服务器状态中， AOF 文件的当前大小\n    aofUpdateCurrentSize();\n    // 记录前一次重写时的大小\n    server.aof_rewrite_base_size = server.aof_current_size;\n    \n    return REDIS_OK;\n\n// 读入错误\nreaderr:\n    // 非预期的末尾，可能是 AOF 文件在写入的中途遭遇了停机\n    if (feof(fp)) {\n        redisLog(REDIS_WARNING,\"Unexpected end of file reading the append only file\");\n    \n    // 文件内容出错\n    } else {\n        redisLog(REDIS_WARNING,\"Unrecoverable error reading the append only file: %s\", strerror(errno));\n    }\n    exit(1);\n\n// 内容格式错误\nfmterr:\n    redisLog(REDIS_WARNING,\"Bad file format reading the append only file: make a backup of your AOF file, then use ./redis-check-aof --fix <filename>\");\n    exit(1);\n}\n\n/* ----------------------------------------------------------------------------\n * AOF rewrite\n * ------------------------------------------------------------------------- */\n\n/* Delegate writing an object to writing a bulk string or bulk long long.\n * This is not placed in rio.c since that adds the redis.h dependency. \n *\n * 将 obj 所指向的整数对象或字符串对象的值写入到 r 当中。\n */\nint rioWriteBulkObject(rio *r, robj *obj) {\n    /* Avoid using getDecodedObject to help copy-on-write (we are often\n     * in a child process when this function is called). */\n    if (obj->encoding == REDIS_ENCODING_INT) {\n        return rioWriteBulkLongLong(r,(long)obj->ptr);\n    } else if (sdsEncodedObject(obj)) {\n        return rioWriteBulkString(r,obj->ptr,sdslen(obj->ptr));\n    } else {\n        redisPanic(\"Unknown string encoding\");\n    }\n}\n\n/* Emit the commands needed to rebuild a list object.\n * The function returns 0 on error, 1 on success. \n *\n * 将重建列表对象所需的命令写入到 r 。\n *\n * 出错返回 0 ，成功返回 1 。\n *\n * 命令的形式如下：  RPUSH item1 item2 ... itemN\n */\nint rewriteListObject(rio *r, robj *key, robj *o) {\n    long long count = 0, items = listTypeLength(o);\n\n    if (o->encoding == REDIS_ENCODING_ZIPLIST) {\n        unsigned char *zl = o->ptr;\n        unsigned char *p = ziplistIndex(zl,0);\n        unsigned char *vstr;\n        unsigned int vlen;\n        long long vlong;\n\n        // 先构建一个 RPUSH key \n        // 然后从 ZIPLIST 中取出最多 REDIS_AOF_REWRITE_ITEMS_PER_CMD 个元素\n        // 之后重复第一步，直到 ZIPLIST 为空\n        while(ziplistGet(p,&vstr,&vlen,&vlong)) {\n            if (count == 0) {\n                int cmd_items = (items > REDIS_AOF_REWRITE_ITEMS_PER_CMD) ?\n                    REDIS_AOF_REWRITE_ITEMS_PER_CMD : items;\n\n                if (rioWriteBulkCount(r,'*',2+cmd_items) == 0) return 0;\n                if (rioWriteBulkString(r,\"RPUSH\",5) == 0) return 0;\n                if (rioWriteBulkObject(r,key) == 0) return 0;\n            }\n            // 取出值\n            if (vstr) {\n                if (rioWriteBulkString(r,(char*)vstr,vlen) == 0) return 0;\n            } else {\n                if (rioWriteBulkLongLong(r,vlong) == 0) return 0;\n            }\n            // 移动指针，并计算被取出元素的数量\n            p = ziplistNext(zl,p);\n            if (++count == REDIS_AOF_REWRITE_ITEMS_PER_CMD) count = 0;\n            items--;\n        }\n    } else if (o->encoding == REDIS_ENCODING_LINKEDLIST) {\n        list *list = o->ptr;\n        listNode *ln;\n        listIter li;\n\n        // 先构建一个 RPUSH key \n        // 然后从双端链表中取出最多 REDIS_AOF_REWRITE_ITEMS_PER_CMD 个元素\n        // 之后重复第一步，直到链表为空\n        listRewind(list,&li);\n        while((ln = listNext(&li))) {\n            robj *eleobj = listNodeValue(ln);\n\n            if (count == 0) {\n                int cmd_items = (items > REDIS_AOF_REWRITE_ITEMS_PER_CMD) ?\n                    REDIS_AOF_REWRITE_ITEMS_PER_CMD : items;\n\n                if (rioWriteBulkCount(r,'*',2+cmd_items) == 0) return 0;\n                if (rioWriteBulkString(r,\"RPUSH\",5) == 0) return 0;\n                if (rioWriteBulkObject(r,key) == 0) return 0;\n            }\n\n            // 取出值\n            if (rioWriteBulkObject(r,eleobj) == 0) return 0;\n\n            // 元素计数\n            if (++count == REDIS_AOF_REWRITE_ITEMS_PER_CMD) count = 0;\n\n            items--;\n        }\n    } else {\n        redisPanic(\"Unknown list encoding\");\n    }\n    return 1;\n}\n\n/* Emit the commands needed to rebuild a set object.\n * The function returns 0 on error, 1 on success. \n *\n * 将重建集合对象所需的命令写入到 r 。\n *\n * 出错返回 0 ，成功返回 1 。\n *\n * 命令的形式如下：  SADD item1 item2 ... itemN\n */\nint rewriteSetObject(rio *r, robj *key, robj *o) {\n    long long count = 0, items = setTypeSize(o);\n\n    if (o->encoding == REDIS_ENCODING_INTSET) {\n        int ii = 0;\n        int64_t llval;\n\n        while(intsetGet(o->ptr,ii++,&llval)) {\n            if (count == 0) {\n                int cmd_items = (items > REDIS_AOF_REWRITE_ITEMS_PER_CMD) ?\n                    REDIS_AOF_REWRITE_ITEMS_PER_CMD : items;\n\n                if (rioWriteBulkCount(r,'*',2+cmd_items) == 0) return 0;\n                if (rioWriteBulkString(r,\"SADD\",4) == 0) return 0;\n                if (rioWriteBulkObject(r,key) == 0) return 0;\n            }\n            if (rioWriteBulkLongLong(r,llval) == 0) return 0;\n            if (++count == REDIS_AOF_REWRITE_ITEMS_PER_CMD) count = 0;\n            items--;\n        }\n    } else if (o->encoding == REDIS_ENCODING_HT) {\n        dictIterator *di = dictGetIterator(o->ptr);\n        dictEntry *de;\n\n        while((de = dictNext(di)) != NULL) {\n            robj *eleobj = dictGetKey(de);\n            if (count == 0) {\n                int cmd_items = (items > REDIS_AOF_REWRITE_ITEMS_PER_CMD) ?\n                    REDIS_AOF_REWRITE_ITEMS_PER_CMD : items;\n\n                if (rioWriteBulkCount(r,'*',2+cmd_items) == 0) return 0;\n                if (rioWriteBulkString(r,\"SADD\",4) == 0) return 0;\n                if (rioWriteBulkObject(r,key) == 0) return 0;\n            }\n            if (rioWriteBulkObject(r,eleobj) == 0) return 0;\n            if (++count == REDIS_AOF_REWRITE_ITEMS_PER_CMD) count = 0;\n            items--;\n        }\n        dictReleaseIterator(di);\n    } else {\n        redisPanic(\"Unknown set encoding\");\n    }\n    return 1;\n}\n\n/* Emit the commands needed to rebuild a sorted set object.\n * The function returns 0 on error, 1 on success. \n *\n * 将重建有序集合对象所需的命令写入到 r 。\n *\n * 出错返回 0 ，成功返回 1 。\n *\n * 命令的形式如下：  ZADD score1 member1 score2 member2 ... scoreN memberN\n */\nint rewriteSortedSetObject(rio *r, robj *key, robj *o) {\n    long long count = 0, items = zsetLength(o);\n\n    if (o->encoding == REDIS_ENCODING_ZIPLIST) {\n        unsigned char *zl = o->ptr;\n        unsigned char *eptr, *sptr;\n        unsigned char *vstr;\n        unsigned int vlen;\n        long long vll;\n        double score;\n\n        eptr = ziplistIndex(zl,0);\n        redisAssert(eptr != NULL);\n        sptr = ziplistNext(zl,eptr);\n        redisAssert(sptr != NULL);\n\n        while (eptr != NULL) {\n            redisAssert(ziplistGet(eptr,&vstr,&vlen,&vll));\n            score = zzlGetScore(sptr);\n\n            if (count == 0) {\n                int cmd_items = (items > REDIS_AOF_REWRITE_ITEMS_PER_CMD) ?\n                    REDIS_AOF_REWRITE_ITEMS_PER_CMD : items;\n\n                if (rioWriteBulkCount(r,'*',2+cmd_items*2) == 0) return 0;\n                if (rioWriteBulkString(r,\"ZADD\",4) == 0) return 0;\n                if (rioWriteBulkObject(r,key) == 0) return 0;\n            }\n            if (rioWriteBulkDouble(r,score) == 0) return 0;\n            if (vstr != NULL) {\n                if (rioWriteBulkString(r,(char*)vstr,vlen) == 0) return 0;\n            } else {\n                if (rioWriteBulkLongLong(r,vll) == 0) return 0;\n            }\n            zzlNext(zl,&eptr,&sptr);\n            if (++count == REDIS_AOF_REWRITE_ITEMS_PER_CMD) count = 0;\n            items--;\n        }\n    } else if (o->encoding == REDIS_ENCODING_SKIPLIST) {\n        zset *zs = o->ptr;\n        dictIterator *di = dictGetIterator(zs->dict);\n        dictEntry *de;\n\n        while((de = dictNext(di)) != NULL) {\n            robj *eleobj = dictGetKey(de);\n            double *score = dictGetVal(de);\n\n            if (count == 0) {\n                int cmd_items = (items > REDIS_AOF_REWRITE_ITEMS_PER_CMD) ?\n                    REDIS_AOF_REWRITE_ITEMS_PER_CMD : items;\n\n                if (rioWriteBulkCount(r,'*',2+cmd_items*2) == 0) return 0;\n                if (rioWriteBulkString(r,\"ZADD\",4) == 0) return 0;\n                if (rioWriteBulkObject(r,key) == 0) return 0;\n            }\n            if (rioWriteBulkDouble(r,*score) == 0) return 0;\n            if (rioWriteBulkObject(r,eleobj) == 0) return 0;\n            if (++count == REDIS_AOF_REWRITE_ITEMS_PER_CMD) count = 0;\n            items--;\n        }\n        dictReleaseIterator(di);\n    } else {\n        redisPanic(\"Unknown sorted zset encoding\");\n    }\n    return 1;\n}\n\n/* Write either the key or the value of the currently selected item of a hash.\n *\n * 选择写入哈希的 key 或者 value 到 r 中。\n *\n * The 'hi' argument passes a valid Redis hash iterator.\n *\n * hi 为 Redis 哈希迭代器\n *\n * The 'what' filed specifies if to write a key or a value and can be\n * either REDIS_HASH_KEY or REDIS_HASH_VALUE.\n *\n * what 决定了要写入的部分，可以是 REDIS_HASH_KEY 或 REDIS_HASH_VALUE\n *\n * The function returns 0 on error, non-zero on success. \n *\n * 出错返回 0 ，成功返回非 0 。\n */\nstatic int rioWriteHashIteratorCursor(rio *r, hashTypeIterator *hi, int what) {\n\n    if (hi->encoding == REDIS_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            return rioWriteBulkString(r, (char*)vstr, vlen);\n        } else {\n            return rioWriteBulkLongLong(r, vll);\n        }\n\n    } else if (hi->encoding == REDIS_ENCODING_HT) {\n        robj *value;\n\n        hashTypeCurrentFromHashTable(hi, what, &value);\n        return rioWriteBulkObject(r, value);\n    }\n\n    redisPanic(\"Unknown hash encoding\");\n    return 0;\n}\n\n/* Emit the commands needed to rebuild a hash object.\n * The function returns 0 on error, 1 on success. \n *\n * 将重建哈希对象所需的命令写入到 r 。\n *\n * 出错返回 0 ，成功返回 1 。\n *\n * 命令的形式如下：HMSET field1 value1 field2 value2 ... fieldN valueN\n */\nint rewriteHashObject(rio *r, robj *key, robj *o) {\n    hashTypeIterator *hi;\n    long long count = 0, items = hashTypeLength(o);\n\n    hi = hashTypeInitIterator(o);\n    while (hashTypeNext(hi) != REDIS_ERR) {\n        if (count == 0) {\n            int cmd_items = (items > REDIS_AOF_REWRITE_ITEMS_PER_CMD) ?\n                REDIS_AOF_REWRITE_ITEMS_PER_CMD : items;\n\n            if (rioWriteBulkCount(r,'*',2+cmd_items*2) == 0) return 0;\n            if (rioWriteBulkString(r,\"HMSET\",5) == 0) return 0;\n            if (rioWriteBulkObject(r,key) == 0) return 0;\n        }\n\n        if (rioWriteHashIteratorCursor(r, hi, REDIS_HASH_KEY) == 0) return 0;\n        if (rioWriteHashIteratorCursor(r, hi, REDIS_HASH_VALUE) == 0) return 0;\n        if (++count == REDIS_AOF_REWRITE_ITEMS_PER_CMD) count = 0;\n        items--;\n    }\n\n    hashTypeReleaseIterator(hi);\n\n    return 1;\n}\n\n/* Write a sequence of commands able to fully rebuild the dataset into\n * \"filename\". Used both by REWRITEAOF and BGREWRITEAOF.\n *\n * 将一集足以还原当前数据集的命令写入到 filename 指定的文件中。\n *\n * 这个函数被 REWRITEAOF 和 BGREWRITEAOF 两个命令调用。\n * （REWRITEAOF 似乎已经是一个废弃的命令）\n *\n * In order to minimize the number of commands needed in the rewritten\n * log Redis uses variadic commands when possible, such as RPUSH, SADD\n * and ZADD. However at max REDIS_AOF_REWRITE_ITEMS_PER_CMD items per time\n * are inserted using a single command. \n *\n * 为了最小化重建数据集所需执行的命令数量，\n * Redis 会尽可能地使用接受可变参数数量的命令，比如 RPUSH 、SADD 和 ZADD 等。\n *\n * 不过单个命令每次处理的元素数量不能超过 REDIS_AOF_REWRITE_ITEMS_PER_CMD 。\n */\nint rewriteAppendOnlyFile(char *filename) {\n    dictIterator *di = NULL;\n    dictEntry *de;\n    rio aof;\n    FILE *fp;\n    char tmpfile[256];\n    int j;\n    long long now = mstime();\n\n    /* Note that we have to use a different temp name here compared to the\n     * one used by rewriteAppendOnlyFileBackground() function. \n     *\n     * 创建临时文件\n     *\n     * 注意这里创建的文件名和 rewriteAppendOnlyFileBackground() 创建的文件名稍有不同\n     */\n    snprintf(tmpfile,256,\"temp-rewriteaof-%d.aof\", (int) getpid());\n    fp = fopen(tmpfile,\"w\");\n    if (!fp) {\n        redisLog(REDIS_WARNING, \"Opening the temp file for AOF rewrite in rewriteAppendOnlyFile(): %s\", strerror(errno));\n        return REDIS_ERR;\n    }\n\n    // 初始化文件 io\n    rioInitWithFile(&aof,fp);\n\n    // 设置每写入 REDIS_AOF_AUTOSYNC_BYTES 字节\n    // 就执行一次 FSYNC \n    // 防止缓存中积累太多命令内容，造成 I/O 阻塞时间过长\n    if (server.aof_rewrite_incremental_fsync)\n        rioSetAutoSync(&aof,REDIS_AOF_AUTOSYNC_BYTES);\n\n    // 遍历所有数据库\n    for (j = 0; j < server.dbnum; j++) {\n\n        char selectcmd[] = \"*2\\r\\n$6\\r\\nSELECT\\r\\n\";\n\n        redisDb *db = server.db+j;\n\n        // 指向键空间\n        dict *d = db->dict;\n        if (dictSize(d) == 0) continue;\n\n        // 创建键空间迭代器\n        di = dictGetSafeIterator(d);\n        if (!di) {\n            fclose(fp);\n            return REDIS_ERR;\n        }\n\n        /* SELECT the new DB \n         *\n         * 首先写入 SELECT 命令，确保之后的数据会被插入到正确的数据库上\n         */\n        if (rioWrite(&aof,selectcmd,sizeof(selectcmd)-1) == 0) goto werr;\n        if (rioWriteBulkLongLong(&aof,j) == 0) goto werr;\n\n        /* Iterate this DB writing every entry \n         *\n         * 遍历数据库所有键，并通过命令将它们的当前状态（值）记录到新 AOF 文件中\n         */\n        while((de = dictNext(di)) != NULL) {\n            sds keystr;\n            robj key, *o;\n            long long expiretime;\n\n            // 取出键\n            keystr = dictGetKey(de);\n\n            // 取出值\n            o = dictGetVal(de);\n            initStaticStringObject(key,keystr);\n\n            // 取出过期时间\n            expiretime = getExpire(db,&key);\n\n            /* If this key is already expired skip it \n             *\n             * 如果键已经过期，那么跳过它，不保存\n             */\n            if (expiretime != -1 && expiretime < now) continue;\n\n            /* Save the key and associated value \n             *\n             * 根据值的类型，选择适当的命令来保存值\n             */\n            if (o->type == REDIS_STRING) {\n                /* Emit a SET command */\n                char cmd[]=\"*3\\r\\n$3\\r\\nSET\\r\\n\";\n                if (rioWrite(&aof,cmd,sizeof(cmd)-1) == 0) goto werr;\n                /* Key and value */\n                if (rioWriteBulkObject(&aof,&key) == 0) goto werr;\n                if (rioWriteBulkObject(&aof,o) == 0) goto werr;\n            } else if (o->type == REDIS_LIST) {\n                if (rewriteListObject(&aof,&key,o) == 0) goto werr;\n            } else if (o->type == REDIS_SET) {\n                if (rewriteSetObject(&aof,&key,o) == 0) goto werr;\n            } else if (o->type == REDIS_ZSET) {\n                if (rewriteSortedSetObject(&aof,&key,o) == 0) goto werr;\n            } else if (o->type == REDIS_HASH) {\n                if (rewriteHashObject(&aof,&key,o) == 0) goto werr;\n            } else {\n                redisPanic(\"Unknown object type\");\n            }\n\n            /* Save the expire time \n             *\n             * 保存键的过期时间\n             */\n            if (expiretime != -1) {\n                char cmd[]=\"*3\\r\\n$9\\r\\nPEXPIREAT\\r\\n\";\n\n                // 写入 PEXPIREAT expiretime 命令\n                if (rioWrite(&aof,cmd,sizeof(cmd)-1) == 0) goto werr;\n                if (rioWriteBulkObject(&aof,&key) == 0) goto werr;\n                if (rioWriteBulkLongLong(&aof,expiretime) == 0) goto werr;\n            }\n        }\n\n        // 释放迭代器\n        dictReleaseIterator(di);\n    }\n\n    /* Make sure data will not remain on the OS's output buffers */\n    // 冲洗并关闭新 AOF 文件\n    if (fflush(fp) == EOF) goto werr;\n    if (aof_fsync(fileno(fp)) == -1) goto werr;\n    if (fclose(fp) == EOF) goto werr;\n\n    /* Use RENAME to make sure the DB file is changed atomically only\n     * if the generate DB file is ok. \n     *\n     * 原子地改名，用重写后的新 AOF 文件覆盖旧 AOF 文件\n     */\n    if (rename(tmpfile,filename) == -1) {\n        redisLog(REDIS_WARNING,\"Error moving temp append only file on the final destination: %s\", strerror(errno));\n        unlink(tmpfile);\n        return REDIS_ERR;\n    }\n\n    redisLog(REDIS_NOTICE,\"SYNC append only file rewrite performed\");\n\n    return REDIS_OK;\n\nwerr:\n    fclose(fp);\n    unlink(tmpfile);\n    redisLog(REDIS_WARNING,\"Write error writing append only file on disk: %s\", strerror(errno));\n    if (di) dictReleaseIterator(di);\n    return REDIS_ERR;\n}\n\n/* This is how rewriting of the append only file in background works:\n * \n * 以下是后台重写 AOF 文件（BGREWRITEAOF）的工作步骤：\n *\n * 1) The user calls BGREWRITEAOF\n *    用户调用 BGREWRITEAOF\n *\n * 2) Redis calls this function, that forks():\n *    Redis 调用这个函数，它执行 fork() ：\n *\n *    2a) the child rewrite the append only file in a temp file.\n *        子进程在临时文件中对 AOF 文件进行重写\n *\n *    2b) the parent accumulates differences in server.aof_rewrite_buf.\n *        父进程将新输入的写命令追加到 server.aof_rewrite_buf 中\n *\n * 3) When the child finished '2a' exists.\n *    当步骤 2a 执行完之后，子进程结束\n *\n * 4) The parent will trap the exit code, if it's OK, will append the\n *    data accumulated into server.aof_rewrite_buf into the temp file, and\n *    finally will rename(2) the temp file in the actual file name.\n *    The the new file is reopened as the new append only file. Profit!\n *\n *    父进程会捕捉子进程的退出信号，\n *    如果子进程的退出状态是 OK 的话，\n *    那么父进程将新输入命令的缓存追加到临时文件，\n *    然后使用 rename(2) 对临时文件改名，用它代替旧的 AOF 文件，\n *    至此，后台 AOF 重写完成。\n */\nint rewriteAppendOnlyFileBackground(void) {\n    pid_t childpid;\n    long long start;\n\n    // 已经有进程在进行 AOF 重写了\n    if (server.aof_child_pid != -1) return REDIS_ERR;\n\n    // 记录 fork 开始前的时间，计算 fork 耗时用\n    start = ustime();\n\n    if ((childpid = fork()) == 0) {\n        char tmpfile[256];\n\n        /* Child */\n\n        // 关闭网络连接 fd\n        closeListeningSockets(0);\n\n        // 为进程设置名字，方便记认\n        redisSetProcTitle(\"redis-aof-rewrite\");\n\n        // 创建临时文件，并进行 AOF 重写\n        snprintf(tmpfile,256,\"temp-rewriteaof-bg-%d.aof\", (int) getpid());\n        if (rewriteAppendOnlyFile(tmpfile) == REDIS_OK) {\n            size_t private_dirty = zmalloc_get_private_dirty();\n\n            if (private_dirty) {\n                redisLog(REDIS_NOTICE,\n                    \"AOF rewrite: %zu MB of memory used by copy-on-write\",\n                    private_dirty/(1024*1024));\n            }\n            // 发送重写成功信号\n            exitFromChild(0);\n        } else {\n            // 发送重写失败信号\n            exitFromChild(1);\n        }\n    } else {\n        /* Parent */\n        // 记录执行 fork 所消耗的时间\n        server.stat_fork_time = ustime()-start;\n\n        if (childpid == -1) {\n            redisLog(REDIS_WARNING,\n                \"Can't rewrite append only file in background: fork: %s\",\n                strerror(errno));\n            return REDIS_ERR;\n        }\n\n        redisLog(REDIS_NOTICE,\n            \"Background append only file rewriting started by pid %d\",childpid);\n\n        // 记录 AOF 重写的信息\n        server.aof_rewrite_scheduled = 0;\n        server.aof_rewrite_time_start = time(NULL);\n        server.aof_child_pid = childpid;\n\n        // 关闭字典自动 rehash\n        updateDictResizePolicy();\n\n        /* We set appendseldb to -1 in order to force the next call to the\n         * feedAppendOnlyFile() to issue a SELECT command, so the differences\n         * accumulated by the parent into server.aof_rewrite_buf will start\n         * with a SELECT statement and it will be safe to merge. \n         *\n         * 将 aof_selected_db 设为 -1 ，\n         * 强制让 feedAppendOnlyFile() 下次执行时引发一个 SELECT 命令，\n         * 从而确保之后新添加的命令会设置到正确的数据库中\n         */\n        server.aof_selected_db = -1;\n        replicationScriptCacheFlush();\n        return REDIS_OK;\n    }\n    return REDIS_OK; /* unreached */\n}\n\nvoid bgrewriteaofCommand(redisClient *c) {\n\n    // 不能重复运行 BGREWRITEAOF\n    if (server.aof_child_pid != -1) {\n        addReplyError(c,\"Background append only file rewriting already in progress\");\n\n    // 如果正在执行 BGSAVE ，那么预定 BGREWRITEAOF\n    // 等 BGSAVE 完成之后， BGREWRITEAOF 就会开始执行\n    } else if (server.rdb_child_pid != -1) {\n        server.aof_rewrite_scheduled = 1;\n        addReplyStatus(c,\"Background append only file rewriting scheduled\");\n\n    // 执行 BGREWRITEAOF\n    } else if (rewriteAppendOnlyFileBackground() == REDIS_OK) {\n        addReplyStatus(c,\"Background append only file rewriting started\");\n\n    } else {\n        addReply(c,shared.err);\n    }\n}\n\n/*\n * 删除 AOF 重写所产生的临时文件\n */\nvoid aofRemoveTempFile(pid_t childpid) {\n    char tmpfile[256];\n\n    snprintf(tmpfile,256,\"temp-rewriteaof-bg-%d.aof\", (int) childpid);\n    unlink(tmpfile);\n}\n\n/* Update the server.aof_current_size filed explicitly using stat(2)\n * to check the size of the file. This is useful after a rewrite or after\n * a restart, normally the size is updated just adding the write length\n * to the current length, that is much faster. \n *\n * 将 aof 文件的当前大小记录到服务器状态中。\n *\n * 通常用于 BGREWRITEAOF 执行之后，或者服务器重启之后。\n */\nvoid aofUpdateCurrentSize(void) {\n    struct redis_stat sb;\n\n    // 读取文件状态\n    if (redis_fstat(server.aof_fd,&sb) == -1) {\n        redisLog(REDIS_WARNING,\"Unable to obtain the AOF file length. stat: %s\",\n            strerror(errno));\n    } else {\n        // 设置到服务器\n        server.aof_current_size = sb.st_size;\n    }\n}\n\n/* A background append only file rewriting (BGREWRITEAOF) terminated its work.\n * Handle this. \n *\n * 当子线程完成 AOF 重写时，父进程调用这个函数。\n */\nvoid backgroundRewriteDoneHandler(int exitcode, int bysignal) {\n    if (!bysignal && exitcode == 0) {\n        int newfd, oldfd;\n        char tmpfile[256];\n        long long now = ustime();\n\n        redisLog(REDIS_NOTICE,\n            \"Background AOF rewrite terminated with success\");\n\n        /* Flush the differences accumulated by the parent to the\n         * rewritten AOF. */\n        // 打开保存新 AOF 文件内容的临时文件\n        snprintf(tmpfile,256,\"temp-rewriteaof-bg-%d.aof\",\n            (int)server.aof_child_pid);\n        newfd = open(tmpfile,O_WRONLY|O_APPEND);\n        if (newfd == -1) {\n            redisLog(REDIS_WARNING,\n                \"Unable to open the temporary AOF produced by the child: %s\", strerror(errno));\n            goto cleanup;\n        }\n\n        // 将累积的重写缓存写入到临时文件中\n        // 这个函数调用的 write 操作会阻塞主进程\n        if (aofRewriteBufferWrite(newfd) == -1) {\n            redisLog(REDIS_WARNING,\n                \"Error trying to flush the parent diff to the rewritten AOF: %s\", strerror(errno));\n            close(newfd);\n            goto cleanup;\n        }\n\n        redisLog(REDIS_NOTICE,\n            \"Parent diff successfully flushed to the rewritten AOF (%lu bytes)\", aofRewriteBufferSize());\n\n        /* The only remaining thing to do is to rename the temporary file to\n         * the configured file and switch the file descriptor used to do AOF\n         * writes. We don't want close(2) or rename(2) calls to block the\n         * server on old file deletion.\n         *\n         * 剩下的工作就是将临时文件改名为 AOF 程序指定的文件名，\n         * 并将新文件的 fd 设为 AOF 程序的写目标。\n         *\n         * 不过这里有一个问题 ——\n         * 我们不想 close(2) 或者 rename(2) 在删除旧文件时阻塞。\n         *\n         * There are two possible scenarios:\n         *\n         * 以下是两个可能的场景：\n         *\n         * 1) AOF is DISABLED and this was a one time rewrite. The temporary\n         * file will be renamed to the configured file. When this file already\n         * exists, it will be unlinked, which may block the server.\n         *\n         * AOF 被关闭，这个是一次单次的写操作。\n         * 临时文件会被改名为 AOF 文件。\n         * 本来已经存在的 AOF 文件会被 unlink ，这可能会阻塞服务器。\n         *\n         * 2) AOF is ENABLED and the rewritten AOF will immediately start\n         * receiving writes. After the temporary file is renamed to the\n         * configured file, the original AOF file descriptor will be closed.\n         * Since this will be the last reference to that file, closing it\n         * causes the underlying file to be unlinked, which may block the\n         * server.\n         *\n         * AOF 被开启，并且重写后的 AOF 文件会立即被用于接收新的写入命令。\n         * 当临时文件被改名为 AOF 文件时，原来的 AOF 文件描述符会被关闭。\n         * 因为 Redis 会是最后一个引用这个文件的进程，\n         * 所以关闭这个文件会引起 unlink ，这可能会阻塞服务器。\n         *\n         * To mitigate the blocking effect of the unlink operation (either\n         * caused by rename(2) in scenario 1, or by close(2) in scenario 2), we\n         * use a background thread to take care of this. First, we\n         * make scenario 1 identical to scenario 2 by opening the target file\n         * when it exists. The unlink operation after the rename(2) will then\n         * be executed upon calling close(2) for its descriptor. Everything to\n         * guarantee atomicity for this switch has already happened by then, so\n         * we don't care what the outcome or duration of that close operation\n         * is, as long as the file descriptor is released again. \n         *\n         * 为了避免出现阻塞现象，程序会将 close(2) 放到后台线程执行，\n         * 这样服务器就可以持续处理请求，不会被中断。\n         */\n        if (server.aof_fd == -1) {\n            /* AOF disabled */\n\n             /* Don't care if this fails: oldfd will be -1 and we handle that.\n              * One notable case of -1 return is if the old file does\n              * not exist. */\n             oldfd = open(server.aof_filename,O_RDONLY|O_NONBLOCK);\n        } else {\n            /* AOF enabled */\n            oldfd = -1; /* We'll set this to the current AOF filedes later. */\n        }\n\n        /* Rename the temporary file. This will not unlink the target file if\n         * it exists, because we reference it with \"oldfd\". \n         *\n         * 对临时文件进行改名，替换现有的 AOF 文件。\n         *\n         * 旧的 AOF 文件不会在这里被 unlink ，因为 oldfd 引用了它。\n         */\n        if (rename(tmpfile,server.aof_filename) == -1) {\n            redisLog(REDIS_WARNING,\n                \"Error trying to rename the temporary AOF file: %s\", strerror(errno));\n            close(newfd);\n            if (oldfd != -1) close(oldfd);\n            goto cleanup;\n        }\n\n        if (server.aof_fd == -1) {\n            /* AOF disabled, we don't need to set the AOF file descriptor\n             * to this new file, so we can close it. \n             *\n             * AOF 被关闭，直接关闭 AOF 文件，\n             * 因为关闭 AOF 本来就会引起阻塞，所以这里就算 close 被阻塞也无所谓\n             */\n            close(newfd);\n        } else {\n            /* AOF enabled, replace the old fd with the new one. \n             *\n             * 用新 AOF 文件的 fd 替换原来 AOF 文件的 fd\n             */\n            oldfd = server.aof_fd;\n            server.aof_fd = newfd;\n\n            // 因为前面进行了 AOF 重写缓存追加，所以这里立即 fsync 一次\n            if (server.aof_fsync == AOF_FSYNC_ALWAYS)\n                aof_fsync(newfd);\n            else if (server.aof_fsync == AOF_FSYNC_EVERYSEC)\n                aof_background_fsync(newfd);\n\n            // 强制引发 SELECT\n            server.aof_selected_db = -1; /* Make sure SELECT is re-issued */\n\n            // 更新 AOF 文件的大小\n            aofUpdateCurrentSize();\n\n            // 记录前一次重写时的大小\n            server.aof_rewrite_base_size = server.aof_current_size;\n\n            /* Clear regular AOF buffer since its contents was just written to\n             * the new AOF from the background rewrite buffer. \n             *\n             * 清空 AOF 缓存，因为它的内容已经被写入过了，没用了\n             */\n            sdsfree(server.aof_buf);\n            server.aof_buf = sdsempty();\n        }\n\n        server.aof_lastbgrewrite_status = REDIS_OK;\n\n        redisLog(REDIS_NOTICE, \"Background AOF rewrite finished successfully\");\n\n        /* Change state from WAIT_REWRITE to ON if needed \n         *\n         * 如果是第一次创建 AOF 文件，那么更新 AOF 状态\n         */\n        if (server.aof_state == REDIS_AOF_WAIT_REWRITE)\n            server.aof_state = REDIS_AOF_ON;\n\n        /* Asynchronously close the overwritten AOF. \n         *\n         * 异步关闭旧 AOF 文件\n         */\n        if (oldfd != -1) bioCreateBackgroundJob(REDIS_BIO_CLOSE_FILE,(void*)(long)oldfd,NULL,NULL);\n\n        redisLog(REDIS_VERBOSE,\n            \"Background AOF rewrite signal handler took %lldus\", ustime()-now);\n\n    // BGREWRITEAOF 重写出错\n    } else if (!bysignal && exitcode != 0) {\n        server.aof_lastbgrewrite_status = REDIS_ERR;\n\n        redisLog(REDIS_WARNING,\n            \"Background AOF rewrite terminated with error\");\n\n    // 未知错误\n    } else {\n        server.aof_lastbgrewrite_status = REDIS_ERR;\n\n        redisLog(REDIS_WARNING,\n            \"Background AOF rewrite terminated by signal %d\", bysignal);\n    }\n\ncleanup:\n\n    // 清空 AOF 缓冲区\n    aofRewriteBufferReset();\n\n    // 移除临时文件\n    aofRemoveTempFile(server.aof_child_pid);\n\n    // 重置默认属性\n    server.aof_child_pid = -1;\n    server.aof_rewrite_time_last = time(NULL)-server.aof_rewrite_time_start;\n    server.aof_rewrite_time_start = -1;\n\n    /* Schedule a new rewrite if we are waiting for it to switch the AOF ON. */\n    if (server.aof_state == REDIS_AOF_WAIT_REWRITE)\n        server.aof_rewrite_scheduled = 1;\n}\n"
  },
  {
    "path": "src/asciilogo.h",
    "content": "/*\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\nchar *ascii_logo =\n\"                _._                                                  \\n\"\n\"           _.-``__ ''-._                                             \\n\"\n\"      _.-``    `.  `_.  ''-._           Redis %s (%s/%d) %s bit\\n\"\n\"  .-`` .-```.  ```\\\\/    _.,_ ''-._                                   \\n\"\n\" (    '      ,       .-`  | `,    )     Running in %s mode\\n\"\n\" |`-._`-...-` __...-.``-._|'` _.-'|     Port: %d\\n\"\n\" |    `-._   `._    /     _.-'    |     PID: %ld\\n\"\n\"  `-._    `-._  `-./  _.-'    _.-'                                   \\n\"\n\" |`-._`-._    `-.__.-'    _.-'_.-'|                                  \\n\"\n\" |    `-._`-._        _.-'_.-'    |           http://redis.io        \\n\"\n\"  `-._    `-._`-.__.-'_.-'    _.-'                                   \\n\"\n\" |`-._`-._    `-.__.-'    _.-'_.-'|                                  \\n\"\n\" |    `-._`-._        _.-'_.-'    |                                  \\n\"\n\"  `-._    `-._`-.__.-'_.-'    _.-'                                   \\n\"\n\"      `-._    `-.__.-'    _.-'                                       \\n\"\n\"          `-._        _.-'                                           \\n\"\n\"              `-.__.-'                                               \\n\\n\";\n"
  },
  {
    "path": "src/bio.c",
    "content": "/* Background I/O service for Redis.\n *\n * Redis 的后台 I/O 服务\n *\n * This file implements operations that we need to perform in the background.\n *\n * bio 实现了将工作放在后台执行的功能。\n *\n * Currently there is only a single operation, that is a background close(2)\n * system call. This is needed as when the process is the last owner of a\n * reference to a file closing it means unlinking it, and the deletion of the\n * file is slow, blocking the server.\n *\n * 目前在后台执行的只有 close(2) 操作：\n * 因为当服务器是某个文件的最后一个拥有者时，\n * 关闭一个文件代表 unlinking 它，\n * 并且删除文件非常慢，会阻塞系统，\n * 所以我们将 close(2) 放到后台进行。\n *\n * (译注：现在不止 close(2) ，连 AOF 文件的 fsync 也是放到后台执行的）\n *\n * In the future we'll either continue implementing new things we need or\n * we'll switch to libeio. However there are probably long term uses for this\n * file as we may want to put here Redis specific background tasks (for instance\n * it is not impossible that we'll need a non blocking FLUSHDB/FLUSHALL\n * implementation).\n *\n * 这个后台服务将来可能会增加更多功能，或者切换到 libeio 上面去。\n * 不过我们可能会长期使用这个文件，以便支持一些 Redis 所特有的后台操作。\n * 比如说，将来我们可能需要一个非阻塞的 FLUSHDB 或者 FLUSHALL 也说不定。\n *\n * DESIGN\n * ------\n *\n * The design is trivial, we have a structure representing a job to perform\n * and a different thread and job queue for every job type.\n * Every thread wait for new jobs in its queue, and process every job\n * sequentially.\n *\n * 设计很简单：\n * 用一个结构表示要执行的工作，而每个类型的工作有一个队列和线程，\n * 每个线程都顺序地执行队列中的工作。\n *\n * Jobs of the same type are guaranteed to be processed from the least\n * recently inserted to the most recently inserted (older jobs processed\n * first).\n *\n * 同一类型的工作按 FIFO 的顺序执行。\n *\n * Currently there is no way for the creator of the job to be notified about\n * the completion of the operation, this will only be added when/if needed.\n *\n * 目前还没有办法在任务完成时通知执行者，在有需要的时候，会实现这个功能。\n *\n * ----------------------------------------------------------------------------\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 \"redis.h\"\n#include \"bio.h\"\n\n// 工作线程，斥互和条件变量\nstatic pthread_t bio_threads[REDIS_BIO_NUM_OPS];\nstatic pthread_mutex_t bio_mutex[REDIS_BIO_NUM_OPS];\nstatic pthread_cond_t bio_condvar[REDIS_BIO_NUM_OPS];\n\n// 存放工作的队列\nstatic list *bio_jobs[REDIS_BIO_NUM_OPS];\n\n/* The following array is used to hold the number of pending jobs for every\n * OP type. This allows us to export the bioPendingJobsOfType() API that is\n * useful when the main thread wants to perform some operation that may involve\n * objects shared with the background thread. The main thread will just wait\n * that there are no longer jobs of this type to be executed before performing\n * the sensible operation. This data is also useful for reporting. */\n// 记录每种类型 job 队列里有多少 job 等待执行\nstatic unsigned long long bio_pending[REDIS_BIO_NUM_OPS];\n\n/* This structure represents a background Job. It is only used locally to this\n * file as the API deos not expose the internals at all.\n *\n * 表示后台任务的数据结构\n *\n * 这个结构只由 API 使用，不会被暴露给外部。\n */\nstruct bio_job {\n\n    // 任务创建时的时间\n    time_t time; /* Time at which the job was created. */\n\n    /* Job specific arguments pointers. If we need to pass more than three\n     * arguments we can just pass a pointer to a structure or alike. \n     *\n     * 任务的参数。参数多于三个时，可以传递数组或者结构\n     */\n    void *arg1, *arg2, *arg3;\n};\n\nvoid *bioProcessBackgroundJobs(void *arg);\n\n/* Make sure we have enough stack to perform all the things we do in the\n * main thread. \n *\n * 子线程栈大小\n */\n#define REDIS_THREAD_STACK_SIZE (1024*1024*4)\n\n/* Initialize the background system, spawning the thread. \n *\n * 初始化后台任务系统，生成线程\n */\nvoid bioInit(void) {\n    pthread_attr_t attr;\n    pthread_t thread;\n    size_t stacksize;\n    int j;\n\n    /* Initialization of state vars and objects \n     *\n     * 初始化 job 队列，以及线程状态\n     */\n    for (j = 0; j < REDIS_BIO_NUM_OPS; j++) {\n        pthread_mutex_init(&bio_mutex[j],NULL);\n        pthread_cond_init(&bio_condvar[j],NULL);\n        bio_jobs[j] = listCreate();\n        bio_pending[j] = 0;\n    }\n\n    /* Set the stack size as by default it may be small in some system \n     *\n     * 设置栈大小\n     */\n    pthread_attr_init(&attr);\n    pthread_attr_getstacksize(&attr,&stacksize);\n    if (!stacksize) stacksize = 1; /* The world is full of Solaris Fixes */\n    while (stacksize < REDIS_THREAD_STACK_SIZE) stacksize *= 2;\n    pthread_attr_setstacksize(&attr, stacksize);\n\n    /* Ready to spawn our threads. We use the single argument the thread\n     * function accepts in order to pass the job ID the thread is\n     * responsible of. \n     *\n     * 创建线程\n     */\n    for (j = 0; j < REDIS_BIO_NUM_OPS; j++) {\n        void *arg = (void*)(unsigned long) j;\n        if (pthread_create(&thread,&attr,bioProcessBackgroundJobs,arg) != 0) {\n            redisLog(REDIS_WARNING,\"Fatal: Can't initialize Background Jobs.\");\n            exit(1);\n        }\n        bio_threads[j] = thread;\n    }\n}\n\n/*\n * 创建后台任务\n */\nvoid bioCreateBackgroundJob(int type, void *arg1, void *arg2, void *arg3) {\n    struct bio_job *job = zmalloc(sizeof(*job));\n\n    job->time = time(NULL);\n    job->arg1 = arg1;\n    job->arg2 = arg2;\n    job->arg3 = arg3;\n\n    pthread_mutex_lock(&bio_mutex[type]);\n\n    // 将新工作推入队列\n    listAddNodeTail(bio_jobs[type],job);\n    bio_pending[type]++;\n\n    pthread_cond_signal(&bio_condvar[type]);\n\n    pthread_mutex_unlock(&bio_mutex[type]);\n}\n\n/*\n * 处理后台任务\n */\nvoid *bioProcessBackgroundJobs(void *arg) {\n    struct bio_job *job;\n    unsigned long type = (unsigned long) arg;\n    sigset_t sigset;\n\n    /* Make the thread killable at any time, so that bioKillThreads()\n     * can work reliably. */\n    pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);\n    pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);\n\n    pthread_mutex_lock(&bio_mutex[type]);\n    /* Block SIGALRM so we are sure that only the main thread will\n     * receive the watchdog signal. */\n    sigemptyset(&sigset);\n    sigaddset(&sigset, SIGALRM);\n    if (pthread_sigmask(SIG_BLOCK, &sigset, NULL))\n        redisLog(REDIS_WARNING,\n            \"Warning: can't mask SIGALRM in bio.c thread: %s\", strerror(errno));\n\n    while(1) {\n        listNode *ln;\n\n        /* The loop always starts with the lock hold. */\n        if (listLength(bio_jobs[type]) == 0) {\n            pthread_cond_wait(&bio_condvar[type],&bio_mutex[type]);\n            continue;\n        }\n\n        /* Pop the job from the queue. \n         *\n         * 取出（但不删除）队列中的首个任务\n         */\n        ln = listFirst(bio_jobs[type]);\n        job = ln->value;\n\n        /* It is now possible to unlock the background system as we know have\n         * a stand alone job structure to process.*/\n        pthread_mutex_unlock(&bio_mutex[type]);\n\n        /* Process the job accordingly to its type. */\n        // 执行任务\n        if (type == REDIS_BIO_CLOSE_FILE) {\n            close((long)job->arg1);\n\n        } else if (type == REDIS_BIO_AOF_FSYNC) {\n            aof_fsync((long)job->arg1);\n\n        } else {\n            redisPanic(\"Wrong job type in bioProcessBackgroundJobs().\");\n        }\n\n        zfree(job);\n\n        /* Lock again before reiterating the loop, if there are no longer\n         * jobs to process we'll block again in pthread_cond_wait(). */\n        pthread_mutex_lock(&bio_mutex[type]);\n        // 将执行完成的任务从队列中删除，并减少任务计数器\n        listDelNode(bio_jobs[type],ln);\n        bio_pending[type]--;\n    }\n}\n\n/* Return the number of pending jobs of the specified type. \n *\n * 返回等待中的 type 类型的工作的数量\n */\nunsigned long long bioPendingJobsOfType(int type) {\n    unsigned long long val;\n\n    pthread_mutex_lock(&bio_mutex[type]);\n    val = bio_pending[type];\n    pthread_mutex_unlock(&bio_mutex[type]);\n\n    return val;\n}\n\n/* Kill the running bio threads in an unclean way. This function should be\n * used only when it's critical to stop the threads for some reason.\n *\n * 不进行清理，直接杀死进程，只在出现严重错误时使用\n *\n * Currently Redis does this only on crash (for instance on SIGSEGV) in order\n * to perform a fast memory check without other threads messing with memory. \n */\nvoid bioKillThreads(void) {\n    int err, j;\n\n    for (j = 0; j < REDIS_BIO_NUM_OPS; j++) {\n        if (pthread_cancel(bio_threads[j]) == 0) {\n            if ((err = pthread_join(bio_threads[j],NULL)) != 0) {\n                redisLog(REDIS_WARNING,\n                    \"Bio thread for job type #%d can be joined: %s\",\n                        j, strerror(err));\n            } else {\n                redisLog(REDIS_WARNING,\n                    \"Bio thread for job type #%d terminated\",j);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/bio.h",
    "content": "/*\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/* Exported API */\nvoid bioInit(void);\nvoid bioCreateBackgroundJob(int type, void *arg1, void *arg2, void *arg3);\nunsigned long long bioPendingJobsOfType(int type);\nvoid bioWaitPendingJobsLE(int type, unsigned long long num);\ntime_t bioOlderJobOfType(int type);\nvoid bioKillThreads(void);\n\n/* Background job opcodes */\n#define REDIS_BIO_CLOSE_FILE    0 /* Deferred close(2) syscall. */\n#define REDIS_BIO_AOF_FSYNC     1 /* Deferred AOF fsync. */\n#define REDIS_BIO_NUM_OPS       2\n"
  },
  {
    "path": "src/bitops.c",
    "content": "/* Bit operations.\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#include \"redis.h\"\n\n/* -----------------------------------------------------------------------------\n * Helpers and low level bit functions.\n * -------------------------------------------------------------------------- */\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// 辅佐函数，被 GETBIT 、 SETBIT 所使用\n// 用于检查字符串的大小有否超过 512 MB\nstatic int getBitOffsetFromArgument(redisClient *c, robj *o, size_t *offset) {\n    long long loffset;\n    char *err = \"bit offset is not an integer or out of range\";\n\n    if (getLongLongFromObjectOrReply(c,o,&loffset,err) != REDIS_OK)\n        return REDIS_ERR;\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 REDIS_ERR;\n    }\n\n    *offset = (size_t)loffset;\n    return REDIS_OK;\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. */\n// 计算长度为 count 的二进制数组指针 s 被设置为 1 的位数量\n// 这个函数只能在最大为 512 MB 的字符串上使用\nsize_t redisPopcount(void *s, long count) {\n    size_t bits = 0;\n    unsigned char *p = s;\n    uint32_t *p4;\n    // 通过查表来计算，对于 1 字节所能表示的值来说\n    // 这些值的二进制表示所带有的 1 的数量\n    // 比如整数 3 的二进制表示 0011 ，带有两个 1\n    // 正好是查表 bitsinbyte[3] == 2\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 16 bytes at a time */\n    // 每次统计 16 字节\n    // 关于这里所使用的优化算法，可以参考：\n    // http://yesteapea.wordpress.com/2013/03/03/counting-the-number-of-set-bits-in-an-integer/\n    p4 = (uint32_t*)p;\n    while(count>=16) {\n        uint32_t aux1, aux2, aux3, aux4;\n\n        aux1 = *p4++;\n        aux2 = *p4++;\n        aux3 = *p4++;\n        aux4 = *p4++;\n        count -= 16;\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        bits += ((((aux1 + (aux1 >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24) +\n                ((((aux2 + (aux2 >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24) +\n                ((((aux3 + (aux3 >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24) +\n                ((((aux4 + (aux4 >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24);\n    }\n\n    /* Count the remaining bytes. */\n    // 不足 16 字节的，剩下的每个字节通过查表来完成\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, 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    int 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    redisPanic(\"End of redisBitpos() reached.\");\n    return 0; /* Just to avoid warnings. */\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/* SETBIT key offset bitvalue */\nvoid setbitCommand(redisClient *c) {\n    robj *o;\n    char *err = \"bit is not an integer or out of range\";\n    size_t bitoffset;\n    int byte, bit;\n    int byteval, bitval;\n    long on;\n\n    // 获取 offset 参数\n    if (getBitOffsetFromArgument(c,c->argv[2],&bitoffset) != REDIS_OK)\n        return;\n\n    // 获取 value 参数\n    if (getLongFromObjectOrReply(c,c->argv[3],&on,err) != REDIS_OK)\n        return;\n\n    /* Bits can only be set or cleared... */\n    // value 参数的值只能是 0 或者 1 ，否则返回错误\n    if (on & ~1) {\n        addReplyError(c,err);\n        return;\n    }\n\n    // 查找字符串对象\n    o = lookupKeyWrite(c->db,c->argv[1]);\n    if (o == NULL) {\n\n        // 对象不存在，创建一个空字符串对象\n        o = createObject(REDIS_STRING,sdsempty());\n\n        // 并添加到数据库\n        dbAdd(c->db,c->argv[1],o);\n\n    } else {\n\n        // 对象存在，检查类型是否字符串\n        if (checkType(c,o,REDIS_STRING)) return;\n\n        o = dbUnshareStringValue(c->db,c->argv[1],o);\n    }\n\n    /* Grow sds value to the right length if necessary */\n    // 计算容纳 offset 参数所指定的偏移量所需的字节数\n    // 如果 o 对象的字节不够长的话，就扩展它\n    // 长度的计算公式是 bitoffset >> 3 + 1\n    // 比如 30 >> 3 + 1 = 4 ，也即是为了设置 offset 30 ，\n    // 我们需要创建一个 4 字节（32 位长的 SDS）\n    byte = bitoffset >> 3;\n    o->ptr = sdsgrowzero(o->ptr,byte+1);\n\n    /* Get current values */\n    // 将指针定位到要设置的位所在的字节上\n    byteval = ((uint8_t*)o->ptr)[byte];\n    // 定位到要设置的位上面\n    bit = 7 - (bitoffset & 0x7);\n    // 记录位现在的值\n    bitval = byteval & (1 << bit);\n\n    /* Update byte with new bit value and return original value */\n    // 更新字节中的位，设置它的值为 on 参数的值\n    byteval &= ~(1 << bit);\n    byteval |= ((on & 0x1) << bit);\n    ((uint8_t*)o->ptr)[byte] = byteval;\n\n    // 发送数据库修改通知\n    signalModifiedKey(c->db,c->argv[1]);\n    notifyKeyspaceEvent(REDIS_NOTIFY_STRING,\"setbit\",c->argv[1],c->db->id);\n    server.dirty++;\n\n    // 向客户端返回位原来的值\n    addReply(c, bitval ? shared.cone : shared.czero);\n}\n\n/* GETBIT key offset */\nvoid getbitCommand(redisClient *c) {\n    robj *o;\n    char llbuf[32];\n    size_t bitoffset;\n    size_t byte, bit;\n    size_t bitval = 0;\n\n    // 读取 offset 参数\n    if (getBitOffsetFromArgument(c,c->argv[2],&bitoffset) != REDIS_OK)\n        return;\n\n    // 查找对象，并进行类型检查\n    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||\n        checkType(c,o,REDIS_STRING)) return;\n\n    // 计算出 offset 所指定的位所在的字节\n    byte = bitoffset >> 3;\n    // 计算出位所在的位置\n    bit = 7 - (bitoffset & 0x7);\n\n    // 取出位\n    if (sdsEncodedObject(o)) {\n        // 字符串编码，直接取值\n        if (byte < sdslen(o->ptr))\n            bitval = ((uint8_t*)o->ptr)[byte] & (1 << bit);\n    } else {\n        // 整数编码，先转换成字符串，再取值\n        if (byte < (size_t)ll2string(llbuf,sizeof(llbuf),(long)o->ptr))\n            bitval = llbuf[byte] & (1 << bit);\n    }\n\n    // 返回位\n    addReply(c, bitval ? shared.cone : shared.czero);\n}\n\n/* BITOP op_name target_key src_key1 src_key2 src_key3 ... src_keyN */\nvoid bitopCommand(redisClient *c) {\n    char *opname = c->argv[1]->ptr;\n    robj *o, *targetkey = c->argv[2];\n    long op, j, numkeys;\n    robj **objects;      /* Array of source objects. */\n    unsigned char **src; /* Array of source strings pointers. */\n    long *len, maxlen = 0; /* Array of length of src strings, and max len. */\n    long minlen = 0;    /* Min len among the input keys. */\n    unsigned char *res = NULL; /* Resulting string. */\n\n    /* Parse the operation name. */\n    // 读入 op 参数，确定要执行的操作\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    // NOT 操作只能接受单个 key 输入\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    // 查找输入键，并将它们放入一个数组里面\n    numkeys = c->argc - 3;\n    // 字符串数组，保存 sds 值\n    src = zmalloc(sizeof(unsigned char*) * numkeys);\n    // 长度数组，保存 sds 的长度\n    len = zmalloc(sizeof(long) * numkeys);\n    // 对象数组，保存字符串对象\n    objects = zmalloc(sizeof(robj*) * numkeys);\n    for (j = 0; j < numkeys; j++) {\n\n        // 查找对象\n        o = lookupKeyRead(c->db,c->argv[j+3]);\n\n        /* Handle non-existing keys as empty strings. */\n        // 不存在的键被视为空字符串\n        if (o == NULL) {\n            objects[j] = NULL;\n            src[j] = NULL;\n            len[j] = 0;\n            minlen = 0;\n            continue;\n        }\n\n        /* Return an error if one of the keys is not a string. */\n        // 键不是字符串类型，返回错误，放弃执行操作\n        if (checkType(c,o,REDIS_STRING)) {\n            for (j = j-1; j >= 0; j--) {\n                if (objects[j])\n                    decrRefCount(objects[j]);\n            }\n            zfree(src);\n            zfree(len);\n            zfree(objects);\n            return;\n        }\n\n        // 记录对象\n        objects[j] = getDecodedObject(o);\n        // 记录 sds\n        src[j] = objects[j]->ptr;\n        // 记录 sds 长度\n        len[j] = sdslen(objects[j]->ptr);\n        \n        // 记录目前最长 sds 的长度\n        if (len[j] > maxlen) maxlen = len[j];\n\n        // 记录目前最短 sds 的长度\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    // 如果有至少一个非空字符串，那么执行计算\n    if (maxlen) {\n\n        // 根据最大长度，创建一个 sds ，该 sds 的所有位都被设置为 0\n        res = (unsigned char*) sdsnewlen(NULL,maxlen);\n\n        unsigned char output, byte;\n        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        // 在键的数量比较少时，进行优化\n        j = 0;\n        if (minlen && 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            // 当要处理的位大于等于 32 位时\n            // 每次载入 4*8 = 32 个位，然后对这些位进行计算，利用缓存，进行加速\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        // 以正常方式执行位运算\n        for (; j < maxlen; j++) {\n            output = (len[0] <= j) ? 0 : src[0][j];\n            if (op == BITOP_NOT) output = ~output;\n            // 遍历所有输入键，对所有输入的 scr[i][j] 字节进行运算\n            for (i = 1; i < numkeys; i++) {\n                // 如果数组的长度不足，那么相应的字节被假设为 0\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            // 保存输出\n            res[j] = output;\n        }\n    }\n\n    // 释放资源\n    for (j = 0; j < numkeys; j++) {\n        if (objects[j])\n            decrRefCount(objects[j]);\n    }\n    zfree(src);\n    zfree(len);\n    zfree(objects);\n\n    /* Store the computed value into the target key */\n    if (maxlen) {\n        // 保存结果到指定键\n        o = createObject(REDIS_STRING,res);\n        setKey(c->db,targetkey,o);\n        notifyKeyspaceEvent(REDIS_NOTIFY_STRING,\"set\",targetkey,c->db->id);\n        decrRefCount(o);\n    } else if (dbDelete(c->db,targetkey)) {\n        // 输入为空，没有产生结果，仅仅删除指定键\n        signalModifiedKey(c->db,targetkey);\n        notifyKeyspaceEvent(REDIS_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(redisClient *c) {\n    robj *o;\n    long start, end, strlen;\n    unsigned char *p;\n    char llbuf[32];\n\n    /* Lookup, check for type, and return 0 for non existing keys. */\n    // 查找 key\n    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||\n        checkType(c,o,REDIS_STRING)) return;\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    // 检查对象的编码，并在有需要时转换值的类型\n    if (o->encoding == REDIS_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    // 如果给定了 start 和 end 参数，那么读入它们\n    if (c->argc == 4) {\n        if (getLongFromObjectOrReply(c,c->argv[2],&start,NULL) != REDIS_OK)\n            return;\n        if (getLongFromObjectOrReply(c,c->argv[3],&end,NULL) != REDIS_OK)\n            return;\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        /* 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        // 超出范围的 case ，略过\n        addReply(c,shared.czero);\n    } else {\n        long bytes = end-start+1;\n\n        // 遍历数组，统计值为 1 的位的数量\n        addReplyLongLong(c,redisPopcount(p+start,bytes));\n    }\n}\n\n/* BITPOS key bit [start [end]] */\nvoid bitposCommand(redisClient *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) != REDIS_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    /* 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        addReplyLongLong(c, bit ? -1 : 0);\n        return;\n    }\n    if (checkType(c,o,REDIS_STRING)) return;\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 == REDIS_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) != REDIS_OK)\n            return;\n        if (c->argc == 5) {\n            if (getLongFromObjectOrReply(c,c->argv[4],&end,NULL) != REDIS_OK)\n                return;\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        /* 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            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}\n"
  },
  {
    "path": "src/blocked.c",
    "content": "/* blocked.c - generic support for blocking operations like BLPOP & WAIT.\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 * API:\n *\n * getTimeoutFromObjectOrReply() is just an utility function to parse a\n * timeout argument since blocking operations usually require a timeout.\n *\n * blockClient() set the REDIS_BLOCKED flag in the client, and set the\n * specified block type 'btype' filed to one of REDIS_BLOCKED_* macros.\n *\n * unblockClient() unblocks the client doing the following:\n * 1) It calls the btype-specific function to cleanup the state.\n * 2) It unblocks the client by unsetting the REDIS_BLOCKED flag.\n * 3) It puts the client into a list of just unblocked clients that are\n *    processed ASAP in the beforeSleep() event loop callback, so that\n *    if there is some query buffer to process, we do it. This is also\n *    required because otherwise there is no 'readable' event fired, we\n *    already read the pending commands. We also set the REDIS_UNBLOCKED\n *    flag to remember the client is in the unblocked_clients list.\n *\n * processUnblockedClients() is called inside the beforeSleep() function\n * to process the query buffer from unblocked clients and remove the clients\n * from the blocked_clients queue.\n *\n * replyToBlockedClientTimedOut() is called by the cron function when\n * a client blocked reaches the specified timeout (if the timeout is set\n * to 0, no timeout is processed).\n * It usually just needs to send a reply to the client.\n *\n * When implementing a new type of blocking opeation, the implementation\n * should modify unblockClient() and replyToBlockedClientTimedOut() in order\n * to handle the btype-specific behavior of this two functions.\n */\n\n#include \"redis.h\"\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. */\n// 根据输入参数，取出最大等待时间\nint getTimeoutFromObjectOrReply(redisClient *c, robj *object, mstime_t *timeout, int unit) {\n    long long tval;\n\n    if (getLongLongFromObjectOrReply(c,object,&tval,\n        \"timeout is not an integer or out of range\") != REDIS_OK)\n        return REDIS_ERR;\n\n    if (tval < 0) {\n        addReplyError(c,\"timeout is negative\");\n        return REDIS_ERR;\n    }\n\n    if (tval > 0) {\n        if (unit == UNIT_SECONDS) tval *= 1000;\n        tval += mstime();\n    }\n    *timeout = tval;\n\n    return REDIS_OK;\n}\n\n/* Block a client for the specific operation type. Once the REDIS_BLOCKED\n * flag is set client query buffer is not longer processed, but accumulated,\n * and will be processed when the client is unblocked. */\n// 对给定的客户端进行阻塞\nvoid blockClient(redisClient *c, int btype) {\n    c->flags |= REDIS_BLOCKED;\n    c->btype = btype;\n    server.bpop_blocked_clients++;\n}\n\n/* This function is called in the beforeSleep() function of the event loop\n * in order to process the pending input buffer of clients that were\n * unblocked after a blocking operation. */\n// 取消所有在 unblocked_clients 链表中的客户端的阻塞状态\nvoid processUnblockedClients(void) {\n    listNode *ln;\n    redisClient *c;\n\n    while (listLength(server.unblocked_clients)) {\n        ln = listFirst(server.unblocked_clients);\n        redisAssert(ln != NULL);\n        c = ln->value;\n        listDelNode(server.unblocked_clients,ln);\n        c->flags &= ~REDIS_UNBLOCKED;\n        c->btype = REDIS_BLOCKED_NONE;\n\n        /* Process remaining data in the input buffer. */\n        if (c->querybuf && sdslen(c->querybuf) > 0) {\n            server.current_client = c;\n            processInputBuffer(c);\n            server.current_client = NULL;\n        }\n    }\n}\n\n/* Unblock a client calling the right function depending on the kind\n * of operation the client is blocking for. */\n// 取消给定的客户端的阻塞状态\nvoid unblockClient(redisClient *c) {\n    if (c->btype == REDIS_BLOCKED_LIST) {\n        unblockClientWaitingData(c);\n    } else if (c->btype == REDIS_BLOCKED_WAIT) {\n        unblockClientWaitingReplicas(c);\n    } else {\n        redisPanic(\"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 &= ~REDIS_BLOCKED;\n    c->flags |= REDIS_UNBLOCKED;\n    c->btype = REDIS_BLOCKED_NONE;\n    server.bpop_blocked_clients--;\n    listAddNodeTail(server.unblocked_clients,c);\n}\n\n/* This function gets called when a blocked client timed out in order to\n * send it a reply of some kind. */\n// 等待超时，向被阻塞的客户端返回通知\nvoid replyToBlockedClientTimedOut(redisClient *c) {\n    if (c->btype == REDIS_BLOCKED_LIST) {\n        addReply(c,shared.nullmultibulk);\n    } else if (c->btype == REDIS_BLOCKED_WAIT) {\n        addReplyLongLong(c,replicationCountAcksByOffset(c->bpop.reploffset));\n    } else {\n        redisPanic(\"Unknown btype in replyToBlockedClientTimedOut().\");\n    }\n}\n\n"
  },
  {
    "path": "src/cluster.c",
    "content": "/* Redis Cluster implementation.\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#include \"redis.h\"\n#include \"cluster.h\"\n#include \"endianconv.h\"\n\n#include <sys/types.h>\n#include <sys/socket.h>\n#include <arpa/inet.h>\n#include <fcntl.h>\n#include <unistd.h>\n#include <sys/socket.h>\n#include <sys/stat.h>\n#include <sys/file.h>\n\n/* A global reference to myself is handy to make code more clear.\n * Myself always points to server.cluster->myself, that is, the clusterNode\n * that represents this node. */\n// 为了方便起见，维持一个 myself 全局变量，让它总是指向 cluster->myself 。\nclusterNode *myself = NULL;\n\nclusterNode *createClusterNode(char *nodename, int flags);\nint clusterAddNode(clusterNode *node);\nvoid clusterAcceptHandler(aeEventLoop *el, int fd, void *privdata, int mask);\nvoid clusterReadHandler(aeEventLoop *el, int fd, void *privdata, int mask);\nvoid clusterSendPing(clusterLink *link, int type);\nvoid clusterSendFail(char *nodename);\nvoid clusterSendFailoverAuthIfNeeded(clusterNode *node, clusterMsg *request);\nvoid clusterUpdateState(void);\nint clusterNodeGetSlotBit(clusterNode *n, int slot);\nsds clusterGenNodesDescription(int filter);\nclusterNode *clusterLookupNode(char *name);\nint clusterNodeAddSlave(clusterNode *master, clusterNode *slave);\nint clusterAddSlot(clusterNode *n, int slot);\nint clusterDelSlot(int slot);\nint clusterDelNodeSlots(clusterNode *node);\nint clusterNodeSetSlotBit(clusterNode *n, int slot);\nvoid clusterSetMaster(clusterNode *n);\nvoid clusterHandleSlaveFailover(void);\nvoid clusterHandleSlaveMigration(int max_slaves);\nint bitmapTestBit(unsigned char *bitmap, int pos);\nvoid clusterDoBeforeSleep(int flags);\nvoid clusterSendUpdate(clusterLink *link, clusterNode *node);\nvoid resetManualFailover(void);\nvoid clusterCloseAllSlots(void);\nvoid clusterSetNodeAsMaster(clusterNode *n);\nvoid clusterDelNode(clusterNode *delnode);\n\n/* -----------------------------------------------------------------------------\n * Initialization\n * -------------------------------------------------------------------------- */\n\n/* Return the greatest configEpoch found in the cluster. */\nuint64_t clusterGetMaxEpoch(void) {\n    uint64_t max = 0;\n    dictIterator *di;\n    dictEntry *de;\n\n    // 选出节点中的最大纪元\n    di = dictGetSafeIterator(server.cluster->nodes);\n    while((de = dictNext(di)) != NULL) {\n        clusterNode *node = dictGetVal(de);\n        if (node->configEpoch > max) max = node->configEpoch;\n    }\n    dictReleaseIterator(di);\n    if (max < server.cluster->currentEpoch) max = server.cluster->currentEpoch;\n    return max;\n}\n\n// 载入集群配置\n/* Load the cluster config from 'filename'.\n *\n * If the file does not exist or is zero-length (this may happen because\n * when we lock the nodes.conf file, we create a zero-length one for the\n * sake of locking if it does not already exist), REDIS_ERR is returned.\n * If the configuration was loaded from the file, REDIS_OK is returned. */\nint clusterLoadConfig(char *filename) {\n    FILE *fp = fopen(filename,\"r\");\n    struct stat sb;\n    char *line;\n    int maxline, j;\n\n    if (fp == NULL) {\n        if (errno == ENOENT) {\n            return REDIS_ERR;\n        } else {\n            redisLog(REDIS_WARNING,\n                \"Loading the cluster node config from %s: %s\",\n                filename, strerror(errno));\n            exit(1);\n        }\n    }\n\n    /* Check if the file is zero-length: if so return REDIS_ERR to signal\n     * we have to write the config. */\n    if (fstat(fileno(fp),&sb) != -1 && sb.st_size == 0) {\n        fclose(fp);\n        return REDIS_ERR;\n    }\n\n    /* Parse the file. Note that single liens of the cluster config file can\n     * be really long as they include all the hash slots of the node.\n     * 集群配置文件中的行可能会非常长，\n     * 因为它会在行里面记录所有哈希槽的节点。\n     *\n     * This means in the worst possible case, half of the Redis slots will be\n     * present in a single line, possibly in importing or migrating state, so\n     * together with the node ID of the sender/receiver.\n     *\n     * 在最坏情况下，一个行可能保存了半数的哈希槽数据，\n     * 并且可能带有导入或导出状态，以及发送者和接受者的 ID 。\n     *\n     * To simplify we allocate 1024+REDIS_CLUSTER_SLOTS*128 bytes per line. \n     *\n     * 为了简单起见，我们为每行分配 1024+REDIS_CLUSTER_SLOTS*128 字节的空间\n     */\n    maxline = 1024+REDIS_CLUSTER_SLOTS*128;\n    line = zmalloc(maxline);\n    while(fgets(line,maxline,fp) != NULL) {\n        int argc;\n        sds *argv;\n        clusterNode *n, *master;\n        char *p, *s;\n\n        /* Skip blank lines, they can be created either by users manually\n         * editing nodes.conf or by the config writing process if stopped\n         * before the truncate() call. */\n        if (line[0] == '\\n') continue;\n\n        /* Split the line into arguments for processing. */\n        argv = sdssplitargs(line,&argc);\n        if (argv == NULL) goto fmterr;\n\n        /* Handle the special \"vars\" line. Don't pretend it is the last\n         * line even if it actually is when generated by Redis. */\n        if (strcasecmp(argv[0],\"vars\") == 0) {\n            for (j = 1; j < argc; j += 2) {\n                if (strcasecmp(argv[j],\"currentEpoch\") == 0) {\n                    server.cluster->currentEpoch =\n                            strtoull(argv[j+1],NULL,10);\n                } else if (strcasecmp(argv[j],\"lastVoteEpoch\") == 0) {\n                    server.cluster->lastVoteEpoch =\n                            strtoull(argv[j+1],NULL,10);\n                } else {\n                    redisLog(REDIS_WARNING,\n                        \"Skipping unknown cluster config variable '%s'\",\n                        argv[j]);\n                }\n            }\n            continue;\n        }\n\n        /* Create this node if it does not exist */\n        // 检查节点是否已经存在\n        n = clusterLookupNode(argv[0]);\n        if (!n) {\n            // 未存在则创建这个节点\n            n = createClusterNode(argv[0],0);\n            clusterAddNode(n);\n        }\n        /* Address and port */\n        // 设置节点的 ip 和 port\n        if ((p = strchr(argv[1],':')) == NULL) goto fmterr;\n        *p = '\\0';\n        memcpy(n->ip,argv[1],strlen(argv[1])+1);\n        n->port = atoi(p+1);\n\n        /* Parse flags */\n        // 分析节点的 flag\n        p = s = argv[2];\n        while(p) {\n            p = strchr(s,',');\n            if (p) *p = '\\0';\n            // 这是节点本身\n            if (!strcasecmp(s,\"myself\")) {\n                redisAssert(server.cluster->myself == NULL);\n                myself = server.cluster->myself = n;\n                n->flags |= REDIS_NODE_MYSELF;\n            // 这是一个主节点\n            } else if (!strcasecmp(s,\"master\")) {\n                n->flags |= REDIS_NODE_MASTER;\n            // 这是一个从节点\n            } else if (!strcasecmp(s,\"slave\")) {\n                n->flags |= REDIS_NODE_SLAVE;\n            // 这是一个疑似下线节点\n            } else if (!strcasecmp(s,\"fail?\")) {\n                n->flags |= REDIS_NODE_PFAIL;\n            // 这是一个已下线节点\n            } else if (!strcasecmp(s,\"fail\")) {\n                n->flags |= REDIS_NODE_FAIL;\n                n->fail_time = mstime();\n            // 等待向节点发送 PING\n            } else if (!strcasecmp(s,\"handshake\")) {\n                n->flags |= REDIS_NODE_HANDSHAKE;\n            // 尚未获得这个节点的地址\n            } else if (!strcasecmp(s,\"noaddr\")) {\n                n->flags |= REDIS_NODE_NOADDR;\n            // 无 flag\n            } else if (!strcasecmp(s,\"noflags\")) {\n                /* nothing to do */\n            } else {\n                redisPanic(\"Unknown flag in redis cluster config file\");\n            }\n            if (p) s = p+1;\n        }\n\n        /* Get master if any. Set the master and populate master's\n         * slave list. */\n        // 如果有主节点的话，那么设置主节点\n        if (argv[3][0] != '-') {\n            master = clusterLookupNode(argv[3]);\n            // 如果主节点不存在，那么添加它\n            if (!master) {\n                master = createClusterNode(argv[3],0);\n                clusterAddNode(master);\n            }\n            // 设置主节点\n            n->slaveof = master;\n            // 将节点 n 加入到主节点 master 的从节点名单中\n            clusterNodeAddSlave(master,n);\n        }\n\n        /* Set ping sent / pong received timestamps */\n        // 设置最近一次发送 PING 命令以及接收 PING 命令回复的时间戳\n        if (atoi(argv[4])) n->ping_sent = mstime();\n        if (atoi(argv[5])) n->pong_received = mstime();\n\n        /* Set configEpoch for this node. */\n        // 设置配置纪元\n        n->configEpoch = strtoull(argv[6],NULL,10);\n\n        /* Populate hash slots served by this instance. */\n        // 取出节点服务的槽\n        for (j = 8; j < argc; j++) {\n            int start, stop;\n\n            // 正在导入或导出槽\n            if (argv[j][0] == '[') {\n                /* Here we handle migrating / importing slots */\n                int slot;\n                char direction;\n                clusterNode *cn;\n\n                p = strchr(argv[j],'-');\n                redisAssert(p != NULL);\n                *p = '\\0';\n                // 导入 or 导出？\n                direction = p[1]; /* Either '>' or '<' */\n                // 槽\n                slot = atoi(argv[j]+1);\n                p += 3;\n                // 目标节点\n                cn = clusterLookupNode(p);\n                // 如果目标不存在，那么创建\n                if (!cn) {\n                    cn = createClusterNode(p,0);\n                    clusterAddNode(cn);\n                }\n                // 根据方向，设定本节点要导入或者导出的槽的目标\n                if (direction == '>') {\n                    server.cluster->migrating_slots_to[slot] = cn;\n                } else {\n                    server.cluster->importing_slots_from[slot] = cn;\n                }\n                continue;\n\n            // 没有导入或导出，这是一个区间范围的槽\n            // 比如 0 - 10086\n            } else if ((p = strchr(argv[j],'-')) != NULL) {\n                *p = '\\0';\n                start = atoi(argv[j]);\n                stop = atoi(p+1);\n\n            // 没有导入或导出，这是单一个槽\n            // 比如 10086\n            } else {\n                start = stop = atoi(argv[j]);\n            }\n\n            // 将槽载入节点\n            while(start <= stop) clusterAddSlot(n, start++);\n        }\n\n        sdsfreesplitres(argv,argc);\n    }\n    zfree(line);\n    fclose(fp);\n\n    /* Config sanity check */\n    redisAssert(server.cluster->myself != NULL);\n    redisLog(REDIS_NOTICE,\"Node configuration loaded, I'm %.40s\", myself->name);\n\n    /* Something that should never happen: currentEpoch smaller than\n     * the max epoch found in the nodes configuration. However we handle this\n     * as some form of protection against manual editing of critical files. */\n    if (clusterGetMaxEpoch() > server.cluster->currentEpoch) {\n        server.cluster->currentEpoch = clusterGetMaxEpoch();\n    }\n    return REDIS_OK;\n\nfmterr:\n    redisLog(REDIS_WARNING,\n        \"Unrecoverable error: corrupted cluster config file.\");\n    fclose(fp);\n    exit(1);\n}\n\n/* Cluster node configuration is exactly the same as CLUSTER NODES output.\n *\n * This function writes the node config and returns 0, on error -1\n * is returned.\n *\n * Note: we need to write the file in an atomic way from the point of view\n * of the POSIX filesystem semantics, so that if the server is stopped\n * or crashes during the write, we'll end with either the old file or the\n * new one. Since we have the full payload to write available we can use\n * a single write to write the whole file. If the pre-existing file was\n * bigger we pad our payload with newlines that are anyway ignored and truncate\n * the file afterward. */\n// 写入 nodes.conf 文件\nint clusterSaveConfig(int do_fsync) {\n    sds ci;\n    size_t content_size;\n    struct stat sb;\n    int fd;\n\n    server.cluster->todo_before_sleep &= ~CLUSTER_TODO_SAVE_CONFIG;\n\n    /* Get the nodes description and concatenate our \"vars\" directive to\n     * save currentEpoch and lastVoteEpoch. */\n    ci = clusterGenNodesDescription(REDIS_NODE_HANDSHAKE);\n    ci = sdscatprintf(ci,\"vars currentEpoch %llu lastVoteEpoch %llu\\n\",\n        (unsigned long long) server.cluster->currentEpoch,\n        (unsigned long long) server.cluster->lastVoteEpoch);\n    content_size = sdslen(ci);\n\n    if ((fd = open(server.cluster_configfile,O_WRONLY|O_CREAT,0644))\n        == -1) goto err;\n\n    /* Pad the new payload if the existing file length is greater. */\n    if (fstat(fd,&sb) != -1) {\n        if (sb.st_size > content_size) {\n            ci = sdsgrowzero(ci,sb.st_size);\n            memset(ci+content_size,'\\n',sb.st_size-content_size);\n        }\n    }\n    if (write(fd,ci,sdslen(ci)) != (ssize_t)sdslen(ci)) goto err;\n    if (do_fsync) {\n        server.cluster->todo_before_sleep &= ~CLUSTER_TODO_FSYNC_CONFIG;\n        fsync(fd);\n    }\n\n    /* Truncate the file if needed to remove the final \\n padding that\n     * is just garbage. */\n    if (content_size != sdslen(ci) && ftruncate(fd,content_size) == -1) {\n        /* ftruncate() failing is not a critical error. */\n    }\n    close(fd);\n    sdsfree(ci);\n    return 0;\n\nerr:\n    if (fd != -1) close(fd);\n    sdsfree(ci);\n    return -1;\n}\n\n// 尝试写入 nodes.conf 文件，失败则退出\nvoid clusterSaveConfigOrDie(int do_fsync) {\n    if (clusterSaveConfig(do_fsync) == -1) {\n        redisLog(REDIS_WARNING,\"Fatal: can't update cluster config file.\");\n        exit(1);\n    }\n}\n\n/* Lock the cluster config using flock(), and leaks the file descritor used to\n * acquire the lock so that the file will be locked forever.\n *\n * This works because we always update nodes.conf with a new version\n * in-place, reopening the file, and writing to it in place (later adjusting\n * the length with ftruncate()).\n *\n * On success REDIS_OK is returned, otherwise an error is logged and\n * the function returns REDIS_ERR to signal a lock was not acquired. */\nint clusterLockConfig(char *filename) {\n    /* To lock it, we need to open the file in a way it is created if\n     * it does not exist, otherwise there is a race condition with other\n     * processes. */\n    int fd = open(filename,O_WRONLY|O_CREAT,0644);\n    if (fd == -1) {\n        redisLog(REDIS_WARNING,\n            \"Can't open %s in order to acquire a lock: %s\",\n            filename, strerror(errno));\n        return REDIS_ERR;\n    }\n\n    if (flock(fd,LOCK_EX|LOCK_NB) == -1) {\n        if (errno == EWOULDBLOCK) {\n            redisLog(REDIS_WARNING,\n                 \"Sorry, the cluster configuration file %s is already used \"\n                 \"by a different Redis Cluster node. Please make sure that \"\n                 \"different nodes use different cluster configuration \"\n                 \"files.\", filename);\n        } else {\n            redisLog(REDIS_WARNING,\n                \"Impossible to lock %s: %s\", filename, strerror(errno));\n        }\n        close(fd);\n        return REDIS_ERR;\n    }\n    /* Lock acquired: leak the 'fd' by not closing it, so that we'll retain the\n     * lock to the file as long as the process exists. */\n    return REDIS_OK;\n}\n\n// 初始化集群\nvoid clusterInit(void) {\n    int saveconf = 0;\n\n    // 初始化配置\n    server.cluster = zmalloc(sizeof(clusterState));\n    server.cluster->myself = NULL;\n    server.cluster->currentEpoch = 0;\n    server.cluster->state = REDIS_CLUSTER_FAIL;\n    server.cluster->size = 1;\n    server.cluster->todo_before_sleep = 0;\n    server.cluster->nodes = dictCreate(&clusterNodesDictType,NULL);\n    server.cluster->nodes_black_list =\n        dictCreate(&clusterNodesBlackListDictType,NULL);\n    server.cluster->failover_auth_time = 0;\n    server.cluster->failover_auth_count = 0;\n    server.cluster->failover_auth_rank = 0;\n    server.cluster->failover_auth_epoch = 0;\n    server.cluster->lastVoteEpoch = 0;\n    server.cluster->stats_bus_messages_sent = 0;\n    server.cluster->stats_bus_messages_received = 0;\n    memset(server.cluster->slots,0, sizeof(server.cluster->slots));\n    clusterCloseAllSlots();\n\n    /* Lock the cluster config file to make sure every node uses\n     * its own nodes.conf. */\n    if (clusterLockConfig(server.cluster_configfile) == REDIS_ERR)\n        exit(1);\n\n    /* Load or create a new nodes configuration. */\n    if (clusterLoadConfig(server.cluster_configfile) == REDIS_ERR) {\n        /* No configuration found. We will just use the random name provided\n         * by the createClusterNode() function. */\n        myself = server.cluster->myself =\n            createClusterNode(NULL,REDIS_NODE_MYSELF|REDIS_NODE_MASTER);\n        redisLog(REDIS_NOTICE,\"No cluster configuration found, I'm %.40s\",\n            myself->name);\n        clusterAddNode(myself);\n        saveconf = 1;\n    }\n\n    // 保存 nodes.conf 文件\n    if (saveconf) clusterSaveConfigOrDie(1);\n\n    /* We need a listening TCP port for our cluster messaging needs. */\n    // 监听 TCP 端口\n    server.cfd_count = 0;\n\n    /* Port sanity check II\n     * The other handshake port check is triggered too late to stop\n     * us from trying to use a too-high cluster port number. */\n    if (server.port > (65535-REDIS_CLUSTER_PORT_INCR)) {\n        redisLog(REDIS_WARNING, \"Redis port number too high. \"\n                   \"Cluster communication port is 10,000 port \"\n                   \"numbers higher than your Redis port. \"\n                   \"Your Redis port number must be \"\n                   \"lower than 55535.\");\n        exit(1);\n    }\n\n    if (listenToPort(server.port+REDIS_CLUSTER_PORT_INCR,\n        server.cfd,&server.cfd_count) == REDIS_ERR)\n    {\n        exit(1);\n    } else {\n        int j;\n\n        for (j = 0; j < server.cfd_count; j++) {\n            // 关联监听事件处理器\n            if (aeCreateFileEvent(server.el, server.cfd[j], AE_READABLE,\n                clusterAcceptHandler, NULL) == AE_ERR)\n                    redisPanic(\"Unrecoverable error creating Redis Cluster \"\n                                \"file event.\");\n        }\n    }\n\n    /* The slots -> keys map is a sorted set. Init it. */\n    // slots -> keys 映射是一个有序集合\n    server.cluster->slots_to_keys = zslCreate();\n    resetManualFailover();\n}\n\n/* Reset a node performing a soft or hard reset:\n *\n * 1) All other nodes are forget.\n * 2) All the assigned / open slots are released.\n * 3) If the node is a slave, it turns into a master.\n * 5) Only for hard reset: a new Node ID is generated.\n * 6) Only for hard reset: currentEpoch and configEpoch are set to 0.\n * 7) The new configuration is saved and the cluster state updated.  */\nvoid clusterReset(int hard) {\n    dictIterator *di;\n    dictEntry *de;\n    int j;\n\n    /* Turn into master. */\n    if (nodeIsSlave(myself)) {\n        clusterSetNodeAsMaster(myself);\n        replicationUnsetMaster();\n    }\n\n    /* Close slots, reset manual failover state. */\n    clusterCloseAllSlots();\n    resetManualFailover();\n\n    /* Unassign all the slots. */\n    for (j = 0; j < REDIS_CLUSTER_SLOTS; j++) clusterDelSlot(j);\n\n    /* Forget all the nodes, but myself. */\n    di = dictGetSafeIterator(server.cluster->nodes);\n    while((de = dictNext(di)) != NULL) {\n        clusterNode *node = dictGetVal(de);\n\n        if (node == myself) continue;\n        clusterDelNode(node);\n    }\n    dictReleaseIterator(di);\n\n    /* Hard reset only: set epochs to 0, change node ID. */\n    if (hard) {\n        sds oldname;\n\n        server.cluster->currentEpoch = 0;\n        server.cluster->lastVoteEpoch = 0;\n        myself->configEpoch = 0;\n\n        /* To change the Node ID we need to remove the old name from the\n         * nodes table, change the ID, and re-add back with new name. */\n        oldname = sdsnewlen(myself->name, REDIS_CLUSTER_NAMELEN);\n        dictDelete(server.cluster->nodes,oldname);\n        sdsfree(oldname);\n        getRandomHexChars(myself->name, REDIS_CLUSTER_NAMELEN);\n        clusterAddNode(myself);\n    }\n\n    /* Make sure to persist the new config and update the state. */\n    clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG|\n                         CLUSTER_TODO_UPDATE_STATE|\n                         CLUSTER_TODO_FSYNC_CONFIG);\n}\n\n/* -----------------------------------------------------------------------------\n * CLUSTER communication link\n * -------------------------------------------------------------------------- */\n\n// 创建节点连接\nclusterLink *createClusterLink(clusterNode *node) {\n    clusterLink *link = zmalloc(sizeof(*link));\n    link->ctime = mstime();\n    link->sndbuf = sdsempty();\n    link->rcvbuf = sdsempty();\n    link->node = node;\n    link->fd = -1;\n    return link;\n}\n\n/* Free a cluster link, but does not free the associated node of course.\n * This function will just make sure that the original node associated\n * with this link will have the 'link' field set to NULL. */\n// 将给定的连接清空\n// 并将包含这个连接的节点的 link 属性设为 NULL\nvoid freeClusterLink(clusterLink *link) {\n\n    // 删除事件处理器\n    if (link->fd != -1) {\n        aeDeleteFileEvent(server.el, link->fd, AE_WRITABLE);\n        aeDeleteFileEvent(server.el, link->fd, AE_READABLE);\n    }\n\n    // 释放输入缓冲区和输出缓冲区\n    sdsfree(link->sndbuf);\n    sdsfree(link->rcvbuf);\n\n    // 将节点的 link 属性设为 NULL\n    if (link->node)\n        link->node->link = NULL;\n\n    // 关闭连接\n    close(link->fd);\n\n    // 释放连接结构\n    zfree(link);\n}\n\n// 监听事件处理器\n#define MAX_CLUSTER_ACCEPTS_PER_CALL 1000\nvoid clusterAcceptHandler(aeEventLoop *el, int fd, void *privdata, int mask) {\n    int cport, cfd;\n    int max = MAX_CLUSTER_ACCEPTS_PER_CALL;\n    char cip[REDIS_IP_STR_LEN];\n    clusterLink *link;\n    REDIS_NOTUSED(el);\n    REDIS_NOTUSED(mask);\n    REDIS_NOTUSED(privdata);\n\n    /* If the server is starting up, don't accept cluster connections:\n     * UPDATE messages may interact with the database content. */\n    if (server.masterhost == NULL && server.loading) return;\n\n    while(max--) {\n        cfd = anetTcpAccept(server.neterr, fd, cip, sizeof(cip), &cport);\n        if (cfd == ANET_ERR) {\n            if (errno != EWOULDBLOCK)\n                redisLog(REDIS_VERBOSE,\n                    \"Accepting cluster node: %s\", server.neterr);\n            return;\n        }\n        anetNonBlock(NULL,cfd);\n        anetEnableTcpNoDelay(NULL,cfd);\n\n        /* Use non-blocking I/O for cluster messages. */\n        redisLog(REDIS_VERBOSE,\"Accepted cluster node %s:%d\", cip, cport);\n        /* Create a link object we use to handle the connection.\n         * It gets passed to the readable handler when data is available.\n         * Initiallly the link->node pointer is set to NULL as we don't know\n         * which node is, but the right node is references once we know the\n         * node identity. */\n        link = createClusterLink(NULL);\n        link->fd = cfd;\n        aeCreateFileEvent(server.el,cfd,AE_READABLE,clusterReadHandler,link);\n    }\n}\n\n/* -----------------------------------------------------------------------------\n * Key space handling\n * -------------------------------------------------------------------------- */\n\n/* We have 16384 hash slots. The hash slot of a given key is obtained\n * as the least significant 14 bits of the crc16 of the key.\n *\n * However if the key contains the {...} pattern, only the part between\n * { and } is hashed. This may be useful in the future to force certain\n * keys to be in the same node (assuming no resharding is in progress). */\n// 计算给定键应该被分配到那个槽\nunsigned int keyHashSlot(char *key, int keylen) {\n    int s, e; /* start-end indexes of { and } */\n\n    for (s = 0; s < keylen; s++)\n        if (key[s] == '{') break;\n\n    /* No '{' ? Hash the whole key. This is the base case. */\n    if (s == keylen) return crc16(key,keylen) & 0x3FFF;\n\n    /* '{' found? Check if we have the corresponding '}'. */\n    for (e = s+1; e < keylen; e++)\n        if (key[e] == '}') break;\n\n    /* No '}' or nothing betweeen {} ? Hash the whole key. */\n    if (e == keylen || e == s+1) return crc16(key,keylen) & 0x3FFF;\n\n    /* If we are here there is both a { and a } on its right. Hash\n     * what is in the middle between { and }. */\n    return crc16(key+s+1,e-s-1) & 0x3FFF;\n}\n\n/* -----------------------------------------------------------------------------\n * CLUSTER node API\n * -------------------------------------------------------------------------- */\n\n/* Create a new cluster node, with the specified flags.\n *\n * 创建一个带有指定 flag 的集群节点。\n *\n * If \"nodename\" is NULL this is considered a first handshake and a random\n * node name is assigned to this node (it will be fixed later when we'll\n * receive the first pong).\n *\n * 如果 nodename 参数为 NULL ，那么表示我们尚未向节点发送 PING ，\n * 集群会为节点设置一个随机的命令，\n * 这个命令在之后接收到节点的 PONG 回复之后就会被更新。\n *\n * The node is created and returned to the user, but it is not automatically\n * added to the nodes hash table. \n *\n * 函数会返回被创建的节点，但不会自动将它添加到当前节点的节点哈希表中\n * （nodes hash table）。\n */\nclusterNode *createClusterNode(char *nodename, int flags) {\n    clusterNode *node = zmalloc(sizeof(*node));\n\n    // 设置名字\n    if (nodename)\n        memcpy(node->name, nodename, REDIS_CLUSTER_NAMELEN);\n    else\n        getRandomHexChars(node->name, REDIS_CLUSTER_NAMELEN);\n\n    // 初始化属性\n    node->ctime = mstime();\n    node->configEpoch = 0;\n    node->flags = flags;\n    memset(node->slots,0,sizeof(node->slots));\n    node->numslots = 0;\n    node->numslaves = 0;\n    node->slaves = NULL;\n    node->slaveof = NULL;\n    node->ping_sent = node->pong_received = 0;\n    node->fail_time = 0;\n    node->link = NULL;\n    memset(node->ip,0,sizeof(node->ip));\n    node->port = 0;\n    node->fail_reports = listCreate();\n    node->voted_time = 0;\n    node->repl_offset_time = 0;\n    node->repl_offset = 0;\n    listSetFreeMethod(node->fail_reports,zfree);\n\n    return node;\n}\n\n/* This function is called every time we get a failure report from a node.\n *\n * 这个函数会在当前节点接到某个节点的下线报告时调用。\n *\n * The side effect is to populate the fail_reports list (or to update\n * the timestamp of an existing report).\n *\n * 函数的作用就是将下线节点的下线报告添加到 fail_reports 列表，\n * 如果这个下线节点的下线报告已经存在，\n * 那么更新该报告的时间戳。\n *\n * 'failing' is the node that is in failure state according to the\n * 'sender' node.\n *\n * failing 参数指向下线节点，而 sender 参数则指向报告 failing 已下线的节点。\n *\n * The function returns 0 if it just updates a timestamp of an existing\n * failure report from the same sender. 1 is returned if a new failure\n * report is created. \n *\n * 函数返回 0 表示对已存在的报告进行了更新，\n * 返回 1 则表示创建了一条新的下线报告。\n */\nint clusterNodeAddFailureReport(clusterNode *failing, clusterNode *sender) {\n\n    // 指向保存下线报告的链表\n    list *l = failing->fail_reports;\n\n    listNode *ln;\n    listIter li;\n    clusterNodeFailReport *fr;\n\n    /* If a failure report from the same sender already exists, just update\n     * the timestamp. */\n    // 查找 sender 节点的下线报告是否已经存在\n    listRewind(l,&li);\n    while ((ln = listNext(&li)) != NULL) {\n        fr = ln->value;\n        // 如果存在的话，那么只更新该报告的时间戳\n        if (fr->node == sender) {\n            fr->time = mstime();\n            return 0;\n        }\n    }\n\n    /* Otherwise create a new report. */\n    // 否则的话，就创建一个新的报告\n    fr = zmalloc(sizeof(*fr));\n    fr->node = sender;\n    fr->time = mstime();\n\n    // 将报告添加到列表\n    listAddNodeTail(l,fr);\n\n    return 1;\n}\n\n/* Remove failure reports that are too old, where too old means reasonably\n * older than the global node timeout. Note that anyway for a node to be\n * flagged as FAIL we need to have a local PFAIL state that is at least\n * older than the global node timeout, so we don't just trust the number\n * of failure reports from other nodes. \n *\n * 移除对 node 节点的过期的下线报告，\n * 多长时间为过期是根据 node timeout 选项的值来决定的。\n *\n * 注意，\n * 要将一个节点标记为 FAIL 状态，\n * 当前节点将 node 标记为 PFAIL 状态的时间至少应该超过 node timeout ，\n * 所以报告 node 已下线的节点数量并不是当前节点将 node 标记为 FAIL 的唯一条件。\n */\nvoid clusterNodeCleanupFailureReports(clusterNode *node) {\n\n    // 指向该节点的所有下线报告\n    list *l = node->fail_reports;\n\n    listNode *ln;\n    listIter li;\n    clusterNodeFailReport *fr;\n\n    // 下线报告的最大保质期（超过这个时间的报告会被删除）\n    mstime_t maxtime = server.cluster_node_timeout *\n                     REDIS_CLUSTER_FAIL_REPORT_VALIDITY_MULT;\n    mstime_t now = mstime();\n\n    // 遍历所有下线报告\n    listRewind(l,&li);\n    while ((ln = listNext(&li)) != NULL) {\n        fr = ln->value;\n        // 删除过期报告\n        if (now - fr->time > maxtime) listDelNode(l,ln);\n    }\n}\n\n/* Remove the failing report for 'node' if it was previously considered\n * failing by 'sender'. This function is called when a node informs us via\n * gossip that a node is OK from its point of view (no FAIL or PFAIL flags).\n *\n * 从 node 节点的下线报告中移除 sender 对 node 的下线报告。\n *\n * 这个函数在以下情况使用：当前节点认为 node 已下线（FAIL 或者 PFAIL），\n * 但 sender 却向当前节点发来报告，说它认为 node 节点没有下线，\n * 那么当前节点就要移除 sender 对 node 的下线报告 \n * —— 如果 sender 曾经报告过 node 下线的话。\n *\n * Note that this function is called relatively often as it gets called even\n * when there are no nodes failing, and is O(N), however when the cluster is\n * fine the failure reports list is empty so the function runs in constant\n * time.\n *\n * 即使在节点没有下线的情况下，这个函数也会被调用，并且调用的次数还比较频繁。\n * 在一般情况下，这个函数的复杂度为 O(N) ，\n * 不过在不存在下线报告的情况下，这个函数的复杂度仅为常数时间。\n *\n * The function returns 1 if the failure report was found and removed.\n * Otherwise 0 is returned. \n *\n * 函数返回 1 表示下线报告已经被成功移除，\n * 0 表示 sender 没有发送过 node 的下线报告，删除失败。\n */\nint clusterNodeDelFailureReport(clusterNode *node, clusterNode *sender) {\n    list *l = node->fail_reports;\n    listNode *ln;\n    listIter li;\n    clusterNodeFailReport *fr;\n\n    /* Search for a failure report from this sender. */\n    // 查找 sender 对 node 的下线报告\n    listRewind(l,&li);\n    while ((ln = listNext(&li)) != NULL) {\n        fr = ln->value;\n        if (fr->node == sender) break;\n    }\n    // sender 没有报告过 node 下线，直接返回\n    if (!ln) return 0; /* No failure report from this sender. */\n\n    /* Remove the failure report. */\n    // 删除 sender 对 node 的下线报告\n    listDelNode(l,ln);\n    // 删除对 node 的下线报告中，过期的报告\n    clusterNodeCleanupFailureReports(node);\n\n    return 1;\n}\n\n/* Return the number of external nodes that believe 'node' is failing,\n * not including this node, that may have a PFAIL or FAIL state for this\n * node as well. \n *\n * 计算不包括本节点在内的，\n * 将 node 标记为 PFAIL 或者 FAIL 的节点的数量。\n */\nint clusterNodeFailureReportsCount(clusterNode *node) {\n\n    // 移除过期的下线报告\n    clusterNodeCleanupFailureReports(node);\n\n    // 统计下线报告的数量\n    return listLength(node->fail_reports);\n}\n\n// 移除主节点 master 的从节点 slave\nint clusterNodeRemoveSlave(clusterNode *master, clusterNode *slave) {\n    int j;\n\n    // 在 slaves 数组中找到从节点 slave 所属的主节点，\n    // 将主节点中的 slave 信息移除\n    for (j = 0; j < master->numslaves; j++) {\n        if (master->slaves[j] == slave) {\n            memmove(master->slaves+j,master->slaves+(j+1),\n                (master->numslaves-1)-j);\n            master->numslaves--;\n            return REDIS_OK;\n        }\n    }\n    return REDIS_ERR;\n}\n\n// 将 slave 加入到 master 的从节点名单中\nint clusterNodeAddSlave(clusterNode *master, clusterNode *slave) {\n    int j;\n\n    /* If it's already a slave, don't add it again. */\n    // 如果 slave 已经存在，那么不做操作\n    for (j = 0; j < master->numslaves; j++)\n        if (master->slaves[j] == slave) return REDIS_ERR;\n\n    // 将 slave 添加到 slaves 数组里面\n    master->slaves = zrealloc(master->slaves,\n        sizeof(clusterNode*)*(master->numslaves+1));\n    master->slaves[master->numslaves] = slave;\n    master->numslaves++;\n\n    return REDIS_OK;\n}\n\n// 重置给定节点的从节点名单\nvoid clusterNodeResetSlaves(clusterNode *n) {\n    zfree(n->slaves);\n    n->numslaves = 0;\n    n->slaves = NULL;\n}\n\nint clusterCountNonFailingSlaves(clusterNode *n) {\n    int j, okslaves = 0;\n\n    for (j = 0; j < n->numslaves; j++)\n        if (!nodeFailed(n->slaves[j])) okslaves++;\n    return okslaves;\n}\n\n// 释放节点\nvoid freeClusterNode(clusterNode *n) {\n    sds nodename;\n\n    nodename = sdsnewlen(n->name, REDIS_CLUSTER_NAMELEN);\n\n    // 从 nodes 表中删除节点\n    redisAssert(dictDelete(server.cluster->nodes,nodename) == DICT_OK);\n    sdsfree(nodename);\n\n    // 移除从节点\n    if (n->slaveof) clusterNodeRemoveSlave(n->slaveof, n);\n\n    // 释放连接\n    if (n->link) freeClusterLink(n->link);\n    \n    // 释放失败报告\n    listRelease(n->fail_reports);\n\n    // 释放节点结构\n    zfree(n);\n}\n\n/* Add a node to the nodes hash table */\n// 将给定 node 添加到节点表里面\nint clusterAddNode(clusterNode *node) {\n    int retval;\n    // 将 node 添加到当前节点的 nodes 表中\n    // 这样接下来当前节点就会创建连向 node 的节点\n    retval = dictAdd(server.cluster->nodes,\n            sdsnewlen(node->name,REDIS_CLUSTER_NAMELEN), node);\n    return (retval == DICT_OK) ? REDIS_OK : REDIS_ERR;\n}\n\n/* Remove a node from the cluster:\n *\n * 从集群中移除一个节点：\n *\n * 1) Mark all the nodes handled by it as unassigned.\n *    将所有由该节点负责的槽全部设置为未分配\n * 2) Remove all the failure reports sent by this node.\n *    移除所有由这个节点发送的下线报告（failure report）\n * 3) Free the node, that will in turn remove it from the hash table\n *    and from the list of slaves of its master, if it is a slave node.\n *    释放这个节点，\n *    清除它在各个节点的 nodes 表中的数据，\n *    如果它是一个从节点的话，\n *    还要在它的主节点的 slaves 表中清除关于这个节点的数据。\n */\nvoid clusterDelNode(clusterNode *delnode) {\n    int j;\n    dictIterator *di;\n    dictEntry *de;\n\n    /* 1) Mark slots as unassigned. */\n    for (j = 0; j < REDIS_CLUSTER_SLOTS; j++) {\n        // 取消向该节点接收槽的计划\n        if (server.cluster->importing_slots_from[j] == delnode)\n            server.cluster->importing_slots_from[j] = NULL;\n        // 取消向该节点移交槽的计划\n        if (server.cluster->migrating_slots_to[j] == delnode)\n            server.cluster->migrating_slots_to[j] = NULL;\n        // 将所有由该节点负责的槽设置为未分配\n        if (server.cluster->slots[j] == delnode)\n            clusterDelSlot(j);\n    }\n\n    /* 2) Remove failure reports. */\n    // 移除所有由该节点发送的下线报告\n    di = dictGetSafeIterator(server.cluster->nodes);\n    while((de = dictNext(di)) != NULL) {\n        clusterNode *node = dictGetVal(de);\n\n        if (node == delnode) continue;\n        clusterNodeDelFailureReport(node,delnode);\n    }\n    dictReleaseIterator(di);\n\n    /* 3) Remove this node from its master's slaves if needed. */\n    // 将节点从它的主节点的从节点列表中移除\n    if (nodeIsSlave(delnode) && delnode->slaveof)\n        clusterNodeRemoveSlave(delnode->slaveof,delnode);\n\n    /* 4) Free the node, unlinking it from the cluster. */\n    // 释放节点\n    freeClusterNode(delnode);\n}\n\n/* Node lookup by name */\n// 根据名字，查找给定的节点\nclusterNode *clusterLookupNode(char *name) {\n    sds s = sdsnewlen(name, REDIS_CLUSTER_NAMELEN);\n    dictEntry *de;\n\n    de = dictFind(server.cluster->nodes,s);\n    sdsfree(s);\n    if (de == NULL) return NULL;\n    return dictGetVal(de);\n}\n\n/* This is only used after the handshake. When we connect a given IP/PORT\n * as a result of CLUSTER MEET we don't have the node name yet, so we\n * pick a random one, and will fix it when we receive the PONG request using\n * this function. */\n// 在第一次向节点发送 CLUSTER MEET 命令的时候\n// 因为发送命令的节点还不知道目标节点的名字\n// 所以它会给目标节点分配一个随机的名字\n// 当目标节点向发送节点返回 PONG 回复时\n// 发送节点就知道了目标节点的 IP 和 port\n// 这时发送节点就可以通过调用这个函数\n// 为目标节点改名\nvoid clusterRenameNode(clusterNode *node, char *newname) {\n    int retval;\n    sds s = sdsnewlen(node->name, REDIS_CLUSTER_NAMELEN);\n\n    redisLog(REDIS_DEBUG,\"Renaming node %.40s into %.40s\",\n        node->name, newname);\n    retval = dictDelete(server.cluster->nodes, s);\n    sdsfree(s);\n    redisAssert(retval == DICT_OK);\n    memcpy(node->name, newname, REDIS_CLUSTER_NAMELEN);\n    clusterAddNode(node);\n}\n\n/* -----------------------------------------------------------------------------\n * CLUSTER nodes blacklist\n *\n * 集群节点黑名单\n *\n * The nodes blacklist is just a way to ensure that a given node with a given\n * Node ID is not readded before some time elapsed (this time is specified\n * in seconds in REDIS_CLUSTER_BLACKLIST_TTL).\n *\n * 黑名单用于禁止一个给定的节点在 REDIS_CLUSTER_BLACKLIST_TTL 指定的时间内，\n * 被重新添加到集群中。\n *\n * This is useful when we want to remove a node from the cluster completely:\n * when CLUSTER FORGET is called, it also puts the node into the blacklist so\n * that even if we receive gossip messages from other nodes that still remember\n * about the node we want to remove, we don't re-add it before some time.\n *\n * 当我们需要从集群中彻底移除一个节点时，就需要用到黑名单：\n * 在执行 CLUSTER FORGET 命令时，节点会被添加进黑名单里面，\n * 这样即使我们从仍然记得被移除节点的其他节点那里收到关于被移除节点的消息，\n * 我们也不会重新将被移除节点添加至集群。\n *\n * Currently the REDIS_CLUSTER_BLACKLIST_TTL is set to 1 minute, this means\n * that redis-trib has 60 seconds to send CLUSTER FORGET messages to nodes\n * in the cluster without dealing with the problem of other nodes re-adding\n * back the node to nodes we already sent the FORGET command to.\n *\n * REDIS_CLUSTER_BLACKLIST_TTL 当前的值为 1 分钟，\n * 这意味着 redis-trib 有 60 秒的时间，可以向集群中的所有节点发送 CLUSTER FORGET\n * 命令，而不必担心有其他节点会将被 CLUSTER FORGET 移除的节点重新添加到集群里面。\n *\n * The data structure used is a hash table with an sds string representing\n * the node ID as key, and the time when it is ok to re-add the node as\n * value.\n *\n * 黑名单的底层实现是一个字典，\n * 字典的键为 SDS 表示的节点 id ，字典的值为可以重新添加节点的时间戳。\n * -------------------------------------------------------------------------- */\n\n#define REDIS_CLUSTER_BLACKLIST_TTL 60      /* 1 minute. */\n\n\n/* Before of the addNode() or Exists() operations we always remove expired\n * entries from the black list. This is an O(N) operation but it is not a\n * problem since add / exists operations are called very infrequently and\n * the hash table is supposed to contain very little elements at max.\n *\n * 在执行 addNode() 操作或者 Exists() 操作之前，\n * 我们总是会先执行这个函数，移除黑名单中的过期节点。\n *\n * 这个函数的复杂度为 O(N) ，不过它不会对效率产生影响，\n * 因为这个函数执行的次数并不频繁，并且字典的链表里面包含的节点数量也非常少。\n *\n * However without the cleanup during long uptimes and with some automated\n * node add/removal procedures, entries could accumulate. \n *\n * 定期清理过期节点是为了防止字典中的节点堆积过多。\n */\nvoid clusterBlacklistCleanup(void) {\n    dictIterator *di;\n    dictEntry *de;\n\n    // 遍历黑名单中的所有节点\n    di = dictGetSafeIterator(server.cluster->nodes_black_list);\n    while((de = dictNext(di)) != NULL) {\n        int64_t expire = dictGetUnsignedIntegerVal(de);\n\n        // 删除过期节点\n        if (expire < server.unixtime)\n            dictDelete(server.cluster->nodes_black_list,dictGetKey(de));\n    }\n    dictReleaseIterator(di);\n}\n\n/* Cleanup the blacklist and add a new node ID to the black list. */\n// 清除黑名单中的过期节点，然后将新的节点添加到黑名单中\nvoid clusterBlacklistAddNode(clusterNode *node) {\n    dictEntry *de;\n    sds id = sdsnewlen(node->name,REDIS_CLUSTER_NAMELEN);\n\n    // 先清理过期名单\n    clusterBlacklistCleanup();\n\n    // 添加节点\n    if (dictAdd(server.cluster->nodes_black_list,id,NULL) == DICT_OK) {\n        /* If the key was added, duplicate the sds string representation of\n         * the key for the next lookup. We'll free it at the end. */\n        id = sdsdup(id);\n    }\n    // 设置过期时间\n    de = dictFind(server.cluster->nodes_black_list,id);\n    dictSetUnsignedIntegerVal(de,time(NULL)+REDIS_CLUSTER_BLACKLIST_TTL);\n    sdsfree(id);\n}\n\n/* Return non-zero if the specified node ID exists in the blacklist.\n * You don't need to pass an sds string here, any pointer to 40 bytes\n * will work. */\n// 检查给定 id 所指定的节点是否存在于黑名单中。\n// nodeid 参数不必是一个 SDS 值，只要一个 40 字节长的字符串即可\nint clusterBlacklistExists(char *nodeid) {\n\n    // 构建 SDS 表示的节点名\n    sds id = sdsnewlen(nodeid,REDIS_CLUSTER_NAMELEN);\n    int retval;\n\n    // 清除过期黑名单\n    clusterBlacklistCleanup();\n\n    // 检查节点是否存在\n    retval = dictFind(server.cluster->nodes_black_list,id) != NULL;\n    sdsfree(id);\n\n    return retval;\n}\n\n/* -----------------------------------------------------------------------------\n * CLUSTER messages exchange - PING/PONG and gossip\n * -------------------------------------------------------------------------- */\n\n/* This function checks if a given node should be marked as FAIL.\n * It happens if the following conditions are met:\n *\n * 此函数用于判断是否需要将 node 标记为 FAIL 。\n *\n * 将 node 标记为 FAIL 需要满足以下两个条件：\n *\n * 1) We received enough failure reports from other master nodes via gossip.\n *    Enough means that the majority of the masters signaled the node is\n *    down recently.\n *    有半数以上的主节点将 node 标记为 PFAIL 状态。\n * 2) We believe this node is in PFAIL state.\n *    当前节点也将 node 标记为 PFAIL 状态。\n *\n * If a failure is detected we also inform the whole cluster about this\n * event trying to force every other node to set the FAIL flag for the node.\n *\n * 如果确认 node 已经进入了 FAIL 状态，\n * 那么节点还会向其他节点发送 FAIL 消息，让其他节点也将 node 标记为 FAIL 。\n *\n * Note that the form of agreement used here is weak, as we collect the majority\n * of masters state during some time, and even if we force agreement by\n * propagating the FAIL message, because of partitions we may not reach every\n * node. However:\n *\n * 注意，集群判断一个 node 进入 FAIL 所需的条件是弱（weak）的，\n * 因为节点们对 node 的状态报告并不是实时的，而是有一段时间间隔\n * （这段时间内 node 的状态可能已经发生了改变），\n * 并且尽管当前节点会向其他节点发送 FAIL 消息，\n * 但因为网络分裂（network partition）的问题，\n * 有一部分节点可能还是会不知道将 node 标记为 FAIL 。\n *\n * 不过：\n *\n * 1) Either we reach the majority and eventually the FAIL state will propagate\n *    to all the cluster.\n *    只要我们成功将 node 标记为 FAIL ，\n *    那么这个 FAIL 状态最终（eventually）总会传播至整个集群的所有节点。\n * 2) Or there is no majority so no slave promotion will be authorized and the\n *    FAIL flag will be cleared after some time.\n *    又或者，因为没有半数的节点支持，当前节点不能将 node 标记为 FAIL ，\n *    所以对 FAIL 节点的故障转移将无法进行， FAIL 标识可能会在之后被移除。\n *    \n */\nvoid markNodeAsFailingIfNeeded(clusterNode *node) {\n    int failures;\n\n    // 标记为 FAIL 所需的节点数量，需要超过集群节点数量的一半\n    int needed_quorum = (server.cluster->size / 2) + 1;\n\n    if (!nodeTimedOut(node)) return; /* We can reach it. */\n    if (nodeFailed(node)) return; /* Already FAILing. */\n\n    // 统计将 node 标记为 PFAIL 或者 FAIL 的节点数量（不包括当前节点）\n    failures = clusterNodeFailureReportsCount(node);\n\n    /* Also count myself as a voter if I'm a master. */\n    // 如果当前节点是主节点，那么将当前节点也算在 failures 之内\n    if (nodeIsMaster(myself)) failures++;\n    // 报告下线节点的数量不足节点总数的一半，不能将节点判断为 FAIL ，返回\n    if (failures < needed_quorum) return; /* No weak agreement from masters. */\n\n    redisLog(REDIS_NOTICE,\n        \"Marking node %.40s as failing (quorum reached).\", node->name);\n\n    /* Mark the node as failing. */\n    // 将 node 标记为 FAIL\n    node->flags &= ~REDIS_NODE_PFAIL;\n    node->flags |= REDIS_NODE_FAIL;\n    node->fail_time = mstime();\n\n    /* Broadcast the failing node name to everybody, forcing all the other\n     * reachable nodes to flag the node as FAIL. */\n    // 如果当前节点是主节点的话，那么向其他节点发送报告 node 的 FAIL 信息\n    // 让其他节点也将 node 标记为 FAIL\n    if (nodeIsMaster(myself)) clusterSendFail(node->name);\n    clusterDoBeforeSleep(CLUSTER_TODO_UPDATE_STATE|CLUSTER_TODO_SAVE_CONFIG);\n}\n\n/* This function is called only if a node is marked as FAIL, but we are able\n * to reach it again. It checks if there are the conditions to undo the FAIL\n * state. \n *\n * 这个函数在当前节点接收到一个被标记为 FAIL 的节点那里收到消息时使用，\n * 它可以检查是否应该将节点的 FAIL 状态移除。\n */\nvoid clearNodeFailureIfNeeded(clusterNode *node) {\n    mstime_t now = mstime();\n\n    redisAssert(nodeFailed(node));\n\n    /* For slaves we always clear the FAIL flag if we can contact the\n     * node again. */\n    // 如果 FAIL 的是从节点，那么当前节点会直接移除该节点的 FAIL\n    if (nodeIsSlave(node) || node->numslots == 0) {\n        redisLog(REDIS_NOTICE,\n            \"Clear FAIL state for node %.40s: %s is reachable again.\",\n                node->name,\n                nodeIsSlave(node) ? \"slave\" : \"master without slots\");\n        // 移除\n        node->flags &= ~REDIS_NODE_FAIL;\n\n        clusterDoBeforeSleep(CLUSTER_TODO_UPDATE_STATE|CLUSTER_TODO_SAVE_CONFIG);\n    }\n\n    /* If it is a master and...\n     *\n     * 如果 FAIL 的是一个主节点，并且：\n     *\n     * 1) The FAIL state is old enough.\n     *    节点被标记为 FAIL 状态已经有一段时间了\n     *\n     * 2) It is yet serving slots from our point of view (not failed over).\n     *    从当前节点的视角来看，这个节点还有负责处理的槽\n     *\n     * Apparently no one is going to fix these slots, clear the FAIL flag. \n     *\n     * 那么说明 FAIL 节点仍然有槽没有迁移完，那么当前节点移除该节点的 FAIL 标识。\n     */\n    if (nodeIsMaster(node) && node->numslots > 0 &&\n        (now - node->fail_time) >\n        (server.cluster_node_timeout * REDIS_CLUSTER_FAIL_UNDO_TIME_MULT))\n    {\n        redisLog(REDIS_NOTICE,\n            \"Clear FAIL state for node %.40s: is reachable again and nobody is serving its slots after some time.\",\n                node->name);\n\n        // 撤销 FAIL 状态\n        node->flags &= ~REDIS_NODE_FAIL;\n\n        clusterDoBeforeSleep(CLUSTER_TODO_UPDATE_STATE|CLUSTER_TODO_SAVE_CONFIG);\n    }\n}\n\n/* Return true if we already have a node in HANDSHAKE state matching the\n * specified ip address and port number. This function is used in order to\n * avoid adding a new handshake node for the same address multiple times. \n *\n * 如果当前节点已经向 ip 和 port 所指定的节点进行了握手，\n * 那么返回 1 。\n *\n * 这个函数用于防止对同一个节点进行多次握手。\n */\nint clusterHandshakeInProgress(char *ip, int port) {\n    dictIterator *di;\n    dictEntry *de;\n\n    // 遍历所有已知节点\n    di = dictGetSafeIterator(server.cluster->nodes);\n    while((de = dictNext(di)) != NULL) {\n        clusterNode *node = dictGetVal(de);\n\n        // 跳过非握手状态的节点，之后剩下的都是正在握手的节点\n        if (!nodeInHandshake(node)) continue;\n\n        // 给定 ip 和 port 的节点正在进行握手\n        if (!strcasecmp(node->ip,ip) && node->port == port) break;\n    }\n    dictReleaseIterator(di);\n\n    // 检查节点是否正在握手\n    return de != NULL;\n}\n\n/* Start an handshake with the specified address if there is not one\n * already in progress. Returns non-zero if the handshake was actually\n * started. On error zero is returned and errno is set to one of the\n * following values:\n *\n * 如果还没有与指定的地址进行过握手，那么进行握手。\n * 返回 1 表示握手已经开始，\n * 返回 0 并将 errno 设置为以下值来表示意外情况：\n *\n * EAGAIN - There is already an handshake in progress for this address.\n *          已经有握手在进行中了。\n * EINVAL - IP or port are not valid. \n *          ip 或者 port 参数不合法。\n */\nint clusterStartHandshake(char *ip, int port) {\n    clusterNode *n;\n    char norm_ip[REDIS_IP_STR_LEN];\n    struct sockaddr_storage sa;\n\n    /* IP sanity check */\n    // ip 合法性检查\n    if (inet_pton(AF_INET,ip,\n            &(((struct sockaddr_in *)&sa)->sin_addr)))\n    {\n        sa.ss_family = AF_INET;\n    } else if (inet_pton(AF_INET6,ip,\n            &(((struct sockaddr_in6 *)&sa)->sin6_addr)))\n    {\n        sa.ss_family = AF_INET6;\n    } else {\n        errno = EINVAL;\n        return 0;\n    }\n\n    /* Port sanity check */\n    // port 合法性检查\n    if (port <= 0 || port > (65535-REDIS_CLUSTER_PORT_INCR)) {\n        errno = EINVAL;\n        return 0;\n    }\n\n    /* Set norm_ip as the normalized string representation of the node\n     * IP address. */\n    if (sa.ss_family == AF_INET)\n        inet_ntop(AF_INET,\n            (void*)&(((struct sockaddr_in *)&sa)->sin_addr),\n            norm_ip,REDIS_IP_STR_LEN);\n    else\n        inet_ntop(AF_INET6,\n            (void*)&(((struct sockaddr_in6 *)&sa)->sin6_addr),\n            norm_ip,REDIS_IP_STR_LEN);\n\n    // 检查节点是否已经发送握手请求，如果是的话，那么直接返回，防止出现重复握手\n    if (clusterHandshakeInProgress(norm_ip,port)) {\n        errno = EAGAIN;\n        return 0;\n    }\n\n    /* Add the node with a random address (NULL as first argument to\n     * createClusterNode()). Everything will be fixed during the\n     * handskake. */\n    // 对给定地址的节点设置一个随机名字\n    // 当 HANDSHAKE 完成时，当前节点会取得给定地址节点的真正名字\n    // 到时会用真名替换随机名\n    n = createClusterNode(NULL,REDIS_NODE_HANDSHAKE|REDIS_NODE_MEET);\n    memcpy(n->ip,norm_ip,sizeof(n->ip));\n    n->port = port;\n\n    // 将节点添加到集群当中\n    clusterAddNode(n);\n\n    return 1;\n}\n\n/* Process the gossip section of PING or PONG packets.\n *\n * 解释 MEET 、 PING 或 PONG 消息中和 gossip 协议有关的信息。\n *\n * Note that this function assumes that the packet is already sanity-checked\n * by the caller, not in the content of the gossip section, but in the\n * length. \n *\n * 注意，这个函数假设调用者已经根据消息的长度，对消息进行过合法性检查。\n */\nvoid clusterProcessGossipSection(clusterMsg *hdr, clusterLink *link) {\n\n    // 记录这条消息中包含了多少个节点的信息\n    uint16_t count = ntohs(hdr->count);\n\n    // 指向第一个节点的信息\n    clusterMsgDataGossip *g = (clusterMsgDataGossip*) hdr->data.ping.gossip;\n\n    // 取出发送者\n    clusterNode *sender = link->node ? link->node : clusterLookupNode(hdr->sender);\n\n    // 遍历所有节点的信息\n    while(count--) {\n        sds ci = sdsempty();\n\n        // 分析节点的 flag\n        uint16_t flags = ntohs(g->flags);\n\n        // 信息节点\n        clusterNode *node;\n\n        // 取出节点的 flag\n        if (flags == 0) ci = sdscat(ci,\"noflags,\");\n        if (flags & REDIS_NODE_MYSELF) ci = sdscat(ci,\"myself,\");\n        if (flags & REDIS_NODE_MASTER) ci = sdscat(ci,\"master,\");\n        if (flags & REDIS_NODE_SLAVE) ci = sdscat(ci,\"slave,\");\n        if (flags & REDIS_NODE_PFAIL) ci = sdscat(ci,\"fail?,\");\n        if (flags & REDIS_NODE_FAIL) ci = sdscat(ci,\"fail,\");\n        if (flags & REDIS_NODE_HANDSHAKE) ci = sdscat(ci,\"handshake,\");\n        if (flags & REDIS_NODE_NOADDR) ci = sdscat(ci,\"noaddr,\");\n        if (ci[sdslen(ci)-1] == ',') ci[sdslen(ci)-1] = ' ';\n\n        redisLog(REDIS_DEBUG,\"GOSSIP %.40s %s:%d %s\",\n            g->nodename,\n            g->ip,\n            ntohs(g->port),\n            ci);\n        sdsfree(ci);\n\n        /* Update our state accordingly to the gossip sections */\n        // 使用消息中的信息对节点进行更新\n        node = clusterLookupNode(g->nodename);\n        // 节点已经存在于当前节点\n        if (node) {\n            /* We already know this node.\n               Handle failure reports, only when the sender is a master. */\n            // 如果 sender 是一个主节点，那么我们需要处理下线报告\n            if (sender && nodeIsMaster(sender) && node != myself) {\n                // 节点处于 FAIL 或者 PFAIL 状态\n                if (flags & (REDIS_NODE_FAIL|REDIS_NODE_PFAIL)) {\n\n                    // 添加 sender 对 node 的下线报告\n                    if (clusterNodeAddFailureReport(node,sender)) {\n                        redisLog(REDIS_VERBOSE,\n                            \"Node %.40s reported node %.40s as not reachable.\",\n                            sender->name, node->name);\n                    }\n\n                    // 尝试将 node 标记为 FAIL\n                    markNodeAsFailingIfNeeded(node);\n\n                // 节点处于正常状态\n                } else {\n\n                    // 如果 sender 曾经发送过对 node 的下线报告\n                    // 那么清除该报告\n                    if (clusterNodeDelFailureReport(node,sender)) {\n                        redisLog(REDIS_VERBOSE,\n                            \"Node %.40s reported node %.40s is back online.\",\n                            sender->name, node->name);\n                    }\n                }\n            }\n\n            /* If we already know this node, but it is not reachable, and\n             * we see a different address in the gossip section, start an\n             * handshake with the (possibly) new address: this will result\n             * into a node address update if the handshake will be\n             * successful. */\n            // 如果节点之前处于 PFAIL 或者 FAIL 状态\n            // 并且该节点的 IP 或者端口号已经发生变化\n            // 那么可能是节点换了新地址，尝试对它进行握手\n            if (node->flags & (REDIS_NODE_FAIL|REDIS_NODE_PFAIL) &&\n                (strcasecmp(node->ip,g->ip) || node->port != ntohs(g->port)))\n            {\n                clusterStartHandshake(g->ip,ntohs(g->port));\n            }\n\n        // 当前节点不认识 node\n        } else {\n            /* If it's not in NOADDR state and we don't have it, we\n             * start a handshake process against this IP/PORT pairs.\n             *\n             * 如果 node 不在 NOADDR 状态，并且当前节点不认识 node \n             * 那么向 node 发送 HANDSHAKE 消息。\n             *\n             * Note that we require that the sender of this gossip message\n             * is a well known node in our cluster, otherwise we risk\n             * joining another cluster.\n             *\n             * 注意，当前节点必须保证 sender 是本集群的节点，\n             * 否则我们将有加入了另一个集群的风险。\n             */\n            if (sender &&\n                !(flags & REDIS_NODE_NOADDR) &&\n                !clusterBlacklistExists(g->nodename))\n            {\n                clusterStartHandshake(g->ip,ntohs(g->port));\n            }\n        }\n\n        /* Next node */\n        // 处理下个节点的信息\n        g++;\n    }\n}\n\n/* IP -> string conversion. 'buf' is supposed to at least be 46 bytes. */\n// 将 ip 转换为字符串\nvoid nodeIp2String(char *buf, clusterLink *link) {\n    anetPeerToString(link->fd, buf, REDIS_IP_STR_LEN, NULL);\n}\n\n/* Update the node address to the IP address that can be extracted\n * from link->fd, and at the specified port.\n *\n * 更新节点的地址， IP 和端口可以从 link->fd 获得。\n *\n * Also disconnect the node link so that we'll connect again to the new\n * address.\n *\n * 并且断开当前的节点连接，并根据新地址创建新连接。\n *\n * If the ip/port pair are already correct no operation is performed at\n * all.\n *\n * 如果 ip 和端口和现在的连接相同，那么不执行任何动作。\n *\n * The function returns 0 if the node address is still the same,\n * otherwise 1 is returned. \n *\n * 函数返回 0 表示地址不变，地址已被更新则返回 1 。\n */\nint nodeUpdateAddressIfNeeded(clusterNode *node, clusterLink *link, int port) {\n    char ip[REDIS_IP_STR_LEN];\n\n    /* We don't proceed if the link is the same as the sender link, as this\n     * function is designed to see if the node link is consistent with the\n     * symmetric link that is used to receive PINGs from the node.\n     *\n     * As a side effect this function never frees the passed 'link', so\n     * it is safe to call during packet processing. */\n    // 连接不变，直接返回\n    if (link == node->link) return 0;\n\n    // 获取字符串格式的 ip 地址\n    nodeIp2String(ip,link);\n    // 获取端口号\n    if (node->port == port && strcmp(ip,node->ip) == 0) return 0;\n\n    /* IP / port is different, update it. */\n    memcpy(node->ip,ip,sizeof(ip));\n    node->port = port;\n\n    // 释放旧连接（新连接会在之后自动创建）\n    if (node->link) freeClusterLink(node->link);\n\n    redisLog(REDIS_WARNING,\"Address updated for node %.40s, now %s:%d\",\n        node->name, node->ip, node->port);\n\n    /* Check if this is our master and we have to change the\n     * replication target as well. */\n    // 如果连接来自当前节点（从节点）的主节点，那么根据新地址设置复制对象\n    if (nodeIsSlave(myself) && myself->slaveof == node)\n        replicationSetMaster(node->ip, node->port);\n    return 1;\n}\n\n/* Reconfigure the specified node 'n' as a master. This function is called when\n * a node that we believed to be a slave is now acting as master in order to\n * update the state of the node. \n *\n * 将节点 n 设置为主节点。\n */\nvoid clusterSetNodeAsMaster(clusterNode *n) {\n\n    // 已经是主节点了。\n    if (nodeIsMaster(n)) return;\n\n    // 移除 slaveof\n    if (n->slaveof) clusterNodeRemoveSlave(n->slaveof,n);\n\n    // 关闭 SLAVE 标识\n    n->flags &= ~REDIS_NODE_SLAVE;\n\n    // 打开 MASTER 标识\n    n->flags |= REDIS_NODE_MASTER;\n\n    // 清零 slaveof 属性\n    n->slaveof = NULL;\n\n    /* Update config and state. */\n    clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG|\n                         CLUSTER_TODO_UPDATE_STATE);\n}\n\n/* This function is called when we receive a master configuration via a\n * PING, PONG or UPDATE packet. What we receive is a node, a configEpoch of the\n * node, and the set of slots claimed under this configEpoch.\n *\n * 这个函数在节点通过 PING 、 PONG 、 UPDATE 消息接收到一个 master 的配置时调用，\n * 函数以一个节点，节点的 configEpoch ，\n * 以及节点在 configEpoch 纪元下的槽配置作为参数。\n *\n * What we do is to rebind the slots with newer configuration compared to our\n * local configuration, and if needed, we turn ourself into a replica of the\n * node (see the function comments for more info).\n *\n * 这个函数要做的就是在 slots 参数的新配置和本节点的当前配置进行对比，\n * 并更新本节点对槽的布局，\n * 如果有需要的话，函数还会将本节点转换为 sender 的从节点，\n * 更多信息请参考函数中的注释。\n *\n * The 'sender' is the node for which we received a configuration update.\n * Sometimes it is not actaully the \"Sender\" of the information, like in the case\n * we receive the info via an UPDATE packet. \n *\n * 根据情况， sender 参数可以是消息的发送者，也可以是消息发送者的主节点。\n */\nvoid clusterUpdateSlotsConfigWith(clusterNode *sender, uint64_t senderConfigEpoch, unsigned char *slots) {\n    int j;\n    clusterNode *curmaster, *newmaster = NULL;\n    /* The dirty slots list is a list of slots for which we lose the ownership\n     * while having still keys inside. This usually happens after a failover\n     * or after a manual cluster reconfiguration operated by the admin.\n     *\n     * If the update message is not able to demote a master to slave (in this\n     * case we'll resync with the master updating the whole key space), we\n     * need to delete all the keys in the slots we lost ownership. */\n    uint16_t dirty_slots[REDIS_CLUSTER_SLOTS];\n    int dirty_slots_count = 0;\n\n    /* Here we set curmaster to this node or the node this node\n     * replicates to if it's a slave. In the for loop we are\n     * interested to check if slots are taken away from curmaster. */\n    // 1）如果当前节点是主节点，那么将 curmaster 设置为当前节点\n    // 2）如果当前节点是从节点，那么将 curmaster 设置为当前节点正在复制的主节点\n    // 稍后在 for 循环中我们将使用 curmaster 检查与当前节点有关的槽是否发生了变动\n    curmaster = nodeIsMaster(myself) ? myself : myself->slaveof;\n\n    if (sender == myself) {\n        redisLog(REDIS_WARNING,\"Discarding UPDATE message about myself.\");\n        return;\n    }\n\n    // 更新槽布局\n    for (j = 0; j < REDIS_CLUSTER_SLOTS; j++) {\n\n        // 如果 slots 中的槽 j 已经被指派，那么执行以下代码\n        if (bitmapTestBit(slots,j)) {\n            /* The slot is already bound to the sender of this message. */\n            if (server.cluster->slots[j] == sender) continue;\n\n            /* The slot is in importing state, it should be modified only\n             * manually via redis-trib (example: a resharding is in progress\n             * and the migrating side slot was already closed and is advertising\n             * a new config. We still want the slot to be closed manually). */\n            if (server.cluster->importing_slots_from[j]) continue;\n\n            /* We rebind the slot to the new node claiming it if:\n             * 1) The slot was unassigned or the new node claims it with a\n             *    greater configEpoch.\n             * 2) We are not currently importing the slot. */\n            if (server.cluster->slots[j] == NULL ||\n                server.cluster->slots[j]->configEpoch < senderConfigEpoch)\n            {\n                /* Was this slot mine, and still contains keys? Mark it as\n                 * a dirty slot. */\n                if (server.cluster->slots[j] == myself &&\n                    countKeysInSlot(j) &&\n                    sender != myself)\n                {\n                    dirty_slots[dirty_slots_count] = j;\n                    dirty_slots_count++;\n                }\n\n                // 负责槽 j 的原节点是当前节点的主节点？\n                // 如果是的话，说明故障转移发生了，将当前节点的复制对象设置为新的主节点\n                if (server.cluster->slots[j] == curmaster)\n                    newmaster = sender;\n\n                // 将槽 j 设为未指派\n                clusterDelSlot(j);\n\n                // 将槽 j 指派给 sender\n                clusterAddSlot(sender,j);\n\n                clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG|\n                                     CLUSTER_TODO_UPDATE_STATE|\n                                     CLUSTER_TODO_FSYNC_CONFIG);\n            }\n        }\n    }\n\n    /* If at least one slot was reassigned from a node to another node\n     * with a greater configEpoch, it is possible that:\n     *\n     * 如果当前节点（或者当前节点的主节点）有至少一个槽被指派到了 sender\n     * 并且 sender 的 configEpoch 比当前节点的纪元要大，\n     * 那么可能发生了：\n     *\n     * 1) We are a master left without slots. This means that we were\n     *    failed over and we should turn into a replica of the new\n     *    master.\n     *    当前节点是一个不再处理任何槽的主节点，\n     *    这时应该将当前节点设置为新主节点的从节点。\n     * 2) We are a slave and our master is left without slots. We need\n     *    to replicate to the new slots owner. \n     *    当前节点是一个从节点，\n     *    并且当前节点的主节点已经不再处理任何槽，\n     *    这时应该将当前节点设置为新主节点的从节点。\n     */\n    if (newmaster && curmaster->numslots == 0) {\n        redisLog(REDIS_WARNING,\n            \"Configuration change detected. Reconfiguring myself \"\n            \"as a replica of %.40s\", sender->name);\n        // 将 sender 设置为当前节点的主节点\n        clusterSetMaster(sender);\n\n        clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG|\n                             CLUSTER_TODO_UPDATE_STATE|\n                             CLUSTER_TODO_FSYNC_CONFIG);\n    } else if (dirty_slots_count) {\n        /* If we are here, we received an update message which removed\n         * ownership for certain slots we still have keys about, but still\n         * we are serving some slots, so this master node was not demoted to\n         * a slave.\n         *\n         * In order to maintain a consistent state between keys and slots\n         * we need to remove all the keys from the slots we lost. */\n        for (j = 0; j < dirty_slots_count; j++)\n            delKeysInSlot(dirty_slots[j]);\n    }\n}\n\n/* This function is called when this node is a master, and we receive from\n * another master a configuration epoch that is equal to our configuration\n * epoch.\n *\n * BACKGROUND\n *\n * It is not possible that different slaves get the same config\n * epoch during a failover election, because the slaves need to get voted\n * by a majority. However when we perform a manual resharding of the cluster\n * the node will assign a configuration epoch to itself without to ask\n * for agreement. Usually resharding happens when the cluster is working well\n * and is supervised by the sysadmin, however it is possible for a failover\n * to happen exactly while the node we are resharding a slot to assigns itself\n * a new configuration epoch, but before it is able to propagate it.\n *\n * So technically it is possible in this condition that two nodes end with\n * the same configuration epoch.\n *\n * Another possibility is that there are bugs in the implementation causing\n * this to happen.\n *\n * Moreover when a new cluster is created, all the nodes start with the same\n * configEpoch. This collision resolution code allows nodes to automatically\n * end with a different configEpoch at startup automatically.\n *\n * In all the cases, we want a mechanism that resolves this issue automatically\n * as a safeguard. The same configuration epoch for masters serving different\n * set of slots is not harmful, but it is if the nodes end serving the same\n * slots for some reason (manual errors or software bugs) without a proper\n * failover procedure.\n *\n * In general we want a system that eventually always ends with different\n * masters having different configuration epochs whatever happened, since\n * nothign is worse than a split-brain condition in a distributed system.\n *\n * BEHAVIOR\n *\n * When this function gets called, what happens is that if this node\n * has the lexicographically smaller Node ID compared to the other node\n * with the conflicting epoch (the 'sender' node), it will assign itself\n * the greatest configuration epoch currently detected among nodes plus 1.\n *\n * This means that even if there are multiple nodes colliding, the node\n * with the greatest Node ID never moves forward, so eventually all the nodes\n * end with a different configuration epoch.\n */\nvoid clusterHandleConfigEpochCollision(clusterNode *sender) {\n    /* Prerequisites: nodes have the same configEpoch and are both masters. */\n    if (sender->configEpoch != myself->configEpoch ||\n        !nodeIsMaster(sender) || !nodeIsMaster(myself)) return;\n    /* Don't act if the colliding node has a smaller Node ID. */\n    if (memcmp(sender->name,myself->name,REDIS_CLUSTER_NAMELEN) <= 0) return;\n    /* Get the next ID available at the best of this node knowledge. */\n    server.cluster->currentEpoch++;\n    myself->configEpoch = server.cluster->currentEpoch;\n    clusterSaveConfigOrDie(1);\n    redisLog(REDIS_VERBOSE,\n        \"WARNING: configEpoch collision with node %.40s.\"\n        \" Updating my configEpoch to %llu\",\n        sender->name,\n        (unsigned long long) myself->configEpoch);\n}\n\n/* When this function is called, there is a packet to process starting\n * at node->rcvbuf. Releasing the buffer is up to the caller, so this\n * function should just handle the higher level stuff of processing the\n * packet, modifying the cluster state if needed.\n *\n * 当这个函数被调用时，说明 node->rcvbuf 中有一条待处理的信息。\n * 信息处理完毕之后的释放工作由调用者处理，所以这个函数只需负责处理信息就可以了。\n *\n * The function returns 1 if the link is still valid after the packet\n * was processed, otherwise 0 if the link was freed since the packet\n * processing lead to some inconsistency error (for instance a PONG\n * received from the wrong sender ID). \n *\n * 如果函数返回 1 ，那么说明处理信息时没有遇到问题，连接依然可用。\n * 如果函数返回 0 ，那么说明信息处理时遇到了不一致问题\n * （比如接收到的 PONG 是发送自不正确的发送者 ID 的），连接已经被释放。\n */\nint clusterProcessPacket(clusterLink *link) {\n\n    // 指向消息头\n    clusterMsg *hdr = (clusterMsg*) link->rcvbuf;\n\n    // 消息的长度\n    uint32_t totlen = ntohl(hdr->totlen);\n\n    // 消息的类型\n    uint16_t type = ntohs(hdr->type);\n\n    // 消息发送者的标识\n    uint16_t flags = ntohs(hdr->flags);\n\n    uint64_t senderCurrentEpoch = 0, senderConfigEpoch = 0;\n\n    clusterNode *sender;\n\n    // 更新接受消息计数器\n    server.cluster->stats_bus_messages_received++;\n\n    redisLog(REDIS_DEBUG,\"--- Processing packet of type %d, %lu bytes\",\n        type, (unsigned long) totlen);\n\n    /* Perform sanity checks */\n    // 合法性检查\n    if (totlen < 16) return 1; /* At least signature, version, totlen, count. */\n    if (ntohs(hdr->ver) != 0) return 1; /* Can't handle versions other than 0.*/\n    if (totlen > sdslen(link->rcvbuf)) return 1;\n    if (type == CLUSTERMSG_TYPE_PING || type == CLUSTERMSG_TYPE_PONG ||\n        type == CLUSTERMSG_TYPE_MEET)\n    {\n        uint16_t count = ntohs(hdr->count);\n        uint32_t explen; /* expected length of this packet */\n\n        explen = sizeof(clusterMsg)-sizeof(union clusterMsgData);\n        explen += (sizeof(clusterMsgDataGossip)*count);\n        if (totlen != explen) return 1;\n    } else if (type == CLUSTERMSG_TYPE_FAIL) {\n        uint32_t explen = sizeof(clusterMsg)-sizeof(union clusterMsgData);\n\n        explen += sizeof(clusterMsgDataFail);\n        if (totlen != explen) return 1;\n    } else if (type == CLUSTERMSG_TYPE_PUBLISH) {\n        uint32_t explen = sizeof(clusterMsg)-sizeof(union clusterMsgData);\n\n        explen += sizeof(clusterMsgDataPublish) +\n                ntohl(hdr->data.publish.msg.channel_len) +\n                ntohl(hdr->data.publish.msg.message_len);\n        if (totlen != explen) return 1;\n    } else if (type == CLUSTERMSG_TYPE_FAILOVER_AUTH_REQUEST ||\n               type == CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK ||\n               type == CLUSTERMSG_TYPE_MFSTART)\n    {\n        uint32_t explen = sizeof(clusterMsg)-sizeof(union clusterMsgData);\n\n        if (totlen != explen) return 1;\n    } else if (type == CLUSTERMSG_TYPE_UPDATE) {\n        uint32_t explen = sizeof(clusterMsg)-sizeof(union clusterMsgData);\n\n        explen += sizeof(clusterMsgDataUpdate);\n        if (totlen != explen) return 1;\n    }\n\n    /* Check if the sender is a known node. */\n    // 查找发送者节点\n    sender = clusterLookupNode(hdr->sender);\n    // 节点存在，并且不是 HANDSHAKE 节点\n    // 那么个更新节点的配置纪元信息\n    if (sender && !nodeInHandshake(sender)) {\n        /* Update our curretEpoch if we see a newer epoch in the cluster. */\n        senderCurrentEpoch = ntohu64(hdr->currentEpoch);\n        senderConfigEpoch = ntohu64(hdr->configEpoch);\n        if (senderCurrentEpoch > server.cluster->currentEpoch)\n            server.cluster->currentEpoch = senderCurrentEpoch;\n        /* Update the sender configEpoch if it is publishing a newer one. */\n        if (senderConfigEpoch > sender->configEpoch) {\n            sender->configEpoch = senderConfigEpoch;\n            clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG|\n                                 CLUSTER_TODO_FSYNC_CONFIG);\n        }\n        /* Update the replication offset info for this node. */\n        sender->repl_offset = ntohu64(hdr->offset);\n        sender->repl_offset_time = mstime();\n        /* If we are a slave performing a manual failover and our master\n         * sent its offset while already paused, populate the MF state. */\n        if (server.cluster->mf_end &&\n            nodeIsSlave(myself) &&\n            myself->slaveof == sender &&\n            hdr->mflags[0] & CLUSTERMSG_FLAG0_PAUSED &&\n            server.cluster->mf_master_offset == 0)\n        {\n            server.cluster->mf_master_offset = sender->repl_offset;\n            redisLog(REDIS_WARNING,\n                \"Received replication offset for paused \"\n                \"master manual failover: %lld\",\n                server.cluster->mf_master_offset);\n        }\n    }\n\n    /* Process packets by type. */\n    // 根据消息的类型，处理节点\n\n    // 这是一条 PING 消息或者 MEET 消息\n    if (type == CLUSTERMSG_TYPE_PING || type == CLUSTERMSG_TYPE_MEET) {\n        redisLog(REDIS_DEBUG,\"Ping packet received: %p\", (void*)link->node);\n\n        /* Add this node if it is new for us and the msg type is MEET.\n         *\n         * 如果当前节点是第一次遇见这个节点，并且对方发来的是 MEET 信息，\n         * 那么将这个节点添加到集群的节点列表里面。\n         *\n         * In this stage we don't try to add the node with the right\n         * flags, slaveof pointer, and so forth, as this details will be\n         * resolved when we'll receive PONGs from the node. \n         *\n         * 节点目前的 flag 、 slaveof 等属性的值都是未设置的，\n         * 等当前节点向对方发送 PING 命令之后，\n         * 这些信息可以从对方回复的 PONG 信息中取得。\n         */\n        if (!sender && type == CLUSTERMSG_TYPE_MEET) {\n            clusterNode *node;\n\n            // 创建 HANDSHAKE 状态的新节点\n            node = createClusterNode(NULL,REDIS_NODE_HANDSHAKE);\n\n            // 设置 IP 和端口\n            nodeIp2String(node->ip,link);\n            node->port = ntohs(hdr->port);\n\n            // 将新节点添加到集群\n            clusterAddNode(node);\n\n            clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG);\n        }\n\n        /* Get info from the gossip section */\n        // 分析并取出消息中的 gossip 节点信息\n        clusterProcessGossipSection(hdr,link);\n\n        /* Anyway reply with a PONG */\n        // 向目标节点返回一个 PONG\n        clusterSendPing(link,CLUSTERMSG_TYPE_PONG);\n    }\n\n    /* PING or PONG: process config information. */\n    // 这是一条 PING 、 PONG 或者 MEET 消息\n    if (type == CLUSTERMSG_TYPE_PING || type == CLUSTERMSG_TYPE_PONG ||\n        type == CLUSTERMSG_TYPE_MEET)\n    {\n        redisLog(REDIS_DEBUG,\"%s packet received: %p\",\n            type == CLUSTERMSG_TYPE_PING ? \"ping\" : \"pong\",\n            (void*)link->node);\n\n        // 连接的 clusterNode 结构存在\n        if (link->node) {\n            // 节点处于 HANDSHAKE 状态\n            if (nodeInHandshake(link->node)) {\n                /* If we already have this node, try to change the\n                 * IP/port of the node with the new one. */\n                if (sender) {\n                    redisLog(REDIS_VERBOSE,\n                        \"Handshake: we already know node %.40s, \"\n                        \"updating the address if needed.\", sender->name);\n                    // 如果有需要的话，更新节点的地址\n                    if (nodeUpdateAddressIfNeeded(sender,link,ntohs(hdr->port)))\n                    {\n                        clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG|\n                                             CLUSTER_TODO_UPDATE_STATE);\n                    }\n                    /* Free this node as we alrady have it. This will\n                     * cause the link to be freed as well. */\n                    // 释放节点\n                    freeClusterNode(link->node);\n                    return 0;\n                }\n\n                /* First thing to do is replacing the random name with the\n                 * right node name if this was a handshake stage. */\n                // 用节点的真名替换在 HANDSHAKE 时创建的随机名字\n                clusterRenameNode(link->node, hdr->sender);\n                redisLog(REDIS_DEBUG,\"Handshake with node %.40s completed.\",\n                    link->node->name);\n\n                // 关闭 HANDSHAKE 状态\n                link->node->flags &= ~REDIS_NODE_HANDSHAKE;\n\n                // 设置节点的角色\n                link->node->flags |= flags&(REDIS_NODE_MASTER|REDIS_NODE_SLAVE);\n\n                clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG);\n\n            // 节点已存在，但它的 id 和当前节点保存的 id 不同\n            } else if (memcmp(link->node->name,hdr->sender,\n                        REDIS_CLUSTER_NAMELEN) != 0)\n            {\n                /* If the reply has a non matching node ID we\n                 * disconnect this node and set it as not having an associated\n                 * address. */\n                // 那么将这个节点设为 NOADDR \n                // 并断开连接\n                redisLog(REDIS_DEBUG,\"PONG contains mismatching sender ID\");\n                link->node->flags |= REDIS_NODE_NOADDR;\n                link->node->ip[0] = '\\0';\n                link->node->port = 0;\n\n                // 断开连接\n                freeClusterLink(link);\n\n                clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG);\n                return 0;\n            }\n        }\n\n        /* Update the node address if it changed. */\n        // 如果发送的消息为 PING \n        // 并且发送者不在 HANDSHAKE 状态\n        // 那么更新发送者的信息\n        if (sender && type == CLUSTERMSG_TYPE_PING &&\n            !nodeInHandshake(sender) &&\n            nodeUpdateAddressIfNeeded(sender,link,ntohs(hdr->port)))\n        {\n            clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG|\n                                 CLUSTER_TODO_UPDATE_STATE);\n        }\n\n        /* Update our info about the node */\n        // 如果这是一条 PONG 消息，那么更新我们关于 node 节点的认识\n        if (link->node && type == CLUSTERMSG_TYPE_PONG) {\n\n            // 最后一次接到该节点的 PONG 的时间\n            link->node->pong_received = mstime();\n\n            // 清零最近一次等待 PING 命令的时间\n            link->node->ping_sent = 0;\n\n            /* The PFAIL condition can be reversed without external\n             * help if it is momentary (that is, if it does not\n             * turn into a FAIL state).\n             *\n             * 接到节点的 PONG 回复，我们可以移除节点的 PFAIL 状态。\n             *\n             * The FAIL condition is also reversible under specific\n             * conditions detected by clearNodeFailureIfNeeded(). \n             *\n             * 如果节点的状态为 FAIL ，\n             * 那么是否撤销该状态要根据 clearNodeFailureIfNeeded() 函数来决定。\n             */\n            if (nodeTimedOut(link->node)) {\n                // 撤销 PFAIL\n                link->node->flags &= ~REDIS_NODE_PFAIL;\n\n                clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG|\n                                     CLUSTER_TODO_UPDATE_STATE);\n            } else if (nodeFailed(link->node)) {\n                // 看是否可以撤销 FAIL\n                clearNodeFailureIfNeeded(link->node);\n            }\n        }\n\n        /* Check for role switch: slave -> master or master -> slave. */\n        // 检测节点的身份信息，并在需要时进行更新\n        if (sender) {\n\n            // 发送消息的节点的 slaveof 为 REDIS_NODE_NULL_NAME\n            // 那么 sender 就是一个主节点\n            if (!memcmp(hdr->slaveof,REDIS_NODE_NULL_NAME,\n                sizeof(hdr->slaveof)))\n            {\n                /* Node is a master. */\n                // 设置 sender 为主节点\n                clusterSetNodeAsMaster(sender);\n\n            // sender 的 slaveof 不为空，那么这是一个从节点\n            } else {\n\n                /* Node is a slave. */\n                // 取出 sender 的主节点\n                clusterNode *master = clusterLookupNode(hdr->slaveof);\n\n                // sender 由主节点变成了从节点，重新配置 sender\n                if (nodeIsMaster(sender)) {\n                    /* Master turned into a slave! Reconfigure the node. */\n\n                    // 删除所有由该节点负责的槽\n                    clusterDelNodeSlots(sender);\n\n                    // 更新标识\n                    sender->flags &= ~REDIS_NODE_MASTER;\n                    sender->flags |= REDIS_NODE_SLAVE;\n\n                    /* Remove the list of slaves from the node. */\n                    // 移除 sender 的从节点名单\n                    if (sender->numslaves) clusterNodeResetSlaves(sender);\n\n                    /* Update config and state. */\n                    clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG|\n                                         CLUSTER_TODO_UPDATE_STATE);\n                }\n\n                /* Master node changed for this slave? */\n\n                // 检查 sender 的主节点是否变更\n                if (master && sender->slaveof != master) {\n                    // 如果 sender 之前的主节点不是现在的主节点\n                    // 那么在旧主节点的从节点列表中移除 sender\n                    if (sender->slaveof)\n                        clusterNodeRemoveSlave(sender->slaveof,sender);\n\n                    // 并在新主节点的从节点列表中添加 sender\n                    clusterNodeAddSlave(master,sender);\n\n                    // 更新 sender 的主节点\n                    sender->slaveof = master;\n\n                    /* Update config. */\n                    clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG);\n                }\n            }\n        }\n\n        /* Update our info about served slots.\n         *\n         * 更新当前节点对 sender 所处理槽的认识。\n         *\n         * Note: this MUST happen after we update the master/slave state\n         * so that REDIS_NODE_MASTER flag will be set. \n         *\n         * 这部分的更新 *必须* 在更新 sender 的主/从节点信息之后，\n         * 因为这里需要用到 REDIS_NODE_MASTER 标识。\n         */\n\n        /* Many checks are only needed if the set of served slots this\n         * instance claims is different compared to the set of slots we have\n         * for it. Check this ASAP to avoid other computational expansive\n         * checks later. */\n        clusterNode *sender_master = NULL; /* Sender or its master if slave. */\n        int dirty_slots = 0; /* Sender claimed slots don't match my view? */\n\n        if (sender) {\n            sender_master = nodeIsMaster(sender) ? sender : sender->slaveof;\n            if (sender_master) {\n                dirty_slots = memcmp(sender_master->slots,\n                        hdr->myslots,sizeof(hdr->myslots)) != 0;\n            }\n        }\n\n        /* 1) If the sender of the message is a master, and we detected that\n         *    the set of slots it claims changed, scan the slots to see if we\n         *    need to update our configuration. */\n        // 如果 sender 是主节点，并且 sender 的槽布局出现了变动\n        // 那么检查当前节点对 sender 的槽布局设置，看是否需要进行更新\n        if (sender && nodeIsMaster(sender) && dirty_slots)\n            clusterUpdateSlotsConfigWith(sender,senderConfigEpoch,hdr->myslots);\n\n        /* 2) We also check for the reverse condition, that is, the sender\n         *    claims to serve slots we know are served by a master with a\n         *    greater configEpoch. If this happens we inform the sender.\n         *\n         *    检测和条件 1 的相反条件，也即是，\n         *    sender 处理的槽的配置纪元比当前节点已知的某个节点的配置纪元要低，\n         *    如果是这样的话，通知 sender 。\n         *\n         * This is useful because sometimes after a partition heals, a\n         * reappearing master may be the last one to claim a given set of\n         * hash slots, but with a configuration that other instances know to\n         * be deprecated. Example:\n         *\n         * 这种情况可能会出现在网络分裂中，\n         * 一个重新上线的主节点可能会带有已经过时的槽布局。\n         *\n         * 比如说：\n         *\n         * A and B are master and slave for slots 1,2,3.\n         * A 负责槽 1 、 2 、 3 ，而 B 是 A 的从节点。\n         *\n         * A is partitioned away, B gets promoted.\n         * A 从网络中分裂出去，B 被提升为主节点。\n         *\n         * B is partitioned away, and A returns available.\n         * B 从网络中分裂出去， A 重新上线（但是它所使用的槽布局是旧的）。\n         *\n         * Usually B would PING A publishing its set of served slots and its\n         * configEpoch, but because of the partition B can't inform A of the\n         * new configuration, so other nodes that have an updated table must\n         * do it. In this way A will stop to act as a master (or can try to\n         * failover if there are the conditions to win the election).\n         *\n         * 在正常情况下， B 应该向 A 发送 PING 消息，告知 A ，自己（B）已经接替了\n         * 槽 1、 2、 3 ，并且带有更更的配置纪元，但因为网络分裂的缘故，\n         * 节点 B 没办法通知节点 A ，\n         * 所以通知节点 A 它带有的槽布局已经更新的工作就交给其他知道 B 带有更高配置纪元的节点来做。\n         * 当 A 接到其他节点关于节点 B 的消息时，\n         * 节点 A 就会停止自己的主节点工作，又或者重新进行故障转移。\n         */\n        if (sender && dirty_slots) {\n            int j;\n\n            for (j = 0; j < REDIS_CLUSTER_SLOTS; j++) {\n\n                // 检测 slots 中的槽 j 是否已经被指派\n                if (bitmapTestBit(hdr->myslots,j)) {\n\n                    // 当前节点认为槽 j 由 sender 负责处理，\n                    // 或者当前节点认为该槽未指派，那么跳过该槽\n                    if (server.cluster->slots[j] == sender ||\n                        server.cluster->slots[j] == NULL) continue;\n\n                    // 当前节点槽 j 的配置纪元比 sender 的配置纪元要大\n                    if (server.cluster->slots[j]->configEpoch >\n                        senderConfigEpoch)\n                    {\n                        redisLog(REDIS_VERBOSE,\n                            \"Node %.40s has old slots configuration, sending \"\n                            \"an UPDATE message about %.40s\",\n                                sender->name, server.cluster->slots[j]->name);\n\n                        // 向 sender 发送关于槽 j 的更新信息\n                        clusterSendUpdate(sender->link,\n                            server.cluster->slots[j]);\n\n                        /* TODO: instead of exiting the loop send every other\n                         * UPDATE packet for other nodes that are the new owner\n                         * of sender's slots. */\n                        break;\n                    }\n                }\n            }\n        }\n\n        /* If our config epoch collides with the sender's try to fix\n         * the problem. */\n        if (sender &&\n            nodeIsMaster(myself) && nodeIsMaster(sender) &&\n            senderConfigEpoch == myself->configEpoch)\n        {\n            clusterHandleConfigEpochCollision(sender);\n        }\n\n        /* Get info from the gossip section */\n        // 分析并提取出消息 gossip 协议部分的信息\n        clusterProcessGossipSection(hdr,link);\n\n    // 这是一条 FAIL 消息： sender 告知当前节点，某个节点已经进入 FAIL 状态。\n    } else if (type == CLUSTERMSG_TYPE_FAIL) {\n        clusterNode *failing;\n\n        if (sender) {\n\n            // 获取下线节点的消息\n            failing = clusterLookupNode(hdr->data.fail.about.nodename);\n            // 下线的节点既不是当前节点，也没有处于 FAIL 状态\n            if (failing &&\n                !(failing->flags & (REDIS_NODE_FAIL|REDIS_NODE_MYSELF)))\n            {\n                redisLog(REDIS_NOTICE,\n                    \"FAIL message received from %.40s about %.40s\",\n                    hdr->sender, hdr->data.fail.about.nodename);\n\n                // 打开 FAIL 状态\n                failing->flags |= REDIS_NODE_FAIL;\n                failing->fail_time = mstime();\n                // 关闭 PFAIL 状态\n                failing->flags &= ~REDIS_NODE_PFAIL;\n                clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG|\n                                     CLUSTER_TODO_UPDATE_STATE);\n            }\n        } else {\n            redisLog(REDIS_NOTICE,\n                \"Ignoring FAIL message from unknonw node %.40s about %.40s\",\n                hdr->sender, hdr->data.fail.about.nodename);\n        }\n\n    // 这是一条 PUBLISH 消息\n    } else if (type == CLUSTERMSG_TYPE_PUBLISH) {\n        robj *channel, *message;\n        uint32_t channel_len, message_len;\n\n        /* Don't bother creating useless objects if there are no\n         * Pub/Sub subscribers. */\n        // 只在有订阅者时创建消息对象\n        if (dictSize(server.pubsub_channels) ||\n           listLength(server.pubsub_patterns))\n        {\n            // 频道长度\n            channel_len = ntohl(hdr->data.publish.msg.channel_len);\n\n            // 消息长度\n            message_len = ntohl(hdr->data.publish.msg.message_len);\n\n            // 频道\n            channel = createStringObject(\n                        (char*)hdr->data.publish.msg.bulk_data,channel_len);\n\n            // 消息\n            message = createStringObject(\n                        (char*)hdr->data.publish.msg.bulk_data+channel_len,\n                        message_len);\n            // 发送消息\n            pubsubPublishMessage(channel,message);\n\n            decrRefCount(channel);\n            decrRefCount(message);\n        }\n\n    // 这是一条请求获得故障迁移授权的消息： sender 请求当前节点为它进行故障转移投票\n    } else if (type == CLUSTERMSG_TYPE_FAILOVER_AUTH_REQUEST) {\n        if (!sender) return 1;  /* We don't know that node. */\n        // 如果条件允许的话，向 sender 投票，支持它进行故障转移\n        clusterSendFailoverAuthIfNeeded(sender,hdr);\n\n    // 这是一条故障迁移投票信息： sender 支持当前节点执行故障转移操作\n    } else if (type == CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK) {\n        if (!sender) return 1;  /* We don't know that node. */\n\n        /* We consider this vote only if the sender is a master serving\n         * a non zero number of slots, and its currentEpoch is greater or\n         * equal to epoch where this node started the election. */\n        // 只有正在处理至少一个槽的主节点的投票会被视为是有效投票\n        // 只有符合以下条件， sender 的投票才算有效：\n        // 1） sender 是主节点\n        // 2） sender 正在处理至少一个槽\n        // 3） sender 的配置纪元大于等于当前节点的配置纪元\n        if (nodeIsMaster(sender) && sender->numslots > 0 &&\n            senderCurrentEpoch >= server.cluster->failover_auth_epoch)\n        {\n            // 增加支持票数\n            server.cluster->failover_auth_count++;\n\n            /* Maybe we reached a quorum here, set a flag to make sure\n             * we check ASAP. */\n            clusterDoBeforeSleep(CLUSTER_TODO_HANDLE_FAILOVER);\n        }\n\n    } else if (type == CLUSTERMSG_TYPE_MFSTART) {\n        /* This message is acceptable only if I'm a master and the sender\n         * is one of my slaves. */\n        if (!sender || sender->slaveof != myself) return 1;\n        /* Manual failover requested from slaves. Initialize the state\n         * accordingly. */\n        resetManualFailover();\n        server.cluster->mf_end = mstime() + REDIS_CLUSTER_MF_TIMEOUT;\n        server.cluster->mf_slave = sender;\n        pauseClients(mstime()+(REDIS_CLUSTER_MF_TIMEOUT*2));\n        redisLog(REDIS_WARNING,\"Manual failover requested by slave %.40s.\",\n            sender->name);\n    } else if (type == CLUSTERMSG_TYPE_UPDATE) {\n        clusterNode *n; /* The node the update is about. */\n        uint64_t reportedConfigEpoch =\n                    ntohu64(hdr->data.update.nodecfg.configEpoch);\n\n        if (!sender) return 1;  /* We don't know the sender. */\n\n        // 获取需要更新的节点\n        n = clusterLookupNode(hdr->data.update.nodecfg.nodename);\n        if (!n) return 1;   /* We don't know the reported node. */\n\n        // 消息的纪元并不大于节点 n 所处的配置纪元\n        // 无须更新\n        if (n->configEpoch >= reportedConfigEpoch) return 1; /* Nothing new. */\n\n        /* If in our current config the node is a slave, set it as a master. */\n        // 如果节点 n 为从节点，但它的槽配置更新了\n        // 那么说明这个节点已经变为主节点，将它设置为主节点\n        if (nodeIsSlave(n)) clusterSetNodeAsMaster(n);\n\n        /* Update the node's configEpoch. */\n        n->configEpoch = reportedConfigEpoch;\n        clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG|\n                             CLUSTER_TODO_FSYNC_CONFIG);\n\n        /* Check the bitmap of served slots and udpate our\n         * config accordingly. */\n        // 将消息中对 n 的槽布局与当前节点对 n 的槽布局进行对比\n        // 在有需要时更新当前节点对 n 的槽布局的认识\n        clusterUpdateSlotsConfigWith(n,reportedConfigEpoch,\n            hdr->data.update.nodecfg.slots);\n    } else {\n        redisLog(REDIS_WARNING,\"Received unknown packet type: %d\", type);\n    }\n    return 1;\n}\n\n/* This function is called when we detect the link with this node is lost.\n\n   这个函数在发现节点的连接已经丢失时使用。\n\n   We set the node as no longer connected. The Cluster Cron will detect\n   this connection and will try to get it connected again.\n\n   我们将节点的状态设置为断开状态，Cluster Cron 会根据该状态尝试重新连接节点。\n\n   Instead if the node is a temporary node used to accept a query, we\n   completely free the node on error. \n\n   如果连接是一个临时连接的话，那么它就会被永久释放，不再进行重连。\n\n   */\nvoid handleLinkIOError(clusterLink *link) {\n    freeClusterLink(link);\n}\n\n/* Send data. This is handled using a trivial send buffer that gets\n * consumed by write(). We don't try to optimize this for speed too much\n * as this is a very low traffic channel. \n *\n * 写事件处理器，用于向集群节点发送信息。\n */\nvoid clusterWriteHandler(aeEventLoop *el, int fd, void *privdata, int mask) {\n    clusterLink *link = (clusterLink*) privdata;\n    ssize_t nwritten;\n    REDIS_NOTUSED(el);\n    REDIS_NOTUSED(mask);\n\n    // 写入信息\n    nwritten = write(fd, link->sndbuf, sdslen(link->sndbuf));\n\n    // 写入错误\n    if (nwritten <= 0) {\n        redisLog(REDIS_DEBUG,\"I/O error writing to node link: %s\",\n            strerror(errno));\n        handleLinkIOError(link);\n        return;\n    }\n\n    // 删除已写入的部分\n    sdsrange(link->sndbuf,nwritten,-1);\n\n    // 如果所有当前节点输出缓冲区里面的所有内容都已经写入完毕\n    // （缓冲区为空）\n    // 那么删除写事件处理器\n    if (sdslen(link->sndbuf) == 0)\n        aeDeleteFileEvent(server.el, link->fd, AE_WRITABLE);\n}\n\n/* Read data. Try to read the first field of the header first to check the\n * full length of the packet. When a whole packet is in memory this function\n * will call the function to process the packet. And so forth. */\n// 读事件处理器\n// 首先读入内容的头，以判断读入内容的长度\n// 如果内容是一个 whole packet ，那么调用函数来处理这个 packet 。\nvoid clusterReadHandler(aeEventLoop *el, int fd, void *privdata, int mask) {\n    char buf[sizeof(clusterMsg)];\n    ssize_t nread;\n    clusterMsg *hdr;\n    clusterLink *link = (clusterLink*) privdata;\n    int readlen, rcvbuflen;\n    REDIS_NOTUSED(el);\n    REDIS_NOTUSED(mask);\n\n    // 尽可能地多读数据\n    while(1) { /* Read as long as there is data to read. */\n\n        // 检查输入缓冲区的长度\n        rcvbuflen = sdslen(link->rcvbuf);\n        // 头信息（8 字节）未读入完\n        if (rcvbuflen < 8) {\n            /* First, obtain the first 8 bytes to get the full message\n             * length. */\n            readlen = 8 - rcvbuflen;\n        // 已读入完整的信息\n        } else {\n            /* Finally read the full message. */\n            hdr = (clusterMsg*) link->rcvbuf;\n            if (rcvbuflen == 8) {\n                /* Perform some sanity check on the message signature\n                 * and length. */\n                if (memcmp(hdr->sig,\"RCmb\",4) != 0 ||\n                    ntohl(hdr->totlen) < CLUSTERMSG_MIN_LEN)\n                {\n                    redisLog(REDIS_WARNING,\n                        \"Bad message length or signature received \"\n                        \"from Cluster bus.\");\n                    handleLinkIOError(link);\n                    return;\n                }\n            }\n            // 记录已读入内容长度\n            readlen = ntohl(hdr->totlen) - rcvbuflen;\n            if (readlen > sizeof(buf)) readlen = sizeof(buf);\n        }\n\n        // 读入内容\n        nread = read(fd,buf,readlen);\n\n        // 没有内容可读\n        if (nread == -1 && errno == EAGAIN) return; /* No more data ready. */\n\n        // 处理读入错误\n        if (nread <= 0) {\n            /* I/O error... */\n            redisLog(REDIS_DEBUG,\"I/O error reading from node link: %s\",\n                (nread == 0) ? \"connection closed\" : strerror(errno));\n            handleLinkIOError(link);\n            return;\n        } else {\n            /* Read data and recast the pointer to the new buffer. */\n            // 将读入的内容追加进输入缓冲区里面\n            link->rcvbuf = sdscatlen(link->rcvbuf,buf,nread);\n            hdr = (clusterMsg*) link->rcvbuf;\n            rcvbuflen += nread;\n        }\n\n        /* Total length obtained? Process this packet. */\n        // 检查已读入内容的长度，看是否整条信息已经被读入了\n        if (rcvbuflen >= 8 && rcvbuflen == ntohl(hdr->totlen)) {\n            // 如果是的话，执行处理信息的函数\n            if (clusterProcessPacket(link)) {\n                sdsfree(link->rcvbuf);\n                link->rcvbuf = sdsempty();\n            } else {\n                return; /* Link no longer valid. */\n            }\n        }\n    }\n}\n\n/* Put stuff into the send buffer.\n *\n * 发送信息\n *\n * It is guaranteed that this function will never have as a side effect\n * the link to be invalidated, so it is safe to call this function\n * from event handlers that will do stuff with the same link later. \n *\n * 因为发送不会对连接本身造成不良的副作用，\n * 所以可以在发送信息的处理器上做一些针对连接本身的动作。\n */\nvoid clusterSendMessage(clusterLink *link, unsigned char *msg, size_t msglen) {\n\n    // 安装写事件处理器\n    if (sdslen(link->sndbuf) == 0 && msglen != 0)\n        aeCreateFileEvent(server.el,link->fd,AE_WRITABLE,\n                    clusterWriteHandler,link);\n\n    // 将信息追加到输出缓冲区\n    link->sndbuf = sdscatlen(link->sndbuf, msg, msglen);\n\n    // 增一发送信息计数\n    server.cluster->stats_bus_messages_sent++;\n}\n\n/* Send a message to all the nodes that are part of the cluster having\n * a connected link.\n *\n * 向节点连接的所有其他节点发送信息。\n *\n * It is guaranteed that this function will never have as a side effect\n * some node->link to be invalidated, so it is safe to call this function\n * from event handlers that will do stuff with node links later. */\nvoid clusterBroadcastMessage(void *buf, size_t len) {\n    dictIterator *di;\n    dictEntry *de;\n\n    // 遍历所有已知节点\n    di = dictGetSafeIterator(server.cluster->nodes);\n    while((de = dictNext(di)) != NULL) {\n        clusterNode *node = dictGetVal(de);\n\n        // 不向未连接节点发送信息\n        if (!node->link) continue;\n\n        // 不向节点自身或者 HANDSHAKE 状态的节点发送信息\n        if (node->flags & (REDIS_NODE_MYSELF|REDIS_NODE_HANDSHAKE))\n            continue;\n\n        // 发送信息\n        clusterSendMessage(node->link,buf,len);\n    }\n    dictReleaseIterator(di);\n}\n\n/* Build the message header */\n// 构建信息\nvoid clusterBuildMessageHdr(clusterMsg *hdr, int type) {\n    int totlen = 0;\n    uint64_t offset;\n    clusterNode *master;\n\n    /* If this node is a master, we send its slots bitmap and configEpoch.\n     *\n     * 如果这是一个主节点，那么发送该节点的槽 bitmap 和配置纪元。\n     *\n     * If this node is a slave we send the master's information instead (the\n     * node is flagged as slave so the receiver knows that it is NOT really\n     * in charge for this slots.\n     * 如果这是一个从节点，\n     * 那么发送这个节点的主节点的槽 bitmap 和配置纪元。\n     *\n     * 因为接收信息的节点通过标识可以知道这个节点是一个从节点，\n     * 所以接收信息的节点不会将从节点错认作是主节点。\n     */\n    master = (nodeIsSlave(myself) && myself->slaveof) ?\n              myself->slaveof : myself;\n\n    // 清零信息头\n    memset(hdr,0,sizeof(*hdr));\n\n    hdr->sig[0] = 'R';\n    hdr->sig[1] = 'C';\n    hdr->sig[2] = 'm';\n    hdr->sig[3] = 'b';\n\n    // 设置信息类型\n    hdr->type = htons(type);\n\n    // 设置信息发送者\n    memcpy(hdr->sender,myself->name,REDIS_CLUSTER_NAMELEN);\n\n    // 设置当前节点负责的槽\n    memcpy(hdr->myslots,master->slots,sizeof(hdr->myslots));\n\n    // 清零 slaveof 域\n    memset(hdr->slaveof,0,REDIS_CLUSTER_NAMELEN);\n\n    // 如果节点是从节点的话，那么设置 slaveof 域\n    if (myself->slaveof != NULL)\n        memcpy(hdr->slaveof,myself->slaveof->name, REDIS_CLUSTER_NAMELEN);\n\n    // 设置端口号\n    hdr->port = htons(server.port);\n\n    // 设置标识\n    hdr->flags = htons(myself->flags);\n\n    // 设置状态\n    hdr->state = server.cluster->state;\n\n    /* Set the currentEpoch and configEpochs. */\n    // 设置集群当前配置纪元\n    hdr->currentEpoch = htonu64(server.cluster->currentEpoch);\n    // 设置主节点当前配置纪元\n    hdr->configEpoch = htonu64(master->configEpoch);\n\n    /* Set the replication offset. */\n    // 设置复制偏移量\n    if (nodeIsSlave(myself))\n        offset = replicationGetSlaveOffset();\n    else\n        offset = server.master_repl_offset;\n    hdr->offset = htonu64(offset);\n\n    /* Set the message flags. */\n    if (nodeIsMaster(myself) && server.cluster->mf_end)\n        hdr->mflags[0] |= CLUSTERMSG_FLAG0_PAUSED;\n\n    /* Compute the message length for certain messages. For other messages\n     * this is up to the caller. */\n    // 计算信息的长度\n    if (type == CLUSTERMSG_TYPE_FAIL) {\n        totlen = sizeof(clusterMsg)-sizeof(union clusterMsgData);\n        totlen += sizeof(clusterMsgDataFail);\n    } else if (type == CLUSTERMSG_TYPE_UPDATE) {\n        totlen = sizeof(clusterMsg)-sizeof(union clusterMsgData);\n        totlen += sizeof(clusterMsgDataUpdate);\n    }\n\n    // 设置信息的长度\n    hdr->totlen = htonl(totlen);\n    /* For PING, PONG, and MEET, fixing the totlen field is up to the caller. */\n}\n\n/* Send a PING or PONG packet to the specified node, making sure to add enough\n * gossip informations. */\n// 向指定节点发送一条 MEET 、 PING 或者 PONG 消息\nvoid clusterSendPing(clusterLink *link, int type) {\n    unsigned char buf[sizeof(clusterMsg)];\n    clusterMsg *hdr = (clusterMsg*) buf;\n    int gossipcount = 0, totlen;\n    /* freshnodes is the number of nodes we can still use to populate the\n     * gossip section of the ping packet. Basically we start with the nodes\n     * we have in memory minus two (ourself and the node we are sending the\n     * message to). Every time we add a node we decrement the counter, so when\n     * it will drop to <= zero we know there is no more gossip info we can\n     * send. */\n    // freshnodes 是用于发送 gossip 信息的计数器\n    // 每次发送一条信息时，程序将 freshnodes 的值减一\n    // 当 freshnodes 的数值小于等于 0 时，程序停止发送 gossip 信息\n    // freshnodes 的数量是节点目前的 nodes 表中的节点数量减去 2 \n    // 这里的 2 指两个节点，一个是 myself 节点（也即是发送信息的这个节点）\n    // 另一个是接受 gossip 信息的节点\n    int freshnodes = dictSize(server.cluster->nodes)-2;\n\n    // 如果发送的信息是 PING ，那么更新最后一次发送 PING 命令的时间戳\n    if (link->node && type == CLUSTERMSG_TYPE_PING)\n        link->node->ping_sent = mstime();\n\n    // 将当前节点的信息（比如名字、地址、端口号、负责处理的槽）记录到消息里面\n    clusterBuildMessageHdr(hdr,type);\n\n    /* Populate the gossip fields */\n    // 从当前节点已知的节点中随机选出两个节点\n    // 并通过这条消息捎带给目标节点，从而实现 gossip 协议\n\n    // 每个节点有 freshnodes 次发送 gossip 信息的机会\n    // 每次向目标节点发送 2 个被选中节点的 gossip 信息（gossipcount 计数）\n    while(freshnodes > 0 && gossipcount < 3) {\n        // 从 nodes 字典中随机选出一个节点（被选中节点）\n        dictEntry *de = dictGetRandomKey(server.cluster->nodes);\n        clusterNode *this = dictGetVal(de);\n\n        clusterMsgDataGossip *gossip;\n        int j;\n\n        /* In the gossip section don't include:\n         * 以下节点不能作为被选中节点：\n         * 1) Myself.\n         *    节点本身。\n         * 2) Nodes in HANDSHAKE state.\n         *    处于 HANDSHAKE 状态的节点。\n         * 3) Nodes with the NOADDR flag set.\n         *    带有 NOADDR 标识的节点\n         * 4) Disconnected nodes if they don't have configured slots.\n         *    因为不处理任何槽而被断开连接的节点 \n         */\n        if (this == myself ||\n            this->flags & (REDIS_NODE_HANDSHAKE|REDIS_NODE_NOADDR) ||\n            (this->link == NULL && this->numslots == 0))\n        {\n                freshnodes--; /* otherwise we may loop forever. */\n                continue;\n        }\n\n        /* Check if we already added this node */\n        // 检查被选中节点是否已经在 hdr->data.ping.gossip 数组里面\n        // 如果是的话说明这个节点之前已经被选中了\n        // 不要再选中它（否则就会出现重复）\n        for (j = 0; j < gossipcount; j++) {\n            if (memcmp(hdr->data.ping.gossip[j].nodename,this->name,\n                    REDIS_CLUSTER_NAMELEN) == 0) break;\n        }\n        if (j != gossipcount) continue;\n\n        /* Add it */\n\n        // 这个被选中节点有效，计数器减一\n        freshnodes--;\n\n        // 指向 gossip 信息结构\n        gossip = &(hdr->data.ping.gossip[gossipcount]);\n\n        // 将被选中节点的名字记录到 gossip 信息\n        memcpy(gossip->nodename,this->name,REDIS_CLUSTER_NAMELEN);\n        // 将被选中节点的 PING 命令发送时间戳记录到 gossip 信息\n        gossip->ping_sent = htonl(this->ping_sent);\n        // 将被选中节点的 PING 命令回复的时间戳记录到 gossip 信息\n        gossip->pong_received = htonl(this->pong_received);\n        // 将被选中节点的 IP 记录到 gossip 信息\n        memcpy(gossip->ip,this->ip,sizeof(this->ip));\n        // 将被选中节点的端口号记录到 gossip 信息\n        gossip->port = htons(this->port);\n        // 将被选中节点的标识值记录到 gossip 信息\n        gossip->flags = htons(this->flags);\n\n        // 这个被选中节点有效，计数器增一\n        gossipcount++;\n    }\n\n    // 计算信息长度\n    totlen = sizeof(clusterMsg)-sizeof(union clusterMsgData);\n    totlen += (sizeof(clusterMsgDataGossip)*gossipcount);\n    // 将被选中节点的数量（gossip 信息中包含了多少个节点的信息）\n    // 记录在 count 属性里面\n    hdr->count = htons(gossipcount);\n    // 将信息的长度记录到信息里面\n    hdr->totlen = htonl(totlen);\n\n    // 发送信息\n    clusterSendMessage(link,buf,totlen);\n}\n\n/* Send a PONG packet to every connected node that's not in handshake state\n * and for which we have a valid link.\n *\n * 向所有未在 HANDSHAKE 状态，并且连接正常的节点发送 PONG 回复。\n *\n * In Redis Cluster pongs are not used just for failure detection, but also\n * to carry important configuration information. So broadcasting a pong is\n * useful when something changes in the configuration and we want to make\n * the cluster aware ASAP (for instance after a slave promotion). *\n * 在集群中， PONG 不仅可以用来检测节点状态，\n * 还可以携带一些重要的信息。\n *\n * 因此广播 PONG 回复在配置发生变化（比如从节点转变为主节点），\n * 并且当前节点想让其他节点尽快知悉这一变化的时候，\n * 就会广播 PONG 回复。\n *\n * The 'target' argument specifies the receiving instances using the\n * defines below:\n *\n * CLUSTER_BROADCAST_ALL -> All known instances.\n * CLUSTER_BROADCAST_LOCAL_SLAVES -> All slaves in my master-slaves ring.\n */\n#define CLUSTER_BROADCAST_ALL 0\n#define CLUSTER_BROADCAST_LOCAL_SLAVES 1\nvoid clusterBroadcastPong(int target) {\n    dictIterator *di;\n    dictEntry *de;\n\n    // 遍历所有节点\n    di = dictGetSafeIterator(server.cluster->nodes);\n    while((de = dictNext(di)) != NULL) {\n        clusterNode *node = dictGetVal(de);\n\n        // 不向未建立连接的节点发送\n        if (!node->link) continue;\n        if (node == myself || nodeInHandshake(node)) continue;\n        if (target == CLUSTER_BROADCAST_LOCAL_SLAVES) {\n            int local_slave =\n                nodeIsSlave(node) && node->slaveof &&\n                (node->slaveof == myself || node->slaveof == myself->slaveof);\n            if (!local_slave) continue;\n        }\n        // 发送 PONG 信息\n        clusterSendPing(node->link,CLUSTERMSG_TYPE_PONG);\n    }\n    dictReleaseIterator(di);\n}\n\n/* Send a PUBLISH message.\n *\n * 发送一条 PUBLISH 消息。\n *\n * If link is NULL, then the message is broadcasted to the whole cluster. \n *\n * 如果 link 参数为 NULL ，那么将消息广播给整个集群。\n */\nvoid clusterSendPublish(clusterLink *link, robj *channel, robj *message) {\n    unsigned char buf[sizeof(clusterMsg)], *payload;\n    clusterMsg *hdr = (clusterMsg*) buf;\n    uint32_t totlen;\n    uint32_t channel_len, message_len;\n\n    // 频道\n    channel = getDecodedObject(channel);\n\n    // 消息\n    message = getDecodedObject(message);\n\n    // 频道和消息的长度\n    channel_len = sdslen(channel->ptr);\n    message_len = sdslen(message->ptr);\n\n    // 构建消息\n    clusterBuildMessageHdr(hdr,CLUSTERMSG_TYPE_PUBLISH);\n    totlen = sizeof(clusterMsg)-sizeof(union clusterMsgData);\n    totlen += sizeof(clusterMsgDataPublish) + channel_len + message_len;\n\n    hdr->data.publish.msg.channel_len = htonl(channel_len);\n    hdr->data.publish.msg.message_len = htonl(message_len);\n    hdr->totlen = htonl(totlen);\n\n    /* Try to use the local buffer if possible */\n    if (totlen < sizeof(buf)) {\n        payload = buf;\n    } else {\n        payload = zmalloc(totlen);\n        memcpy(payload,hdr,sizeof(*hdr));\n        hdr = (clusterMsg*) payload;\n    }\n\n    // 保存频道和消息到消息结构中\n    memcpy(hdr->data.publish.msg.bulk_data,channel->ptr,sdslen(channel->ptr));\n    memcpy(hdr->data.publish.msg.bulk_data+sdslen(channel->ptr),\n        message->ptr,sdslen(message->ptr));\n\n    // 选择发送到节点还是广播至整个集群\n    if (link)\n        clusterSendMessage(link,payload,totlen);\n    else\n        clusterBroadcastMessage(payload,totlen);\n\n    decrRefCount(channel);\n    decrRefCount(message);\n    if (payload != buf) zfree(payload);\n}\n\n/* Send a FAIL message to all the nodes we are able to contact.\n *\n * 向当前节点已知的所有节点发送 FAIL 信息。\n *\n * The FAIL message is sent when we detect that a node is failing\n * (REDIS_NODE_PFAIL) and we also receive a gossip confirmation of this:\n * we switch the node state to REDIS_NODE_FAIL and ask all the other\n * nodes to do the same ASAP. \n *\n * 如果当前节点将 node 标记为 PFAIL 状态，\n * 并且通过 gossip 协议，\n * 从足够数量的节点那些得到了 node 已经下线的支持， \n * 那么当前节点会将 node 标记为 FAIL ，\n * 并执行这个函数，向其他 node 发送 FAIL 消息，\n * 要求它们也将 node 标记为 FAIL 。\n */\nvoid clusterSendFail(char *nodename) {\n    unsigned char buf[sizeof(clusterMsg)];\n    clusterMsg *hdr = (clusterMsg*) buf;\n\n    // 创建下线消息\n    clusterBuildMessageHdr(hdr,CLUSTERMSG_TYPE_FAIL);\n\n    // 记录命令\n    memcpy(hdr->data.fail.about.nodename,nodename,REDIS_CLUSTER_NAMELEN);\n\n    // 广播消息\n    clusterBroadcastMessage(buf,ntohl(hdr->totlen));\n}\n\n/* Send an UPDATE message to the specified link carrying the specified 'node'\n * slots configuration. The node name, slots bitmap, and configEpoch info\n * are included. \n *\n * 向连接 link 发送包含给定 node 槽配置的 UPDATE 消息，\n * 包括节点名称，槽位图，以及配置纪元。\n */\nvoid clusterSendUpdate(clusterLink *link, clusterNode *node) {\n    unsigned char buf[sizeof(clusterMsg)];\n    clusterMsg *hdr = (clusterMsg*) buf;\n\n    if (link == NULL) return;\n\n    // 创建消息\n    clusterBuildMessageHdr(hdr,CLUSTERMSG_TYPE_UPDATE);\n\n    // 设置节点名\n    memcpy(hdr->data.update.nodecfg.nodename,node->name,REDIS_CLUSTER_NAMELEN);\n\n    // 设置配置纪元\n    hdr->data.update.nodecfg.configEpoch = htonu64(node->configEpoch);\n\n    // 更新节点的槽位图\n    memcpy(hdr->data.update.nodecfg.slots,node->slots,sizeof(node->slots));\n\n    // 发送信息\n    clusterSendMessage(link,buf,ntohl(hdr->totlen));\n}\n\n/* -----------------------------------------------------------------------------\n * CLUSTER Pub/Sub support\n *\n * For now we do very little, just propagating PUBLISH messages across the whole\n * cluster. In the future we'll try to get smarter and avoiding propagating those\n * messages to hosts without receives for a given channel.\n * -------------------------------------------------------------------------- */\n// 向整个集群的 channel 频道中广播消息 messages\nvoid clusterPropagatePublish(robj *channel, robj *message) {\n    clusterSendPublish(NULL, channel, message);\n}\n\n/* -----------------------------------------------------------------------------\n * SLAVE node specific functions\n * -------------------------------------------------------------------------- */\n\n/* This function sends a FAILOVE_AUTH_REQUEST message to every node in order to\n * see if there is the quorum for this slave instance to failover its failing\n * master.\n *\n * 向其他所有节点发送 FAILOVE_AUTH_REQUEST 信息，\n * 看它们是否同意由这个从节点来对下线的主节点进行故障转移。\n *\n * Note that we send the failover request to everybody, master and slave nodes,\n * but only the masters are supposed to reply to our query. \n *\n * 信息会被发送给所有节点，包括主节点和从节点，但只有主节点会回复这条信息。 \n */\nvoid clusterRequestFailoverAuth(void) {\n    unsigned char buf[sizeof(clusterMsg)];\n    clusterMsg *hdr = (clusterMsg*) buf;\n    uint32_t totlen;\n\n    // 设置信息头（包含当前节点的信息）\n    clusterBuildMessageHdr(hdr,CLUSTERMSG_TYPE_FAILOVER_AUTH_REQUEST);\n    /* If this is a manual failover, set the CLUSTERMSG_FLAG0_FORCEACK bit\n     * in the header to communicate the nodes receiving the message that\n     * they should authorized the failover even if the master is working. */\n    if (server.cluster->mf_end) hdr->mflags[0] |= CLUSTERMSG_FLAG0_FORCEACK;\n    totlen = sizeof(clusterMsg)-sizeof(union clusterMsgData);\n    hdr->totlen = htonl(totlen);\n\n    // 发送信息\n    clusterBroadcastMessage(buf,totlen);\n}\n\n/* Send a FAILOVER_AUTH_ACK message to the specified node. */\n// 向节点 node 投票，支持它进行故障迁移\nvoid clusterSendFailoverAuth(clusterNode *node) {\n    unsigned char buf[sizeof(clusterMsg)];\n    clusterMsg *hdr = (clusterMsg*) buf;\n    uint32_t totlen;\n\n    if (!node->link) return;\n    clusterBuildMessageHdr(hdr,CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK);\n    totlen = sizeof(clusterMsg)-sizeof(union clusterMsgData);\n    hdr->totlen = htonl(totlen);\n    clusterSendMessage(node->link,buf,totlen);\n}\n\n/* Send a MFSTART message to the specified node. */\n// 向给定的节点发送一条 MFSTART 消息\nvoid clusterSendMFStart(clusterNode *node) {\n    unsigned char buf[sizeof(clusterMsg)];\n    clusterMsg *hdr = (clusterMsg*) buf;\n    uint32_t totlen;\n\n    if (!node->link) return;\n    clusterBuildMessageHdr(hdr,CLUSTERMSG_TYPE_MFSTART);\n    totlen = sizeof(clusterMsg)-sizeof(union clusterMsgData);\n    hdr->totlen = htonl(totlen);\n    clusterSendMessage(node->link,buf,totlen);\n}\n\n/* Vote for the node asking for our vote if there are the conditions. */\n// 在条件满足的情况下，为请求进行故障转移的节点 node 进行投票，支持它进行故障转移\nvoid clusterSendFailoverAuthIfNeeded(clusterNode *node, clusterMsg *request) {\n\n    // 请求节点的主节点\n    clusterNode *master = node->slaveof;\n\n    // 请求节点的当前配置纪元\n    uint64_t requestCurrentEpoch = ntohu64(request->currentEpoch);\n\n    // 请求节点想要获得投票的纪元\n    uint64_t requestConfigEpoch = ntohu64(request->configEpoch);\n\n    // 请求节点的槽布局\n    unsigned char *claimed_slots = request->myslots;\n    int force_ack = request->mflags[0] & CLUSTERMSG_FLAG0_FORCEACK;\n    int j;\n\n    /* IF we are not a master serving at least 1 slot, we don't have the\n     * right to vote, as the cluster size in Redis Cluster is the number\n     * of masters serving at least one slot, and quorum is the cluster\n     * size + 1 */\n\n    // 如果节点为从节点，或者是一个没有处理任何槽的主节点，\n    // 那么它没有投票权\n    if (nodeIsSlave(myself) || myself->numslots == 0) return;\n\n    /* Request epoch must be >= our currentEpoch. */\n    // 请求的配置纪元必须大于等于当前节点的配置纪元\n    if (requestCurrentEpoch < server.cluster->currentEpoch) return;\n\n    /* I already voted for this epoch? Return ASAP. */\n    // 已经投过票了\n    if (server.cluster->lastVoteEpoch == server.cluster->currentEpoch) return;\n\n    /* Node must be a slave and its master down.\n     * The master can be non failing if the request is flagged\n     * with CLUSTERMSG_FLAG0_FORCEACK (manual failover). */\n    if (nodeIsMaster(node) || master == NULL ||\n        (!nodeFailed(master) && !force_ack)) return;\n\n    /* We did not voted for a slave about this master for two\n     * times the node timeout. This is not strictly needed for correctness\n     * of the algorithm but makes the base case more linear. */\n    // 如果之前一段时间已经对请求节点进行过投票，那么不进行投票\n    if (mstime() - node->slaveof->voted_time < server.cluster_node_timeout * 2)\n        return;\n\n    /* The slave requesting the vote must have a configEpoch for the claimed\n     * slots that is >= the one of the masters currently serving the same\n     * slots in the current configuration. */\n    for (j = 0; j < REDIS_CLUSTER_SLOTS; j++) {\n\n        // 跳过未指派节点\n        if (bitmapTestBit(claimed_slots, j) == 0) continue;\n\n        // 查找是否有某个槽的配置纪元大于节点请求的纪元\n        if (server.cluster->slots[j] == NULL ||\n            server.cluster->slots[j]->configEpoch <= requestConfigEpoch)\n        {\n            continue;\n        }\n\n        // 如果有的话，说明节点请求的纪元已经过期，没有必要进行投票\n        /* If we reached this point we found a slot that in our current slots\n         * is served by a master with a greater configEpoch than the one claimed\n         * by the slave requesting our vote. Refuse to vote for this slave. */\n        return;\n    }\n\n    /* We can vote for this slave. */\n    // 为节点投票\n    clusterSendFailoverAuth(node);\n    // 更新时间值\n    server.cluster->lastVoteEpoch = server.cluster->currentEpoch;\n    node->slaveof->voted_time = mstime();\n}\n\n/* This function returns the \"rank\" of this instance, a slave, in the context\n * of its master-slaves ring. The rank of the slave is given by the number of\n * other slaves for the same master that have a better replication offset\n * compared to the local one (better means, greater, so they claim more data).\n *\n * A slave with rank 0 is the one with the greatest (most up to date)\n * replication offset, and so forth. Note that because how the rank is computed\n * multiple slaves may have the same rank, in case they have the same offset.\n *\n * The slave rank is used to add a delay to start an election in order to\n * get voted and replace a failing master. Slaves with better replication\n * offsets are more likely to win. */\nint clusterGetSlaveRank(void) {\n    long long myoffset;\n    int j, rank = 0;\n    clusterNode *master;\n\n    redisAssert(nodeIsSlave(myself));\n    master = myself->slaveof;\n    if (master == NULL) return 0; /* Never called by slaves without master. */\n\n    myoffset = replicationGetSlaveOffset();\n    for (j = 0; j < master->numslaves; j++)\n        if (master->slaves[j] != myself &&\n            master->slaves[j]->repl_offset > myoffset) rank++;\n    return rank;\n}\n\n/* This function is called if we are a slave node and our master serving\n * a non-zero amount of hash slots is in FAIL state.\n *\n * 如果当前节点是一个从节点，并且它正在复制的一个负责非零个槽的主节点处于 FAIL 状态，\n * 那么执行这个函数。\n *\n * The gaol of this function is:\n *\n * 这个函数有三个目标：\n *\n * 1) To check if we are able to perform a failover, is our data updated?\n *    检查是否可以对主节点执行一次故障转移，节点的关于主节点的信息是否准确和最新（updated）？\n * 2) Try to get elected by masters.\n *    选举一个新的主节点\n * 3) Perform the failover informing all the other nodes.\n *    执行故障转移，并通知其他节点\n */\nvoid clusterHandleSlaveFailover(void) {\n    mstime_t data_age;\n    mstime_t auth_age = mstime() - server.cluster->failover_auth_time;\n    int needed_quorum = (server.cluster->size / 2) + 1;\n    int manual_failover = server.cluster->mf_end != 0 &&\n                          server.cluster->mf_can_start;\n    int j;\n    mstime_t auth_timeout, auth_retry_time;\n\n    server.cluster->todo_before_sleep &= ~CLUSTER_TODO_HANDLE_FAILOVER;\n\n    /* Compute the failover timeout (the max time we have to send votes\n     * and wait for replies), and the failover retry time (the time to wait\n     * before waiting again.\n     *\n     * Timeout is MIN(NODE_TIMEOUT*2,2000) milliseconds.\n     * Retry is two times the Timeout.\n     */\n    auth_timeout = server.cluster_node_timeout*2;\n    if (auth_timeout < 2000) auth_timeout = 2000;\n    auth_retry_time = auth_timeout*2;\n\n    /* Pre conditions to run the function, that must be met both in case\n     * of an automatic or manual failover:\n     * 1) We are a slave.\n     * 2) Our master is flagged as FAIL, or this is a manual failover.\n     * 3) It is serving slots. */\n    if (nodeIsMaster(myself) ||\n        myself->slaveof == NULL ||\n        (!nodeFailed(myself->slaveof) && !manual_failover) ||\n        myself->slaveof->numslots == 0) return;\n\n    /* Set data_age to the number of seconds we are disconnected from\n     * the master. */\n    // 将 data_age 设置为从节点与主节点的断开秒数\n    if (server.repl_state == REDIS_REPL_CONNECTED) {\n        data_age = (mstime_t)(server.unixtime - server.master->lastinteraction)\n                   * 1000;\n    } else {\n        data_age = (mstime_t)(server.unixtime - server.repl_down_since) * 1000;\n    }\n\n    /* Remove the node timeout from the data age as it is fine that we are\n     * disconnected from our master at least for the time it was down to be\n     * flagged as FAIL, that's the baseline. */\n    // node timeout 的时间不计入断线时间之内\n    if (data_age > server.cluster_node_timeout)\n        data_age -= server.cluster_node_timeout;\n\n    /* Check if our data is recent enough. For now we just use a fixed\n     * constant of ten times the node timeout since the cluster should\n     * react much faster to a master down.\n     *\n     * Check bypassed for manual failovers. */\n    // 检查这个从节点的数据是否较新：\n    // 目前的检测办法是断线时间不能超过 node timeout 的十倍\n    if (data_age >\n        ((mstime_t)server.repl_ping_slave_period * 1000) +\n        (server.cluster_node_timeout * REDIS_CLUSTER_SLAVE_VALIDITY_MULT))\n    {\n        if (!manual_failover) return;\n    }\n\n    /* If the previous failover attempt timedout and the retry time has\n     * elapsed, we can setup a new one. */\n    if (auth_age > auth_retry_time) {\n        server.cluster->failover_auth_time = mstime() +\n            500 + /* Fixed delay of 500 milliseconds, let FAIL msg propagate. */\n            random() % 500; /* Random delay between 0 and 500 milliseconds. */\n        server.cluster->failover_auth_count = 0;\n        server.cluster->failover_auth_sent = 0;\n        server.cluster->failover_auth_rank = clusterGetSlaveRank();\n        /* We add another delay that is proportional to the slave rank.\n         * Specifically 1 second * rank. This way slaves that have a probably\n         * less updated replication offset, are penalized. */\n        server.cluster->failover_auth_time +=\n            server.cluster->failover_auth_rank * 1000;\n        /* However if this is a manual failover, no delay is needed. */\n        if (server.cluster->mf_end) {\n            server.cluster->failover_auth_time = mstime();\n            server.cluster->failover_auth_rank = 0;\n        }\n        redisLog(REDIS_WARNING,\n            \"Start of election delayed for %lld milliseconds \"\n            \"(rank #%d, offset %lld).\",\n            server.cluster->failover_auth_time - mstime(),\n            server.cluster->failover_auth_rank,\n            replicationGetSlaveOffset());\n        /* Now that we have a scheduled election, broadcast our offset\n         * to all the other slaves so that they'll updated their offsets\n         * if our offset is better. */\n        clusterBroadcastPong(CLUSTER_BROADCAST_LOCAL_SLAVES);\n        return;\n    }\n\n    /* It is possible that we received more updated offsets from other\n     * slaves for the same master since we computed our election delay.\n     * Update the delay if our rank changed.\n     *\n     * Not performed if this is a manual failover. */\n    if (server.cluster->failover_auth_sent == 0 &&\n        server.cluster->mf_end == 0)\n    {\n        int newrank = clusterGetSlaveRank();\n        if (newrank > server.cluster->failover_auth_rank) {\n            long long added_delay =\n                (newrank - server.cluster->failover_auth_rank) * 1000;\n            server.cluster->failover_auth_time += added_delay;\n            server.cluster->failover_auth_rank = newrank;\n            redisLog(REDIS_WARNING,\n                \"Slave rank updated to #%d, added %lld milliseconds of delay.\",\n                newrank, added_delay);\n        }\n    }\n\n    /* Return ASAP if we can't still start the election. */\n    // 如果执行故障转移的时间未到，先返回\n    if (mstime() < server.cluster->failover_auth_time) return;\n\n    /* Return ASAP if the election is too old to be valid. */\n    // 如果距离应该执行故障转移的时间已经过了很久\n    // 那么不应该再执行故障转移了（因为可能已经没有需要了）\n    // 直接返回\n    if (auth_age > auth_timeout) return;\n\n    /* Ask for votes if needed. */\n    // 向其他节点发送故障转移请求\n    if (server.cluster->failover_auth_sent == 0) {\n\n        // 增加配置纪元\n        server.cluster->currentEpoch++;\n\n        // 记录发起故障转移的配置纪元\n        server.cluster->failover_auth_epoch = server.cluster->currentEpoch;\n\n        redisLog(REDIS_WARNING,\"Starting a failover election for epoch %llu.\",\n            (unsigned long long) server.cluster->currentEpoch);\n\n        // 向其他所有节点发送信息，看它们是否支持由本节点来对下线主节点进行故障转移\n        clusterRequestFailoverAuth();\n\n        // 打开标识，表示已发送信息\n        server.cluster->failover_auth_sent = 1;\n\n        // TODO:\n        // 在进入下个事件循环之前，执行：\n        // 1）保存配置文件\n        // 2）更新节点状态\n        // 3）同步配置\n        clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG|\n                             CLUSTER_TODO_UPDATE_STATE|\n                             CLUSTER_TODO_FSYNC_CONFIG);\n        return; /* Wait for replies. */\n    }\n\n    /* Check if we reached the quorum. */\n    // 如果当前节点获得了足够多的投票，那么对下线主节点进行故障转移\n    if (server.cluster->failover_auth_count >= needed_quorum) {\n        // 旧主节点\n        clusterNode *oldmaster = myself->slaveof;\n\n        redisLog(REDIS_WARNING,\n            \"Failover election won: I'm the new master.\");\n\n        /* We have the quorum, perform all the steps to correctly promote\n         * this slave to a master.\n         *\n         * 1) Turn this node into a master. \n         *    将当前节点的身份由从节点改为主节点\n         */\n        clusterSetNodeAsMaster(myself);\n        // 让从节点取消复制，成为新的主节点\n        replicationUnsetMaster();\n\n        /* 2) Claim all the slots assigned to our master. */\n        // 接收所有主节点负责处理的槽\n        for (j = 0; j < REDIS_CLUSTER_SLOTS; j++) {\n            if (clusterNodeGetSlotBit(oldmaster,j)) {\n                // 将槽设置为未分配的\n                clusterDelSlot(j);\n                // 将槽的负责人设置为当前节点\n                clusterAddSlot(myself,j);\n            }\n        }\n\n        /* 3) Update my configEpoch to the epoch of the election. */\n        // 更新集群配置纪元\n        myself->configEpoch = server.cluster->failover_auth_epoch;\n\n        /* 4) Update state and save config. */\n        // 更新节点状态\n        clusterUpdateState();\n        // 并保存配置文件\n        clusterSaveConfigOrDie(1);\n\n        /* 5) Pong all the other nodes so that they can update the state\n         *    accordingly and detect that we switched to master role. */\n        // 向所有节点发送 PONG 信息\n        // 让它们可以知道当前节点已经升级为主节点了\n        clusterBroadcastPong(CLUSTER_BROADCAST_ALL);\n\n        /* 6) If there was a manual failover in progress, clear the state. */\n        // 如果有手动故障转移正在执行，那么清理和它有关的状态\n        resetManualFailover();\n    }\n}\n\n/* -----------------------------------------------------------------------------\n * CLUSTER slave migration\n *\n * Slave migration is the process that allows a slave of a master that is\n * already covered by at least another slave, to \"migrate\" to a master that\n * is orpaned, that is, left with no working slaves.\n * -------------------------------------------------------------------------- */\n\n/* This function is responsible to decide if this replica should be migrated\n * to a different (orphaned) master. It is called by the clusterCron() function\n * only if:\n *\n * 1) We are a slave node.\n * 2) It was detected that there is at least one orphaned master in\n *    the cluster.\n * 3) We are a slave of one of the masters with the greatest number of\n *    slaves.\n *\n * This checks are performed by the caller since it requires to iterate\n * the nodes anyway, so we spend time into clusterHandleSlaveMigration()\n * if definitely needed.\n *\n * The fuction is called with a pre-computed max_slaves, that is the max\n * number of working (not in FAIL state) slaves for a single master.\n *\n * Additional conditions for migration are examined inside the function.\n */\nvoid clusterHandleSlaveMigration(int max_slaves) {\n    int j, okslaves = 0;\n    clusterNode *mymaster = myself->slaveof, *target = NULL, *candidate = NULL;\n    dictIterator *di;\n    dictEntry *de;\n\n    /* Step 1: Don't migrate if the cluster state is not ok. */\n    if (server.cluster->state != REDIS_CLUSTER_OK) return;\n\n    /* Step 2: Don't migrate if my master will not be left with at least\n     *         'migration-barrier' slaves after my migration. */\n    if (mymaster == NULL) return;\n    for (j = 0; j < mymaster->numslaves; j++)\n        if (!nodeFailed(mymaster->slaves[j]) &&\n            !nodeTimedOut(mymaster->slaves[j])) okslaves++;\n    if (okslaves <= server.cluster_migration_barrier) return;\n\n    /* Step 3: Idenitfy a candidate for migration, and check if among the\n     * masters with the greatest number of ok slaves, I'm the one with the\n     * smaller node ID.\n     *\n     * Note that this means that eventually a replica migration will occurr\n     * since slaves that are reachable again always have their FAIL flag\n     * cleared. At the same time this does not mean that there are no\n     * race conditions possible (two slaves migrating at the same time), but\n     * this is extremely unlikely to happen, and harmless. */\n    candidate = myself;\n    di = dictGetSafeIterator(server.cluster->nodes);\n    while((de = dictNext(di)) != NULL) {\n        clusterNode *node = dictGetVal(de);\n        int okslaves;\n\n        /* Only iterate over working masters. */\n        if (nodeIsSlave(node) || nodeFailed(node)) continue;\n        okslaves = clusterCountNonFailingSlaves(node);\n\n        if (okslaves == 0 && target == NULL && node->numslots > 0)\n            target = node;\n\n        if (okslaves == max_slaves) {\n            for (j = 0; j < node->numslaves; j++) {\n                if (memcmp(node->slaves[j]->name,\n                           candidate->name,\n                           REDIS_CLUSTER_NAMELEN) < 0)\n                {\n                    candidate = node->slaves[j];\n                }\n            }\n        }\n    }\n\n    /* Step 4: perform the migration if there is a target, and if I'm the\n     * candidate. */\n    if (target && candidate == myself) {\n        redisLog(REDIS_WARNING,\"Migrating to orphaned master %.40s\",\n            target->name);\n        clusterSetMaster(target);\n    }\n}\n\n/* -----------------------------------------------------------------------------\n * CLUSTER manual failover\n *\n * This are the important steps performed by slaves during a manual failover:\n * 1) User send CLUSTER FAILOVER command. The failover state is initialized\n *    setting mf_end to the millisecond unix time at which we'll abort the\n *    attempt.\n * 2) Slave sends a MFSTART message to the master requesting to pause clients\n *    for two times the manual failover timeout REDIS_CLUSTER_MF_TIMEOUT.\n *    When master is paused for manual failover, it also starts to flag\n *    packets with CLUSTERMSG_FLAG0_PAUSED.\n * 3) Slave waits for master to send its replication offset flagged as PAUSED.\n * 4) If slave received the offset from the master, and its offset matches,\n *    mf_can_start is set to 1, and clusterHandleSlaveFailover() will perform\n *    the failover as usually, with the difference that the vote request\n *    will be modified to force masters to vote for a slave that has a\n *    working master.\n *\n * From the point of view of the master things are simpler: when a\n * PAUSE_CLIENTS packet is received the master sets mf_end as well and\n * the sender in mf_slave. During the time limit for the manual failover\n * the master will just send PINGs more often to this slave, flagged with\n * the PAUSED flag, so that the slave will set mf_master_offset when receiving\n * a packet from the master with this flag set.\n *\n * The gaol of the manual failover is to perform a fast failover without\n * data loss due to the asynchronous master-slave replication.\n * -------------------------------------------------------------------------- */\n\n/* Reset the manual failover state. This works for both masters and slavesa\n * as all the state about manual failover is cleared.\n *\n * 重置与手动故障转移有关的状态，主节点和从节点都可以使用。\n *\n * The function can be used both to initialize the manual failover state at\n * startup or to abort a manual failover in progress. \n * 这个函数既可以用于在启动集群时进行初始化，\n * 又可以实际地应用在手动故障转移的情况。\n */\nvoid resetManualFailover(void) {\n    if (server.cluster->mf_end && clientsArePaused()) {\n        server.clients_pause_end_time = 0;\n        clientsArePaused(); /* Just use the side effect of the function. */\n    }\n    server.cluster->mf_end = 0; /* No manual failover in progress. */\n    server.cluster->mf_can_start = 0;\n    server.cluster->mf_slave = NULL;\n    server.cluster->mf_master_offset = 0;\n}\n\n/* If a manual failover timed out, abort it. */\nvoid manualFailoverCheckTimeout(void) {\n    if (server.cluster->mf_end && server.cluster->mf_end < mstime()) {\n        redisLog(REDIS_WARNING,\"Manual failover timed out.\");\n        resetManualFailover();\n    }\n}\n\n/* This function is called from the cluster cron function in order to go\n * forward with a manual failover state machine. */\nvoid clusterHandleManualFailover(void) {\n    /* Return ASAP if no manual failover is in progress. */\n    if (server.cluster->mf_end == 0) return;\n\n    /* If mf_can_start is non-zero, the failover was alrady triggered so the\n     * next steps are performed by clusterHandleSlaveFailover(). */\n    if (server.cluster->mf_can_start) return;\n\n    if (server.cluster->mf_master_offset == 0) return; /* Wait for offset... */\n\n    if (server.cluster->mf_master_offset == replicationGetSlaveOffset()) {\n        /* Our replication offset matches the master replication offset\n         * announced after clients were paused. We can start the failover. */\n        server.cluster->mf_can_start = 1;\n        redisLog(REDIS_WARNING,\n            \"All master replication stream processed, \"\n            \"manual failover can start.\");\n    }\n}\n\n/* -----------------------------------------------------------------------------\n * CLUSTER cron job\n * -------------------------------------------------------------------------- */\n\n/* This is executed 10 times every second */\n// 集群常规操作函数，默认每秒执行 10 次（每间隔 100 毫秒执行一次）\nvoid clusterCron(void) {\n    dictIterator *di;\n    dictEntry *de;\n    int update_state = 0;\n    int orphaned_masters; /* How many masters there are without ok slaves. */\n    int max_slaves; /* Max number of ok slaves for a single master. */\n    int this_slaves; /* Number of ok slaves for our master (if we are slave). */\n    mstime_t min_pong = 0, now = mstime();\n    clusterNode *min_pong_node = NULL;\n    // 迭代计数器，一个静态变量\n    static unsigned long long iteration = 0;\n    mstime_t handshake_timeout;\n\n    // 记录一次迭代\n    iteration++; /* Number of times this function was called so far. */\n\n    /* The handshake timeout is the time after which a handshake node that was\n     * not turned into a normal node is removed from the nodes. Usually it is\n     * just the NODE_TIMEOUT value, but when NODE_TIMEOUT is too small we use\n     * the value of 1 second. */\n    // 如果一个 handshake 节点没有在 handshake timeout 内\n    // 转换成普通节点（normal node），\n    // 那么节点会从 nodes 表中移除这个 handshake 节点\n    // 一般来说 handshake timeout 的值总是等于 NODE_TIMEOUT\n    // 不过如果 NODE_TIMEOUT 太少的话，程序会将值设为 1 秒钟\n    handshake_timeout = server.cluster_node_timeout;\n    if (handshake_timeout < 1000) handshake_timeout = 1000;\n\n    /* Check if we have disconnected nodes and re-establish the connection. */\n    // 向集群中的所有断线或者未连接节点发送消息\n    di = dictGetSafeIterator(server.cluster->nodes);\n    while((de = dictNext(di)) != NULL) {\n        clusterNode *node = dictGetVal(de);\n\n        // 跳过当前节点以及没有地址的节点\n        if (node->flags & (REDIS_NODE_MYSELF|REDIS_NODE_NOADDR)) continue;\n\n        /* A Node in HANDSHAKE state has a limited lifespan equal to the\n         * configured node timeout. */\n        // 如果 handshake 节点已超时，释放它\n        if (nodeInHandshake(node) && now - node->ctime > handshake_timeout) {\n            freeClusterNode(node);\n            continue;\n        }\n\n        // 为未创建连接的节点创建连接\n        if (node->link == NULL) {\n            int fd;\n            mstime_t old_ping_sent;\n            clusterLink *link;\n\n            fd = anetTcpNonBlockBindConnect(server.neterr, node->ip,\n                node->port+REDIS_CLUSTER_PORT_INCR,\n                    server.bindaddr_count ? server.bindaddr[0] : NULL);\n            if (fd == -1) {\n                redisLog(REDIS_DEBUG, \"Unable to connect to \"\n                    \"Cluster Node [%s]:%d -> %s\", node->ip,\n                    node->port+REDIS_CLUSTER_PORT_INCR,\n                    server.neterr);\n                continue;\n            }\n            link = createClusterLink(node);\n            link->fd = fd;\n            node->link = link;\n            aeCreateFileEvent(server.el,link->fd,AE_READABLE,\n                    clusterReadHandler,link);\n            /* Queue a PING in the new connection ASAP: this is crucial\n             * to avoid false positives in failure detection.\n             *\n             * If the node is flagged as MEET, we send a MEET message instead\n             * of a PING one, to force the receiver to add us in its node\n             * table. */\n            // 向新连接的节点发送 PING 命令，防止节点被识进入下线\n            // 如果节点被标记为 MEET ，那么发送 MEET 命令，否则发送 PING 命令\n            old_ping_sent = node->ping_sent;\n            clusterSendPing(link, node->flags & REDIS_NODE_MEET ?\n                    CLUSTERMSG_TYPE_MEET : CLUSTERMSG_TYPE_PING);\n\n            // 这不是第一次发送 PING 信息，所以可以还原这个时间\n            // 等 clusterSendPing() 函数来更新它\n            if (old_ping_sent) {\n                /* If there was an active ping before the link was\n                 * disconnected, we want to restore the ping time, otherwise\n                 * replaced by the clusterSendPing() call. */\n                node->ping_sent = old_ping_sent;\n            }\n\n            /* We can clear the flag after the first packet is sent.\n             *\n             * 在发送 MEET 信息之后，清除节点的 MEET 标识。\n             *\n             * If we'll never receive a PONG, we'll never send new packets\n             * to this node. Instead after the PONG is received and we\n             * are no longer in meet/handshake status, we want to send\n             * normal PING packets. \n             *\n             * 如果当前节点（发送者）没能收到 MEET 信息的回复，\n             * 那么它将不再向目标节点发送命令。\n             *\n             * 如果接收到回复的话，那么节点将不再处于 HANDSHAKE 状态，\n             * 并继续向目标节点发送普通 PING 命令。\n             */\n            node->flags &= ~REDIS_NODE_MEET;\n\n            redisLog(REDIS_DEBUG,\"Connecting with Node %.40s at %s:%d\",\n                    node->name, node->ip, node->port+REDIS_CLUSTER_PORT_INCR);\n        }\n    }\n    dictReleaseIterator(di);\n\n    /* Ping some random node 1 time every 10 iterations, so that we usually ping\n     * one random node every second. */\n    // clusterCron() 每执行 10 次（至少间隔一秒钟），就向一个随机节点发送 gossip 信息\n    if (!(iteration % 10)) {\n        int j;\n\n        /* Check a few random nodes and ping the one with the oldest\n         * pong_received time. */\n        // 随机 5 个节点，选出其中一个\n        for (j = 0; j < 5; j++) {\n\n            // 随机在集群中挑选节点\n            de = dictGetRandomKey(server.cluster->nodes);\n            clusterNode *this = dictGetVal(de);\n\n            /* Don't ping nodes disconnected or with a ping currently active. */\n            // 不要 PING 连接断开的节点，也不要 PING 最近已经 PING 过的节点\n            if (this->link == NULL || this->ping_sent != 0) continue;\n\n            if (this->flags & (REDIS_NODE_MYSELF|REDIS_NODE_HANDSHAKE))\n                continue;\n\n            // 选出 5 个随机节点中最近一次接收 PONG 回复距离现在最旧的节点\n            if (min_pong_node == NULL || min_pong > this->pong_received) {\n                min_pong_node = this;\n                min_pong = this->pong_received;\n            }\n        }\n\n        // 向最久没有收到 PONG 回复的节点发送 PING 命令\n        if (min_pong_node) {\n            redisLog(REDIS_DEBUG,\"Pinging node %.40s\", min_pong_node->name);\n            clusterSendPing(min_pong_node->link, CLUSTERMSG_TYPE_PING);\n        }\n    }\n\n    // 遍历所有节点，检查是否需要将某个节点标记为下线\n    /* Iterate nodes to check if we need to flag something as failing.\n     * This loop is also responsible to:\n     * 1) Check if there are orphaned masters (masters without non failing\n     *    slaves).\n     * 2) Count the max number of non failing slaves for a single master.\n     * 3) Count the number of slaves for our master, if we are a slave. */\n    orphaned_masters = 0;\n    max_slaves = 0;\n    this_slaves = 0;\n    di = dictGetSafeIterator(server.cluster->nodes);\n    while((de = dictNext(di)) != NULL) {\n        clusterNode *node = dictGetVal(de);\n        now = mstime(); /* Use an updated time at every iteration. */\n        mstime_t delay;\n\n        // 跳过节点本身、无地址节点、HANDSHAKE 状态的节点\n        if (node->flags &\n            (REDIS_NODE_MYSELF|REDIS_NODE_NOADDR|REDIS_NODE_HANDSHAKE))\n                continue;\n\n        /* Orphaned master check, useful only if the current instance\n         * is a slave that may migrate to another master. */\n        if (nodeIsSlave(myself) && nodeIsMaster(node) && !nodeFailed(node)) {\n            int okslaves = clusterCountNonFailingSlaves(node);\n\n            if (okslaves == 0 && node->numslots > 0) orphaned_masters++;\n            if (okslaves > max_slaves) max_slaves = okslaves;\n            if (nodeIsSlave(myself) && myself->slaveof == node)\n                this_slaves = okslaves;\n        }\n\n        /* If we are waiting for the PONG more than half the cluster\n         * timeout, reconnect the link: maybe there is a connection\n         * issue even if the node is alive. */\n        // 如果等到 PONG 到达的时间超过了 node timeout 一半的连接\n        // 因为尽管节点依然正常，但连接可能已经出问题了\n        if (node->link && /* is connected */\n            now - node->link->ctime >\n            server.cluster_node_timeout && /* was not already reconnected */\n            node->ping_sent && /* we already sent a ping */\n            node->pong_received < node->ping_sent && /* still waiting pong */\n            /* and we are waiting for the pong more than timeout/2 */\n            now - node->ping_sent > server.cluster_node_timeout/2)\n        {\n            /* Disconnect the link, it will be reconnected automatically. */\n            // 释放连接，下次 clusterCron() 会自动重连\n            freeClusterLink(node->link);\n        }\n\n        /* If we have currently no active ping in this instance, and the\n         * received PONG is older than half the cluster timeout, send\n         * a new ping now, to ensure all the nodes are pinged without\n         * a too big delay. */\n        // 如果目前没有在 PING 节点\n        // 并且已经有 node timeout 一半的时间没有从节点那里收到 PONG 回复\n        // 那么向节点发送一个 PING ，确保节点的信息不会太旧\n        // （因为一部分节点可能一直没有被随机中）\n        if (node->link &&\n            node->ping_sent == 0 &&\n            (now - node->pong_received) > server.cluster_node_timeout/2)\n        {\n            clusterSendPing(node->link, CLUSTERMSG_TYPE_PING);\n            continue;\n        }\n\n        /* If we are a master and one of the slaves requested a manual\n         * failover, ping it continuously. */\n        // 如果这是一个主节点，并且有一个从服务器请求进行手动故障转移\n        // 那么向从服务器发送 PING 。\n        if (server.cluster->mf_end &&\n            nodeIsMaster(myself) &&\n            server.cluster->mf_slave == node &&\n            node->link)\n        {\n            clusterSendPing(node->link, CLUSTERMSG_TYPE_PING);\n            continue;\n        }\n\n        /* Check only if we have an active ping for this instance. */\n        // 以下代码只在节点发送了 PING 命令的情况下执行\n        if (node->ping_sent == 0) continue;\n\n        /* Compute the delay of the PONG. Note that if we already received\n         * the PONG, then node->ping_sent is zero, so can't reach this\n         * code at all. */\n        // 计算等待 PONG 回复的时长\n        delay = now - node->ping_sent;\n\n        // 等待 PONG 回复的时长超过了限制值，将目标节点标记为 PFAIL （疑似下线）\n        if (delay > server.cluster_node_timeout) {\n            /* Timeout reached. Set the node as possibly failing if it is\n             * not already in this state. */\n            if (!(node->flags & (REDIS_NODE_PFAIL|REDIS_NODE_FAIL))) {\n                redisLog(REDIS_DEBUG,\"*** NODE %.40s possibly failing\",\n                    node->name);\n                // 打开疑似下线标记\n                node->flags |= REDIS_NODE_PFAIL;\n                update_state = 1;\n            }\n        }\n    }\n    dictReleaseIterator(di);\n\n    /* If we are a slave node but the replication is still turned off,\n     * enable it if we know the address of our master and it appears to\n     * be up. */\n    // 如果从节点没有在复制主节点，那么对从节点进行设置\n    if (nodeIsSlave(myself) &&\n        server.masterhost == NULL &&\n        myself->slaveof &&\n        nodeHasAddr(myself->slaveof))\n    {\n        replicationSetMaster(myself->slaveof->ip, myself->slaveof->port);\n    }\n\n    /* Abourt a manual failover if the timeout is reached. */\n    manualFailoverCheckTimeout();\n\n    if (nodeIsSlave(myself)) {\n        clusterHandleManualFailover();\n        clusterHandleSlaveFailover();\n        /* If there are orphaned slaves, and we are a slave among the masters\n         * with the max number of non-failing slaves, consider migrating to\n         * the orphaned masters. Note that it does not make sense to try\n         * a migration if there is no master with at least *two* working\n         * slaves. */\n        if (orphaned_masters && max_slaves >= 2 && this_slaves == max_slaves)\n            clusterHandleSlaveMigration(max_slaves);\n    }\n\n    // 更新集群状态\n    if (update_state || server.cluster->state == REDIS_CLUSTER_FAIL)\n        clusterUpdateState();\n}\n\n/* This function is called before the event handler returns to sleep for\n * events. It is useful to perform operations that must be done ASAP in\n * reaction to events fired but that are not safe to perform inside event\n * handlers, or to perform potentially expansive tasks that we need to do\n * a single time before replying to clients. \n *\n * 在进入下个事件循环时调用。\n * 这个函数做的事都是需要尽快执行，但是不能在执行文件事件期间做的事情。\n */\nvoid clusterBeforeSleep(void) {\n\n    /* Handle failover, this is needed when it is likely that there is already\n     * the quorum from masters in order to react fast. */\n    // 执行故障迁移\n    if (server.cluster->todo_before_sleep & CLUSTER_TODO_HANDLE_FAILOVER)\n        clusterHandleSlaveFailover();\n\n    /* Update the cluster state. */\n    // 更新节点的状态\n    if (server.cluster->todo_before_sleep & CLUSTER_TODO_UPDATE_STATE)\n        clusterUpdateState();\n\n    /* Save the config, possibly using fsync. */\n    // 保存 nodes.conf 配置文件\n    if (server.cluster->todo_before_sleep & CLUSTER_TODO_SAVE_CONFIG) {\n        int fsync = server.cluster->todo_before_sleep &\n                    CLUSTER_TODO_FSYNC_CONFIG;\n        clusterSaveConfigOrDie(fsync);\n    }\n\n    /* Reset our flags (not strictly needed since every single function\n     * called for flags set should be able to clear its flag). */\n    server.cluster->todo_before_sleep = 0;\n}\n\n// 打开 todo_before_sleep 的指定标识\n// 每个标识代表了节点在结束一个事件循环时要做的工作\nvoid clusterDoBeforeSleep(int flags) {\n    server.cluster->todo_before_sleep |= flags;\n}\n\n/* -----------------------------------------------------------------------------\n * Slots management\n * -------------------------------------------------------------------------- */\n\n/* Test bit 'pos' in a generic bitmap. Return 1 if the bit is set,\n * otherwise 0. */\n// 检查位图 bitmap 的 pos 位置是否已经被设置\n// 返回 1 表示已被设置，返回 0 表示未被设置。\nint bitmapTestBit(unsigned char *bitmap, int pos) {\n    off_t byte = pos/8;\n    int bit = pos&7;\n    return (bitmap[byte] & (1<<bit)) != 0;\n}\n\n/* Set the bit at position 'pos' in a bitmap. */\n// 设置位图 bitmap 在 pos 位置的值\nvoid bitmapSetBit(unsigned char *bitmap, int pos) {\n    off_t byte = pos/8;\n    int bit = pos&7;\n    bitmap[byte] |= 1<<bit;\n}\n\n/* Clear the bit at position 'pos' in a bitmap. */\n// 清除位图 bitmap 在 pos 位置的值\nvoid bitmapClearBit(unsigned char *bitmap, int pos) {\n    off_t byte = pos/8;\n    int bit = pos&7;\n    bitmap[byte] &= ~(1<<bit);\n}\n\n/* Set the slot bit and return the old value. */\n// 为槽二进制位设置新值，并返回旧值\nint clusterNodeSetSlotBit(clusterNode *n, int slot) {\n    int old = bitmapTestBit(n->slots,slot);\n    bitmapSetBit(n->slots,slot);\n    if (!old) n->numslots++;\n    return old;\n}\n\n/* Clear the slot bit and return the old value. */\n// 清空槽二进制位，并返回旧值\nint clusterNodeClearSlotBit(clusterNode *n, int slot) {\n    int old = bitmapTestBit(n->slots,slot);\n    bitmapClearBit(n->slots,slot);\n    if (old) n->numslots--;\n    return old;\n}\n\n/* Return the slot bit from the cluster node structure. */\n// 返回槽的二进制位的值\nint clusterNodeGetSlotBit(clusterNode *n, int slot) {\n    return bitmapTestBit(n->slots,slot);\n}\n\n/* Add the specified slot to the list of slots that node 'n' will\n * serve. Return REDIS_OK if the operation ended with success.\n * If the slot is already assigned to another instance this is considered\n * an error and REDIS_ERR is returned. */\n// 将槽 slot 添加到节点 n 需要处理的槽的列表中\n// 添加成功返回 REDIS_OK ,如果槽已经由这个节点处理了\n// 那么返回 REDIS_ERR 。\nint clusterAddSlot(clusterNode *n, int slot) {\n\n    // 槽 slot 已经是节点 n 处理的了\n    if (server.cluster->slots[slot]) return REDIS_ERR;\n\n    // 设置 bitmap\n    clusterNodeSetSlotBit(n,slot);\n\n    // 更新集群状态\n    server.cluster->slots[slot] = n;\n\n    return REDIS_OK;\n}\n\n/* Delete the specified slot marking it as unassigned.\n *\n * 将指定槽标记为未分配（unassigned）。\n *\n * Returns REDIS_OK if the slot was assigned, otherwise if the slot was\n * already unassigned REDIS_ERR is returned. \n *\n * 标记成功返回 REDIS_OK ，\n * 如果槽已经是未分配的，那么返回 REDIS_ERR 。\n */\nint clusterDelSlot(int slot) {\n\n    // 获取当前处理槽 slot 的节点 n\n    clusterNode *n = server.cluster->slots[slot];\n\n    if (!n) return REDIS_ERR;\n\n    // 清除位图\n    redisAssert(clusterNodeClearSlotBit(n,slot) == 1);\n\n    // 清空负责处理槽的节点\n    server.cluster->slots[slot] = NULL;\n\n    return REDIS_OK;\n}\n\n/* Delete all the slots associated with the specified node.\n * The number of deleted slots is returned. */\n// 删除所有由给定节点处理的槽，并返回被删除槽的数量\nint clusterDelNodeSlots(clusterNode *node) {\n    int deleted = 0, j;\n\n    for (j = 0; j < REDIS_CLUSTER_SLOTS; j++) {\n        // 如果这个槽由该节点负责，那么删除它\n        if (clusterNodeGetSlotBit(node,j)) clusterDelSlot(j);\n        deleted++;\n    }\n    return deleted;\n}\n\n/* Clear the migrating / importing state for all the slots.\n * This is useful at initialization and when turning a master into slave. */\n// 清理所有槽的迁移和导入状态\n// 通常在初始化或者将主节点转为从节点时使用\nvoid clusterCloseAllSlots(void) {\n    memset(server.cluster->migrating_slots_to,0,\n        sizeof(server.cluster->migrating_slots_to));\n    memset(server.cluster->importing_slots_from,0,\n        sizeof(server.cluster->importing_slots_from));\n}\n\n/* -----------------------------------------------------------------------------\n * Cluster state evaluation function\n * -------------------------------------------------------------------------- */\n\n/* The following are defines that are only used in the evaluation function\n * and are based on heuristics. Actaully the main point about the rejoin and\n * writable delay is that they should be a few orders of magnitude larger\n * than the network latency. */\n#define REDIS_CLUSTER_MAX_REJOIN_DELAY 5000\n#define REDIS_CLUSTER_MIN_REJOIN_DELAY 500\n#define REDIS_CLUSTER_WRITABLE_DELAY 2000\n\n// 更新节点状态\nvoid clusterUpdateState(void) {\n    int j, new_state;\n    int unreachable_masters = 0;\n    static mstime_t among_minority_time;\n    static mstime_t first_call_time = 0;\n\n    server.cluster->todo_before_sleep &= ~CLUSTER_TODO_UPDATE_STATE;\n\n    /* If this is a master node, wait some time before turning the state\n     * into OK, since it is not a good idea to rejoin the cluster as a writable\n     * master, after a reboot, without giving the cluster a chance to\n     * reconfigure this node. Note that the delay is calculated starting from\n     * the first call to this function and not since the server start, in order\n     * to don't count the DB loading time. */\n    if (first_call_time == 0) first_call_time = mstime();\n    if (nodeIsMaster(myself) &&\n        mstime() - first_call_time < REDIS_CLUSTER_WRITABLE_DELAY) return;\n\n    /* Start assuming the state is OK. We'll turn it into FAIL if there\n     * are the right conditions. */\n\n    // 先假设节点状态为 OK ，后面再检测节点是否真的下线\n    new_state = REDIS_CLUSTER_OK;\n\n    /* Check if all the slots are covered. */\n    // 检查是否所有槽都已经有某个节点在处理\n    for (j = 0; j < REDIS_CLUSTER_SLOTS; j++) {\n        if (server.cluster->slots[j] == NULL ||\n            server.cluster->slots[j]->flags & (REDIS_NODE_FAIL))\n        {\n            new_state = REDIS_CLUSTER_FAIL;\n            break;\n        }\n    }\n\n    /* Compute the cluster size, that is the number of master nodes\n     * serving at least a single slot.\n     *\n     * At the same time count the number of unreachable masters with\n     * at least one node. */\n    // 统计在线并且正在处理至少一个槽的 master 的数量，\n    // 以及下线 master 的数量\n    {\n        dictIterator *di;\n        dictEntry *de;\n\n        server.cluster->size = 0;\n        di = dictGetSafeIterator(server.cluster->nodes);\n        while((de = dictNext(di)) != NULL) {\n            clusterNode *node = dictGetVal(de);\n\n            if (nodeIsMaster(node) && node->numslots) {\n                server.cluster->size++;\n                if (node->flags & (REDIS_NODE_FAIL|REDIS_NODE_PFAIL))\n                    unreachable_masters++;\n            }\n        }\n        dictReleaseIterator(di);\n    }\n\n    /* If we can't reach at least half the masters, change the cluster state\n     * to FAIL, as we are not even able to mark nodes as FAIL in this side\n     * of the netsplit because of lack of majority.\n     *\n     * 如果不能连接到半数以上节点，那么将我们自己的状态设置为 FAIL\n     * 因为在少于半数节点的情况下，节点是无法将一个节点判断为 FAIL 的。\n     */\n    {\n        int needed_quorum = (server.cluster->size / 2) + 1;\n\n        if (unreachable_masters >= needed_quorum) {\n            new_state = REDIS_CLUSTER_FAIL;\n            among_minority_time = mstime();\n        }\n    }\n\n    /* Log a state change */\n    // 记录状态变更\n    if (new_state != server.cluster->state) {\n        mstime_t rejoin_delay = server.cluster_node_timeout;\n\n        /* If the instance is a master and was partitioned away with the\n         * minority, don't let it accept queries for some time after the\n         * partition heals, to make sure there is enough time to receive\n         * a configuration update. */\n        if (rejoin_delay > REDIS_CLUSTER_MAX_REJOIN_DELAY)\n            rejoin_delay = REDIS_CLUSTER_MAX_REJOIN_DELAY;\n        if (rejoin_delay < REDIS_CLUSTER_MIN_REJOIN_DELAY)\n            rejoin_delay = REDIS_CLUSTER_MIN_REJOIN_DELAY;\n\n        if (new_state == REDIS_CLUSTER_OK &&\n            nodeIsMaster(myself) &&\n            mstime() - among_minority_time < rejoin_delay)\n        {\n            return;\n        }\n\n        /* Change the state and log the event. */\n        redisLog(REDIS_WARNING,\"Cluster state changed: %s\",\n            new_state == REDIS_CLUSTER_OK ? \"ok\" : \"fail\");\n\n        // 设置新状态\n        server.cluster->state = new_state;\n    }\n}\n\n/* This function is called after the node startup in order to verify that data\n * loaded from disk is in agreement with the cluster configuration:\n *\n * 1) If we find keys about hash slots we have no responsibility for, the\n *    following happens:\n *    A) If no other node is in charge according to the current cluster\n *       configuration, we add these slots to our node.\n *    B) If according to our config other nodes are already in charge for\n *       this lots, we set the slots as IMPORTING from our point of view\n *       in order to justify we have those slots, and in order to make\n *       redis-trib aware of the issue, so that it can try to fix it.\n * 2) If we find data in a DB different than DB0 we return REDIS_ERR to\n *    signal the caller it should quit the server with an error message\n *    or take other actions.\n *\n * The function always returns REDIS_OK even if it will try to correct\n * the error described in \"1\". However if data is found in DB different\n * from DB0, REDIS_ERR is returned.\n *\n * The function also uses the logging facility in order to warn the user\n * about desynchronizations between the data we have in memory and the\n * cluster configuration. */\n// 检查当前节点的节点配置是否正确，包含的数据是否正确\n// 在启动集群时被调用（看 redis.c ）\nint verifyClusterConfigWithData(void) {\n    int j;\n    int update_config = 0;\n\n    /* If this node is a slave, don't perform the check at all as we\n     * completely depend on the replication stream. */\n    // 不对从节点进行检查\n    if (nodeIsSlave(myself)) return REDIS_OK;\n\n    /* Make sure we only have keys in DB0. */\n    // 确保只有 0 号数据库有数据\n    for (j = 1; j < server.dbnum; j++) {\n        if (dictSize(server.db[j].dict)) return REDIS_ERR;\n    }\n\n    /* Check that all the slots we see populated memory have a corresponding\n     * entry in the cluster table. Otherwise fix the table. */\n    // 检查槽表是否都有相应的节点，如果不是的话，进行修复\n    for (j = 0; j < REDIS_CLUSTER_SLOTS; j++) {\n        if (!countKeysInSlot(j)) continue; /* No keys in this slot. */\n        /* Check if we are assigned to this slot or if we are importing it.\n         * In both cases check the next slot as the configuration makes\n         * sense. */\n        // 跳过正在导入的槽\n        if (server.cluster->slots[j] == myself ||\n            server.cluster->importing_slots_from[j] != NULL) continue;\n\n        /* If we are here data and cluster config don't agree, and we have\n         * slot 'j' populated even if we are not importing it, nor we are\n         * assigned to this slot. Fix this condition. */\n\n        update_config++;\n        /* Case A: slot is unassigned. Take responsability for it. */\n        if (server.cluster->slots[j] == NULL) {\n            // 处理未被接受的槽\n            redisLog(REDIS_WARNING, \"I've keys about slot %d that is \"\n                                    \"unassigned. Taking responsability \"\n                                    \"for it.\",j);\n            clusterAddSlot(myself,j);\n        } else {\n            // 如果一个槽已经被其他节点接管\n            // 那么将槽中的资料发送给对方\n            redisLog(REDIS_WARNING, \"I've keys about slot %d that is \"\n                                    \"already assigned to a different node. \"\n                                    \"Setting it in importing state.\",j);\n            server.cluster->importing_slots_from[j] = server.cluster->slots[j];\n        }\n    }\n\n    // 保存 nodes.conf 文件\n    if (update_config) clusterSaveConfigOrDie(1);\n\n    return REDIS_OK;\n}\n\n/* -----------------------------------------------------------------------------\n * SLAVE nodes handling\n * -------------------------------------------------------------------------- */\n\n/* Set the specified node 'n' as master for this node.\n * If this node is currently a master, it is turned into a slave. */\n// 将节点 n 设置为当前节点的主节点\n// 如果当前节点为主节点，那么将它转换为从节点\nvoid clusterSetMaster(clusterNode *n) {\n    redisAssert(n != myself);\n    redisAssert(myself->numslots == 0);\n\n    if (nodeIsMaster(myself)) {\n        myself->flags &= ~REDIS_NODE_MASTER;\n        myself->flags |= REDIS_NODE_SLAVE;\n        clusterCloseAllSlots();\n    } else {\n        if (myself->slaveof)\n            clusterNodeRemoveSlave(myself->slaveof,myself);\n    }\n\n    // 将 slaveof 属性指向主节点\n    myself->slaveof = n;\n\n    // 设置主节点的 IP 和地址，开始对它进行复制\n    clusterNodeAddSlave(n,myself);\n    replicationSetMaster(n->ip, n->port);\n    resetManualFailover();\n}\n\n/* -----------------------------------------------------------------------------\n * CLUSTER command\n * -------------------------------------------------------------------------- */\n\n/* Generate a csv-alike representation of the specified cluster node.\n * See clusterGenNodesDescription() top comment for more information.\n *\n * The function returns the string representation as an SDS string. */\n// 生成节点的状态描述信息\nsds clusterGenNodeDescription(clusterNode *node) {\n    int j, start;\n    sds ci;\n\n    /* Node coordinates */\n    ci = sdscatprintf(sdsempty(),\"%.40s %s:%d \",\n        node->name,\n        node->ip,\n        node->port);\n\n    /* Flags */\n    if (node->flags == 0) ci = sdscat(ci,\"noflags,\");\n    if (node->flags & REDIS_NODE_MYSELF) ci = sdscat(ci,\"myself,\");\n    if (node->flags & REDIS_NODE_MASTER) ci = sdscat(ci,\"master,\");\n    if (node->flags & REDIS_NODE_SLAVE) ci = sdscat(ci,\"slave,\");\n    if (node->flags & REDIS_NODE_PFAIL) ci = sdscat(ci,\"fail?,\");\n    if (node->flags & REDIS_NODE_FAIL) ci = sdscat(ci,\"fail,\");\n    if (node->flags & REDIS_NODE_HANDSHAKE) ci =sdscat(ci,\"handshake,\");\n    if (node->flags & REDIS_NODE_NOADDR) ci = sdscat(ci,\"noaddr,\");\n    if (ci[sdslen(ci)-1] == ',') ci[sdslen(ci)-1] = ' ';\n\n    /* Slave of... or just \"-\" */\n    if (node->slaveof)\n        ci = sdscatprintf(ci,\"%.40s \",node->slaveof->name);\n    else\n        ci = sdscatprintf(ci,\"- \");\n\n    /* Latency from the POV of this node, link status */\n    ci = sdscatprintf(ci,\"%lld %lld %llu %s\",\n        (long long) node->ping_sent,\n        (long long) node->pong_received,\n        (unsigned long long) node->configEpoch,\n        (node->link || node->flags & REDIS_NODE_MYSELF) ?\n                    \"connected\" : \"disconnected\");\n\n    /* Slots served by this instance */\n    start = -1;\n    for (j = 0; j < REDIS_CLUSTER_SLOTS; j++) {\n        int bit;\n\n        if ((bit = clusterNodeGetSlotBit(node,j)) != 0) {\n            if (start == -1) start = j;\n        }\n        if (start != -1 && (!bit || j == REDIS_CLUSTER_SLOTS-1)) {\n            if (bit && j == REDIS_CLUSTER_SLOTS-1) j++;\n\n            if (start == j-1) {\n                ci = sdscatprintf(ci,\" %d\",start);\n            } else {\n                ci = sdscatprintf(ci,\" %d-%d\",start,j-1);\n            }\n            start = -1;\n        }\n    }\n\n    /* Just for MYSELF node we also dump info about slots that\n     * we are migrating to other instances or importing from other\n     * instances. */\n    if (node->flags & REDIS_NODE_MYSELF) {\n        for (j = 0; j < REDIS_CLUSTER_SLOTS; j++) {\n            if (server.cluster->migrating_slots_to[j]) {\n                ci = sdscatprintf(ci,\" [%d->-%.40s]\",j,\n                    server.cluster->migrating_slots_to[j]->name);\n            } else if (server.cluster->importing_slots_from[j]) {\n                ci = sdscatprintf(ci,\" [%d-<-%.40s]\",j,\n                    server.cluster->importing_slots_from[j]->name);\n            }\n        }\n    }\n    return ci;\n}\n\n/* Generate a csv-alike representation of the nodes we are aware of,\n * including the \"myself\" node, and return an SDS string containing the\n * representation (it is up to the caller to free it).\n *\n * 以 csv 格式记录当前节点已知所有节点的信息（包括当前节点自身），\n * 这些信息被保存到一个 sds 里面，并作为函数值返回。\n *\n * All the nodes matching at least one of the node flags specified in\n * \"filter\" are excluded from the output, so using zero as a filter will\n * include all the known nodes in the representation, including nodes in\n * the HANDSHAKE state.\n *\n * filter 参数可以用来指定节点的 flag 标识，\n * 带有被指定标识的节点不会被记录在输出结构中，\n * filter 为 0 表示记录所有节点的信息，包括 HANDSHAKE 状态的节点。\n *\n * The representation obtained using this function is used for the output\n * of the CLUSTER NODES function, and as format for the cluster\n * configuration file (nodes.conf) for a given node. \n *\n * 这个函数生成的结果会被用于 CLUSTER NODES 命令，\n * 以及用于生成 nodes.conf 配置文件。\n */\nsds clusterGenNodesDescription(int filter) {\n    sds ci = sdsempty(), ni;\n    dictIterator *di;\n    dictEntry *de;\n\n    // 遍历集群中的所有节点\n    di = dictGetSafeIterator(server.cluster->nodes);\n    while((de = dictNext(di)) != NULL) {\n        clusterNode *node = dictGetVal(de);\n\n        // 不打印包含指定 flag 的节点\n        if (node->flags & filter) continue;\n\n        ni = clusterGenNodeDescription(node);\n        ci = sdscatsds(ci,ni);\n        sdsfree(ni);\n        ci = sdscatlen(ci,\"\\n\",1);\n    }\n    dictReleaseIterator(di);\n\n    return ci;\n}\n\n// 取出一个 slot 数值\nint getSlotOrReply(redisClient *c, robj *o) {\n    long long slot;\n\n    if (getLongLongFromObject(o,&slot) != REDIS_OK ||\n        slot < 0 || slot >= REDIS_CLUSTER_SLOTS)\n    {\n        addReplyError(c,\"Invalid or out of range slot\");\n        return -1;\n    }\n    return (int) slot;\n}\n\n// CLUSTER 命令的实现\nvoid clusterCommand(redisClient *c) {\n\n    // 不能在非集群模式下使用该命令\n    if (server.cluster_enabled == 0) {\n        addReplyError(c,\"This instance has cluster support disabled\");\n        return;\n    }\n\n    if (!strcasecmp(c->argv[1]->ptr,\"meet\") && c->argc == 4) {\n        /* CLUSTER MEET <ip> <port> */\n        // 将给定地址的节点添加到当前节点所处的集群里面\n\n        long long port;\n\n        // 检查 port 参数的合法性\n        if (getLongLongFromObject(c->argv[3], &port) != REDIS_OK) {\n            addReplyErrorFormat(c,\"Invalid TCP port specified: %s\",\n                                (char*)c->argv[3]->ptr);\n            return;\n        }\n\n        // 尝试与给定地址的节点进行连接\n        if (clusterStartHandshake(c->argv[2]->ptr,port) == 0 &&\n            errno == EINVAL)\n        {\n            // 连接失败\n            addReplyErrorFormat(c,\"Invalid node address specified: %s:%s\",\n                            (char*)c->argv[2]->ptr, (char*)c->argv[3]->ptr);\n        } else {\n            // 连接成功\n            addReply(c,shared.ok);\n        }\n\n    } else if (!strcasecmp(c->argv[1]->ptr,\"nodes\") && c->argc == 2) {\n        /* CLUSTER NODES */\n        // 列出集群所有节点的信息\n        robj *o;\n        sds ci = clusterGenNodesDescription(0);\n\n        o = createObject(REDIS_STRING,ci);\n        addReplyBulk(c,o);\n        decrRefCount(o);\n\n    } else if (!strcasecmp(c->argv[1]->ptr,\"flushslots\") && c->argc == 2) {\n        /* CLUSTER FLUSHSLOTS */\n        // 删除当前节点的所有槽，让它变为不处理任何槽\n\n        // 删除槽必须在数据库为空的情况下进行\n        if (dictSize(server.db[0].dict) != 0) {\n            addReplyError(c,\"DB must be empty to perform CLUSTER FLUSHSLOTS.\");\n            return;\n        }\n        // 删除所有由该节点处理的槽\n        clusterDelNodeSlots(myself);\n        clusterDoBeforeSleep(CLUSTER_TODO_UPDATE_STATE|CLUSTER_TODO_SAVE_CONFIG);\n        addReply(c,shared.ok);\n\n    } else if ((!strcasecmp(c->argv[1]->ptr,\"addslots\") ||\n               !strcasecmp(c->argv[1]->ptr,\"delslots\")) && c->argc >= 3)\n    {\n        /* CLUSTER ADDSLOTS <slot> [slot] ... */\n        // 将一个或多个 slot 添加到当前节点\n\n        /* CLUSTER DELSLOTS <slot> [slot] ... */\n        // 从当前节点中删除一个或多个 slot\n    \n        int j, slot;\n\n        // 一个数组，记录所有要添加或者删除的槽\n        unsigned char *slots = zmalloc(REDIS_CLUSTER_SLOTS);\n\n        // 检查这是 delslots 还是 addslots\n        int del = !strcasecmp(c->argv[1]->ptr,\"delslots\");\n\n        // 将 slots 数组的所有值设置为 0\n        memset(slots,0,REDIS_CLUSTER_SLOTS);\n\n        /* Check that all the arguments are parsable and that all the\n         * slots are not already busy. */\n        // 处理所有输入 slot 参数\n        for (j = 2; j < c->argc; j++) {\n\n            // 获取 slot 数字\n            if ((slot = getSlotOrReply(c,c->argv[j])) == -1) {\n                zfree(slots);\n                return;\n            }\n\n            // 如果这是 delslots 命令，并且指定槽为未指定，那么返回一个错误\n            if (del && server.cluster->slots[slot] == NULL) {\n                addReplyErrorFormat(c,\"Slot %d is already unassigned\", slot);\n                zfree(slots);\n                return;\n            // 如果这是 addslots 命令，并且槽已经有节点在负责，那么返回一个错误\n            } else if (!del && server.cluster->slots[slot]) {\n                addReplyErrorFormat(c,\"Slot %d is already busy\", slot);\n                zfree(slots);\n                return;\n            }\n\n            // 如果某个槽指定了一次以上，那么返回一个错误\n            if (slots[slot]++ == 1) {\n                addReplyErrorFormat(c,\"Slot %d specified multiple times\",\n                    (int)slot);\n                zfree(slots);\n                return;\n            }\n        }\n\n        // 处理所有输入 slot\n        for (j = 0; j < REDIS_CLUSTER_SLOTS; j++) {\n            if (slots[j]) {\n                int retval;\n\n                /* If this slot was set as importing we can clear this \n                 * state as now we are the real owner of the slot. */\n                // 如果指定 slot 之前的状态为载入状态，那么现在可以清除这一状态\n                // 因为当前节点现在已经是 slot 的负责人了\n                if (server.cluster->importing_slots_from[j])\n                    server.cluster->importing_slots_from[j] = NULL;\n\n                // 添加或者删除指定 slot\n                retval = del ? clusterDelSlot(j) :\n                               clusterAddSlot(myself,j);\n                redisAssertWithInfo(c,NULL,retval == REDIS_OK);\n            }\n        }\n        zfree(slots);\n        clusterDoBeforeSleep(CLUSTER_TODO_UPDATE_STATE|CLUSTER_TODO_SAVE_CONFIG);\n        addReply(c,shared.ok);\n\n    } else if (!strcasecmp(c->argv[1]->ptr,\"setslot\") && c->argc >= 4) {\n        /* SETSLOT 10 MIGRATING <node ID> */\n        /* SETSLOT 10 IMPORTING <node ID> */\n        /* SETSLOT 10 STABLE */\n        /* SETSLOT 10 NODE <node ID> */\n        int slot;\n        clusterNode *n;\n\n        // 取出 slot 值\n        if ((slot = getSlotOrReply(c,c->argv[2])) == -1) return;\n\n        // CLUSTER SETSLOT <slot> MIGRATING <node id>\n        // 将本节点的槽 slot 迁移至 node id 所指定的节点\n        if (!strcasecmp(c->argv[3]->ptr,\"migrating\") && c->argc == 5) {\n            // 被迁移的槽必须属于本节点\n            if (server.cluster->slots[slot] != myself) {\n                addReplyErrorFormat(c,\"I'm not the owner of hash slot %u\",slot);\n                return;\n            }\n\n            // 迁移的目标节点必须是本节点已知的\n            if ((n = clusterLookupNode(c->argv[4]->ptr)) == NULL) {\n                addReplyErrorFormat(c,\"I don't know about node %s\",\n                    (char*)c->argv[4]->ptr);\n                return;\n            }\n\n            // 为槽设置迁移目标节点\n            server.cluster->migrating_slots_to[slot] = n;\n\n        // CLUSTER SETSLOT <slot> IMPORTING <node id>\n        // 从节点 node id 中导入槽 slot 到本节点\n        } else if (!strcasecmp(c->argv[3]->ptr,\"importing\") && c->argc == 5) {\n\n            // 如果 slot 槽本身已经由本节点处理，那么无须进行导入\n            if (server.cluster->slots[slot] == myself) {\n                addReplyErrorFormat(c,\n                    \"I'm already the owner of hash slot %u\",slot);\n                return;\n            }\n\n            // node id 指定的节点必须是本节点已知的，这样才能从目标节点导入槽\n            if ((n = clusterLookupNode(c->argv[4]->ptr)) == NULL) {\n                addReplyErrorFormat(c,\"I don't know about node %s\",\n                    (char*)c->argv[3]->ptr);\n                return;\n            }\n\n            // 为槽设置导入目标节点\n            server.cluster->importing_slots_from[slot] = n;\n\n        } else if (!strcasecmp(c->argv[3]->ptr,\"stable\") && c->argc == 4) {\n            /* CLUSTER SETSLOT <SLOT> STABLE */\n            // 取消对槽 slot 的迁移或者导入\n\n            server.cluster->importing_slots_from[slot] = NULL;\n            server.cluster->migrating_slots_to[slot] = NULL;\n\n        } else if (!strcasecmp(c->argv[3]->ptr,\"node\") && c->argc == 5) {\n            /* CLUSTER SETSLOT <SLOT> NODE <NODE ID> */\n            // 将未指派 slot 指派给 node id 指定的节点\n\n            // 查找目标节点\n            clusterNode *n = clusterLookupNode(c->argv[4]->ptr);\n\n            // 目标节点必须已存在\n            if (!n) {\n                addReplyErrorFormat(c,\"Unknown node %s\",\n                    (char*)c->argv[4]->ptr);\n                return;\n            }\n\n            /* If this hash slot was served by 'myself' before to switch\n             * make sure there are no longer local keys for this hash slot. */\n            // 如果这个槽之前由当前节点负责处理，那么必须保证槽里面没有键存在\n            if (server.cluster->slots[slot] == myself && n != myself) {\n                if (countKeysInSlot(slot) != 0) {\n                    addReplyErrorFormat(c,\n                        \"Can't assign hashslot %d to a different node \"\n                        \"while I still hold keys for this hash slot.\", slot);\n                    return;\n                }\n            }\n            /* If this slot is in migrating status but we have no keys\n             * for it assigning the slot to another node will clear\n             * the migratig status. */\n            if (countKeysInSlot(slot) == 0 &&\n                server.cluster->migrating_slots_to[slot])\n                server.cluster->migrating_slots_to[slot] = NULL;\n\n            /* If this node was importing this slot, assigning the slot to\n             * itself also clears the importing status. */\n            // 撤销本节点对 slot 的导入计划\n            if (n == myself &&\n                server.cluster->importing_slots_from[slot])\n            {\n                /* This slot was manually migrated, set this node configEpoch\n                 * to a new epoch so that the new version can be propagated\n                 * by the cluster.\n                 *\n                 * Note that if this ever results in a collision with another\n                 * node getting the same configEpoch, for example because a\n                 * failover happens at the same time we close the slot, the\n                 * configEpoch collision resolution will fix it assigning\n                 * a different epoch to each node. */\n                uint64_t maxEpoch = clusterGetMaxEpoch();\n\n                if (myself->configEpoch == 0 ||\n                    myself->configEpoch != maxEpoch)\n                {\n                    server.cluster->currentEpoch++;\n                    myself->configEpoch = server.cluster->currentEpoch;\n                    clusterDoBeforeSleep(CLUSTER_TODO_FSYNC_CONFIG);\n                    redisLog(REDIS_WARNING,\n                        \"configEpoch set to %llu after importing slot %d\",\n                        (unsigned long long) myself->configEpoch, slot);\n                }\n                server.cluster->importing_slots_from[slot] = NULL;\n            }\n\n            // 将槽设置为未指派\n            clusterDelSlot(slot);\n\n            // 将槽指派给目标节点\n            clusterAddSlot(n,slot);\n\n        } else {\n            addReplyError(c,\n                \"Invalid CLUSTER SETSLOT action or number of arguments\");\n            return;\n        }\n        clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG|CLUSTER_TODO_UPDATE_STATE);\n        addReply(c,shared.ok);\n\n    } else if (!strcasecmp(c->argv[1]->ptr,\"info\") && c->argc == 2) {\n        /* CLUSTER INFO */\n        // 打印出集群的当前信息\n\n        char *statestr[] = {\"ok\",\"fail\",\"needhelp\"};\n        int slots_assigned = 0, slots_ok = 0, slots_pfail = 0, slots_fail = 0;\n        int j;\n\n        // 统计集群中的已指派节点、已下线节点、疑似下线节点和正常节点的数量\n        for (j = 0; j < REDIS_CLUSTER_SLOTS; j++) {\n            clusterNode *n = server.cluster->slots[j];\n\n            // 跳过未指派节点\n            if (n == NULL) continue;\n\n            // 统计已指派节点的数量\n            slots_assigned++;\n            if (nodeFailed(n)) {\n                slots_fail++;\n            } else if (nodeTimedOut(n)) {\n                slots_pfail++;\n            } else {\n                // 正常节点\n                slots_ok++;\n            }\n        }\n\n        // 打印信息\n        sds info = sdscatprintf(sdsempty(),\n            \"cluster_state:%s\\r\\n\"\n            \"cluster_slots_assigned:%d\\r\\n\"\n            \"cluster_slots_ok:%d\\r\\n\"\n            \"cluster_slots_pfail:%d\\r\\n\"\n            \"cluster_slots_fail:%d\\r\\n\"\n            \"cluster_known_nodes:%lu\\r\\n\"\n            \"cluster_size:%d\\r\\n\"\n            \"cluster_current_epoch:%llu\\r\\n\"\n            \"cluster_stats_messages_sent:%lld\\r\\n\"\n            \"cluster_stats_messages_received:%lld\\r\\n\"\n            , statestr[server.cluster->state],\n            slots_assigned,\n            slots_ok,\n            slots_pfail,\n            slots_fail,\n            dictSize(server.cluster->nodes),\n            server.cluster->size,\n            (unsigned long long) server.cluster->currentEpoch,\n            server.cluster->stats_bus_messages_sent,\n            server.cluster->stats_bus_messages_received\n        );\n        addReplySds(c,sdscatprintf(sdsempty(),\"$%lu\\r\\n\",\n            (unsigned long)sdslen(info)));\n        addReplySds(c,info);\n        addReply(c,shared.crlf);\n\n    } else if (!strcasecmp(c->argv[1]->ptr,\"saveconfig\") && c->argc == 2) {\n        // CLUSTER SAVECONFIG 命令\n        // 将 nodes.conf 文件保存到磁盘里面\n\n        // 保存\n        int retval = clusterSaveConfig(1);\n\n        // 检查错误\n        if (retval == 0)\n            addReply(c,shared.ok);\n        else\n            addReplyErrorFormat(c,\"error saving the cluster node config: %s\",\n                strerror(errno));\n\n    } else if (!strcasecmp(c->argv[1]->ptr,\"keyslot\") && c->argc == 3) {\n        /* CLUSTER KEYSLOT <key> */\n        // 返回 key 应该被 hash 到那个槽上\n\n        sds key = c->argv[2]->ptr;\n\n        addReplyLongLong(c,keyHashSlot(key,sdslen(key)));\n\n    } else if (!strcasecmp(c->argv[1]->ptr,\"countkeysinslot\") && c->argc == 3) {\n        /* CLUSTER COUNTKEYSINSLOT <slot> */\n        // 计算指定 slot 上的键数量\n\n        long long slot;\n\n        // 取出 slot 参数\n        if (getLongLongFromObjectOrReply(c,c->argv[2],&slot,NULL) != REDIS_OK)\n            return;\n        if (slot < 0 || slot >= REDIS_CLUSTER_SLOTS) {\n            addReplyError(c,\"Invalid slot\");\n            return;\n        }\n\n        addReplyLongLong(c,countKeysInSlot(slot));\n\n    } else if (!strcasecmp(c->argv[1]->ptr,\"getkeysinslot\") && c->argc == 4) {\n        /* CLUSTER GETKEYSINSLOT <slot> <count> */\n        // 打印 count 个属于 slot 槽的键\n\n        long long maxkeys, slot;\n        unsigned int numkeys, j;\n        robj **keys;\n\n        // 取出 slot 参数\n        if (getLongLongFromObjectOrReply(c,c->argv[2],&slot,NULL) != REDIS_OK)\n            return;\n        // 取出 count 参数\n        if (getLongLongFromObjectOrReply(c,c->argv[3],&maxkeys,NULL)\n            != REDIS_OK)\n            return;\n        // 检查参数的合法性\n        if (slot < 0 || slot >= REDIS_CLUSTER_SLOTS || maxkeys < 0) {\n            addReplyError(c,\"Invalid slot or number of keys\");\n            return;\n        }\n\n        // 分配一个保存键的数组\n        keys = zmalloc(sizeof(robj*)*maxkeys);\n        // 将键记录到 keys 数组\n        numkeys = getKeysInSlot(slot, keys, maxkeys);\n\n        // 打印获得的键\n        addReplyMultiBulkLen(c,numkeys);\n        for (j = 0; j < numkeys; j++) addReplyBulk(c,keys[j]);\n        zfree(keys);\n\n    } else if (!strcasecmp(c->argv[1]->ptr,\"forget\") && c->argc == 3) {\n        /* CLUSTER FORGET <NODE ID> */\n        // 从集群中删除 NODE_ID 指定的节点\n\n        // 查找 NODE_ID 指定的节点\n        clusterNode *n = clusterLookupNode(c->argv[2]->ptr);\n\n        // 该节点不存在于集群中\n        if (!n) {\n            addReplyErrorFormat(c,\"Unknown node %s\", (char*)c->argv[2]->ptr);\n            return;\n        } else if (n == myself) {\n            addReplyError(c,\"I tried hard but I can't forget myself...\");\n            return;\n        } else if (nodeIsSlave(myself) && myself->slaveof == n) {\n            addReplyError(c,\"Can't forget my master!\");\n            return;\n        }\n\n        // 将集群添加到黑名单\n        clusterBlacklistAddNode(n);\n        // 从集群中删除该节点\n        clusterDelNode(n);\n        clusterDoBeforeSleep(CLUSTER_TODO_UPDATE_STATE|\n                             CLUSTER_TODO_SAVE_CONFIG);\n        addReply(c,shared.ok);\n\n    } else if (!strcasecmp(c->argv[1]->ptr,\"replicate\") && c->argc == 3) {\n        /* CLUSTER REPLICATE <NODE ID> */\n        // 将当前节点设置为 NODE_ID 指定的节点的从节点（复制品）\n\n        // 根据名字查找节点\n        clusterNode *n = clusterLookupNode(c->argv[2]->ptr);\n\n        /* Lookup the specified node in our table. */\n        if (!n) {\n            addReplyErrorFormat(c,\"Unknown node %s\", (char*)c->argv[2]->ptr);\n            return;\n        }\n\n        /* I can't replicate myself. */\n        // 指定节点是自己，不能进行复制\n        if (n == myself) {\n            addReplyError(c,\"Can't replicate myself\");\n            return;\n        }\n\n        /* Can't replicate a slave. */\n        // 不能复制一个从节点\n        if (n->slaveof != NULL) {\n            addReplyError(c,\"I can only replicate a master, not a slave.\");\n            return;\n        }\n\n        /* If the instance is currently a master, it should have no assigned\n         * slots nor keys to accept to replicate some other node.\n         * Slaves can switch to another master without issues. */\n        // 节点必须没有被指派任何槽，并且数据库必须为空\n        if (nodeIsMaster(myself) &&\n            (myself->numslots != 0 || dictSize(server.db[0].dict) != 0)) {\n            addReplyError(c,\n                \"To set a master the node must be empty and \"\n                \"without assigned slots.\");\n            return;\n        }\n\n        /* Set the master. */\n        // 将节点 n 设为本节点的主节点\n        clusterSetMaster(n);\n        clusterDoBeforeSleep(CLUSTER_TODO_UPDATE_STATE|CLUSTER_TODO_SAVE_CONFIG);\n        addReply(c,shared.ok);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"slaves\") && c->argc == 3) {\n        /* CLUSTER SLAVES <NODE ID> */\n        // 打印给定主节点的所有从节点的信息\n\n        clusterNode *n = clusterLookupNode(c->argv[2]->ptr);\n        int j;\n\n        /* Lookup the specified node in our table. */\n        if (!n) {\n            addReplyErrorFormat(c,\"Unknown node %s\", (char*)c->argv[2]->ptr);\n            return;\n        }\n\n        if (nodeIsSlave(n)) {\n            addReplyError(c,\"The specified node is not a master\");\n            return;\n        }\n\n        addReplyMultiBulkLen(c,n->numslaves);\n        for (j = 0; j < n->numslaves; j++) {\n            sds ni = clusterGenNodeDescription(n->slaves[j]);\n            addReplyBulkCString(c,ni);\n            sdsfree(ni);\n        }\n    } else if (!strcasecmp(c->argv[1]->ptr,\"failover\") &&\n               (c->argc == 2 || c->argc == 3))\n    {\n        /* CLUSTER FAILOVER [FORCE] */\n        // 执行手动故障转移\n\n        int force = 0;\n\n        if (c->argc == 3) {\n            if (!strcasecmp(c->argv[2]->ptr,\"force\")) {\n                force = 1;\n            } else {\n                addReply(c,shared.syntaxerr);\n                return;\n            }\n        }\n\n        // 命令只能发送给从节点\n        if (nodeIsMaster(myself)) {\n            addReplyError(c,\"You should send CLUSTER FAILOVER to a slave\");\n            return;\n        } else if (!force &&\n                   (myself->slaveof == NULL || nodeFailed(myself->slaveof) ||\n                   myself->slaveof->link == NULL))\n        {\n            // 如果主节点已下线或者处于失效状态\n            // 并且命令没有给定 force 参数，那么命令执行失败\n            addReplyError(c,\"Master is down or failed, \"\n                            \"please use CLUSTER FAILOVER FORCE\");\n            return;\n        }\n\n        // 重置手动故障转移的有关属性\n        resetManualFailover();\n        // 设定手动故障转移的最大执行时限\n        server.cluster->mf_end = mstime() + REDIS_CLUSTER_MF_TIMEOUT;\n\n        /* If this is a forced failover, we don't need to talk with our master\n         * to agree about the offset. We just failover taking over it without\n         * coordination. */\n        // 如果这是强制的手动 failover ，那么直接开始 failover ，\n        // 无须向其他 master 沟通偏移量。\n        if (force) {\n            // 如果这是强制的手动故障转移，那么直接开始执行故障转移操作\n            server.cluster->mf_can_start = 1;\n        } else {\n            // 如果不是强制的话，那么需要和主节点比对相互的偏移量是否一致\n            clusterSendMFStart(myself->slaveof);\n        }\n        redisLog(REDIS_WARNING,\"Manual failover user request accepted.\");\n        addReply(c,shared.ok);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"set-config-epoch\") && c->argc == 3)\n    {\n        /* CLUSTER SET-CONFIG-EPOCH <epoch>\n         *\n         * The user is allowed to set the config epoch only when a node is\n         * totally fresh: no config epoch, no other known node, and so forth.\n         * This happens at cluster creation time to start with a cluster where\n         * every node has a different node ID, without to rely on the conflicts\n         * resolution system which is too slow when a big cluster is created. */\n        long long epoch;\n\n        if (getLongLongFromObjectOrReply(c,c->argv[2],&epoch,NULL) != REDIS_OK)\n            return;\n\n        if (epoch < 0) {\n            addReplyErrorFormat(c,\"Invalid config epoch specified: %lld\",epoch);\n        } else if (dictSize(server.cluster->nodes) > 1) {\n            addReplyError(c,\"The user can assign a config epoch only when the \"\n                            \"node does not know any other node.\");\n        } else if (myself->configEpoch != 0) {\n            addReplyError(c,\"Node config epoch is already non-zero\");\n        } else {\n            myself->configEpoch = epoch;\n            /* No need to fsync the config here since in the unlucky event\n             * of a failure to persist the config, the conflict resolution code\n             * will assign an unique config to this node. */\n            clusterDoBeforeSleep(CLUSTER_TODO_UPDATE_STATE|\n                                 CLUSTER_TODO_SAVE_CONFIG);\n            addReply(c,shared.ok);\n        }\n    } else if (!strcasecmp(c->argv[1]->ptr,\"reset\") &&\n               (c->argc == 2 || c->argc == 3))\n    {\n        /* CLUSTER RESET [SOFT|HARD] */\n        int hard = 0;\n\n        /* Parse soft/hard argument. Default is soft. */\n        if (c->argc == 3) {\n            if (!strcasecmp(c->argv[2]->ptr,\"hard\")) {\n                hard = 1;\n            } else if (!strcasecmp(c->argv[2]->ptr,\"soft\")) {\n                hard = 0;\n            } else {\n                addReply(c,shared.syntaxerr);\n                return;\n            }\n        }\n\n        /* Slaves can be reset while containing data, but not master nodes\n         * that must be empty. */\n        if (nodeIsMaster(myself) && dictSize(c->db->dict) != 0) {\n            addReplyError(c,\"CLUSTER RESET can't be called with \"\n                            \"master nodes containing keys\");\n            return;\n        }\n        clusterReset(hard);\n        addReply(c,shared.ok);\n    } else {\n        addReplyError(c,\"Wrong CLUSTER subcommand or number of arguments\");\n    }\n}\n\n/* -----------------------------------------------------------------------------\n * DUMP, RESTORE and MIGRATE commands\n * -------------------------------------------------------------------------- */\n\n/* Generates a DUMP-format representation of the object 'o', adding it to the\n * io stream pointed by 'rio'. This function can't fail. \n *\n * 创建对象 o 的一个 DUMP 格式表示，\n * 并将它添加到 rio 指针指向的 io 流当中。\n */\nvoid createDumpPayload(rio *payload, robj *o) {\n    unsigned char buf[2];\n    uint64_t crc;\n\n    /* Serialize the object in a RDB-like format. It consist of an object type\n     * byte followed by the serialized object. This is understood by RESTORE. */\n    // 将对象序列化为一个 RDB 格式对象\n    // 序列化对象以对象类型为首，后跟序列化后的对象\n    // 如图\n    //\n    // |<-- RDB payload  -->|\n    //      序列化数据\n    // +-------------+------+\n    // | 1 byte type | obj  |\n    // +-------------+------+\n    rioInitWithBuffer(payload,sdsempty());\n    redisAssert(rdbSaveObjectType(payload,o));\n    redisAssert(rdbSaveObject(payload,o));\n\n    /* Write the footer, this is how it looks like:\n     * ----------------+---------------------+---------------+\n     * ... RDB payload | 2 bytes RDB version | 8 bytes CRC64 |\n     * ----------------+---------------------+---------------+\n     * RDB version and CRC are both in little endian.\n     */\n\n    /* RDB version */\n    // 写入 RDB 版本\n    buf[0] = REDIS_RDB_VERSION & 0xff;\n    buf[1] = (REDIS_RDB_VERSION >> 8) & 0xff;\n    payload->io.buffer.ptr = sdscatlen(payload->io.buffer.ptr,buf,2);\n\n    /* CRC64 */\n    // 写入 CRC 校验和\n    crc = crc64(0,(unsigned char*)payload->io.buffer.ptr,\n                sdslen(payload->io.buffer.ptr));\n    memrev64ifbe(&crc);\n    payload->io.buffer.ptr = sdscatlen(payload->io.buffer.ptr,&crc,8);\n\n    // 整个数据的结构:\n    //\n    // | <--- 序列化数据 -->|\n    // +-------------+------+---------------------+---------------+\n    // | 1 byte type | obj  | 2 bytes RDB version | 8 bytes CRC64 |\n    // +-------------+------+---------------------+---------------+\n\n}\n\n/* Verify that the RDB version of the dump payload matches the one of this Redis\n * instance and that the checksum is ok.\n *\n * 检查输入的 DUMP 数据中， RDB 版本是否和当前 Redis 实例所使用的 RDB 版本相同，\n * 并检查校验和是否正确。\n *\n * If the DUMP payload looks valid REDIS_OK is returned, otherwise REDIS_ERR\n * is returned. \n *\n * 检查正常返回 REDIS_OK ，否则返回 REDIS_ERR 。\n */\nint verifyDumpPayload(unsigned char *p, size_t len) {\n    unsigned char *footer;\n    uint16_t rdbver;\n    uint64_t crc;\n\n    /* At least 2 bytes of RDB version and 8 of CRC64 should be present. */\n    // 因为序列化数据至少包含 2 个字节的 RDB 版本\n    // 以及 8 个字节的 CRC64 校验和\n    // 所以序列化数据不可能少于 10 个字节\n    if (len < 10) return REDIS_ERR;\n\n    // 指向数据的最后 10 个字节\n    footer = p+(len-10);\n\n    /* Verify RDB version */\n    // 检查序列化数据的版本号，看是否和当前实例使用的版本号一致\n    rdbver = (footer[1] << 8) | footer[0];\n    if (rdbver != REDIS_RDB_VERSION) return REDIS_ERR;\n\n    /* Verify CRC64 */\n    // 检查数据的 CRC64 校验和是否正确\n    crc = crc64(0,p,len-8);\n    memrev64ifbe(&crc);\n    return (memcmp(&crc,footer+2,8) == 0) ? REDIS_OK : REDIS_ERR;\n}\n\n/* DUMP keyname\n * DUMP is actually not used by Redis Cluster but it is the obvious\n * complement of RESTORE and can be useful for different applications. */\nvoid dumpCommand(redisClient *c) {\n    robj *o, *dumpobj;\n    rio payload;\n\n    /* Check if the key is here. */\n    // 取出给定键的值\n    if ((o = lookupKeyRead(c->db,c->argv[1])) == NULL) {\n        addReply(c,shared.nullbulk);\n        return;\n    }\n\n    /* Create the DUMP encoded representation. */\n    // 创建给定值的一个 DUMP 编码表示\n    createDumpPayload(&payload,o);\n\n    /* Transfer to the client */\n    // 将编码后的键值对数据返回给客户端\n    dumpobj = createObject(REDIS_STRING,payload.io.buffer.ptr);\n    addReplyBulk(c,dumpobj);\n    decrRefCount(dumpobj);\n\n    return;\n}\n\n/* RESTORE key ttl serialized-value [REPLACE] */\n// 根据给定的 DUMP 数据，还原出一个键值对数据，并将它保存到数据库里面\nvoid restoreCommand(redisClient *c) {\n    long long ttl;\n    rio payload;\n    int j, type, replace = 0;\n    robj *obj;\n\n    /* Parse additional options */\n    // 是否使用了 REPLACE 选项？\n    for (j = 4; j < c->argc; j++) {\n        if (!strcasecmp(c->argv[j]->ptr,\"replace\")) {\n            replace = 1;\n        } else {\n            addReply(c,shared.syntaxerr);\n            return;\n        }\n    }\n\n    /* Make sure this key does not already exist here... */\n    // 如果没有给定 REPLACE 选项，并且键已经存在，那么返回错误\n    if (!replace && lookupKeyWrite(c->db,c->argv[1]) != NULL) {\n        addReply(c,shared.busykeyerr);\n        return;\n    }\n\n    /* Check if the TTL value makes sense */\n    // 取出（可能有的） TTL 值\n    if (getLongLongFromObjectOrReply(c,c->argv[2],&ttl,NULL) != REDIS_OK) {\n        return;\n    } else if (ttl < 0) {\n        addReplyError(c,\"Invalid TTL value, must be >= 0\");\n        return;\n    }\n\n    /* Verify RDB version and data checksum. */\n    // 检查 RDB 版本和校验和\n    if (verifyDumpPayload(c->argv[3]->ptr,sdslen(c->argv[3]->ptr)) == REDIS_ERR)\n    {\n        addReplyError(c,\"DUMP payload version or checksum are wrong\");\n        return;\n    }\n\n    // 读取 DUMP 数据，并反序列化出键值对的类型和值\n    rioInitWithBuffer(&payload,c->argv[3]->ptr);\n    if (((type = rdbLoadObjectType(&payload)) == -1) ||\n        ((obj = rdbLoadObject(type,&payload)) == NULL))\n    {\n        addReplyError(c,\"Bad data format\");\n        return;\n    }\n\n    /* Remove the old key if needed. */\n    // 如果给定了 REPLACE 选项，那么先删除数据库中已存在的同名键\n    if (replace) dbDelete(c->db,c->argv[1]);\n\n    /* Create the key and set the TTL if any */\n    // 将键值对添加到数据库\n    dbAdd(c->db,c->argv[1],obj);\n\n    // 如果键带有 TTL 的话，设置键的 TTL\n    if (ttl) setExpire(c->db,c->argv[1],mstime()+ttl);\n\n    signalModifiedKey(c->db,c->argv[1]);\n\n    addReply(c,shared.ok);\n    server.dirty++;\n}\n\n/* MIGRATE socket cache implementation.\n *\n * MIGRATE 套接字缓存实现\n *\n * We take a map between host:ip and a TCP socket that we used to connect\n * to this instance in recent time.\n *\n * 保存一个字典，字典的键为 host:ip ，值为最近使用的连接向指定地址的 TCP 套接字。\n *\n * This sockets are closed when the max number we cache is reached, and also\n * in serverCron() when they are around for more than a few seconds. \n *\n * 这个字典在缓存数达到上限时被释放，\n * 并且 serverCron() 也会定期删除字典中的一些过期套接字。\n */\n// 最大缓存数\n#define MIGRATE_SOCKET_CACHE_ITEMS 64 /* max num of items in the cache. */\n// 套接字保质期（超过这个时间的套接字会被删除）\n#define MIGRATE_SOCKET_CACHE_TTL 10 /* close cached socekts after 10 sec. */\n\ntypedef struct migrateCachedSocket {\n\n    // 套接字描述符\n    int fd;\n\n    // 最后一次使用的时间\n    time_t last_use_time;\n\n} migrateCachedSocket;\n\n/* Return a TCP scoket connected with the target instance, possibly returning\n * a cached one.\n *\n * 返回一个连接向指定地址的 TCP 套接字，这个套接字可能是一个缓存套接字。\n *\n * This function is responsible of sending errors to the client if a\n * connection can't be established. In this case -1 is returned.\n * Otherwise on success the socket is returned, and the caller should not\n * attempt to free it after usage.\n *\n * 如果连接出错，那么函数返回 -1 。\n * 如果连接正常，那么函数返回 TCP 套接字描述符。\n *\n * If the caller detects an error while using the socket, migrateCloseSocket()\n * should be called so that the connection will be craeted from scratch\n * the next time. \n *\n * 如果调用者在使用这个函数返回的套接字时遇上错误，\n * 那么调用者会使用 migrateCloseSocket() 来关闭出错的套接字，\n * 这样下次要连接相同地址时，服务器就会创建新的套接字来进行连接。\n */\nint migrateGetSocket(redisClient *c, robj *host, robj *port, long timeout) {\n    int fd;\n    sds name = sdsempty();\n    migrateCachedSocket *cs;\n\n    /* Check if we have an already cached socket for this ip:port pair. */\n    // 根据 ip 和 port 创建地址名字\n    name = sdscatlen(name,host->ptr,sdslen(host->ptr));\n    name = sdscatlen(name,\":\",1);\n    name = sdscatlen(name,port->ptr,sdslen(port->ptr));\n    \n    // 在套接字缓存中查找套接字是否已经存在\n    cs = dictFetchValue(server.migrate_cached_sockets,name);\n    // 缓存存在，更新最后一次使用时间，以免它被当作过期套接字而被释放\n    if (cs) {\n        sdsfree(name);\n        cs->last_use_time = server.unixtime;\n        return cs->fd;\n    }\n\n    /* No cached socket, create one. */\n    // 没有缓存，创建一个新的缓存\n    if (dictSize(server.migrate_cached_sockets) == MIGRATE_SOCKET_CACHE_ITEMS) {\n\n        // 如果缓存数已经达到上线，那么在创建套接字之前，先随机删除一个连接\n\n        /* Too many items, drop one at random. */\n        dictEntry *de = dictGetRandomKey(server.migrate_cached_sockets);\n        cs = dictGetVal(de);\n        close(cs->fd);\n        zfree(cs);\n        dictDelete(server.migrate_cached_sockets,dictGetKey(de));\n    }\n\n    /* Create the socket */\n    // 创建连接\n    fd = anetTcpNonBlockConnect(server.neterr,c->argv[1]->ptr,\n                atoi(c->argv[2]->ptr));\n    if (fd == -1) {\n        sdsfree(name);\n        addReplyErrorFormat(c,\"Can't connect to target node: %s\",\n            server.neterr);\n        return -1;\n    }\n    anetEnableTcpNoDelay(server.neterr,fd);\n\n    /* Check if it connects within the specified timeout. */\n    // 检查连接的超时设置\n    if ((aeWait(fd,AE_WRITABLE,timeout) & AE_WRITABLE) == 0) {\n        sdsfree(name);\n        addReplySds(c,\n            sdsnew(\"-IOERR error or timeout connecting to the client\\r\\n\"));\n        close(fd);\n        return -1;\n    }\n\n    /* Add to the cache and return it to the caller. */\n    // 将连接添加到缓存\n    cs = zmalloc(sizeof(*cs));\n    cs->fd = fd;\n    cs->last_use_time = server.unixtime;\n    dictAdd(server.migrate_cached_sockets,name,cs);\n\n    return fd;\n}\n\n/* Free a migrate cached connection. */\n// 释放一个缓存连接\nvoid migrateCloseSocket(robj *host, robj *port) {\n    sds name = sdsempty();\n    migrateCachedSocket *cs;\n\n    // 根据 ip 和 port 创建连接的名字\n    name = sdscatlen(name,host->ptr,sdslen(host->ptr));\n    name = sdscatlen(name,\":\",1);\n    name = sdscatlen(name,port->ptr,sdslen(port->ptr));\n    // 查找连接\n    cs = dictFetchValue(server.migrate_cached_sockets,name);\n    if (!cs) {\n        sdsfree(name);\n        return;\n    }\n\n    // 关闭连接\n    close(cs->fd);\n    zfree(cs);\n\n    // 从缓存中删除该连接\n    dictDelete(server.migrate_cached_sockets,name);\n    sdsfree(name);\n}\n\n// 移除过期的连接，由 redis.c/serverCron() 调用\nvoid migrateCloseTimedoutSockets(void) {\n    dictIterator *di = dictGetSafeIterator(server.migrate_cached_sockets);\n    dictEntry *de;\n\n    while((de = dictNext(di)) != NULL) {\n        migrateCachedSocket *cs = dictGetVal(de);\n\n        // 如果套接字最后一次使用的时间已经超过 MIGRATE_SOCKET_CACHE_TTL \n        // 那么表示该套接字过期，释放它！\n        if ((server.unixtime - cs->last_use_time) > MIGRATE_SOCKET_CACHE_TTL) {\n            close(cs->fd);\n            zfree(cs);\n            dictDelete(server.migrate_cached_sockets,dictGetKey(de));\n        }\n    }\n    dictReleaseIterator(di);\n}\n\n/* MIGRATE host port key dbid timeout [COPY | REPLACE] */\nvoid migrateCommand(redisClient *c) {\n    int fd, copy, replace, j;\n    long timeout;\n    long dbid;\n    long long ttl, expireat;\n    robj *o;\n    rio cmd, payload;\n    int retry_num = 0;\n\ntry_again:\n    /* Initialization */\n    copy = 0;\n    replace = 0;\n    ttl = 0;\n\n    /* Parse additional options */\n    // 读入 COPY 或者 REPLACE 选项\n    for (j = 6; j < c->argc; j++) {\n        if (!strcasecmp(c->argv[j]->ptr,\"copy\")) {\n            copy = 1;\n        } else if (!strcasecmp(c->argv[j]->ptr,\"replace\")) {\n            replace = 1;\n        } else {\n            addReply(c,shared.syntaxerr);\n            return;\n        }\n    }\n\n    /* Sanity check */\n    // 检查输入参数的正确性\n    if (getLongFromObjectOrReply(c,c->argv[5],&timeout,NULL) != REDIS_OK)\n        return;\n    if (getLongFromObjectOrReply(c,c->argv[4],&dbid,NULL) != REDIS_OK)\n        return;\n    if (timeout <= 0) timeout = 1000;\n\n    /* Check if the key is here. If not we reply with success as there is\n     * nothing to migrate (for instance the key expired in the meantime), but\n     * we include such information in the reply string. */\n    // 取出键的值对象\n    if ((o = lookupKeyRead(c->db,c->argv[3])) == NULL) {\n        addReplySds(c,sdsnew(\"+NOKEY\\r\\n\"));\n        return;\n    }\n\n    /* Connect */\n    // 获取套接字连接\n    fd = migrateGetSocket(c,c->argv[1],c->argv[2],timeout);\n    if (fd == -1) return; /* error sent to the client by migrateGetSocket() */\n\n    /* Create RESTORE payload and generate the protocol to call the command. */\n    // 创建用于指定数据库的 SELECT 命令，以免键值对被还原到了错误的地方\n    rioInitWithBuffer(&cmd,sdsempty());\n    redisAssertWithInfo(c,NULL,rioWriteBulkCount(&cmd,'*',2));\n    redisAssertWithInfo(c,NULL,rioWriteBulkString(&cmd,\"SELECT\",6));\n    redisAssertWithInfo(c,NULL,rioWriteBulkLongLong(&cmd,dbid));\n\n    // 取出键的过期时间戳\n    expireat = getExpire(c->db,c->argv[3]);\n    if (expireat != -1) {\n        ttl = expireat-mstime();\n        if (ttl < 1) ttl = 1;\n    }\n    redisAssertWithInfo(c,NULL,rioWriteBulkCount(&cmd,'*',replace ? 5 : 4));\n\n    // 如果运行在集群模式下，那么发送的命令为 RESTORE-ASKING\n    // 如果运行在非集群模式下，那么发送的命令为 RESTORE\n    if (server.cluster_enabled)\n        redisAssertWithInfo(c,NULL,\n            rioWriteBulkString(&cmd,\"RESTORE-ASKING\",14));\n    else\n        redisAssertWithInfo(c,NULL,rioWriteBulkString(&cmd,\"RESTORE\",7));\n\n    // 写入键名和过期时间\n    redisAssertWithInfo(c,NULL,sdsEncodedObject(c->argv[3]));\n    redisAssertWithInfo(c,NULL,rioWriteBulkString(&cmd,c->argv[3]->ptr,\n            sdslen(c->argv[3]->ptr)));\n    redisAssertWithInfo(c,NULL,rioWriteBulkLongLong(&cmd,ttl));\n\n    /* Emit the payload argument, that is the serialized object using\n     * the DUMP format. */\n    // 将值对象进行序列化\n    createDumpPayload(&payload,o);\n    // 写入序列化对象\n    redisAssertWithInfo(c,NULL,rioWriteBulkString(&cmd,payload.io.buffer.ptr,\n                                sdslen(payload.io.buffer.ptr)));\n    sdsfree(payload.io.buffer.ptr);\n\n    /* Add the REPLACE option to the RESTORE command if it was specified\n     * as a MIGRATE option. */\n    // 是否设置了 REPLACE 命令？\n    if (replace)\n        // 写入 REPLACE 参数\n        redisAssertWithInfo(c,NULL,rioWriteBulkString(&cmd,\"REPLACE\",7));\n\n    /* Transfer the query to the other node in 64K chunks. */\n    // 以 64 kb 每次的大小向对方发送数据\n    errno = 0;\n    {\n        sds buf = cmd.io.buffer.ptr;\n        size_t pos = 0, towrite;\n        int nwritten = 0;\n\n        while ((towrite = sdslen(buf)-pos) > 0) {\n            towrite = (towrite > (64*1024) ? (64*1024) : towrite);\n            nwritten = syncWrite(fd,buf+pos,towrite,timeout);\n            if (nwritten != (signed)towrite) goto socket_wr_err;\n            pos += nwritten;\n        }\n    }\n\n    /* Read back the reply. */\n    // 读取命令的回复\n    {\n        char buf1[1024];\n        char buf2[1024];\n\n        /* Read the two replies */\n        if (syncReadLine(fd, buf1, sizeof(buf1), timeout) <= 0)\n            goto socket_rd_err;\n        if (syncReadLine(fd, buf2, sizeof(buf2), timeout) <= 0)\n            goto socket_rd_err;\n\n        // 检查 RESTORE 命令执行是否成功\n\n        if (buf1[0] == '-' || buf2[0] == '-') {\n\n            // 执行出错。。。\n\n            addReplyErrorFormat(c,\"Target instance replied with error: %s\",\n                (buf1[0] == '-') ? buf1+1 : buf2+1);\n        } else {\n\n            // 执行成功。。。\n\n            robj *aux;\n\n            // 如果没有指定 COPY 选项，那么删除本机数据库中的键\n            if (!copy) {\n                /* No COPY option: remove the local key, signal the change. */\n                dbDelete(c->db,c->argv[3]);\n                signalModifiedKey(c->db,c->argv[3]);\n            }\n            addReply(c,shared.ok);\n            server.dirty++;\n\n            /* Translate MIGRATE as DEL for replication/AOF. */\n            // 如果键被删除了的话，向 AOF 文件和从服务器/节点发送一个 DEL 命令\n            aux = createStringObject(\"DEL\",3);\n            rewriteClientCommandVector(c,2,aux,c->argv[3]);\n            decrRefCount(aux);\n        }\n    }\n\n    sdsfree(cmd.io.buffer.ptr);\n    return;\n\nsocket_wr_err:\n    sdsfree(cmd.io.buffer.ptr);\n    migrateCloseSocket(c->argv[1],c->argv[2]);\n    if (errno != ETIMEDOUT && retry_num++ == 0) goto try_again;\n    addReplySds(c,\n        sdsnew(\"-IOERR error or timeout writing to target instance\\r\\n\"));\n    return;\n\nsocket_rd_err:\n    sdsfree(cmd.io.buffer.ptr);\n    migrateCloseSocket(c->argv[1],c->argv[2]);\n    if (errno != ETIMEDOUT && retry_num++ == 0) goto try_again;\n    addReplySds(c,\n        sdsnew(\"-IOERR error or timeout reading from target node\\r\\n\"));\n    return;\n}\n\n/* -----------------------------------------------------------------------------\n * Cluster functions related to serving / redirecting clients\n * -------------------------------------------------------------------------- */\n\n/* The ASKING command is required after a -ASK redirection.\n *\n * 客户端在接到 -ASK 转向之后，需要发送 ASKING 命令。\n *\n * The client should issue ASKING before to actually send the command to\n * the target instance. See the Redis Cluster specification for more\n * information. \n *\n * 客户端应该在向目标节点发送命令之前，向节点发送 ASKING 命令。\n * 具体原因请参考 Redis 集群规范。\n */\nvoid askingCommand(redisClient *c) {\n\n    if (server.cluster_enabled == 0) {\n        addReplyError(c,\"This instance has cluster support disabled\");\n        return;\n    }\n\n    // 打开客户端的标识\n    c->flags |= REDIS_ASKING;\n\n    addReply(c,shared.ok);\n}\n\n/* The READONLY command is uesd by clients to enter the read-only mode.\n * In this mode slaves will not redirect clients as long as clients access\n * with read-only commands to keys that are served by the slave's master. */\nvoid readonlyCommand(redisClient *c) {\n    if (server.cluster_enabled == 0) {\n        addReplyError(c,\"This instance has cluster support disabled\");\n        return;\n    }\n    c->flags |= REDIS_READONLY;\n    addReply(c,shared.ok);\n}\n\n/* The READWRITE command just clears the READONLY command state. */\nvoid readwriteCommand(redisClient *c) {\n    c->flags &= ~REDIS_READONLY;\n    addReply(c,shared.ok);\n}\n\n/* Return the pointer to the cluster node that is able to serve the command.\n * For the function to succeed the command should only target either:\n *\n * 1) A single key (even multiple times like LPOPRPUSH mylist mylist).\n * 2) Multiple keys in the same hash slot, while the slot is stable (no\n *    resharding in progress).\n *\n * On success the function returns the node that is able to serve the request.\n * If the node is not 'myself' a redirection must be perfomed. The kind of\n * redirection is specified setting the integer passed by reference\n * 'error_code', which will be set to REDIS_CLUSTER_REDIR_ASK or\n * REDIS_CLUSTER_REDIR_MOVED.\n *\n * When the node is 'myself' 'error_code' is set to REDIS_CLUSTER_REDIR_NONE.\n *\n * If the command fails NULL is returned, and the reason of the failure is\n * provided via 'error_code', which will be set to:\n *\n * REDIS_CLUSTER_REDIR_CROSS_SLOT if the request contains multiple keys that\n * don't belong to the same hash slot.\n *\n * REDIS_CLUSTER_REDIR_UNSTABLE if the request contains mutliple keys\n * belonging to the same slot, but the slot is not stable (in migration or\n * importing state, likely because a resharding is in progress). */\nclusterNode *getNodeByQuery(redisClient *c, struct redisCommand *cmd, robj **argv, int argc, int *hashslot, int *error_code) {\n\n    // 初始化为 NULL ，\n    // 如果输入命令是无参数命令，那么 n 就会继续为 NULL\n    clusterNode *n = NULL;\n\n    robj *firstkey = NULL;\n    int multiple_keys = 0;\n    multiState *ms, _ms;\n    multiCmd mc;\n    int i, slot = 0, migrating_slot = 0, importing_slot = 0, missing_keys = 0;\n\n    /* Set error code optimistically for the base case. */\n    if (error_code) *error_code = REDIS_CLUSTER_REDIR_NONE;\n\n    /* We handle all the cases as if they were EXEC commands, so we have\n     * a common code path for everything */\n    // 集群可以执行事务，\n    // 但必须确保事务中的所有命令都是针对某个相同的键进行的\n    // 这个 if 和接下来的 for 进行的就是这一合法性检测\n    if (cmd->proc == execCommand) {\n        /* If REDIS_MULTI flag is not set EXEC is just going to return an\n         * error. */\n        if (!(c->flags & REDIS_MULTI)) return myself;\n        ms = &c->mstate;\n    } else {\n        /* In order to have a single codepath create a fake Multi State\n         * structure if the client is not in MULTI/EXEC state, this way\n         * we have a single codepath below. */\n        ms = &_ms;\n        _ms.commands = &mc;\n        _ms.count = 1;\n        mc.argv = argv;\n        mc.argc = argc;\n        mc.cmd = cmd;\n    }\n\n    /* Check that all the keys are in the same hash slot, and obtain this\n     * slot and the node associated. */\n    for (i = 0; i < ms->count; i++) {\n        struct redisCommand *mcmd;\n        robj **margv;\n        int margc, *keyindex, numkeys, j;\n\n        mcmd = ms->commands[i].cmd;\n        margc = ms->commands[i].argc;\n        margv = ms->commands[i].argv;\n\n        // 定位命令的键位置\n        keyindex = getKeysFromCommand(mcmd,margv,margc,&numkeys);\n        // 遍历命令中的所有键\n        for (j = 0; j < numkeys; j++) {\n            robj *thiskey = margv[keyindex[j]];\n            int thisslot = keyHashSlot((char*)thiskey->ptr,\n                                       sdslen(thiskey->ptr));\n\n            if (firstkey == NULL) {\n                // 这是事务中第一个被处理的键\n                // 获取该键的槽和负责处理该槽的节点\n                /* This is the first key we see. Check what is the slot\n                 * and node. */\n                firstkey = thiskey;\n                slot = thisslot;\n                n = server.cluster->slots[slot];\n                redisAssertWithInfo(c,firstkey,n != NULL);\n                /* If we are migrating or importing this slot, we need to check\n                 * if we have all the keys in the request (the only way we\n                 * can safely serve the request, otherwise we return a TRYAGAIN\n                 * error). To do so we set the importing/migrating state and\n                 * increment a counter for every missing key. */\n                if (n == myself &&\n                    server.cluster->migrating_slots_to[slot] != NULL)\n                {\n                    migrating_slot = 1;\n                } else if (server.cluster->importing_slots_from[slot] != NULL) {\n                    importing_slot = 1;\n                }\n            } else {\n                /* If it is not the first key, make sure it is exactly\n                 * the same key as the first we saw. */\n                if (!equalStringObjects(firstkey,thiskey)) {\n                    if (slot != thisslot) {\n                        /* Error: multiple keys from different slots. */\n                        getKeysFreeResult(keyindex);\n                        if (error_code)\n                            *error_code = REDIS_CLUSTER_REDIR_CROSS_SLOT;\n                        return NULL;\n                    } else {\n                        /* Flag this request as one with multiple different\n                         * keys. */\n                        multiple_keys = 1;\n                    }\n                }\n            }\n\n            /* Migarting / Improrting slot? Count keys we don't have. */\n            if ((migrating_slot || importing_slot) &&\n                lookupKeyRead(&server.db[0],thiskey) == NULL)\n            {\n                missing_keys++;\n            }\n        }\n        getKeysFreeResult(keyindex);\n    }\n\n    /* No key at all in command? then we can serve the request\n     * without redirections or errors. */\n    if (n == NULL) return myself;\n\n    /* Return the hashslot by reference. */\n    if (hashslot) *hashslot = slot;\n\n    /* This request is about a slot we are migrating into another instance?\n     * Then if we have all the keys. */\n\n    /* If we don't have all the keys and we are migrating the slot, send\n     * an ASK redirection. */\n    if (migrating_slot && missing_keys) {\n        if (error_code) *error_code = REDIS_CLUSTER_REDIR_ASK;\n        return server.cluster->migrating_slots_to[slot];\n    }\n\n    /* If we are receiving the slot, and the client correctly flagged the\n     * request as \"ASKING\", we can serve the request. However if the request\n     * involves multiple keys and we don't have them all, the only option is\n     * to send a TRYAGAIN error. */\n    if (importing_slot &&\n        (c->flags & REDIS_ASKING || cmd->flags & REDIS_CMD_ASKING))\n    {\n        if (multiple_keys && missing_keys) {\n            if (error_code) *error_code = REDIS_CLUSTER_REDIR_UNSTABLE;\n            return NULL;\n        } else {\n            return myself;\n        }\n    }\n\n    /* Handle the read-only client case reading from a slave: if this\n     * node is a slave and the request is about an hash slot our master\n     * is serving, we can reply without redirection. */\n    if (c->flags & REDIS_READONLY &&\n        cmd->flags & REDIS_CMD_READONLY &&\n        nodeIsSlave(myself) &&\n        myself->slaveof == n)\n    {\n        return myself;\n    }\n\n    /* Base case: just return the right node. However if this node is not\n     * myself, set error_code to MOVED since we need to issue a rediretion. */\n    if (n != myself && error_code) *error_code = REDIS_CLUSTER_REDIR_MOVED;\n\n    // 返回负责处理槽 slot 的节点 n\n    return n;\n}\n"
  },
  {
    "path": "src/cluster.h",
    "content": "#ifndef __REDIS_CLUSTER_H\n#define __REDIS_CLUSTER_H\n\n/*-----------------------------------------------------------------------------\n * Redis cluster data structures, defines, exported API.\n *----------------------------------------------------------------------------*/\n\n// 槽数量\n#define REDIS_CLUSTER_SLOTS 16384\n// 集群在线\n#define REDIS_CLUSTER_OK 0          /* Everything looks ok */\n// 集群下线\n#define REDIS_CLUSTER_FAIL 1        /* The cluster can't work */\n// 节点名字的长度\n#define REDIS_CLUSTER_NAMELEN 40    /* sha1 hex length */\n// 集群的实际端口号 = 用户指定的端口号 + REDIS_CLUSTER_PORT_INCR\n#define REDIS_CLUSTER_PORT_INCR 10000 /* Cluster port = baseport + PORT_INCR */\n\n/* The following defines are amunt of time, sometimes expressed as\n * multiplicators of the node timeout value (when ending with MULT). \n *\n * 以下是和时间有关的一些常量，\n * 以 _MULTI 结尾的常量会作为时间值的乘法因子来使用。\n */\n// 默认节点超时时限\n#define REDIS_CLUSTER_DEFAULT_NODE_TIMEOUT 15000\n// 检验下线报告的乘法因子\n#define REDIS_CLUSTER_FAIL_REPORT_VALIDITY_MULT 2 /* Fail report validity. */\n// 撤销主节点 FAIL 状态的乘法因子\n#define REDIS_CLUSTER_FAIL_UNDO_TIME_MULT 2 /* Undo fail if master is back. */\n// 撤销主节点 FAIL 状态的加法因子\n#define REDIS_CLUSTER_FAIL_UNDO_TIME_ADD 10 /* Some additional time. */\n// 在检查从节点数据是否有效时使用的乘法因子\n#define REDIS_CLUSTER_SLAVE_VALIDITY_MULT 10 /* Slave data validity. */\n// 在执行故障转移之前需要等待的秒数，似乎已经废弃\n#define REDIS_CLUSTER_FAILOVER_DELAY 5 /* Seconds */\n// 未使用，似乎已经废弃\n#define REDIS_CLUSTER_DEFAULT_MIGRATION_BARRIER 1\n// 在进行手动的故障转移之前，需要等待的超时时间\n#define REDIS_CLUSTER_MF_TIMEOUT 5000 /* Milliseconds to do a manual failover. */\n// 未使用，似乎已经废弃\n#define REDIS_CLUSTER_MF_PAUSE_MULT 2 /* Master pause manual failover mult. */\n\n/* Redirection errors returned by getNodeByQuery(). */\n/* 由 getNodeByQuery() 函数返回的转向错误。 */\n// 节点可以处理这个命令\n#define REDIS_CLUSTER_REDIR_NONE 0          /* Node can serve the request. */\n// 键在其他槽\n#define REDIS_CLUSTER_REDIR_CROSS_SLOT 1    /* Keys in different slots. */\n// 键所处的槽正在进行 reshard\n#define REDIS_CLUSTER_REDIR_UNSTABLE 2      /* Keys in slot resharding. */\n// 需要进行 ASK 转向\n#define REDIS_CLUSTER_REDIR_ASK 3           /* -ASK redirection required. */\n// 需要进行 MOVED 转向\n#define REDIS_CLUSTER_REDIR_MOVED 4         /* -MOVED redirection required. */\n\n// 前置定义，防止编译错误\nstruct clusterNode;\n\n\n/* clusterLink encapsulates everything needed to talk with a remote node. */\n// clusterLink 包含了与其他节点进行通讯所需的全部信息\ntypedef struct clusterLink {\n\n    // 连接的创建时间\n    mstime_t ctime;             /* Link creation time */\n\n    // TCP 套接字描述符\n    int fd;                     /* TCP socket file descriptor */\n\n    // 输出缓冲区，保存着等待发送给其他节点的消息（message）。\n    sds sndbuf;                 /* Packet send buffer */\n\n    // 输入缓冲区，保存着从其他节点接收到的消息。\n    sds rcvbuf;                 /* Packet reception buffer */\n\n    // 与这个连接相关联的节点，如果没有的话就为 NULL\n    struct clusterNode *node;   /* Node related to this link if any, or NULL */\n\n} clusterLink;\n\n/* Cluster node flags and macros. */\n// 该节点为主节点\n#define REDIS_NODE_MASTER 1     /* The node is a master */\n// 该节点为从节点\n#define REDIS_NODE_SLAVE 2      /* The node is a slave */\n// 该节点疑似下线，需要对它的状态进行确认\n#define REDIS_NODE_PFAIL 4      /* Failure? Need acknowledge */\n// 该节点已下线\n#define REDIS_NODE_FAIL 8       /* The node is believed to be malfunctioning */\n// 该节点是当前节点自身\n#define REDIS_NODE_MYSELF 16    /* This node is myself */\n// 该节点还未与当前节点完成第一次 PING - PONG 通讯\n#define REDIS_NODE_HANDSHAKE 32 /* We have still to exchange the first ping */\n// 该节点没有地址\n#define REDIS_NODE_NOADDR   64  /* We don't know the address of this node */\n// 当前节点还未与该节点进行过接触\n// 带有这个标识会让当前节点发送 MEET 命令而不是 PING 命令\n#define REDIS_NODE_MEET 128     /* Send a MEET message to this node */\n// 该节点被选中为新的主节点\n#define REDIS_NODE_PROMOTED 256 /* Master was a slave propoted by failover */\n// 空名字（在节点为主节点时，用作消息中的 slaveof 属性的值）\n#define REDIS_NODE_NULL_NAME \"\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\"\n\n// 用于判断节点身份和状态的一系列宏\n#define nodeIsMaster(n) ((n)->flags & REDIS_NODE_MASTER)\n#define nodeIsSlave(n) ((n)->flags & REDIS_NODE_SLAVE)\n#define nodeInHandshake(n) ((n)->flags & REDIS_NODE_HANDSHAKE)\n#define nodeHasAddr(n) (!((n)->flags & REDIS_NODE_NOADDR))\n#define nodeWithoutAddr(n) ((n)->flags & REDIS_NODE_NOADDR)\n#define nodeTimedOut(n) ((n)->flags & REDIS_NODE_PFAIL)\n#define nodeFailed(n) ((n)->flags & REDIS_NODE_FAIL)\n\n/* This structure represent elements of node->fail_reports. */\n// 每个 clusterNodeFailReport 结构保存了一条其他节点对目标节点的下线报告\n// （认为目标节点已经下线）\nstruct clusterNodeFailReport {\n\n    // 报告目标节点已经下线的节点\n    struct clusterNode *node;  /* Node reporting the failure condition. */\n\n    // 最后一次从 node 节点收到下线报告的时间\n    // 程序使用这个时间戳来检查下线报告是否过期\n    mstime_t time;             /* Time of the last report from this node. */\n\n} typedef clusterNodeFailReport;\n\n\n// 节点状态\nstruct clusterNode {\n\n    // 创建节点的时间\n    mstime_t ctime; /* Node object creation time. */\n\n    // 节点的名字，由 40 个十六进制字符组成\n    // 例如 68eef66df23420a5862208ef5b1a7005b806f2ff\n    char name[REDIS_CLUSTER_NAMELEN]; /* Node name, hex string, sha1-size */\n\n    // 节点标识\n    // 使用各种不同的标识值记录节点的角色（比如主节点或者从节点），\n    // 以及节点目前所处的状态（比如在线或者下线）。\n    int flags;      /* REDIS_NODE_... */\n\n    // 节点当前的配置纪元，用于实现故障转移\n    uint64_t configEpoch; /* Last configEpoch observed for this node */\n\n    // 由这个节点负责处理的槽\n    // 一共有 REDIS_CLUSTER_SLOTS / 8 个字节长\n    // 每个字节的每个位记录了一个槽的保存状态\n    // 位的值为 1 表示槽正由本节点处理，值为 0 则表示槽并非本节点处理\n    // 比如 slots[0] 的第一个位保存了槽 0 的保存情况\n    // slots[0] 的第二个位保存了槽 1 的保存情况，以此类推\n    unsigned char slots[REDIS_CLUSTER_SLOTS/8]; /* slots handled by this node */\n\n    // 该节点负责处理的槽数量\n    int numslots;   /* Number of slots handled by this node */\n\n    // 如果本节点是主节点，那么用这个属性记录从节点的数量\n    int numslaves;  /* Number of slave nodes, if this is a master */\n\n    // 指针数组，指向各个从节点\n    struct clusterNode **slaves; /* pointers to slave nodes */\n\n    // 如果这是一个从节点，那么指向主节点\n    struct clusterNode *slaveof; /* pointer to the master node */\n\n    // 最后一次发送 PING 命令的时间\n    mstime_t ping_sent;      /* Unix time we sent latest ping */\n\n    // 最后一次接收 PONG 回复的时间戳\n    mstime_t pong_received;  /* Unix time we received the pong */\n\n    // 最后一次被设置为 FAIL 状态的时间\n    mstime_t fail_time;      /* Unix time when FAIL flag was set */\n\n    // 最后一次给某个从节点投票的时间\n    mstime_t voted_time;     /* Last time we voted for a slave of this master */\n\n    // 最后一次从这个节点接收到复制偏移量的时间\n    mstime_t repl_offset_time;  /* Unix time we received offset for this node */\n\n    // 这个节点的复制偏移量\n    long long repl_offset;      /* Last known repl offset for this node. */\n\n    // 节点的 IP 地址\n    char ip[REDIS_IP_STR_LEN];  /* Latest known IP address of this node */\n\n    // 节点的端口号\n    int port;                   /* Latest known port of this node */\n\n    // 保存连接节点所需的有关信息\n    clusterLink *link;          /* TCP/IP link with this node */\n\n    // 一个链表，记录了所有其他节点对该节点的下线报告\n    list *fail_reports;         /* List of nodes signaling this as failing */\n\n};\ntypedef struct clusterNode clusterNode;\n\n\n// 集群状态，每个节点都保存着一个这样的状态，记录了它们眼中的集群的样子。\n// 另外，虽然这个结构主要用于记录集群的属性，但是为了节约资源，\n// 有些与节点有关的属性，比如 slots_to_keys 、 failover_auth_count \n// 也被放到了这个结构里面。\ntypedef struct clusterState {\n\n    // 指向当前节点的指针\n    clusterNode *myself;  /* This node */\n\n    // 集群当前的配置纪元，用于实现故障转移\n    uint64_t currentEpoch;\n\n    // 集群当前的状态：是在线还是下线\n    int state;            /* REDIS_CLUSTER_OK, REDIS_CLUSTER_FAIL, ... */\n\n    // 集群中至少处理着一个槽的节点的数量。\n    int size;             /* Num of master nodes with at least one slot */\n\n    // 集群节点名单（包括 myself 节点）\n    // 字典的键为节点的名字，字典的值为 clusterNode 结构\n    dict *nodes;          /* Hash table of name -> clusterNode structures */\n\n    // 节点黑名单，用于 CLUSTER FORGET 命令\n    // 防止被 FORGET 的命令重新被添加到集群里面\n    // （不过现在似乎没有在使用的样子，已废弃？还是尚未实现？）\n    dict *nodes_black_list; /* Nodes we don't re-add for a few seconds. */\n\n    // 记录要从当前节点迁移到目标节点的槽，以及迁移的目标节点\n    // migrating_slots_to[i] = NULL 表示槽 i 未被迁移\n    // migrating_slots_to[i] = clusterNode_A 表示槽 i 要从本节点迁移至节点 A\n    clusterNode *migrating_slots_to[REDIS_CLUSTER_SLOTS];\n\n    // 记录要从源节点迁移到本节点的槽，以及进行迁移的源节点\n    // importing_slots_from[i] = NULL 表示槽 i 未进行导入\n    // importing_slots_from[i] = clusterNode_A 表示正从节点 A 中导入槽 i\n    clusterNode *importing_slots_from[REDIS_CLUSTER_SLOTS];\n\n    // 负责处理各个槽的节点\n    // 例如 slots[i] = clusterNode_A 表示槽 i 由节点 A 处理\n    clusterNode *slots[REDIS_CLUSTER_SLOTS];\n\n    // 跳跃表，表中以槽作为分值，键作为成员，对槽进行有序排序\n    // 当需要对某些槽进行区间（range）操作时，这个跳跃表可以提供方便\n    // 具体操作定义在 db.c 里面\n    zskiplist *slots_to_keys;\n\n    /* The following fields are used to take the slave state on elections. */\n    // 以下这些域被用于进行故障转移选举\n\n    // 上次执行选举或者下次执行选举的时间\n    mstime_t failover_auth_time; /* Time of previous or next election. */\n\n    // 节点获得的投票数量\n    int failover_auth_count;    /* Number of votes received so far. */\n\n    // 如果值为 1 ，表示本节点已经向其他节点发送了投票请求\n    int failover_auth_sent;     /* True if we already asked for votes. */\n\n    int failover_auth_rank;     /* This slave rank for current auth request. */\n\n    uint64_t failover_auth_epoch; /* Epoch of the current election. */\n\n    /* Manual failover state in common. */\n    /* 共用的手动故障转移状态 */\n\n    // 手动故障转移执行的时间限制\n    mstime_t mf_end;            /* Manual failover time limit (ms unixtime).\n                                   It is zero if there is no MF in progress. */\n    /* Manual failover state of master. */\n    /* 主服务器的手动故障转移状态 */\n    clusterNode *mf_slave;      /* Slave performing the manual failover. */\n    /* Manual failover state of slave. */\n    /* 从服务器的手动故障转移状态 */\n    long long mf_master_offset; /* Master offset the slave needs to start MF\n                                   or zero if stil not received. */\n    // 指示手动故障转移是否可以开始的标志值\n    // 值为非 0 时表示各个主服务器可以开始投票\n    int mf_can_start;           /* If non-zero signal that the manual failover\n                                   can start requesting masters vote. */\n\n    /* The followign fields are uesd by masters to take state on elections. */\n    /* 以下这些域由主服务器使用，用于记录选举时的状态 */\n\n    // 集群最后一次进行投票的纪元\n    uint64_t lastVoteEpoch;     /* Epoch of the last vote granted. */\n\n    // 在进入下个事件循环之前要做的事情，以各个 flag 来记录\n    int todo_before_sleep; /* Things to do in clusterBeforeSleep(). */\n\n    // 通过 cluster 连接发送的消息数量\n    long long stats_bus_messages_sent;  /* Num of msg sent via cluster bus. */\n\n    // 通过 cluster 接收到的消息数量\n    long long stats_bus_messages_received; /* Num of msg rcvd via cluster bus.*/\n\n} clusterState;\n\n/* clusterState todo_before_sleep flags. */\n// 以下每个 flag 代表了一个服务器在开始下一个事件循环之前\n// 要做的事情\n#define CLUSTER_TODO_HANDLE_FAILOVER (1<<0)\n#define CLUSTER_TODO_UPDATE_STATE (1<<1)\n#define CLUSTER_TODO_SAVE_CONFIG (1<<2)\n#define CLUSTER_TODO_FSYNC_CONFIG (1<<3)\n\n/* Redis cluster messages header */\n\n/* Note that the PING, PONG and MEET messages are actually the same exact\n * kind of packet. PONG is the reply to ping, in the exact format as a PING,\n * while MEET is a special PING that forces the receiver to add the sender\n * as a node (if it is not already in the list). */\n// 注意，PING 、 PONG 和 MEET 实际上是同一种消息。\n// PONG 是对 PING 的回复，它的实际格式也为 PING 消息，\n// 而 MEET 则是一种特殊的 PING 消息，用于强制消息的接收者将消息的发送者添加到集群中\n// （如果节点尚未在节点列表中的话）\n// PING\n#define CLUSTERMSG_TYPE_PING 0          /* Ping */\n// PONG （回复 PING）\n#define CLUSTERMSG_TYPE_PONG 1          /* Pong (reply to Ping) */\n// 请求将某个节点添加到集群中\n#define CLUSTERMSG_TYPE_MEET 2          /* Meet \"let's join\" message */\n// 将某个节点标记为 FAIL\n#define CLUSTERMSG_TYPE_FAIL 3          /* Mark node xxx as failing */\n// 通过发布与订阅功能广播消息\n#define CLUSTERMSG_TYPE_PUBLISH 4       /* Pub/Sub Publish propagation */\n// 请求进行故障转移操作，要求消息的接收者通过投票来支持消息的发送者\n#define CLUSTERMSG_TYPE_FAILOVER_AUTH_REQUEST 5 /* May I failover? */\n// 消息的接收者同意向消息的发送者投票\n#define CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK 6     /* Yes, you have my vote */\n// 槽布局已经发生变化，消息发送者要求消息接收者进行相应的更新\n#define CLUSTERMSG_TYPE_UPDATE 7        /* Another node slots configuration */\n// 为了进行手动故障转移，暂停各个客户端\n#define CLUSTERMSG_TYPE_MFSTART 8       /* Pause clients for manual failover */\n\n/* Initially we don't know our \"name\", but we'll find it once we connect\n * to the first node, using the getsockname() function. Then we'll use this\n * address for all the next messages. */\n\ntypedef struct {\n\n    // 节点的名字\n    // 在刚开始的时候，节点的名字会是随机的\n    // 当 MEET 信息发送并得到回复之后，集群就会为节点设置正式的名字\n    char nodename[REDIS_CLUSTER_NAMELEN];\n\n    // 最后一次向该节点发送 PING 消息的时间戳\n    uint32_t ping_sent;\n\n    // 最后一次从该节点接收到 PONG 消息的时间戳\n    uint32_t pong_received;\n\n    // 节点的 IP 地址\n    char ip[REDIS_IP_STR_LEN];    /* IP address last time it was seen */\n\n    // 节点的端口号\n    uint16_t port;  /* port last time it was seen */\n\n    // 节点的标识值\n    uint16_t flags;\n\n    // 对齐字节，不使用\n    uint32_t notused; /* for 64 bit alignment */\n\n} clusterMsgDataGossip;\n\ntypedef struct {\n\n    // 下线节点的名字\n    char nodename[REDIS_CLUSTER_NAMELEN];\n\n} clusterMsgDataFail;\n\ntypedef struct {\n\n    // 频道名长度\n    uint32_t channel_len;\n\n    // 消息长度\n    uint32_t message_len;\n\n    // 消息内容，格式为 频道名+消息\n    // bulk_data[0:channel_len-1] 为频道名\n    // bulk_data[channel_len:channel_len+message_len-1] 为消息\n    unsigned char bulk_data[8]; /* defined as 8 just for alignment concerns. */\n\n} clusterMsgDataPublish;\n\ntypedef struct {\n\n    // 节点的配置纪元\n    uint64_t configEpoch; /* Config epoch of the specified instance. */\n\n    // 节点的名字\n    char nodename[REDIS_CLUSTER_NAMELEN]; /* Name of the slots owner. */\n\n    // 节点的槽布局\n    unsigned char slots[REDIS_CLUSTER_SLOTS/8]; /* Slots bitmap. */\n\n} clusterMsgDataUpdate;\n\nunion clusterMsgData {\n\n    /* PING, MEET and PONG */\n    struct {\n        /* Array of N clusterMsgDataGossip structures */\n        // 每条消息都包含两个 clusterMsgDataGossip 结构\n        clusterMsgDataGossip gossip[1];\n    } ping;\n\n    /* FAIL */\n    struct {\n        clusterMsgDataFail about;\n    } fail;\n\n    /* PUBLISH */\n    struct {\n        clusterMsgDataPublish msg;\n    } publish;\n\n    /* UPDATE */\n    struct {\n        clusterMsgDataUpdate nodecfg;\n    } update;\n\n};\n\n// 用来表示集群消息的结构（消息头，header）\ntypedef struct {\n    char sig[4];        /* Siganture \"RCmb\" (Redis Cluster message bus). */\n    // 消息的长度（包括这个消息头的长度和消息正文的长度）\n    uint32_t totlen;    /* Total length of this message */\n    uint16_t ver;       /* Protocol version, currently set to 0. */\n    uint16_t notused0;  /* 2 bytes not used. */\n\n    // 消息的类型\n    uint16_t type;      /* Message type */\n\n    // 消息正文包含的节点信息数量\n    // 只在发送 MEET 、 PING 和 PONG 这三种 Gossip 协议消息时使用\n    uint16_t count;     /* Only used for some kind of messages. */\n\n    // 消息发送者的配置纪元\n    uint64_t currentEpoch;  /* The epoch accordingly to the sending node. */\n\n    // 如果消息发送者是一个主节点，那么这里记录的是消息发送者的配置纪元\n    // 如果消息发送者是一个从节点，那么这里记录的是消息发送者正在复制的主节点的配置纪元\n    uint64_t configEpoch;   /* The config epoch if it's a master, or the last\n                               epoch advertised by its master if it is a\n                               slave. */\n\n    // 节点的复制偏移量\n    uint64_t offset;    /* Master replication offset if node is a master or\n                           processed replication offset if node is a slave. */\n\n    // 消息发送者的名字（ID）\n    char sender[REDIS_CLUSTER_NAMELEN]; /* Name of the sender node */\n\n    // 消息发送者目前的槽指派信息\n    unsigned char myslots[REDIS_CLUSTER_SLOTS/8];\n\n    // 如果消息发送者是一个从节点，那么这里记录的是消息发送者正在复制的主节点的名字\n    // 如果消息发送者是一个主节点，那么这里记录的是 REDIS_NODE_NULL_NAME\n    // （一个 40 字节长，值全为 0 的字节数组）\n    char slaveof[REDIS_CLUSTER_NAMELEN];\n\n    char notused1[32];  /* 32 bytes reserved for future usage. */\n\n    // 消息发送者的端口号\n    uint16_t port;      /* Sender TCP base port */\n\n    // 消息发送者的标识值\n    uint16_t flags;     /* Sender node flags */\n\n    // 消息发送者所处集群的状态\n    unsigned char state; /* Cluster state from the POV of the sender */\n\n    // 消息标志\n    unsigned char mflags[3]; /* Message flags: CLUSTERMSG_FLAG[012]_... */\n\n    // 消息的正文（或者说，内容）\n    union clusterMsgData data;\n\n} clusterMsg;\n\n#define CLUSTERMSG_MIN_LEN (sizeof(clusterMsg)-sizeof(union clusterMsgData))\n\n/* Message flags better specify the packet content or are used to\n * provide some information about the node state. */\n#define CLUSTERMSG_FLAG0_PAUSED (1<<0) /* Master paused for manual failover. */\n#define CLUSTERMSG_FLAG0_FORCEACK (1<<1) /* Give ACK to AUTH_REQUEST even if\n                                            master is up. */\n\n/* ---------------------- API exported outside cluster.c -------------------- */\nclusterNode *getNodeByQuery(redisClient *c, struct redisCommand *cmd, robj **argv, int argc, int *hashslot, int *ask);\n\n#endif /* __REDIS_CLUSTER_H */\n"
  },
  {
    "path": "src/config.c",
    "content": "/* Configuration file parsing and CONFIG GET/SET commands implementation.\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#include \"redis.h\"\n#include \"cluster.h\"\n\n#include <fcntl.h>\n#include <sys/stat.h>\n\nstatic struct {\n    const char     *name;\n    const int       value;\n} validSyslogFacilities[] = {\n    {\"user\",    LOG_USER},\n    {\"local0\",  LOG_LOCAL0},\n    {\"local1\",  LOG_LOCAL1},\n    {\"local2\",  LOG_LOCAL2},\n    {\"local3\",  LOG_LOCAL3},\n    {\"local4\",  LOG_LOCAL4},\n    {\"local5\",  LOG_LOCAL5},\n    {\"local6\",  LOG_LOCAL6},\n    {\"local7\",  LOG_LOCAL7},\n    {NULL, 0}\n};\n\nclientBufferLimitsConfig clientBufferLimitsDefaults[REDIS_CLIENT_LIMIT_NUM_CLASSES] = {\n    {0, 0, 0}, /* normal */\n    {1024*1024*256, 1024*1024*64, 60}, /* slave */\n    {1024*1024*32, 1024*1024*8, 60}  /* pubsub */\n};\n\n/*-----------------------------------------------------------------------------\n * Config file parsing\n *----------------------------------------------------------------------------*/\n\nint yesnotoi(char *s) {\n    if (!strcasecmp(s,\"yes\")) return 1;\n    else if (!strcasecmp(s,\"no\")) return 0;\n    else return -1;\n}\n\nvoid appendServerSaveParams(time_t seconds, int changes) {\n    server.saveparams = zrealloc(server.saveparams,sizeof(struct saveparam)*(server.saveparamslen+1));\n    server.saveparams[server.saveparamslen].seconds = seconds;\n    server.saveparams[server.saveparamslen].changes = changes;\n    server.saveparamslen++;\n}\n\nvoid resetServerSaveParams() {\n    zfree(server.saveparams);\n    server.saveparams = NULL;\n    server.saveparamslen = 0;\n}\n\nvoid loadServerConfigFromString(char *config) {\n    char *err = NULL;\n    int linenum = 0, totlines, i;\n    int slaveof_linenum = 0;\n    sds *lines;\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        // 移除字符串的前置空白和后缀空白\n        lines[i] = sdstrim(lines[i],\" \\t\\r\\n\");\n\n        /* Skip comments and blank lines */\n        // 跳过空白行\n        if (lines[i][0] == '#' || lines[i][0] == '\\0') continue;\n\n        /* Split into arguments */\n        // 将字符串分割成多个参数\n        argv = sdssplitargs(lines[i],&argc);\n        if (argv == NULL) {\n            err = \"Unbalanced quotes in configuration line\";\n            goto loaderr;\n        }\n\n        /* Skip this line if the resulting command vector is empty. */\n        // 跳过空白参数\n        if (argc == 0) {\n            sdsfreesplitres(argv,argc);\n            continue;\n        }\n\n        // 将选项名字转换成小写\n        // 例如 TIMEOUT 转换成 timeout\n        sdstolower(argv[0]);\n\n        /* Execute config directives */\n        if (!strcasecmp(argv[0],\"timeout\") && argc == 2) {\n            server.maxidletime = atoi(argv[1]);\n            if (server.maxidletime < 0) {\n                err = \"Invalid timeout value\"; goto loaderr;\n            }\n        } else if (!strcasecmp(argv[0],\"tcp-keepalive\") && argc == 2) {\n            server.tcpkeepalive = atoi(argv[1]);\n            if (server.tcpkeepalive < 0) {\n                err = \"Invalid tcp-keepalive value\"; goto loaderr;\n            }\n        } else if (!strcasecmp(argv[0],\"port\") && argc == 2) {\n            server.port = atoi(argv[1]);\n            if (server.port < 0 || server.port > 65535) {\n                err = \"Invalid port\"; goto loaderr;\n            }\n        } else if (!strcasecmp(argv[0],\"tcp-backlog\") && argc == 2) {\n            server.tcp_backlog = atoi(argv[1]);\n            if (server.tcp_backlog < 0) {\n                err = \"Invalid backlog value\"; goto loaderr;\n            }\n        } else if (!strcasecmp(argv[0],\"bind\") && argc >= 2) {\n            int j, addresses = argc-1;\n\n            if (addresses > REDIS_BINDADDR_MAX) {\n                err = \"Too many bind addresses specified\"; goto loaderr;\n            }\n            for (j = 0; j < addresses; j++)\n                server.bindaddr[j] = zstrdup(argv[j+1]);\n            server.bindaddr_count = addresses;\n        } else if (!strcasecmp(argv[0],\"unixsocket\") && argc == 2) {\n            server.unixsocket = zstrdup(argv[1]);\n        } else if (!strcasecmp(argv[0],\"unixsocketperm\") && argc == 2) {\n            errno = 0;\n            server.unixsocketperm = (mode_t)strtol(argv[1], NULL, 8);\n            if (errno || server.unixsocketperm > 0777) {\n                err = \"Invalid socket file permissions\"; goto loaderr;\n            }\n        } else if (!strcasecmp(argv[0],\"save\")) {\n            if (argc == 3) {\n                int seconds = atoi(argv[1]);\n                int changes = atoi(argv[2]);\n                if (seconds < 1 || changes < 0) {\n                    err = \"Invalid save parameters\"; goto loaderr;\n                }\n                appendServerSaveParams(seconds,changes);\n            } else if (argc == 2 && !strcasecmp(argv[1],\"\")) {\n                resetServerSaveParams();\n            }\n        } else if (!strcasecmp(argv[0],\"dir\") && argc == 2) {\n            if (chdir(argv[1]) == -1) {\n                redisLog(REDIS_WARNING,\"Can't chdir to '%s': %s\",\n                    argv[1], strerror(errno));\n                exit(1);\n            }\n        } else if (!strcasecmp(argv[0],\"loglevel\") && argc == 2) {\n            if (!strcasecmp(argv[1],\"debug\")) server.verbosity = REDIS_DEBUG;\n            else if (!strcasecmp(argv[1],\"verbose\")) server.verbosity = REDIS_VERBOSE;\n            else if (!strcasecmp(argv[1],\"notice\")) server.verbosity = REDIS_NOTICE;\n            else if (!strcasecmp(argv[1],\"warning\")) server.verbosity = REDIS_WARNING;\n            else {\n                err = \"Invalid log level. Must be one of debug, notice, warning\";\n                goto loaderr;\n            }\n        } else if (!strcasecmp(argv[0],\"logfile\") && argc == 2) {\n            FILE *logfp;\n\n            zfree(server.logfile);\n            server.logfile = zstrdup(argv[1]);\n            if (server.logfile[0] != '\\0') {\n                /* Test if we are able to open the file. The server will not\n                 * be able to abort just for this problem later... */\n                logfp = fopen(server.logfile,\"a\");\n                if (logfp == NULL) {\n                    err = sdscatprintf(sdsempty(),\n                        \"Can't open the log file: %s\", strerror(errno));\n                    goto loaderr;\n                }\n                fclose(logfp);\n            }\n        } else if (!strcasecmp(argv[0],\"syslog-enabled\") && argc == 2) {\n            if ((server.syslog_enabled = yesnotoi(argv[1])) == -1) {\n                err = \"argument must be 'yes' or 'no'\"; goto loaderr;\n            }\n        } else if (!strcasecmp(argv[0],\"syslog-ident\") && argc == 2) {\n            if (server.syslog_ident) zfree(server.syslog_ident);\n            server.syslog_ident = zstrdup(argv[1]);\n        } else if (!strcasecmp(argv[0],\"syslog-facility\") && argc == 2) {\n            int i;\n\n            for (i = 0; validSyslogFacilities[i].name; i++) {\n                if (!strcasecmp(validSyslogFacilities[i].name, argv[1])) {\n                    server.syslog_facility = validSyslogFacilities[i].value;\n                    break;\n                }\n            }\n\n            if (!validSyslogFacilities[i].name) {\n                err = \"Invalid log facility. Must be one of USER or between LOCAL0-LOCAL7\";\n                goto loaderr;\n            }\n        } else if (!strcasecmp(argv[0],\"databases\") && argc == 2) {\n            server.dbnum = atoi(argv[1]);\n            if (server.dbnum < 1) {\n                err = \"Invalid number of databases\"; goto loaderr;\n            }\n        } else if (!strcasecmp(argv[0],\"include\") && argc == 2) {\n            loadServerConfig(argv[1],NULL);\n        } else if (!strcasecmp(argv[0],\"maxclients\") && argc == 2) {\n            server.maxclients = atoi(argv[1]);\n            if (server.maxclients < 1) {\n                err = \"Invalid max clients limit\"; goto loaderr;\n            }\n        } else if (!strcasecmp(argv[0],\"maxmemory\") && argc == 2) {\n            server.maxmemory = memtoll(argv[1],NULL);\n        } else if (!strcasecmp(argv[0],\"maxmemory-policy\") && argc == 2) {\n            if (!strcasecmp(argv[1],\"volatile-lru\")) {\n                server.maxmemory_policy = REDIS_MAXMEMORY_VOLATILE_LRU;\n            } else if (!strcasecmp(argv[1],\"volatile-random\")) {\n                server.maxmemory_policy = REDIS_MAXMEMORY_VOLATILE_RANDOM;\n            } else if (!strcasecmp(argv[1],\"volatile-ttl\")) {\n                server.maxmemory_policy = REDIS_MAXMEMORY_VOLATILE_TTL;\n            } else if (!strcasecmp(argv[1],\"allkeys-lru\")) {\n                server.maxmemory_policy = REDIS_MAXMEMORY_ALLKEYS_LRU;\n            } else if (!strcasecmp(argv[1],\"allkeys-random\")) {\n                server.maxmemory_policy = REDIS_MAXMEMORY_ALLKEYS_RANDOM;\n            } else if (!strcasecmp(argv[1],\"noeviction\")) {\n                server.maxmemory_policy = REDIS_MAXMEMORY_NO_EVICTION;\n            } else {\n                err = \"Invalid maxmemory policy\";\n                goto loaderr;\n            }\n        } else if (!strcasecmp(argv[0],\"maxmemory-samples\") && argc == 2) {\n            server.maxmemory_samples = atoi(argv[1]);\n            if (server.maxmemory_samples <= 0) {\n                err = \"maxmemory-samples must be 1 or greater\";\n                goto loaderr;\n            }\n        } else if (!strcasecmp(argv[0],\"slaveof\") && argc == 3) {\n            slaveof_linenum = linenum;\n            server.masterhost = sdsnew(argv[1]);\n            server.masterport = atoi(argv[2]);\n            server.repl_state = REDIS_REPL_CONNECT;\n        } else if (!strcasecmp(argv[0],\"repl-ping-slave-period\") && argc == 2) {\n            server.repl_ping_slave_period = atoi(argv[1]);\n            if (server.repl_ping_slave_period <= 0) {\n                err = \"repl-ping-slave-period must be 1 or greater\";\n                goto loaderr;\n            }\n        } else if (!strcasecmp(argv[0],\"repl-timeout\") && argc == 2) {\n            server.repl_timeout = atoi(argv[1]);\n            if (server.repl_timeout <= 0) {\n                err = \"repl-timeout must be 1 or greater\";\n                goto loaderr;\n            }\n        } else if (!strcasecmp(argv[0],\"repl-disable-tcp-nodelay\") && argc==2) {\n            if ((server.repl_disable_tcp_nodelay = yesnotoi(argv[1])) == -1) {\n                err = \"argument must be 'yes' or 'no'\"; goto loaderr;\n            }\n        } else if (!strcasecmp(argv[0],\"repl-backlog-size\") && argc == 2) {\n            long long size = memtoll(argv[1],NULL);\n            if (size <= 0) {\n                err = \"repl-backlog-size must be 1 or greater.\";\n                goto loaderr;\n            }\n            resizeReplicationBacklog(size);\n        } else if (!strcasecmp(argv[0],\"repl-backlog-ttl\") && argc == 2) {\n            server.repl_backlog_time_limit = atoi(argv[1]);\n            if (server.repl_backlog_time_limit < 0) {\n                err = \"repl-backlog-ttl can't be negative \";\n                goto loaderr;\n            }\n        } else if (!strcasecmp(argv[0],\"masterauth\") && argc == 2) {\n        \tserver.masterauth = zstrdup(argv[1]);\n        } else if (!strcasecmp(argv[0],\"slave-serve-stale-data\") && argc == 2) {\n            if ((server.repl_serve_stale_data = yesnotoi(argv[1])) == -1) {\n                err = \"argument must be 'yes' or 'no'\"; goto loaderr;\n            }\n        } else if (!strcasecmp(argv[0],\"slave-read-only\") && argc == 2) {\n            if ((server.repl_slave_ro = yesnotoi(argv[1])) == -1) {\n                err = \"argument must be 'yes' or 'no'\"; goto loaderr;\n            }\n        } else if (!strcasecmp(argv[0],\"rdbcompression\") && argc == 2) {\n            if ((server.rdb_compression = yesnotoi(argv[1])) == -1) {\n                err = \"argument must be 'yes' or 'no'\"; goto loaderr;\n            }\n        } else if (!strcasecmp(argv[0],\"rdbchecksum\") && argc == 2) {\n            if ((server.rdb_checksum = yesnotoi(argv[1])) == -1) {\n                err = \"argument must be 'yes' or 'no'\"; goto loaderr;\n            }\n        } else if (!strcasecmp(argv[0],\"activerehashing\") && argc == 2) {\n            if ((server.activerehashing = yesnotoi(argv[1])) == -1) {\n                err = \"argument must be 'yes' or 'no'\"; goto loaderr;\n            }\n        } else if (!strcasecmp(argv[0],\"daemonize\") && argc == 2) {\n            if ((server.daemonize = yesnotoi(argv[1])) == -1) {\n                err = \"argument must be 'yes' or 'no'\"; goto loaderr;\n            }\n        } else if (!strcasecmp(argv[0],\"hz\") && argc == 2) {\n            server.hz = atoi(argv[1]);\n            if (server.hz < REDIS_MIN_HZ) server.hz = REDIS_MIN_HZ;\n            if (server.hz > REDIS_MAX_HZ) server.hz = REDIS_MAX_HZ;\n        } else if (!strcasecmp(argv[0],\"appendonly\") && argc == 2) {\n            int yes;\n\n            if ((yes = yesnotoi(argv[1])) == -1) {\n                err = \"argument must be 'yes' or 'no'\"; goto loaderr;\n            }\n            server.aof_state = yes ? REDIS_AOF_ON : REDIS_AOF_OFF;\n        } else if (!strcasecmp(argv[0],\"appendfilename\") && argc == 2) {\n            if (!pathIsBaseName(argv[1])) {\n                err = \"appendfilename can't be a path, just a filename\";\n                goto loaderr;\n            }\n            zfree(server.aof_filename);\n            server.aof_filename = zstrdup(argv[1]);\n        } else if (!strcasecmp(argv[0],\"no-appendfsync-on-rewrite\")\n                   && argc == 2) {\n            if ((server.aof_no_fsync_on_rewrite= yesnotoi(argv[1])) == -1) {\n                err = \"argument must be 'yes' or 'no'\"; goto loaderr;\n            }\n        } else if (!strcasecmp(argv[0],\"appendfsync\") && argc == 2) {\n            if (!strcasecmp(argv[1],\"no\")) {\n                server.aof_fsync = AOF_FSYNC_NO;\n            } else if (!strcasecmp(argv[1],\"always\")) {\n                server.aof_fsync = AOF_FSYNC_ALWAYS;\n            } else if (!strcasecmp(argv[1],\"everysec\")) {\n                server.aof_fsync = AOF_FSYNC_EVERYSEC;\n            } else {\n                err = \"argument must be 'no', 'always' or 'everysec'\";\n                goto loaderr;\n            }\n        } else if (!strcasecmp(argv[0],\"auto-aof-rewrite-percentage\") &&\n                   argc == 2)\n        {\n            server.aof_rewrite_perc = atoi(argv[1]);\n            if (server.aof_rewrite_perc < 0) {\n                err = \"Invalid negative percentage for AOF auto rewrite\";\n                goto loaderr;\n            }\n        } else if (!strcasecmp(argv[0],\"auto-aof-rewrite-min-size\") &&\n                   argc == 2)\n        {\n            server.aof_rewrite_min_size = memtoll(argv[1],NULL);\n        } else if (!strcasecmp(argv[0],\"aof-rewrite-incremental-fsync\") &&\n                   argc == 2)\n        {\n            if ((server.aof_rewrite_incremental_fsync = yesnotoi(argv[1])) == -1) {\n                err = \"argument must be 'yes' or 'no'\"; goto loaderr;\n            }\n        } else if (!strcasecmp(argv[0],\"requirepass\") && argc == 2) {\n            if (strlen(argv[1]) > REDIS_AUTHPASS_MAX_LEN) {\n                err = \"Password is longer than REDIS_AUTHPASS_MAX_LEN\";\n                goto loaderr;\n            }\n            server.requirepass = zstrdup(argv[1]);\n        } else if (!strcasecmp(argv[0],\"pidfile\") && argc == 2) {\n            zfree(server.pidfile);\n            server.pidfile = zstrdup(argv[1]);\n        } else if (!strcasecmp(argv[0],\"dbfilename\") && argc == 2) {\n            if (!pathIsBaseName(argv[1])) {\n                err = \"dbfilename can't be a path, just a filename\";\n                goto loaderr;\n            }\n            zfree(server.rdb_filename);\n            server.rdb_filename = zstrdup(argv[1]);\n        } else if (!strcasecmp(argv[0],\"hash-max-ziplist-entries\") && argc == 2) {\n            server.hash_max_ziplist_entries = memtoll(argv[1], NULL);\n        } else if (!strcasecmp(argv[0],\"hash-max-ziplist-value\") && argc == 2) {\n            server.hash_max_ziplist_value = memtoll(argv[1], NULL);\n        } else if (!strcasecmp(argv[0],\"list-max-ziplist-entries\") && argc == 2){\n            server.list_max_ziplist_entries = memtoll(argv[1], NULL);\n        } else if (!strcasecmp(argv[0],\"list-max-ziplist-value\") && argc == 2) {\n            server.list_max_ziplist_value = memtoll(argv[1], NULL);\n        } else if (!strcasecmp(argv[0],\"set-max-intset-entries\") && argc == 2) {\n            server.set_max_intset_entries = memtoll(argv[1], NULL);\n        } else if (!strcasecmp(argv[0],\"zset-max-ziplist-entries\") && argc == 2) {\n            server.zset_max_ziplist_entries = memtoll(argv[1], NULL);\n        } else if (!strcasecmp(argv[0],\"zset-max-ziplist-value\") && argc == 2) {\n            server.zset_max_ziplist_value = memtoll(argv[1], NULL);\n        } else if (!strcasecmp(argv[0],\"hll-sparse-max-bytes\") && argc == 2) {\n            server.hll_sparse_max_bytes = memtoll(argv[1], NULL);\n        } else if (!strcasecmp(argv[0],\"rename-command\") && argc == 3) {\n            struct redisCommand *cmd = lookupCommand(argv[1]);\n            int retval;\n\n            if (!cmd) {\n                err = \"No such command in rename-command\";\n                goto loaderr;\n            }\n\n            /* If the target command name is the empty string we just\n             * remove it from the command table. */\n            retval = dictDelete(server.commands, argv[1]);\n            redisAssert(retval == DICT_OK);\n\n            /* Otherwise we re-add the command under a different name. */\n            if (sdslen(argv[2]) != 0) {\n                sds copy = sdsdup(argv[2]);\n\n                retval = dictAdd(server.commands, copy, cmd);\n                if (retval != DICT_OK) {\n                    sdsfree(copy);\n                    err = \"Target command name already exists\"; goto loaderr;\n                }\n            }\n        } else if (!strcasecmp(argv[0],\"cluster-enabled\") && argc == 2) {\n            if ((server.cluster_enabled = yesnotoi(argv[1])) == -1) {\n                err = \"argument must be 'yes' or 'no'\"; goto loaderr;\n            }\n        } else if (!strcasecmp(argv[0],\"cluster-config-file\") && argc == 2) {\n            zfree(server.cluster_configfile);\n            server.cluster_configfile = zstrdup(argv[1]);\n        } else if (!strcasecmp(argv[0],\"cluster-node-timeout\") && argc == 2) {\n            server.cluster_node_timeout = strtoll(argv[1],NULL,10);\n            if (server.cluster_node_timeout <= 0) {\n                err = \"cluster node timeout must be 1 or greater\"; goto loaderr;\n            }\n        } else if (!strcasecmp(argv[0],\"cluster-migration-barrier\")\n                   && argc == 2)\n        {\n            server.cluster_migration_barrier = atoi(argv[1]);\n            if (server.cluster_migration_barrier < 0) {\n                err = \"cluster migration barrier must be positive\";\n                goto loaderr;\n            }\n        } else if (!strcasecmp(argv[0],\"lua-time-limit\") && argc == 2) {\n            server.lua_time_limit = strtoll(argv[1],NULL,10);\n        } else if (!strcasecmp(argv[0],\"slowlog-log-slower-than\") &&\n                   argc == 2)\n        {\n            server.slowlog_log_slower_than = strtoll(argv[1],NULL,10);\n        } else if (!strcasecmp(argv[0],\"slowlog-max-len\") && argc == 2) {\n            server.slowlog_max_len = strtoll(argv[1],NULL,10);\n        } else if (!strcasecmp(argv[0],\"client-output-buffer-limit\") &&\n                   argc == 5)\n        {\n            int class = getClientLimitClassByName(argv[1]);\n            unsigned long long hard, soft;\n            int soft_seconds;\n\n            if (class == -1) {\n                err = \"Unrecognized client limit class\";\n                goto loaderr;\n            }\n            hard = memtoll(argv[2],NULL);\n            soft = memtoll(argv[3],NULL);\n            soft_seconds = atoi(argv[4]);\n            if (soft_seconds < 0) {\n                err = \"Negative number of seconds in soft limit is invalid\";\n                goto loaderr;\n            }\n            server.client_obuf_limits[class].hard_limit_bytes = hard;\n            server.client_obuf_limits[class].soft_limit_bytes = soft;\n            server.client_obuf_limits[class].soft_limit_seconds = soft_seconds;\n        } else if (!strcasecmp(argv[0],\"stop-writes-on-bgsave-error\") &&\n                   argc == 2) {\n            if ((server.stop_writes_on_bgsave_err = yesnotoi(argv[1])) == -1) {\n                err = \"argument must be 'yes' or 'no'\"; goto loaderr;\n            }\n        } else if (!strcasecmp(argv[0],\"slave-priority\") && argc == 2) {\n            server.slave_priority = atoi(argv[1]);\n        } else if (!strcasecmp(argv[0],\"min-slaves-to-write\") && argc == 2) {\n            server.repl_min_slaves_to_write = atoi(argv[1]);\n            if (server.repl_min_slaves_to_write < 0) {\n                err = \"Invalid value for min-slaves-to-write.\"; goto loaderr;\n            }\n        } else if (!strcasecmp(argv[0],\"min-slaves-max-lag\") && argc == 2) {\n            server.repl_min_slaves_max_lag = atoi(argv[1]);\n            if (server.repl_min_slaves_max_lag < 0) {\n                err = \"Invalid value for min-slaves-max-lag.\"; goto loaderr;\n            }\n        } else if (!strcasecmp(argv[0],\"notify-keyspace-events\") && argc == 2) {\n            int flags = keyspaceEventsStringToFlags(argv[1]);\n\n            if (flags == -1) {\n                err = \"Invalid event class character. Use 'g$lshzxeA'.\";\n                goto loaderr;\n            }\n            server.notify_keyspace_events = flags;\n        } else if (!strcasecmp(argv[0],\"sentinel\")) {\n            /* argc == 1 is handled by main() as we need to enter the sentinel\n             * mode ASAP. */\n            // 如果 SENTINEL 命令不为空，那么执行以下代码\n            if (argc != 1) {\n                // 如果 SENTINEL 模式未开启，那么出错\n                if (!server.sentinel_mode) {\n                    err = \"sentinel directive while not in sentinel mode\";\n                    goto loaderr;\n                }\n                // 载入 SENTINEL 相关选项\n                err = sentinelHandleConfiguration(argv+1,argc-1);\n                if (err) goto loaderr;\n            }\n        } else {\n            err = \"Bad directive or wrong number of arguments\"; goto loaderr;\n        }\n        sdsfreesplitres(argv,argc);\n    }\n\n    /* Sanity checks. */\n    if (server.cluster_enabled && server.masterhost) {\n        linenum = slaveof_linenum;\n        i = linenum-1;\n        err = \"slaveof directive not allowed in cluster mode\";\n        goto loaderr;\n    }\n\n    sdsfreesplitres(lines,totlines);\n    return;\n\nloaderr:\n    fprintf(stderr, \"\\n*** FATAL CONFIG FILE ERROR ***\\n\");\n    fprintf(stderr, \"Reading the configuration file, at line %d\\n\", linenum);\n    fprintf(stderr, \">>> '%s'\\n\", lines[i]);\n    fprintf(stderr, \"%s\\n\", err);\n    exit(1);\n}\n\n/* Load the server configuration from the specified filename.\n *\n * 从给定文件中载入服务器配置。\n *\n * The function appends the additional configuration directives stored\n * in the 'options' string to the config file before loading.\n *\n * options 字符串会被追加到文件所载入的内容的后面。\n *\n * Both filename and options can be NULL, in such a case are considered\n * empty. This way loadServerConfig can be used to just load a file or\n * just load a string. \n *\n * filename 和 options 都可以是 NULL ，在这种情况下，\n * 服务器配置文件视为空文件。\n */\nvoid loadServerConfig(char *filename, char *options) {\n    sds config = sdsempty();\n    char buf[REDIS_CONFIGLINE_MAX+1];\n\n    /* Load the file content */\n    // 载入文件内容\n    if (filename) {\n        FILE *fp;\n\n        if (filename[0] == '-' && filename[1] == '\\0') {\n            fp = stdin;\n        } else {\n            if ((fp = fopen(filename,\"r\")) == NULL) {\n                redisLog(REDIS_WARNING,\n                    \"Fatal error, can't open config file '%s'\", filename);\n                exit(1);\n            }\n        }\n        while(fgets(buf,REDIS_CONFIGLINE_MAX+1,fp) != NULL)\n            config = sdscat(config,buf);\n        if (fp != stdin) fclose(fp);\n    }\n\n    /* Append the additional options */\n    // 追加 options 字符串到内容的末尾\n    if (options) {\n        config = sdscat(config,\"\\n\");\n        config = sdscat(config,options);\n    }\n\n    // 根据字符串内容，设置服务器配置\n    loadServerConfigFromString(config);\n\n    sdsfree(config);\n}\n\n/*-----------------------------------------------------------------------------\n * CONFIG SET implementation\n *----------------------------------------------------------------------------*/\n\nvoid configSetCommand(redisClient *c) {\n    robj *o;\n    long long ll;\n    redisAssertWithInfo(c,c->argv[2],sdsEncodedObject(c->argv[2]));\n    redisAssertWithInfo(c,c->argv[3],sdsEncodedObject(c->argv[3]));\n    o = c->argv[3];\n\n    if (!strcasecmp(c->argv[2]->ptr,\"dbfilename\")) {\n        if (!pathIsBaseName(o->ptr)) {\n            addReplyError(c, \"dbfilename can't be a path, just a filename\");\n            return;\n        }\n        zfree(server.rdb_filename);\n        server.rdb_filename = zstrdup(o->ptr);\n    } else if (!strcasecmp(c->argv[2]->ptr,\"requirepass\")) {\n        if (sdslen(o->ptr) > REDIS_AUTHPASS_MAX_LEN) goto badfmt;\n        zfree(server.requirepass);\n        server.requirepass = ((char*)o->ptr)[0] ? zstrdup(o->ptr) : NULL;\n    } else if (!strcasecmp(c->argv[2]->ptr,\"masterauth\")) {\n        zfree(server.masterauth);\n        server.masterauth = ((char*)o->ptr)[0] ? zstrdup(o->ptr) : NULL;\n    } else if (!strcasecmp(c->argv[2]->ptr,\"maxmemory\")) {\n        if (getLongLongFromObject(o,&ll) == REDIS_ERR ||\n            ll < 0) goto badfmt;\n        server.maxmemory = ll;\n        if (server.maxmemory) {\n            if (server.maxmemory < zmalloc_used_memory()) {\n                redisLog(REDIS_WARNING,\"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            }\n            freeMemoryIfNeeded();\n        }\n    } else if (!strcasecmp(c->argv[2]->ptr,\"maxclients\")) {\n        int orig_value = server.maxclients;\n\n        if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 1) goto badfmt;\n\n        /* Try to check if the OS is capable of supporting so many FDs. */\n        server.maxclients = ll;\n        if (ll > orig_value) {\n            adjustOpenFilesLimit();\n            if (server.maxclients != ll) {\n                addReplyErrorFormat(c,\"The operating system is not able to handle the specified number of clients, try with %d\", server.maxclients);\n                server.maxclients = orig_value;\n                return;\n            }\n            if (aeGetSetSize(server.el) <\n                server.maxclients + REDIS_EVENTLOOP_FDSET_INCR)\n            {\n                if (aeResizeSetSize(server.el,\n                    server.maxclients + REDIS_EVENTLOOP_FDSET_INCR) == AE_ERR)\n                {\n                    addReplyError(c,\"The event loop API used by Redis is not able to handle the specified number of clients\");\n                    server.maxclients = orig_value;\n                    return;\n                }\n            }\n        }\n    } else if (!strcasecmp(c->argv[2]->ptr,\"hz\")) {\n        if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;\n        server.hz = ll;\n        if (server.hz < REDIS_MIN_HZ) server.hz = REDIS_MIN_HZ;\n        if (server.hz > REDIS_MAX_HZ) server.hz = REDIS_MAX_HZ;\n    } else if (!strcasecmp(c->argv[2]->ptr,\"maxmemory-policy\")) {\n        if (!strcasecmp(o->ptr,\"volatile-lru\")) {\n            server.maxmemory_policy = REDIS_MAXMEMORY_VOLATILE_LRU;\n        } else if (!strcasecmp(o->ptr,\"volatile-random\")) {\n            server.maxmemory_policy = REDIS_MAXMEMORY_VOLATILE_RANDOM;\n        } else if (!strcasecmp(o->ptr,\"volatile-ttl\")) {\n            server.maxmemory_policy = REDIS_MAXMEMORY_VOLATILE_TTL;\n        } else if (!strcasecmp(o->ptr,\"allkeys-lru\")) {\n            server.maxmemory_policy = REDIS_MAXMEMORY_ALLKEYS_LRU;\n        } else if (!strcasecmp(o->ptr,\"allkeys-random\")) {\n            server.maxmemory_policy = REDIS_MAXMEMORY_ALLKEYS_RANDOM;\n        } else if (!strcasecmp(o->ptr,\"noeviction\")) {\n            server.maxmemory_policy = REDIS_MAXMEMORY_NO_EVICTION;\n        } else {\n            goto badfmt;\n        }\n    } else if (!strcasecmp(c->argv[2]->ptr,\"maxmemory-samples\")) {\n        if (getLongLongFromObject(o,&ll) == REDIS_ERR ||\n            ll <= 0) goto badfmt;\n        server.maxmemory_samples = ll;\n    } else if (!strcasecmp(c->argv[2]->ptr,\"timeout\")) {\n        if (getLongLongFromObject(o,&ll) == REDIS_ERR ||\n            ll < 0 || ll > LONG_MAX) goto badfmt;\n        server.maxidletime = ll;\n    } else if (!strcasecmp(c->argv[2]->ptr,\"tcp-keepalive\")) {\n        if (getLongLongFromObject(o,&ll) == REDIS_ERR ||\n            ll < 0 || ll > INT_MAX) goto badfmt;\n        server.tcpkeepalive = ll;\n    } else if (!strcasecmp(c->argv[2]->ptr,\"appendfsync\")) {\n        if (!strcasecmp(o->ptr,\"no\")) {\n            server.aof_fsync = AOF_FSYNC_NO;\n        } else if (!strcasecmp(o->ptr,\"everysec\")) {\n            server.aof_fsync = AOF_FSYNC_EVERYSEC;\n        } else if (!strcasecmp(o->ptr,\"always\")) {\n            server.aof_fsync = AOF_FSYNC_ALWAYS;\n        } else {\n            goto badfmt;\n        }\n    } else if (!strcasecmp(c->argv[2]->ptr,\"no-appendfsync-on-rewrite\")) {\n        int yn = yesnotoi(o->ptr);\n\n        if (yn == -1) goto badfmt;\n        server.aof_no_fsync_on_rewrite = yn;\n    } else if (!strcasecmp(c->argv[2]->ptr,\"appendonly\")) {\n        int enable = yesnotoi(o->ptr);\n\n        if (enable == -1) goto badfmt;\n        if (enable == 0 && server.aof_state != REDIS_AOF_OFF) {\n            stopAppendOnly();\n        } else if (enable && server.aof_state == REDIS_AOF_OFF) {\n            if (startAppendOnly() == REDIS_ERR) {\n                addReplyError(c,\n                    \"Unable to turn on AOF. Check server logs.\");\n                return;\n            }\n        }\n    } else if (!strcasecmp(c->argv[2]->ptr,\"auto-aof-rewrite-percentage\")) {\n        if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;\n        server.aof_rewrite_perc = ll;\n    } else if (!strcasecmp(c->argv[2]->ptr,\"auto-aof-rewrite-min-size\")) {\n        if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;\n        server.aof_rewrite_min_size = ll;\n    } else if (!strcasecmp(c->argv[2]->ptr,\"aof-rewrite-incremental-fsync\")) {\n        int yn = yesnotoi(o->ptr);\n\n        if (yn == -1) goto badfmt;\n        server.aof_rewrite_incremental_fsync = yn;\n    } else if (!strcasecmp(c->argv[2]->ptr,\"save\")) {\n        int vlen, j;\n        sds *v = sdssplitlen(o->ptr,sdslen(o->ptr),\" \",1,&vlen);\n\n        /* Perform sanity check before setting the new config:\n         * - Even number of args\n         * - Seconds >= 1, changes >= 0 */\n        if (vlen & 1) {\n            sdsfreesplitres(v,vlen);\n            goto badfmt;\n        }\n        for (j = 0; j < vlen; j++) {\n            char *eptr;\n            long val;\n\n            val = strtoll(v[j], &eptr, 10);\n            if (eptr[0] != '\\0' ||\n                ((j & 1) == 0 && val < 1) ||\n                ((j & 1) == 1 && val < 0)) {\n                sdsfreesplitres(v,vlen);\n                goto badfmt;\n            }\n        }\n        /* Finally set the new config */\n        resetServerSaveParams();\n        for (j = 0; j < vlen; j += 2) {\n            time_t seconds;\n            int changes;\n\n            seconds = strtoll(v[j],NULL,10);\n            changes = strtoll(v[j+1],NULL,10);\n            appendServerSaveParams(seconds, changes);\n        }\n        sdsfreesplitres(v,vlen);\n    } else if (!strcasecmp(c->argv[2]->ptr,\"slave-serve-stale-data\")) {\n        int yn = yesnotoi(o->ptr);\n\n        if (yn == -1) goto badfmt;\n        server.repl_serve_stale_data = yn;\n    } else if (!strcasecmp(c->argv[2]->ptr,\"slave-read-only\")) {\n        int yn = yesnotoi(o->ptr);\n\n        if (yn == -1) goto badfmt;\n        server.repl_slave_ro = yn;\n    } else if (!strcasecmp(c->argv[2]->ptr,\"dir\")) {\n        if (chdir((char*)o->ptr) == -1) {\n            addReplyErrorFormat(c,\"Changing directory: %s\", strerror(errno));\n            return;\n        }\n    } else if (!strcasecmp(c->argv[2]->ptr,\"hash-max-ziplist-entries\")) {\n        if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;\n        server.hash_max_ziplist_entries = ll;\n    } else if (!strcasecmp(c->argv[2]->ptr,\"hash-max-ziplist-value\")) {\n        if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;\n        server.hash_max_ziplist_value = ll;\n    } else if (!strcasecmp(c->argv[2]->ptr,\"list-max-ziplist-entries\")) {\n        if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;\n        server.list_max_ziplist_entries = ll;\n    } else if (!strcasecmp(c->argv[2]->ptr,\"list-max-ziplist-value\")) {\n        if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;\n        server.list_max_ziplist_value = ll;\n    } else if (!strcasecmp(c->argv[2]->ptr,\"set-max-intset-entries\")) {\n        if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;\n        server.set_max_intset_entries = ll;\n    } else if (!strcasecmp(c->argv[2]->ptr,\"zset-max-ziplist-entries\")) {\n        if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;\n        server.zset_max_ziplist_entries = ll;\n    } else if (!strcasecmp(c->argv[2]->ptr,\"zset-max-ziplist-value\")) {\n        if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;\n        server.zset_max_ziplist_value = ll;\n    } else if (!strcasecmp(c->argv[2]->ptr,\"hll-sparse-max-bytes\")) {\n        if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;\n        server.hll_sparse_max_bytes = ll;\n    } else if (!strcasecmp(c->argv[2]->ptr,\"lua-time-limit\")) {\n        if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;\n        server.lua_time_limit = ll;\n    } else if (!strcasecmp(c->argv[2]->ptr,\"slowlog-log-slower-than\")) {\n        if (getLongLongFromObject(o,&ll) == REDIS_ERR) goto badfmt;\n        server.slowlog_log_slower_than = ll;\n    } else if (!strcasecmp(c->argv[2]->ptr,\"slowlog-max-len\")) {\n        if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;\n        server.slowlog_max_len = (unsigned)ll;\n    } else if (!strcasecmp(c->argv[2]->ptr,\"loglevel\")) {\n        if (!strcasecmp(o->ptr,\"warning\")) {\n            server.verbosity = REDIS_WARNING;\n        } else if (!strcasecmp(o->ptr,\"notice\")) {\n            server.verbosity = REDIS_NOTICE;\n        } else if (!strcasecmp(o->ptr,\"verbose\")) {\n            server.verbosity = REDIS_VERBOSE;\n        } else if (!strcasecmp(o->ptr,\"debug\")) {\n            server.verbosity = REDIS_DEBUG;\n        } else {\n            goto badfmt;\n        }\n    } else if (!strcasecmp(c->argv[2]->ptr,\"client-output-buffer-limit\")) {\n        int vlen, j;\n        sds *v = sdssplitlen(o->ptr,sdslen(o->ptr),\" \",1,&vlen);\n\n        /* We need a multiple of 4: <class> <hard> <soft> <soft_seconds> */\n        if (vlen % 4) {\n            sdsfreesplitres(v,vlen);\n            goto badfmt;\n        }\n\n        /* Sanity check of single arguments, so that we either refuse the\n         * whole configuration string or accept it all, even if a single\n         * error in a single client class is present. */\n        for (j = 0; j < vlen; j++) {\n            char *eptr;\n            long val;\n\n            if ((j % 4) == 0) {\n                if (getClientLimitClassByName(v[j]) == -1) {\n                    sdsfreesplitres(v,vlen);\n                    goto badfmt;\n                }\n            } else {\n                val = strtoll(v[j], &eptr, 10);\n                if (eptr[0] != '\\0' || val < 0) {\n                    sdsfreesplitres(v,vlen);\n                    goto badfmt;\n                }\n            }\n        }\n        /* Finally set the new config */\n        for (j = 0; j < vlen; j += 4) {\n            int class;\n            unsigned long long hard, soft;\n            int soft_seconds;\n\n            class = getClientLimitClassByName(v[j]);\n            hard = strtoll(v[j+1],NULL,10);\n            soft = strtoll(v[j+2],NULL,10);\n            soft_seconds = strtoll(v[j+3],NULL,10);\n\n            server.client_obuf_limits[class].hard_limit_bytes = hard;\n            server.client_obuf_limits[class].soft_limit_bytes = soft;\n            server.client_obuf_limits[class].soft_limit_seconds = soft_seconds;\n        }\n        sdsfreesplitres(v,vlen);\n    } else if (!strcasecmp(c->argv[2]->ptr,\"stop-writes-on-bgsave-error\")) {\n        int yn = yesnotoi(o->ptr);\n\n        if (yn == -1) goto badfmt;\n        server.stop_writes_on_bgsave_err = yn;\n    } else if (!strcasecmp(c->argv[2]->ptr,\"repl-ping-slave-period\")) {\n        if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll <= 0) goto badfmt;\n        server.repl_ping_slave_period = ll;\n    } else if (!strcasecmp(c->argv[2]->ptr,\"repl-timeout\")) {\n        if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll <= 0) goto badfmt;\n        server.repl_timeout = ll;\n    } else if (!strcasecmp(c->argv[2]->ptr,\"repl-backlog-size\")) {\n        if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll <= 0) goto badfmt;\n        resizeReplicationBacklog(ll);\n    } else if (!strcasecmp(c->argv[2]->ptr,\"repl-backlog-ttl\")) {\n        if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;\n        server.repl_backlog_time_limit = ll;\n    } else if (!strcasecmp(c->argv[2]->ptr,\"watchdog-period\")) {\n        if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;\n        if (ll)\n            enableWatchdog(ll);\n        else\n            disableWatchdog();\n    } else if (!strcasecmp(c->argv[2]->ptr,\"rdbcompression\")) {\n        int yn = yesnotoi(o->ptr);\n\n        if (yn == -1) goto badfmt;\n        server.rdb_compression = yn;\n    } else if (!strcasecmp(c->argv[2]->ptr,\"notify-keyspace-events\")) {\n        int flags = keyspaceEventsStringToFlags(o->ptr);\n\n        if (flags == -1) goto badfmt;\n        server.notify_keyspace_events = flags;\n    } else if (!strcasecmp(c->argv[2]->ptr,\"repl-disable-tcp-nodelay\")) {\n        int yn = yesnotoi(o->ptr);\n\n        if (yn == -1) goto badfmt;\n        server.repl_disable_tcp_nodelay = yn;\n    } else if (!strcasecmp(c->argv[2]->ptr,\"slave-priority\")) {\n        if (getLongLongFromObject(o,&ll) == REDIS_ERR ||\n            ll < 0) goto badfmt;\n        server.slave_priority = ll;\n    } else if (!strcasecmp(c->argv[2]->ptr,\"min-slaves-to-write\")) {\n        if (getLongLongFromObject(o,&ll) == REDIS_ERR ||\n            ll < 0) goto badfmt;\n        server.repl_min_slaves_to_write = ll;\n        refreshGoodSlavesCount();\n    } else if (!strcasecmp(c->argv[2]->ptr,\"min-slaves-max-lag\")) {\n        if (getLongLongFromObject(o,&ll) == REDIS_ERR ||\n            ll < 0) goto badfmt;\n        server.repl_min_slaves_max_lag = ll;\n        refreshGoodSlavesCount();\n    } else if (!strcasecmp(c->argv[2]->ptr,\"cluster-node-timeout\")) {\n        if (getLongLongFromObject(o,&ll) == REDIS_ERR ||\n            ll <= 0) goto badfmt;\n        server.cluster_node_timeout = ll;\n    } else if (!strcasecmp(c->argv[2]->ptr,\"cluster-migration-barrier\")) {\n        if (getLongLongFromObject(o,&ll) == REDIS_ERR ||\n            ll < 0) goto badfmt;\n        server.cluster_migration_barrier = ll;\n    } else {\n        addReplyErrorFormat(c,\"Unsupported CONFIG parameter: %s\",\n            (char*)c->argv[2]->ptr);\n        return;\n    }\n    addReply(c,shared.ok);\n    return;\n\nbadfmt: /* Bad format errors */\n    addReplyErrorFormat(c,\"Invalid argument '%s' for CONFIG SET '%s'\",\n            (char*)o->ptr,\n            (char*)c->argv[2]->ptr);\n}\n\n/*-----------------------------------------------------------------------------\n * CONFIG GET implementation\n *----------------------------------------------------------------------------*/\n\n#define config_get_string_field(_name,_var) do { \\\n    if (stringmatch(pattern,_name,0)) { \\\n        addReplyBulkCString(c,_name); \\\n        addReplyBulkCString(c,_var ? _var : \"\"); \\\n        matches++; \\\n    } \\\n} while(0);\n\n#define config_get_bool_field(_name,_var) do { \\\n    if (stringmatch(pattern,_name,0)) { \\\n        addReplyBulkCString(c,_name); \\\n        addReplyBulkCString(c,_var ? \"yes\" : \"no\"); \\\n        matches++; \\\n    } \\\n} while(0);\n\n#define config_get_numerical_field(_name,_var) do { \\\n    if (stringmatch(pattern,_name,0)) { \\\n        ll2string(buf,sizeof(buf),_var); \\\n        addReplyBulkCString(c,_name); \\\n        addReplyBulkCString(c,buf); \\\n        matches++; \\\n    } \\\n} while(0);\n\nvoid configGetCommand(redisClient *c) {\n    robj *o = c->argv[2];\n    void *replylen = addDeferredMultiBulkLength(c);\n    char *pattern = o->ptr;\n    char buf[128];\n    int matches = 0;\n    redisAssertWithInfo(c,o,sdsEncodedObject(o));\n\n    /* String values */\n    config_get_string_field(\"dbfilename\",server.rdb_filename);\n    config_get_string_field(\"requirepass\",server.requirepass);\n    config_get_string_field(\"masterauth\",server.masterauth);\n    config_get_string_field(\"unixsocket\",server.unixsocket);\n    config_get_string_field(\"logfile\",server.logfile);\n    config_get_string_field(\"pidfile\",server.pidfile);\n\n    /* Numerical values */\n    config_get_numerical_field(\"maxmemory\",server.maxmemory);\n    config_get_numerical_field(\"maxmemory-samples\",server.maxmemory_samples);\n    config_get_numerical_field(\"timeout\",server.maxidletime);\n    config_get_numerical_field(\"tcp-keepalive\",server.tcpkeepalive);\n    config_get_numerical_field(\"auto-aof-rewrite-percentage\",\n            server.aof_rewrite_perc);\n    config_get_numerical_field(\"auto-aof-rewrite-min-size\",\n            server.aof_rewrite_min_size);\n    config_get_numerical_field(\"hash-max-ziplist-entries\",\n            server.hash_max_ziplist_entries);\n    config_get_numerical_field(\"hash-max-ziplist-value\",\n            server.hash_max_ziplist_value);\n    config_get_numerical_field(\"list-max-ziplist-entries\",\n            server.list_max_ziplist_entries);\n    config_get_numerical_field(\"list-max-ziplist-value\",\n            server.list_max_ziplist_value);\n    config_get_numerical_field(\"set-max-intset-entries\",\n            server.set_max_intset_entries);\n    config_get_numerical_field(\"zset-max-ziplist-entries\",\n            server.zset_max_ziplist_entries);\n    config_get_numerical_field(\"zset-max-ziplist-value\",\n            server.zset_max_ziplist_value);\n    config_get_numerical_field(\"hll-sparse-max-bytes\",\n            server.hll_sparse_max_bytes);\n    config_get_numerical_field(\"lua-time-limit\",server.lua_time_limit);\n    config_get_numerical_field(\"slowlog-log-slower-than\",\n            server.slowlog_log_slower_than);\n    config_get_numerical_field(\"slowlog-max-len\",\n            server.slowlog_max_len);\n    config_get_numerical_field(\"port\",server.port);\n    config_get_numerical_field(\"tcp-backlog\",server.tcp_backlog);\n    config_get_numerical_field(\"databases\",server.dbnum);\n    config_get_numerical_field(\"repl-ping-slave-period\",server.repl_ping_slave_period);\n    config_get_numerical_field(\"repl-timeout\",server.repl_timeout);\n    config_get_numerical_field(\"repl-backlog-size\",server.repl_backlog_size);\n    config_get_numerical_field(\"repl-backlog-ttl\",server.repl_backlog_time_limit);\n    config_get_numerical_field(\"maxclients\",server.maxclients);\n    config_get_numerical_field(\"watchdog-period\",server.watchdog_period);\n    config_get_numerical_field(\"slave-priority\",server.slave_priority);\n    config_get_numerical_field(\"min-slaves-to-write\",server.repl_min_slaves_to_write);\n    config_get_numerical_field(\"min-slaves-max-lag\",server.repl_min_slaves_max_lag);\n    config_get_numerical_field(\"hz\",server.hz);\n    config_get_numerical_field(\"cluster-node-timeout\",server.cluster_node_timeout);\n    config_get_numerical_field(\"cluster-migration-barrier\",server.cluster_migration_barrier);\n\n    /* Bool (yes/no) values */\n    config_get_bool_field(\"no-appendfsync-on-rewrite\",\n            server.aof_no_fsync_on_rewrite);\n    config_get_bool_field(\"slave-serve-stale-data\",\n            server.repl_serve_stale_data);\n    config_get_bool_field(\"slave-read-only\",\n            server.repl_slave_ro);\n    config_get_bool_field(\"stop-writes-on-bgsave-error\",\n            server.stop_writes_on_bgsave_err);\n    config_get_bool_field(\"daemonize\", server.daemonize);\n    config_get_bool_field(\"rdbcompression\", server.rdb_compression);\n    config_get_bool_field(\"rdbchecksum\", server.rdb_checksum);\n    config_get_bool_field(\"activerehashing\", server.activerehashing);\n    config_get_bool_field(\"repl-disable-tcp-nodelay\",\n            server.repl_disable_tcp_nodelay);\n    config_get_bool_field(\"aof-rewrite-incremental-fsync\",\n            server.aof_rewrite_incremental_fsync);\n\n    /* Everything we can't handle with macros follows. */\n\n    if (stringmatch(pattern,\"appendonly\",0)) {\n        addReplyBulkCString(c,\"appendonly\");\n        addReplyBulkCString(c,server.aof_state == REDIS_AOF_OFF ? \"no\" : \"yes\");\n        matches++;\n    }\n    if (stringmatch(pattern,\"dir\",0)) {\n        char buf[1024];\n\n        if (getcwd(buf,sizeof(buf)) == NULL)\n            buf[0] = '\\0';\n\n        addReplyBulkCString(c,\"dir\");\n        addReplyBulkCString(c,buf);\n        matches++;\n    }\n    if (stringmatch(pattern,\"maxmemory-policy\",0)) {\n        char *s;\n\n        switch(server.maxmemory_policy) {\n        case REDIS_MAXMEMORY_VOLATILE_LRU: s = \"volatile-lru\"; break;\n        case REDIS_MAXMEMORY_VOLATILE_TTL: s = \"volatile-ttl\"; break;\n        case REDIS_MAXMEMORY_VOLATILE_RANDOM: s = \"volatile-random\"; break;\n        case REDIS_MAXMEMORY_ALLKEYS_LRU: s = \"allkeys-lru\"; break;\n        case REDIS_MAXMEMORY_ALLKEYS_RANDOM: s = \"allkeys-random\"; break;\n        case REDIS_MAXMEMORY_NO_EVICTION: s = \"noeviction\"; break;\n        default: s = \"unknown\"; break; /* too harmless to panic */\n        }\n        addReplyBulkCString(c,\"maxmemory-policy\");\n        addReplyBulkCString(c,s);\n        matches++;\n    }\n    if (stringmatch(pattern,\"appendfsync\",0)) {\n        char *policy;\n\n        switch(server.aof_fsync) {\n        case AOF_FSYNC_NO: policy = \"no\"; break;\n        case AOF_FSYNC_EVERYSEC: policy = \"everysec\"; break;\n        case AOF_FSYNC_ALWAYS: policy = \"always\"; break;\n        default: policy = \"unknown\"; break; /* too harmless to panic */\n        }\n        addReplyBulkCString(c,\"appendfsync\");\n        addReplyBulkCString(c,policy);\n        matches++;\n    }\n    if (stringmatch(pattern,\"save\",0)) {\n        sds buf = sdsempty();\n        int j;\n\n        for (j = 0; j < server.saveparamslen; j++) {\n            buf = sdscatprintf(buf,\"%jd %d\",\n                    (intmax_t)server.saveparams[j].seconds,\n                    server.saveparams[j].changes);\n            if (j != server.saveparamslen-1)\n                buf = sdscatlen(buf,\" \",1);\n        }\n        addReplyBulkCString(c,\"save\");\n        addReplyBulkCString(c,buf);\n        sdsfree(buf);\n        matches++;\n    }\n    if (stringmatch(pattern,\"loglevel\",0)) {\n        char *s;\n\n        switch(server.verbosity) {\n        case REDIS_WARNING: s = \"warning\"; break;\n        case REDIS_VERBOSE: s = \"verbose\"; break;\n        case REDIS_NOTICE: s = \"notice\"; break;\n        case REDIS_DEBUG: s = \"debug\"; break;\n        default: s = \"unknown\"; break; /* too harmless to panic */\n        }\n        addReplyBulkCString(c,\"loglevel\");\n        addReplyBulkCString(c,s);\n        matches++;\n    }\n    if (stringmatch(pattern,\"client-output-buffer-limit\",0)) {\n        sds buf = sdsempty();\n        int j;\n\n        for (j = 0; j < REDIS_CLIENT_LIMIT_NUM_CLASSES; j++) {\n            buf = sdscatprintf(buf,\"%s %llu %llu %ld\",\n                    getClientLimitClassName(j),\n                    server.client_obuf_limits[j].hard_limit_bytes,\n                    server.client_obuf_limits[j].soft_limit_bytes,\n                    (long) server.client_obuf_limits[j].soft_limit_seconds);\n            if (j != REDIS_CLIENT_LIMIT_NUM_CLASSES-1)\n                buf = sdscatlen(buf,\" \",1);\n        }\n        addReplyBulkCString(c,\"client-output-buffer-limit\");\n        addReplyBulkCString(c,buf);\n        sdsfree(buf);\n        matches++;\n    }\n    if (stringmatch(pattern,\"unixsocketperm\",0)) {\n        char buf[32];\n        snprintf(buf,sizeof(buf),\"%o\",server.unixsocketperm);\n        addReplyBulkCString(c,\"unixsocketperm\");\n        addReplyBulkCString(c,buf);\n        matches++;\n    }\n    if (stringmatch(pattern,\"slaveof\",0)) {\n        char buf[256];\n\n        addReplyBulkCString(c,\"slaveof\");\n        if (server.masterhost)\n            snprintf(buf,sizeof(buf),\"%s %d\",\n                server.masterhost, server.masterport);\n        else\n            buf[0] = '\\0';\n        addReplyBulkCString(c,buf);\n        matches++;\n    }\n    if (stringmatch(pattern,\"notify-keyspace-events\",0)) {\n        robj *flagsobj = createObject(REDIS_STRING,\n            keyspaceEventsFlagsToString(server.notify_keyspace_events));\n\n        addReplyBulkCString(c,\"notify-keyspace-events\");\n        addReplyBulk(c,flagsobj);\n        decrRefCount(flagsobj);\n        matches++;\n    }\n    if (stringmatch(pattern,\"bind\",0)) {\n        sds aux = sdsjoin(server.bindaddr,server.bindaddr_count,\" \");\n\n        addReplyBulkCString(c,\"bind\");\n        addReplyBulkCString(c,aux);\n        sdsfree(aux);\n        matches++;\n    }\n    setDeferredMultiBulkLength(c,replylen,matches*2);\n}\n\n/*-----------------------------------------------------------------------------\n * CONFIG REWRITE implementation\n *----------------------------------------------------------------------------*/\n\n#define REDIS_CONFIG_REWRITE_SIGNATURE \"# Generated by CONFIG REWRITE\"\n\n/* We use the following dictionary type to store where a configuration\n * option is mentioned in the old configuration file, so it's\n * like \"maxmemory\" -> list of line numbers (first line is zero). */\nunsigned int dictSdsCaseHash(const void *key);\nint dictSdsKeyCaseCompare(void *privdata, const void *key1, const void *key2);\nvoid dictSdsDestructor(void *privdata, void *val);\nvoid dictListDestructor(void *privdata, void *val);\n\n/* Sentinel config rewriting is implemented inside sentinel.c by\n * rewriteConfigSentinelOption(). */\nvoid rewriteConfigSentinelOption(struct rewriteConfigState *state);\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/* 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. */\nvoid rewriteConfigAppendLine(struct rewriteConfigState *state, sds line) {\n    state->lines = zrealloc(state->lines, sizeof(char*) * (state->numlines+1));\n    state->lines[state->numlines++] = line;\n}\n\n/* Populate the option -> list of line numbers map. */\nvoid rewriteConfigAddLineNumberToOption(struct rewriteConfigState *state, sds option, int linenum) {\n    list *l = dictFetchValue(state->option_to_line,option);\n\n    if (l == NULL) {\n        l = listCreate();\n        dictAdd(state->option_to_line,sdsdup(option),l);\n    }\n    listAddNodeTail(l,(void*)(long)linenum);\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. */\nvoid rewriteConfigMarkAsProcessed(struct rewriteConfigState *state, char *option) {\n    sds opt = sdsnew(option);\n\n    if (dictAdd(state->rewritten,opt,NULL) != DICT_OK) sdsfree(opt);\n}\n\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. */\nstruct rewriteConfigState *rewriteConfigReadOldFile(char *path) {\n    FILE *fp = fopen(path,\"r\");\n    struct rewriteConfigState *state = zmalloc(sizeof(*state));\n    char buf[REDIS_CONFIGLINE_MAX+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,REDIS_CONFIGLINE_MAX+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/* 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. */\nvoid rewriteConfigRewriteLine(struct rewriteConfigState *state, char *option, sds line, int force) {\n    sds o = sdsnew(option);\n    list *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        listNode *ln = listFirst(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        listDelNode(l,ln);\n        if (listLength(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/* 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. */\nint 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. */\nvoid rewriteConfigBytesOption(struct rewriteConfigState *state, char *option, long long value, long long defvalue) {\n    char buf[64];\n    int force = value != defvalue;\n    sds line;\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 a yes/no option. */\nvoid rewriteConfigYesNoOption(struct rewriteConfigState *state, char *option, int value, int defvalue) {\n    int force = value != defvalue;\n    sds line = sdscatprintf(sdsempty(),\"%s %s\",option,\n        value ? \"yes\" : \"no\");\n\n    rewriteConfigRewriteLine(state,option,line,force);\n}\n\n/* Rewrite a string option. */\nvoid rewriteConfigStringOption(struct rewriteConfigState *state, char *option, char *value, char *defvalue) {\n    int force = 1;\n    sds line;\n\n    /* String options set to NULL need to be not present at all in the\n     * configuration file to be set to NULL again at the next reboot. */\n    if (value == NULL) {\n        rewriteConfigMarkAsProcessed(state,option);\n        return;\n    }\n\n    /* Compare the strings as sds strings to have a binary safe comparison. */\n    if (defvalue && strcmp(value,defvalue) == 0) force = 0;\n\n    line = sdsnew(option);\n    line = sdscatlen(line, \" \", 1);\n    line = sdscatrepr(line, value, strlen(value));\n\n    rewriteConfigRewriteLine(state,option,line,force);\n}\n\n/* Rewrite a numerical (long long range) option. */\nvoid rewriteConfigNumericalOption(struct rewriteConfigState *state, char *option, long long value, long long defvalue) {\n    int force = value != defvalue;\n    sds line = sdscatprintf(sdsempty(),\"%s %lld\",option,value);\n\n    rewriteConfigRewriteLine(state,option,line,force);\n}\n\n/* Rewrite a octal option. */\nvoid rewriteConfigOctalOption(struct rewriteConfigState *state, char *option, int value, int defvalue) {\n    int force = value != defvalue;\n    sds line = sdscatprintf(sdsempty(),\"%s %o\",option,value);\n\n    rewriteConfigRewriteLine(state,option,line,force);\n}\n\n/* Rewrite an enumeration option, after the \"value\" every enum/value pair\n * is specified, terminated by NULL. After NULL the default value is\n * specified. See how the function is used for more information. */\nvoid rewriteConfigEnumOption(struct rewriteConfigState *state, char *option, int value, ...) {\n    va_list ap;\n    char *enum_name, *matching_name = NULL;\n    int enum_val, def_val, force;\n    sds line;\n    \n    va_start(ap, value);\n    while(1) {\n        enum_name = va_arg(ap,char*);\n        enum_val = va_arg(ap,int);\n        if (enum_name == NULL) {\n            def_val = enum_val;\n            break;\n        }\n        if (value == enum_val) matching_name = enum_name;\n    }\n    va_end(ap);\n    \n    force = value != def_val;\n    line = sdscatprintf(sdsempty(),\"%s %s\",option,matching_name);\n    rewriteConfigRewriteLine(state,option,line,force);\n}\n\n/* Rewrite the syslog-fability option. */\nvoid rewriteConfigSyslogfacilityOption(struct rewriteConfigState *state) {\n    int value = server.syslog_facility, j;\n    int force = value != LOG_LOCAL0;\n    char *name = NULL, *option = \"syslog-facility\";\n    sds line;\n\n    for (j = 0; validSyslogFacilities[j].name; j++) {\n        if (validSyslogFacilities[j].value == value) {\n            name = (char*) validSyslogFacilities[j].name;\n            break;\n        }\n    }\n    line = sdscatprintf(sdsempty(),\"%s %s\",option,name);\n    rewriteConfigRewriteLine(state,option,line,force);\n}\n\n/* Rewrite the save option. */\nvoid rewriteConfigSaveOption(struct rewriteConfigState *state) {\n    int j;\n    sds line;\n\n    /* Note that if there are no save parameters at all, all the current\n     * config line with \"save\" will be detected as orphaned and deleted,\n     * resulting into no RDB persistence as expected. */\n    for (j = 0; j < server.saveparamslen; j++) {\n        line = sdscatprintf(sdsempty(),\"save %ld %d\",\n            (long) server.saveparams[j].seconds, server.saveparams[j].changes);\n        rewriteConfigRewriteLine(state,\"save\",line,1);\n    }\n    /* Mark \"save\" as processed in case server.saveparamslen is zero. */\n    rewriteConfigMarkAsProcessed(state,\"save\");\n}\n\n/* Rewrite the dir option, always using absolute paths.*/\nvoid rewriteConfigDirOption(struct rewriteConfigState *state) {\n    char cwd[1024];\n\n    if (getcwd(cwd,sizeof(cwd)) == NULL) {\n        rewriteConfigMarkAsProcessed(state,\"dir\");\n        return; /* no rewrite on error. */\n    }\n    rewriteConfigStringOption(state,\"dir\",cwd,NULL);\n}\n\n/* Rewrite the slaveof option. */\nvoid rewriteConfigSlaveofOption(struct rewriteConfigState *state) {\n    char *option = \"slaveof\";\n    sds line;\n\n    /* If this is a master, we want all the slaveof config options\n     * in the file to be removed. Note that if this is a cluster instance\n     * we don't want a slaveof directive inside redis.conf. */\n    if (server.cluster_enabled || server.masterhost == NULL) {\n        rewriteConfigMarkAsProcessed(state,\"slaveof\");\n        return;\n    }\n    line = sdscatprintf(sdsempty(),\"%s %s %d\", option,\n        server.masterhost, server.masterport);\n    rewriteConfigRewriteLine(state,option,line,1);\n}\n\n/* Rewrite the notify-keyspace-events option. */\nvoid rewriteConfigNotifykeyspaceeventsOption(struct rewriteConfigState *state) {\n    int force = server.notify_keyspace_events != 0;\n    char *option = \"notify-keyspace-events\";\n    sds line, flags;\n\n    flags = keyspaceEventsFlagsToString(server.notify_keyspace_events);\n    line = sdsnew(option);\n    line = sdscatlen(line, \" \", 1);\n    line = sdscatrepr(line, flags, sdslen(flags));\n    sdsfree(flags);\n    rewriteConfigRewriteLine(state,option,line,force);\n}\n\n/* Rewrite the client-output-buffer-limit option. */\nvoid rewriteConfigClientoutputbufferlimitOption(struct rewriteConfigState *state) {\n    int j;\n    char *option = \"client-output-buffer-limit\";\n\n    for (j = 0; j < REDIS_CLIENT_LIMIT_NUM_CLASSES; j++) {\n        int force = (server.client_obuf_limits[j].hard_limit_bytes !=\n                    clientBufferLimitsDefaults[j].hard_limit_bytes) ||\n                    (server.client_obuf_limits[j].soft_limit_bytes !=\n                    clientBufferLimitsDefaults[j].soft_limit_bytes) ||\n                    (server.client_obuf_limits[j].soft_limit_seconds !=\n                    clientBufferLimitsDefaults[j].soft_limit_seconds);\n        sds line;\n        char hard[64], soft[64];\n\n        rewriteConfigFormatMemory(hard,sizeof(hard),\n                server.client_obuf_limits[j].hard_limit_bytes);\n        rewriteConfigFormatMemory(soft,sizeof(soft),\n                server.client_obuf_limits[j].soft_limit_bytes);\n\n        line = sdscatprintf(sdsempty(),\"%s %s %s %s %ld\",\n                option, getClientLimitClassName(j), hard, soft,\n                (long) server.client_obuf_limits[j].soft_limit_seconds);\n        rewriteConfigRewriteLine(state,option,line,force);\n    }\n}\n\n/* Rewrite the bind option. */\nvoid rewriteConfigBindOption(struct rewriteConfigState *state) {\n    int force = 1;\n    sds line, addresses;\n    char *option = \"bind\";\n\n    /* Nothing to rewrite if we don't have bind addresses. */\n    if (server.bindaddr_count == 0) {\n        rewriteConfigMarkAsProcessed(state,option);\n        return;\n    }\n\n    /* Rewrite as bind <addr1> <addr2> ... <addrN> */\n    addresses = sdsjoin(server.bindaddr,server.bindaddr_count,\" \");\n    line = sdsnew(option);\n    line = sdscatlen(line, \" \", 1);\n    line = sdscatsds(line, addresses);\n    sdsfree(addresses);\n\n    rewriteConfigRewriteLine(state,option,line,force);\n}\n\n/* Glue together the configuration lines in the current configuration\n * rewrite state into a single string, stripping multiple empty lines. */\nsds 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/* Free the configuration rewrite state. */\nvoid rewriteConfigReleaseState(struct rewriteConfigState *state) {\n    sdsfreesplitres(state->lines,state->numlines);\n    dictRelease(state->option_to_line);\n    dictRelease(state->rewritten);\n    zfree(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. */\nvoid rewriteConfigRemoveOrphaned(struct rewriteConfigState *state) {\n    dictIterator *di = dictGetIterator(state->option_to_line);\n    dictEntry *de;\n\n    while((de = dictNext(di)) != NULL) {\n        list *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            redisLog(REDIS_DEBUG,\"Not rewritten option: %s\", option);\n            continue;\n        }\n\n        while(listLength(l)) {\n            listNode *ln = listFirst(l);\n            int linenum = (long) ln->value;\n\n            sdsfree(state->lines[linenum]);\n            state->lines[linenum] = sdsempty();\n            listDelNode(l,ln);\n        }\n    }\n    dictReleaseIterator(di);\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. */\nint 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 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. */\nint rewriteConfig(char *path) {\n    struct rewriteConfigState *state;\n    sds newcontent;\n    int retval;\n\n    /* Step 1: read the old config into our rewrite state. */\n    if ((state = rewriteConfigReadOldFile(path)) == NULL) return -1;\n\n    /* Step 2: rewrite every single option, replacing or appending it inside\n     * the rewrite state. */\n\n    rewriteConfigYesNoOption(state,\"daemonize\",server.daemonize,0);\n    rewriteConfigStringOption(state,\"pidfile\",server.pidfile,REDIS_DEFAULT_PID_FILE);\n    rewriteConfigNumericalOption(state,\"port\",server.port,REDIS_SERVERPORT);\n    rewriteConfigNumericalOption(state,\"tcp-backlog\",server.tcp_backlog,REDIS_TCP_BACKLOG);\n    rewriteConfigBindOption(state);\n    rewriteConfigStringOption(state,\"unixsocket\",server.unixsocket,NULL);\n    rewriteConfigOctalOption(state,\"unixsocketperm\",server.unixsocketperm,REDIS_DEFAULT_UNIX_SOCKET_PERM);\n    rewriteConfigNumericalOption(state,\"timeout\",server.maxidletime,REDIS_MAXIDLETIME);\n    rewriteConfigNumericalOption(state,\"tcp-keepalive\",server.tcpkeepalive,REDIS_DEFAULT_TCP_KEEPALIVE);\n    rewriteConfigEnumOption(state,\"loglevel\",server.verbosity,\n        \"debug\", REDIS_DEBUG,\n        \"verbose\", REDIS_VERBOSE,\n        \"notice\", REDIS_NOTICE,\n        \"warning\", REDIS_WARNING,\n        NULL, REDIS_DEFAULT_VERBOSITY);\n    rewriteConfigStringOption(state,\"logfile\",server.logfile,REDIS_DEFAULT_LOGFILE);\n    rewriteConfigYesNoOption(state,\"syslog-enabled\",server.syslog_enabled,REDIS_DEFAULT_SYSLOG_ENABLED);\n    rewriteConfigStringOption(state,\"syslog-ident\",server.syslog_ident,REDIS_DEFAULT_SYSLOG_IDENT);\n    rewriteConfigSyslogfacilityOption(state);\n    rewriteConfigSaveOption(state);\n    rewriteConfigNumericalOption(state,\"databases\",server.dbnum,REDIS_DEFAULT_DBNUM);\n    rewriteConfigYesNoOption(state,\"stop-writes-on-bgsave-error\",server.stop_writes_on_bgsave_err,REDIS_DEFAULT_STOP_WRITES_ON_BGSAVE_ERROR);\n    rewriteConfigYesNoOption(state,\"rdbcompression\",server.rdb_compression,REDIS_DEFAULT_RDB_COMPRESSION);\n    rewriteConfigYesNoOption(state,\"rdbchecksum\",server.rdb_checksum,REDIS_DEFAULT_RDB_CHECKSUM);\n    rewriteConfigStringOption(state,\"dbfilename\",server.rdb_filename,REDIS_DEFAULT_RDB_FILENAME);\n    rewriteConfigDirOption(state);\n    rewriteConfigSlaveofOption(state);\n    rewriteConfigStringOption(state,\"masterauth\",server.masterauth,NULL);\n    rewriteConfigYesNoOption(state,\"slave-serve-stale-data\",server.repl_serve_stale_data,REDIS_DEFAULT_SLAVE_SERVE_STALE_DATA);\n    rewriteConfigYesNoOption(state,\"slave-read-only\",server.repl_slave_ro,REDIS_DEFAULT_SLAVE_READ_ONLY);\n    rewriteConfigNumericalOption(state,\"repl-ping-slave-period\",server.repl_ping_slave_period,REDIS_REPL_PING_SLAVE_PERIOD);\n    rewriteConfigNumericalOption(state,\"repl-timeout\",server.repl_timeout,REDIS_REPL_TIMEOUT);\n    rewriteConfigBytesOption(state,\"repl-backlog-size\",server.repl_backlog_size,REDIS_DEFAULT_REPL_BACKLOG_SIZE);\n    rewriteConfigBytesOption(state,\"repl-backlog-ttl\",server.repl_backlog_time_limit,REDIS_DEFAULT_REPL_BACKLOG_TIME_LIMIT);\n    rewriteConfigYesNoOption(state,\"repl-disable-tcp-nodelay\",server.repl_disable_tcp_nodelay,REDIS_DEFAULT_REPL_DISABLE_TCP_NODELAY);\n    rewriteConfigNumericalOption(state,\"slave-priority\",server.slave_priority,REDIS_DEFAULT_SLAVE_PRIORITY);\n    rewriteConfigNumericalOption(state,\"min-slaves-to-write\",server.repl_min_slaves_to_write,REDIS_DEFAULT_MIN_SLAVES_TO_WRITE);\n    rewriteConfigNumericalOption(state,\"min-slaves-max-lag\",server.repl_min_slaves_max_lag,REDIS_DEFAULT_MIN_SLAVES_MAX_LAG);\n    rewriteConfigStringOption(state,\"requirepass\",server.requirepass,NULL);\n    rewriteConfigNumericalOption(state,\"maxclients\",server.maxclients,REDIS_MAX_CLIENTS);\n    rewriteConfigBytesOption(state,\"maxmemory\",server.maxmemory,REDIS_DEFAULT_MAXMEMORY);\n    rewriteConfigEnumOption(state,\"maxmemory-policy\",server.maxmemory_policy,\n        \"volatile-lru\", REDIS_MAXMEMORY_VOLATILE_LRU,\n        \"allkeys-lru\", REDIS_MAXMEMORY_ALLKEYS_LRU,\n        \"volatile-random\", REDIS_MAXMEMORY_VOLATILE_RANDOM,\n        \"allkeys-random\", REDIS_MAXMEMORY_ALLKEYS_RANDOM,\n        \"volatile-ttl\", REDIS_MAXMEMORY_VOLATILE_TTL,\n        \"noeviction\", REDIS_MAXMEMORY_NO_EVICTION,\n        NULL, REDIS_DEFAULT_MAXMEMORY_POLICY);\n    rewriteConfigNumericalOption(state,\"maxmemory-samples\",server.maxmemory_samples,REDIS_DEFAULT_MAXMEMORY_SAMPLES);\n    rewriteConfigYesNoOption(state,\"appendonly\",server.aof_state != REDIS_AOF_OFF,0);\n    rewriteConfigStringOption(state,\"appendfilename\",server.aof_filename,REDIS_DEFAULT_AOF_FILENAME);\n    rewriteConfigEnumOption(state,\"appendfsync\",server.aof_fsync,\n        \"everysec\", AOF_FSYNC_EVERYSEC,\n        \"always\", AOF_FSYNC_ALWAYS,\n        \"no\", AOF_FSYNC_NO,\n        NULL, REDIS_DEFAULT_AOF_FSYNC);\n    rewriteConfigYesNoOption(state,\"no-appendfsync-on-rewrite\",server.aof_no_fsync_on_rewrite,REDIS_DEFAULT_AOF_NO_FSYNC_ON_REWRITE);\n    rewriteConfigNumericalOption(state,\"auto-aof-rewrite-percentage\",server.aof_rewrite_perc,REDIS_AOF_REWRITE_PERC);\n    rewriteConfigBytesOption(state,\"auto-aof-rewrite-min-size\",server.aof_rewrite_min_size,REDIS_AOF_REWRITE_MIN_SIZE);\n    rewriteConfigNumericalOption(state,\"lua-time-limit\",server.lua_time_limit,REDIS_LUA_TIME_LIMIT);\n    rewriteConfigYesNoOption(state,\"cluster-enabled\",server.cluster_enabled,0);\n    rewriteConfigStringOption(state,\"cluster-config-file\",server.cluster_configfile,REDIS_DEFAULT_CLUSTER_CONFIG_FILE);\n    rewriteConfigNumericalOption(state,\"cluster-node-timeout\",server.cluster_node_timeout,REDIS_CLUSTER_DEFAULT_NODE_TIMEOUT);\n    rewriteConfigNumericalOption(state,\"cluster-migration-barrier\",server.cluster_migration_barrier,REDIS_CLUSTER_DEFAULT_MIGRATION_BARRIER);\n    rewriteConfigNumericalOption(state,\"slowlog-log-slower-than\",server.slowlog_log_slower_than,REDIS_SLOWLOG_LOG_SLOWER_THAN);\n    rewriteConfigNumericalOption(state,\"slowlog-max-len\",server.slowlog_max_len,REDIS_SLOWLOG_MAX_LEN);\n    rewriteConfigNotifykeyspaceeventsOption(state);\n    rewriteConfigNumericalOption(state,\"hash-max-ziplist-entries\",server.hash_max_ziplist_entries,REDIS_HASH_MAX_ZIPLIST_ENTRIES);\n    rewriteConfigNumericalOption(state,\"hash-max-ziplist-value\",server.hash_max_ziplist_value,REDIS_HASH_MAX_ZIPLIST_VALUE);\n    rewriteConfigNumericalOption(state,\"list-max-ziplist-entries\",server.list_max_ziplist_entries,REDIS_LIST_MAX_ZIPLIST_ENTRIES);\n    rewriteConfigNumericalOption(state,\"list-max-ziplist-value\",server.list_max_ziplist_value,REDIS_LIST_MAX_ZIPLIST_VALUE);\n    rewriteConfigNumericalOption(state,\"set-max-intset-entries\",server.set_max_intset_entries,REDIS_SET_MAX_INTSET_ENTRIES);\n    rewriteConfigNumericalOption(state,\"zset-max-ziplist-entries\",server.zset_max_ziplist_entries,REDIS_ZSET_MAX_ZIPLIST_ENTRIES);\n    rewriteConfigNumericalOption(state,\"zset-max-ziplist-value\",server.zset_max_ziplist_value,REDIS_ZSET_MAX_ZIPLIST_VALUE);\n    rewriteConfigNumericalOption(state,\"hll-sparse-max-bytes\",server.hll_sparse_max_bytes,REDIS_DEFAULT_HLL_SPARSE_MAX_BYTES);\n    rewriteConfigYesNoOption(state,\"activerehashing\",server.activerehashing,REDIS_DEFAULT_ACTIVE_REHASHING);\n    rewriteConfigClientoutputbufferlimitOption(state);\n    rewriteConfigNumericalOption(state,\"hz\",server.hz,REDIS_DEFAULT_HZ);\n    rewriteConfigYesNoOption(state,\"aof-rewrite-incremental-fsync\",server.aof_rewrite_incremental_fsync,REDIS_DEFAULT_AOF_REWRITE_INCREMENTAL_FSYNC);\n    if (server.sentinel_mode) rewriteConfigSentinelOption(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\n    sdsfree(newcontent);\n    rewriteConfigReleaseState(state);\n    return retval;\n}\n\n/*-----------------------------------------------------------------------------\n * CONFIG command entry point\n *----------------------------------------------------------------------------*/\n\nvoid configCommand(redisClient *c) {\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            redisLog(REDIS_WARNING,\"CONFIG REWRITE failed: %s\", strerror(errno));\n            addReplyErrorFormat(c,\"Rewriting config file: %s\", strerror(errno));\n        } else {\n            redisLog(REDIS_WARNING,\"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    }\n    return;\n\nbadarity:\n    addReplyErrorFormat(c,\"Wrong number of arguments for CONFIG %s\",\n        (char*) c->argv[1]->ptr);\n}\n"
  },
  {
    "path": "src/config.h",
    "content": "/*\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#ifndef __CONFIG_H\n#define __CONFIG_H\n\n#ifdef __APPLE__\n#include <AvailabilityMacros.h>\n#endif\n\n/* Define redis_fstat to fstat or fstat64() */\n#if defined(__APPLE__) && !defined(MAC_OS_X_VERSION_10_6)\n#define redis_fstat fstat64\n#define redis_stat stat64\n#else\n#define redis_fstat fstat\n#define redis_stat stat\n#endif\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#endif\n\n/* Test for task_info() */\n#if defined(__APPLE__)\n#define HAVE_TASKINFO 1\n#endif\n\n/* Test for backtrace() */\n#if defined(__APPLE__) || defined(__linux__)\n#define HAVE_BACKTRACE 1\n#endif\n\n/* Test for polling API */\n#ifdef __linux__\n#define HAVE_EPOLL 1\n#endif\n\n#if (defined(__APPLE__) && defined(MAC_OS_X_VERSION_10_6)) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined (__NetBSD__)\n#define HAVE_KQUEUE 1\n#endif\n\n#ifdef __sun\n#include <sys/feature_tests.h>\n#ifdef _DTRACE_VERSION\n#define HAVE_EVPORT 1\n#endif\n#endif\n\n/* Define aof_fsync to fdatasync() in Linux and fsync() for all the rest */\n#ifdef __linux__\n#define aof_fsync fdatasync\n#else\n#define aof_fsync fsync\n#endif\n\n/* Define rdb_fsync_range to sync_file_range() on Linux, otherwise we use\n * the plain fsync() call. */\n#ifdef __linux__\n#include <linux/version.h>\n#include <features.h>\n#if defined(__GLIBC__) && defined(__GLIBC_PREREQ)\n#if (LINUX_VERSION_CODE >= 0x020611 && __GLIBC_PREREQ(2, 6))\n#define HAVE_SYNC_FILE_RANGE 1\n#endif\n#else\n#if (LINUX_VERSION_CODE >= 0x020611)\n#define HAVE_SYNC_FILE_RANGE 1\n#endif\n#endif\n#endif\n\n#ifdef HAVE_SYNC_FILE_RANGE\n#define rdb_fsync_range(fd,off,size) sync_file_range(fd,off,size,SYNC_FILE_RANGE_WAIT_BEFORE|SYNC_FILE_RANGE_WRITE)\n#else\n#define rdb_fsync_range(fd,off,size) fsync(fd)\n#endif\n\n/* Check if we can use setproctitle().\n * BSD systems have support for it, we provide an implementation for\n * Linux and osx. */\n#if (defined __NetBSD__ || defined __FreeBSD__ || defined __OpenBSD__)\n#define USE_SETPROCTITLE\n#endif\n\n#if (defined __linux || defined __APPLE__)\n#define USE_SETPROCTITLE\n#define INIT_SETPROCTITLE_REPLACEMENT\nvoid spt_init(int argc, char *argv[]);\nvoid setproctitle(const char *fmt, ...);\n#endif\n\n/* Byte ordering detection */\n#include <sys/types.h> /* This will likely define BYTE_ORDER */\n\n#ifndef BYTE_ORDER\n#if (BSD >= 199103)\n# include <machine/endian.h>\n#else\n#if defined(linux) || defined(__linux__)\n# include <endian.h>\n#else\n#define\tLITTLE_ENDIAN\t1234\t/* least-significant byte first (vax, pc) */\n#define\tBIG_ENDIAN\t4321\t/* most-significant byte first (IBM, net) */\n#define\tPDP_ENDIAN\t3412\t/* LSB first in word, MSW first in long (pdp)*/\n\n#if defined(__i386__) || defined(__x86_64__) || defined(__amd64__) || \\\n   defined(vax) || defined(ns32000) || defined(sun386) || \\\n   defined(MIPSEL) || defined(_MIPSEL) || defined(BIT_ZERO_ON_RIGHT) || \\\n   defined(__alpha__) || defined(__alpha)\n#define BYTE_ORDER    LITTLE_ENDIAN\n#endif\n\n#if defined(sel) || defined(pyr) || defined(mc68000) || defined(sparc) || \\\n    defined(is68k) || defined(tahoe) || defined(ibm032) || defined(ibm370) || \\\n    defined(MIPSEB) || defined(_MIPSEB) || defined(_IBMR2) || defined(DGUX) ||\\\n    defined(apollo) || defined(__convex__) || defined(_CRAY) || \\\n    defined(__hppa) || defined(__hp9000) || \\\n    defined(__hp9000s300) || defined(__hp9000s700) || \\\n    defined (BIT_ZERO_ON_LEFT) || defined(m68k) || defined(__sparc)\n#define BYTE_ORDER\tBIG_ENDIAN\n#endif\n#endif /* linux */\n#endif /* BSD */\n#endif /* BYTE_ORDER */\n\n/* Sometimes after including an OS-specific header that defines the\n * endianess we end with __BYTE_ORDER but not with BYTE_ORDER that is what\n * the Redis code uses. In this case let's define everything without the\n * underscores. */\n#ifndef BYTE_ORDER\n#ifdef __BYTE_ORDER\n#if defined(__LITTLE_ENDIAN) && defined(__BIG_ENDIAN)\n#ifndef LITTLE_ENDIAN\n#define LITTLE_ENDIAN __LITTLE_ENDIAN\n#endif\n#ifndef BIG_ENDIAN\n#define BIG_ENDIAN __BIG_ENDIAN\n#endif\n#if (__BYTE_ORDER == __LITTLE_ENDIAN)\n#define BYTE_ORDER LITTLE_ENDIAN\n#else\n#define BYTE_ORDER BIG_ENDIAN\n#endif\n#endif\n#endif\n#endif\n\n#if !defined(BYTE_ORDER) || \\\n    (BYTE_ORDER != BIG_ENDIAN && BYTE_ORDER != LITTLE_ENDIAN)\n\t/* you must determine what the correct bit order is for\n\t * your compiler - the next line is an intentional error\n\t * which will force your compiles to bomb until you fix\n\t * the above macros.\n\t */\n#error \"Undefined or invalid BYTE_ORDER\"\n#endif\n\n#if (__i386 || __amd64) && __GNUC__\n#define GNUC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)\n#if (GNUC_VERSION >= 40100) || defined(__clang__)\n#define HAVE_ATOMIC\n#endif\n#endif\n\n#endif\n"
  },
  {
    "path": "src/crc16.c",
    "content": "#include \"redis.h\"\n\n/*      \n * Copyright 2001-2010 Georges Menie (www.menie.org)\n * Copyright 2010-2012 Salvatore Sanfilippo (adapted to Redis coding style)\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\n *       notice, 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 the University of California, Berkeley nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/* CRC16 implementation according to CCITT standards.\n *\n * Note by @antirez: this is actually the XMODEM CRC 16 algorithm, using the\n * following parameters:\n *\n * Name                       : \"XMODEM\", also known as \"ZMODEM\", \"CRC-16/ACORN\"\n * Width                      : 16 bit\n * Poly                       : 1021 (That is actually x^16 + x^12 + x^5 + 1)\n * Initialization             : 0000\n * Reflect Input byte         : False\n * Reflect Output CRC         : False\n * Xor constant to output CRC : 0000\n * Output for \"123456789\"     : 31C3\n */\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  \nuint16_t crc16(const char *buf, int len) {\n    int counter;\n    uint16_t crc = 0;\n    for (counter = 0; counter < len; counter++)\n            crc = (crc<<8) ^ crc16tab[((crc>>8) ^ *buf++)&0x00FF];\n    return crc;\n}\n"
  },
  {
    "path": "src/crc64.c",
    "content": "/* Redis uses the CRC64 variant with \"Jones\" coefficients and init value of 0.\n *\n * Specification of this CRC64 variant follows:\n * Name: crc-64-jones\n * Width: 64 bites\n * Poly: 0xad93d23594c935a9\n * Reflected In: True\n * Xor_In: 0xffffffffffffffff\n * Reflected_Out: True\n * Xor_Out: 0x0\n * Check(\"123456789\"): 0xe9c6d914c4b8d9ca\n *\n * Copyright (c) 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#include <stdint.h>\n\nstatic const uint64_t crc64_tab[256] = {\n    UINT64_C(0x0000000000000000), UINT64_C(0x7ad870c830358979),\n    UINT64_C(0xf5b0e190606b12f2), UINT64_C(0x8f689158505e9b8b),\n    UINT64_C(0xc038e5739841b68f), UINT64_C(0xbae095bba8743ff6),\n    UINT64_C(0x358804e3f82aa47d), UINT64_C(0x4f50742bc81f2d04),\n    UINT64_C(0xab28ecb46814fe75), UINT64_C(0xd1f09c7c5821770c),\n    UINT64_C(0x5e980d24087fec87), UINT64_C(0x24407dec384a65fe),\n    UINT64_C(0x6b1009c7f05548fa), UINT64_C(0x11c8790fc060c183),\n    UINT64_C(0x9ea0e857903e5a08), UINT64_C(0xe478989fa00bd371),\n    UINT64_C(0x7d08ff3b88be6f81), UINT64_C(0x07d08ff3b88be6f8),\n    UINT64_C(0x88b81eabe8d57d73), UINT64_C(0xf2606e63d8e0f40a),\n    UINT64_C(0xbd301a4810ffd90e), UINT64_C(0xc7e86a8020ca5077),\n    UINT64_C(0x4880fbd87094cbfc), UINT64_C(0x32588b1040a14285),\n    UINT64_C(0xd620138fe0aa91f4), UINT64_C(0xacf86347d09f188d),\n    UINT64_C(0x2390f21f80c18306), UINT64_C(0x594882d7b0f40a7f),\n    UINT64_C(0x1618f6fc78eb277b), UINT64_C(0x6cc0863448deae02),\n    UINT64_C(0xe3a8176c18803589), UINT64_C(0x997067a428b5bcf0),\n    UINT64_C(0xfa11fe77117cdf02), UINT64_C(0x80c98ebf2149567b),\n    UINT64_C(0x0fa11fe77117cdf0), UINT64_C(0x75796f2f41224489),\n    UINT64_C(0x3a291b04893d698d), UINT64_C(0x40f16bccb908e0f4),\n    UINT64_C(0xcf99fa94e9567b7f), UINT64_C(0xb5418a5cd963f206),\n    UINT64_C(0x513912c379682177), UINT64_C(0x2be1620b495da80e),\n    UINT64_C(0xa489f35319033385), UINT64_C(0xde51839b2936bafc),\n    UINT64_C(0x9101f7b0e12997f8), UINT64_C(0xebd98778d11c1e81),\n    UINT64_C(0x64b116208142850a), UINT64_C(0x1e6966e8b1770c73),\n    UINT64_C(0x8719014c99c2b083), UINT64_C(0xfdc17184a9f739fa),\n    UINT64_C(0x72a9e0dcf9a9a271), UINT64_C(0x08719014c99c2b08),\n    UINT64_C(0x4721e43f0183060c), UINT64_C(0x3df994f731b68f75),\n    UINT64_C(0xb29105af61e814fe), UINT64_C(0xc849756751dd9d87),\n    UINT64_C(0x2c31edf8f1d64ef6), UINT64_C(0x56e99d30c1e3c78f),\n    UINT64_C(0xd9810c6891bd5c04), UINT64_C(0xa3597ca0a188d57d),\n    UINT64_C(0xec09088b6997f879), UINT64_C(0x96d1784359a27100),\n    UINT64_C(0x19b9e91b09fcea8b), UINT64_C(0x636199d339c963f2),\n    UINT64_C(0xdf7adabd7a6e2d6f), UINT64_C(0xa5a2aa754a5ba416),\n    UINT64_C(0x2aca3b2d1a053f9d), UINT64_C(0x50124be52a30b6e4),\n    UINT64_C(0x1f423fcee22f9be0), UINT64_C(0x659a4f06d21a1299),\n    UINT64_C(0xeaf2de5e82448912), UINT64_C(0x902aae96b271006b),\n    UINT64_C(0x74523609127ad31a), UINT64_C(0x0e8a46c1224f5a63),\n    UINT64_C(0x81e2d7997211c1e8), UINT64_C(0xfb3aa75142244891),\n    UINT64_C(0xb46ad37a8a3b6595), UINT64_C(0xceb2a3b2ba0eecec),\n    UINT64_C(0x41da32eaea507767), UINT64_C(0x3b024222da65fe1e),\n    UINT64_C(0xa2722586f2d042ee), UINT64_C(0xd8aa554ec2e5cb97),\n    UINT64_C(0x57c2c41692bb501c), UINT64_C(0x2d1ab4dea28ed965),\n    UINT64_C(0x624ac0f56a91f461), UINT64_C(0x1892b03d5aa47d18),\n    UINT64_C(0x97fa21650afae693), UINT64_C(0xed2251ad3acf6fea),\n    UINT64_C(0x095ac9329ac4bc9b), UINT64_C(0x7382b9faaaf135e2),\n    UINT64_C(0xfcea28a2faafae69), UINT64_C(0x8632586aca9a2710),\n    UINT64_C(0xc9622c4102850a14), UINT64_C(0xb3ba5c8932b0836d),\n    UINT64_C(0x3cd2cdd162ee18e6), UINT64_C(0x460abd1952db919f),\n    UINT64_C(0x256b24ca6b12f26d), UINT64_C(0x5fb354025b277b14),\n    UINT64_C(0xd0dbc55a0b79e09f), UINT64_C(0xaa03b5923b4c69e6),\n    UINT64_C(0xe553c1b9f35344e2), UINT64_C(0x9f8bb171c366cd9b),\n    UINT64_C(0x10e3202993385610), UINT64_C(0x6a3b50e1a30ddf69),\n    UINT64_C(0x8e43c87e03060c18), UINT64_C(0xf49bb8b633338561),\n    UINT64_C(0x7bf329ee636d1eea), UINT64_C(0x012b592653589793),\n    UINT64_C(0x4e7b2d0d9b47ba97), UINT64_C(0x34a35dc5ab7233ee),\n    UINT64_C(0xbbcbcc9dfb2ca865), UINT64_C(0xc113bc55cb19211c),\n    UINT64_C(0x5863dbf1e3ac9dec), UINT64_C(0x22bbab39d3991495),\n    UINT64_C(0xadd33a6183c78f1e), UINT64_C(0xd70b4aa9b3f20667),\n    UINT64_C(0x985b3e827bed2b63), UINT64_C(0xe2834e4a4bd8a21a),\n    UINT64_C(0x6debdf121b863991), UINT64_C(0x1733afda2bb3b0e8),\n    UINT64_C(0xf34b37458bb86399), UINT64_C(0x8993478dbb8deae0),\n    UINT64_C(0x06fbd6d5ebd3716b), UINT64_C(0x7c23a61ddbe6f812),\n    UINT64_C(0x3373d23613f9d516), UINT64_C(0x49aba2fe23cc5c6f),\n    UINT64_C(0xc6c333a67392c7e4), UINT64_C(0xbc1b436e43a74e9d),\n    UINT64_C(0x95ac9329ac4bc9b5), UINT64_C(0xef74e3e19c7e40cc),\n    UINT64_C(0x601c72b9cc20db47), UINT64_C(0x1ac40271fc15523e),\n    UINT64_C(0x5594765a340a7f3a), UINT64_C(0x2f4c0692043ff643),\n    UINT64_C(0xa02497ca54616dc8), UINT64_C(0xdafce7026454e4b1),\n    UINT64_C(0x3e847f9dc45f37c0), UINT64_C(0x445c0f55f46abeb9),\n    UINT64_C(0xcb349e0da4342532), UINT64_C(0xb1eceec59401ac4b),\n    UINT64_C(0xfebc9aee5c1e814f), UINT64_C(0x8464ea266c2b0836),\n    UINT64_C(0x0b0c7b7e3c7593bd), UINT64_C(0x71d40bb60c401ac4),\n    UINT64_C(0xe8a46c1224f5a634), UINT64_C(0x927c1cda14c02f4d),\n    UINT64_C(0x1d148d82449eb4c6), UINT64_C(0x67ccfd4a74ab3dbf),\n    UINT64_C(0x289c8961bcb410bb), UINT64_C(0x5244f9a98c8199c2),\n    UINT64_C(0xdd2c68f1dcdf0249), UINT64_C(0xa7f41839ecea8b30),\n    UINT64_C(0x438c80a64ce15841), UINT64_C(0x3954f06e7cd4d138),\n    UINT64_C(0xb63c61362c8a4ab3), UINT64_C(0xcce411fe1cbfc3ca),\n    UINT64_C(0x83b465d5d4a0eece), UINT64_C(0xf96c151de49567b7),\n    UINT64_C(0x76048445b4cbfc3c), UINT64_C(0x0cdcf48d84fe7545),\n    UINT64_C(0x6fbd6d5ebd3716b7), UINT64_C(0x15651d968d029fce),\n    UINT64_C(0x9a0d8ccedd5c0445), UINT64_C(0xe0d5fc06ed698d3c),\n    UINT64_C(0xaf85882d2576a038), UINT64_C(0xd55df8e515432941),\n    UINT64_C(0x5a3569bd451db2ca), UINT64_C(0x20ed197575283bb3),\n    UINT64_C(0xc49581ead523e8c2), UINT64_C(0xbe4df122e51661bb),\n    UINT64_C(0x3125607ab548fa30), UINT64_C(0x4bfd10b2857d7349),\n    UINT64_C(0x04ad64994d625e4d), UINT64_C(0x7e7514517d57d734),\n    UINT64_C(0xf11d85092d094cbf), UINT64_C(0x8bc5f5c11d3cc5c6),\n    UINT64_C(0x12b5926535897936), UINT64_C(0x686de2ad05bcf04f),\n    UINT64_C(0xe70573f555e26bc4), UINT64_C(0x9ddd033d65d7e2bd),\n    UINT64_C(0xd28d7716adc8cfb9), UINT64_C(0xa85507de9dfd46c0),\n    UINT64_C(0x273d9686cda3dd4b), UINT64_C(0x5de5e64efd965432),\n    UINT64_C(0xb99d7ed15d9d8743), UINT64_C(0xc3450e196da80e3a),\n    UINT64_C(0x4c2d9f413df695b1), UINT64_C(0x36f5ef890dc31cc8),\n    UINT64_C(0x79a59ba2c5dc31cc), UINT64_C(0x037deb6af5e9b8b5),\n    UINT64_C(0x8c157a32a5b7233e), UINT64_C(0xf6cd0afa9582aa47),\n    UINT64_C(0x4ad64994d625e4da), UINT64_C(0x300e395ce6106da3),\n    UINT64_C(0xbf66a804b64ef628), UINT64_C(0xc5bed8cc867b7f51),\n    UINT64_C(0x8aeeace74e645255), UINT64_C(0xf036dc2f7e51db2c),\n    UINT64_C(0x7f5e4d772e0f40a7), UINT64_C(0x05863dbf1e3ac9de),\n    UINT64_C(0xe1fea520be311aaf), UINT64_C(0x9b26d5e88e0493d6),\n    UINT64_C(0x144e44b0de5a085d), UINT64_C(0x6e963478ee6f8124),\n    UINT64_C(0x21c640532670ac20), UINT64_C(0x5b1e309b16452559),\n    UINT64_C(0xd476a1c3461bbed2), UINT64_C(0xaeaed10b762e37ab),\n    UINT64_C(0x37deb6af5e9b8b5b), UINT64_C(0x4d06c6676eae0222),\n    UINT64_C(0xc26e573f3ef099a9), UINT64_C(0xb8b627f70ec510d0),\n    UINT64_C(0xf7e653dcc6da3dd4), UINT64_C(0x8d3e2314f6efb4ad),\n    UINT64_C(0x0256b24ca6b12f26), UINT64_C(0x788ec2849684a65f),\n    UINT64_C(0x9cf65a1b368f752e), UINT64_C(0xe62e2ad306bafc57),\n    UINT64_C(0x6946bb8b56e467dc), UINT64_C(0x139ecb4366d1eea5),\n    UINT64_C(0x5ccebf68aecec3a1), UINT64_C(0x2616cfa09efb4ad8),\n    UINT64_C(0xa97e5ef8cea5d153), UINT64_C(0xd3a62e30fe90582a),\n    UINT64_C(0xb0c7b7e3c7593bd8), UINT64_C(0xca1fc72bf76cb2a1),\n    UINT64_C(0x45775673a732292a), UINT64_C(0x3faf26bb9707a053),\n    UINT64_C(0x70ff52905f188d57), UINT64_C(0x0a2722586f2d042e),\n    UINT64_C(0x854fb3003f739fa5), UINT64_C(0xff97c3c80f4616dc),\n    UINT64_C(0x1bef5b57af4dc5ad), UINT64_C(0x61372b9f9f784cd4),\n    UINT64_C(0xee5fbac7cf26d75f), UINT64_C(0x9487ca0fff135e26),\n    UINT64_C(0xdbd7be24370c7322), UINT64_C(0xa10fceec0739fa5b),\n    UINT64_C(0x2e675fb4576761d0), UINT64_C(0x54bf2f7c6752e8a9),\n    UINT64_C(0xcdcf48d84fe75459), UINT64_C(0xb71738107fd2dd20),\n    UINT64_C(0x387fa9482f8c46ab), UINT64_C(0x42a7d9801fb9cfd2),\n    UINT64_C(0x0df7adabd7a6e2d6), UINT64_C(0x772fdd63e7936baf),\n    UINT64_C(0xf8474c3bb7cdf024), UINT64_C(0x829f3cf387f8795d),\n    UINT64_C(0x66e7a46c27f3aa2c), UINT64_C(0x1c3fd4a417c62355),\n    UINT64_C(0x935745fc4798b8de), UINT64_C(0xe98f353477ad31a7),\n    UINT64_C(0xa6df411fbfb21ca3), UINT64_C(0xdc0731d78f8795da),\n    UINT64_C(0x536fa08fdfd90e51), UINT64_C(0x29b7d047efec8728),\n};\n\nuint64_t crc64(uint64_t crc, const unsigned char *s, uint64_t l) {\n    uint64_t j;\n\n    for (j = 0; j < l; j++) {\n        uint8_t byte = s[j];\n        crc = crc64_tab[(uint8_t)crc ^ byte] ^ (crc >> 8);\n    }\n    return crc;\n}\n\n/* Test main */\n#ifdef TEST_MAIN\n#include <stdio.h>\nint main(void) {\n    printf(\"e9c6d914c4b8d9ca == %016llx\\n\",\n        (unsigned long long) crc64(0,(unsigned char*)\"123456789\",9));\n    return 0;\n}\n#endif\n"
  },
  {
    "path": "src/crc64.h",
    "content": "#ifndef CRC64_H\n#define CRC64_H\n\n#include <stdint.h>\n\nuint64_t crc64(uint64_t crc, const unsigned char *s, uint64_t l);\n\n#endif\n"
  },
  {
    "path": "src/db.c",
    "content": "/*\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 \"redis.h\"\n#include \"cluster.h\"\n\n#include <signal.h>\n#include <ctype.h>\n\nvoid slotToKeyAdd(robj *key);\nvoid slotToKeyDel(robj *key);\nvoid slotToKeyFlush(void);\n\n/*-----------------------------------------------------------------------------\n * C-level DB API\n *----------------------------------------------------------------------------*/\n\n/*\n * 从数据库 db 中取出键 key 的值（对象）\n *\n * 如果 key 的值存在，那么返回该值；否则，返回 NULL 。\n */\nrobj *lookupKey(redisDb *db, robj *key) {\n\n    // 查找键空间\n    dictEntry *de = dictFind(db->dict,key->ptr);\n\n    // 节点存在\n    if (de) {\n        \n\n        // 取出值\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        // 更新时间信息（只在不存在子进程时执行，防止破坏 copy-on-write 机制）\n        if (server.rdb_child_pid == -1 && server.aof_child_pid == -1)\n            val->lru = LRU_CLOCK();\n\n        // 返回值\n        return val;\n    } else {\n\n        // 节点不存在\n\n        return NULL;\n    }\n}\n\n/*\n * 为执行读取操作而取出键 key 在数据库 db 中的值。\n *\n * 并根据是否成功找到值，更新服务器的命中/不命中信息。\n *\n * 找到时返回值对象，没找到返回 NULL 。\n */\nrobj *lookupKeyRead(redisDb *db, robj *key) {\n    robj *val;\n\n    // 检查 key 释放已经过期\n    expireIfNeeded(db,key);\n\n    // 从数据库中取出键的值\n    val = lookupKey(db,key);\n\n    // 更新命中/不命中信息\n    if (val == NULL)\n        server.stat_keyspace_misses++;\n    else\n        server.stat_keyspace_hits++;\n\n    // 返回值\n    return val;\n}\n\n/*\n * 为执行写入操作而取出键 key 在数据库 db 中的值。\n *\n * 和 lookupKeyRead 不同，这个函数不会更新服务器的命中/不命中信息。\n *\n * 找到时返回值对象，没找到返回 NULL 。\n */\nrobj *lookupKeyWrite(redisDb *db, robj *key) {\n\n    // 删除过期键\n    expireIfNeeded(db,key);\n\n    // 查找并返回 key 的值对象\n    return lookupKey(db,key);\n}\n\n/*\n * 为执行读取操作而从数据库中查找返回 key 的值。\n *\n * 如果 key 存在，那么返回 key 的值对象。\n *\n * 如果 key 不存在，那么向客户端发送 reply 参数中的信息，并返回 NULL 。\n */\nrobj *lookupKeyReadOrReply(redisClient *c, robj *key, robj *reply) {\n\n    // 查找\n    robj *o = lookupKeyRead(c->db, key);\n\n    // 决定是否发送信息\n    if (!o) addReply(c,reply);\n\n    return o;\n}\n\n/*\n * 为执行写入操作而从数据库中查找返回 key 的值。\n *\n * 如果 key 存在，那么返回 key 的值对象。\n *\n * 如果 key 不存在，那么向客户端发送 reply 参数中的信息，并返回 NULL 。\n */\n\nrobj *lookupKeyWriteOrReply(redisClient *c, robj *key, robj *reply) {\n\n    robj *o = lookupKeyWrite(c->db, key);\n\n    if (!o) addReply(c,reply);\n\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 * 尝试将键值对 key 和 val 添加到数据库中。\n *\n * 调用者负责对 key 和 val 的引用计数进行增加。\n *\n * The program is aborted if the key already exists. \n *\n * 程序在键已经存在时会停止。\n */\nvoid dbAdd(redisDb *db, robj *key, robj *val) {\n\n    // 复制键名\n    sds copy = sdsdup(key->ptr);\n\n    // 尝试添加键值对\n    int retval = dictAdd(db->dict, copy, val);\n\n    // 如果键已经存在，那么停止\n    redisAssertWithInfo(NULL,key,retval == REDIS_OK);\n\n    // 如果开启了集群模式，那么将键保存到槽里面\n    if (server.cluster_enabled) slotToKeyAdd(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 *\n * 为已存在的键关联一个新值。\n *\n * 调用者负责对新值 val 的引用计数进行增加。\n *\n * This function does not modify the expire time of the existing key.\n *\n * 这个函数不会修改键的过期时间。\n *\n * The program is aborted if the key was not already present. \n *\n * 如果键不存在，那么函数停止。\n */\nvoid dbOverwrite(redisDb *db, robj *key, robj *val) {\n    dictEntry *de = dictFind(db->dict,key->ptr);\n    \n    // 节点必须存在，否则中止\n    redisAssertWithInfo(NULL,key,de != NULL);\n\n    // 覆写旧值\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 * 高层次的 SET 操作函数。\n *\n * 这个函数可以在不管键 key 是否存在的情况下，将它和 val 关联起来。\n *\n * 1) The ref count of the value object is incremented.\n *    值对象的引用计数会被增加\n *\n * 2) clients WATCHing for the destination key notified.\n *    监视键 key 的客户端会收到键已经被修改的通知\n *\n * 3) The expire time of the key is reset (the key is made persistent). \n *    键的过期时间会被移除（键变为持久的）\n */\nvoid setKey(redisDb *db, robj *key, robj *val) {\n\n    // 添加或覆写数据库中的键值对\n    if (lookupKeyWrite(db,key) == NULL) {\n        dbAdd(db,key,val);\n    } else {\n        dbOverwrite(db,key,val);\n    }\n\n    incrRefCount(val);\n\n    // 移除键的过期时间\n    removeExpire(db,key);\n\n    // 发送键修改通知\n    signalModifiedKey(db,key);\n}\n\n/*\n * 检查键 key 是否存在于数据库中，存在返回 1 ，不存在返回 0 。\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 * 随机从数据库中取出一个键，并以字符串对象的方式返回这个键。\n *\n * 如果数据库为空，那么返回 NULL 。\n *\n * The function makes sure to return keys not already expired. \n *\n * 这个函数保证被返回的键都是未过期的。\n */\nrobj *dbRandomKey(redisDb *db) {\n    dictEntry *de;\n\n    while(1) {\n        sds key;\n        robj *keyobj;\n\n        // 从键空间中随机取出一个键节点\n        de = dictGetRandomKey(db->dict);\n\n        // 数据库为空\n        if (de == NULL) return NULL;\n\n        // 取出键\n        key = dictGetKey(de);\n        // 为键创建一个字符串对象，对象的值为键的名字\n        keyobj = createStringObject(key,sdslen(key));\n        // 检查键是否带有过期时间\n        if (dictFind(db->expires,key)) {\n            // 如果键已经过期，那么将它删除，并继续随机下个键\n            if (expireIfNeeded(db,keyobj)) {\n                decrRefCount(keyobj);\n                continue; /* search for another key. This expired. */\n            }\n        }\n\n        // 返回被随机到的键（的名字）\n        return keyobj;\n    }\n}\n\n/* Delete a key, value, and associated expiration entry if any, from the DB \n *\n * 从数据库中删除给定的键，键的值，以及键的过期时间。\n *\n * 删除成功返回 1 ，因为键不存在而导致删除失败时，返回 0 。\n */\nint dbDelete(redisDb *db, robj *key) {\n\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    // 删除键的过期时间\n    if (dictSize(db->expires) > 0) dictDelete(db->expires,key->ptr);\n\n    // 删除键值对\n    if (dictDelete(db->dict,key->ptr) == DICT_OK) {\n        // 如果开启了集群模式，那么从槽中删除给定的键\n        if (server.cluster_enabled) slotToKeyDel(key);\n        return 1;\n    } else {\n        // 键不存在\n        return 0;\n    }\n}\n\n/* Prepare the string object stored at 'key' to be modified destructively\n * to implement commands like SETBIT or APPEND.\n *\n * An object is usually ready to be modified unless one of the two conditions\n * are true:\n *\n * 1) The object 'o' is shared (refcount > 1), we don't want to affect\n *    other users.\n * 2) The object encoding is not \"RAW\".\n *\n * If the object is found in one of the above conditions (or both) by the\n * function, an unshared / not-encoded copy of the string object is stored\n * at 'key' in the specified 'db'. Otherwise the object 'o' itself is\n * returned.\n *\n * USAGE:\n *\n * The object 'o' is what the caller already obtained by looking up 'key'\n * in 'db', the usage pattern looks like this:\n *\n * o = lookupKeyWrite(db,key);\n * if (checkType(c,o,REDIS_STRING)) return;\n * o = dbUnshareStringValue(db,key,o);\n *\n * At this point the caller is ready to modify the object, for example\n * using an sdscat() call to append some data, or anything else.\n */\nrobj *dbUnshareStringValue(redisDb *db, robj *key, robj *o) {\n    redisAssert(o->type == REDIS_STRING);\n    if (o->refcount != 1 || o->encoding != REDIS_ENCODING_RAW) {\n        robj *decoded = getDecodedObject(o);\n        o = createRawStringObject(decoded->ptr, sdslen(decoded->ptr));\n        decrRefCount(decoded);\n        dbOverwrite(db,key,o);\n    }\n    return o;\n}\n\n/*\n * 清空服务器的所有数据。\n */\nlong long emptyDb(void(callback)(void*)) {\n    int j;\n    long long removed = 0;\n\n    // 清空所有数据库\n    for (j = 0; j < server.dbnum; j++) {\n\n        // 记录被删除键的数量\n        removed += dictSize(server.db[j].dict);\n\n        // 删除所有键值对\n        dictEmpty(server.db[j].dict,callback);\n        // 删除所有键的过期时间\n        dictEmpty(server.db[j].expires,callback);\n    }\n\n    // 如果开启了集群模式，那么还要移除槽记录\n    if (server.cluster_enabled) slotToKeyFlush();\n\n    // 返回键的数量\n    return removed;\n}\n\n/*\n * 将客户端的目标数据库切换为 id 所指定的数据库\n */\nint selectDb(redisClient *c, int id) {\n\n    // 确保 id 在正确范围内\n    if (id < 0 || id >= server.dbnum)\n        return REDIS_ERR;\n\n    // 切换数据库（更新指针）\n    c->db = &server.db[id];\n\n    return REDIS_OK;\n}\n\n/*-----------------------------------------------------------------------------\n * Hooks for key space changes.\n *\n * 键空间改动的钩子。\n *\n * Every time a key in the database is modified the function\n * signalModifiedKey() is called.\n *\n * 每当数据库中的键被改动时， signalModifiedKey() 函数都会被调用。\n *\n * Every time a DB is flushed the function signalFlushDb() is called.\n *\n * 每当一个数据库被清空时， signalFlushDb() 都会被调用。\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 * 与类型无关的数据库操作。\n *----------------------------------------------------------------------------*/\n\n/*\n * 清空客户端指定的数据库\n */\nvoid flushdbCommand(redisClient *c) {\n\n    server.dirty += dictSize(c->db->dict);\n\n    // 发送通知\n    signalFlushedDb(c->db->id);\n\n    // 清空指定数据库中的 dict 和 expires 字典\n    dictEmpty(c->db->dict,NULL);\n    dictEmpty(c->db->expires,NULL);\n\n    // 如果开启了集群模式，那么还要移除槽记录\n    if (server.cluster_enabled) slotToKeyFlush();\n\n    addReply(c,shared.ok);\n}\n\n/*\n * 清空服务器中的所有数据库\n */\nvoid flushallCommand(redisClient *c) {\n\n    // 发送通知\n    signalFlushedDb(-1);\n\n    // 清空所有数据库\n    server.dirty += emptyDb(NULL);\n    addReply(c,shared.ok);\n\n    // 如果正在保存新的 RDB ，那么取消保存操作\n    if (server.rdb_child_pid != -1) {\n        kill(server.rdb_child_pid,SIGUSR1);\n        rdbRemoveTempFile(server.rdb_child_pid);\n    }\n\n    // 更新 RDB 文件\n    if (server.saveparamslen > 0) {\n        /* Normally rdbSave() will reset dirty, but we don't want this here\n         * as otherwise FLUSHALL will not be replicated nor put into the AOF. */\n        // rdbSave() 会清空服务器的 dirty 属性\n        // 但为了确保 FLUSHALL 命令会被正常传播，\n        // 程序需要保存并在 rdbSave() 调用之后还原服务器的 dirty 属性\n        int saved_dirty = server.dirty;\n\n        rdbSave(server.rdb_filename);\n\n        server.dirty = saved_dirty;\n    }\n\n    server.dirty++;\n}\n\nvoid delCommand(redisClient *c) {\n    int deleted = 0, j;\n\n    // 遍历所有输入键\n    for (j = 1; j < c->argc; j++) {\n\n        // 先删除过期的键\n        expireIfNeeded(c->db,c->argv[j]);\n\n        // 尝试删除键\n        if (dbDelete(c->db,c->argv[j])) {\n\n            // 删除键成功，发送通知\n\n            signalModifiedKey(c->db,c->argv[j]);\n            notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,\n                \"del\",c->argv[j],c->db->id);\n\n            server.dirty++;\n\n            // 成功删除才增加 deleted 计数器的值\n            deleted++;\n        }\n    }\n\n    // 返回被删除键的数量\n    addReplyLongLong(c,deleted);\n}\n\nvoid existsCommand(redisClient *c) {\n\n    // 检查键是否已经过期，如果已过期的话，那么将它删除\n    // 这可以避免已过期的键被误认为存在\n    expireIfNeeded(c->db,c->argv[1]);\n\n    // 在数据库中查找\n    if (dbExists(c->db,c->argv[1])) {\n        addReply(c, shared.cone);\n    } else {\n        addReply(c, shared.czero);\n    }\n}\n\nvoid selectCommand(redisClient *c) {\n    long id;\n\n    // 不合法的数据库号码\n    if (getLongFromObjectOrReply(c, c->argv[1], &id,\n        \"invalid DB index\") != REDIS_OK)\n        return;\n\n    if (server.cluster_enabled && id != 0) {\n        addReplyError(c,\"SELECT is not allowed in cluster mode\");\n        return;\n    }\n\n    // 切换数据库\n    if (selectDb(c,id) == REDIS_ERR) {\n        addReplyError(c,\"invalid DB index\");\n    } else {\n        addReply(c,shared.ok);\n    }\n}\n\nvoid randomkeyCommand(redisClient *c) {\n    robj *key;\n\n    // 随机返回键\n    if ((key = dbRandomKey(c->db)) == NULL) {\n        addReply(c,shared.nullbulk);\n        return;\n    }\n\n    addReplyBulk(c,key);\n    decrRefCount(key);\n}\n\nvoid keysCommand(redisClient *c) {\n    dictIterator *di;\n    dictEntry *de;\n\n    // 模式\n    sds pattern = c->argv[1]->ptr;\n\n    int plen = sdslen(pattern), allkeys;\n    unsigned long numkeys = 0;\n    void *replylen = addDeferredMultiBulkLength(c);\n\n    // 遍历整个数据库，返回（名字）和模式匹配的键\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        // 将键名和模式进行比对\n        if (allkeys || stringmatchlen(pattern,plen,key,sdslen(key),0)) {\n\n            // 创建一个保存键名字的字符串对象\n            keyobj = createStringObject(key,sdslen(key));\n\n            // 删除已过期键\n            if (expireIfNeeded(c->db,keyobj) == 0) {\n                addReplyBulk(c,keyobj);\n                numkeys++;\n            }\n\n            decrRefCount(keyobj);\n        }\n    }\n    dictReleaseIterator(di);\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    list *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 == REDIS_SET) {\n        key = dictGetKey(de);\n        incrRefCount(key);\n    } else if (o->type == REDIS_HASH) {\n        key = dictGetKey(de);\n        incrRefCount(key);\n        val = dictGetVal(de);\n        incrRefCount(val);\n    } else if (o->type == REDIS_ZSET) {\n        key = dictGetKey(de);\n        incrRefCount(key);\n        val = createStringObjectFromLongDouble(*(double*)dictGetVal(de));\n    } else {\n        redisPanic(\"Type not handled in SCAN callback.\");\n    }\n\n    listAddNodeTail(keys, key);\n    if (val) listAddNodeTail(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 REDIS_OK. Otherwise return REDIS_ERR and send an error to the\n * client. */\nint parseScanCursorOrReply(redisClient *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 REDIS_ERR;\n    }\n    return REDIS_OK;\n}\n\n/* This command implements SCAN, HSCAN and SSCAN commands.\n *\n * 这是 SCAN 、 HSCAN 、 SSCAN 命令的实现函数。\n *\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 * 如果给定了对象 o ，那么它必须是一个哈希对象或者集合对象，\n * 如果 o 为 NULL 的话，函数将使用当前数据库作为迭代对象。\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 * 如果参数 o 不为 NULL ，那么说明它是一个键对象，函数将跳过这些键对象，\n * 对给定的命令选项进行分析（parse）。\n *\n * In the case of a Hash object the function returns both the field and value\n * of every element on the Hash. \n *\n * 如果被迭代的是哈希对象，那么函数返回的是键值对。\n */\nvoid scanGenericCommand(redisClient *c, robj *o, unsigned long cursor) {\n    int rv;\n    int i, j;\n    char buf[REDIS_LONGSTR_SIZE];\n    list *keys = listCreate();\n    listNode *node, *nextnode;\n    long count = 10;\n    sds pat;\n    int patlen, use_pattern = 0;\n    dict *ht;\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    // 输入类型检查\n    redisAssert(o == NULL || o->type == REDIS_SET || o->type == REDIS_HASH ||\n                o->type == REDIS_ZSET);\n\n    /* Set i to the first option argument. The previous one is the cursor. */\n    // 设置第一个选项参数的索引位置\n    // 0    1      2      3  \n    // SCAN OPTION <op_arg>         SCAN 命令的选项值从索引 2 开始\n    // HSCAN <key> OPTION <op_arg>  而其他 *SCAN 命令的选项值从索引 3 开始\n    i = (o == NULL) ? 2 : 3; /* Skip the key argument if needed. */\n\n    /* Step 1: Parse options. */\n    // 分析选项参数\n    while (i < c->argc) {\n        j = c->argc - i;\n\n        // COUNT <number>\n        if (!strcasecmp(c->argv[i]->ptr, \"count\") && j >= 2) {\n            if (getLongFromObjectOrReply(c, c->argv[i+1], &count, NULL)\n                != REDIS_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\n        // MATCH <pattern>\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\n        // error\n        } else {\n            addReply(c,shared.syntaxerr);\n            goto cleanup;\n        }\n    }\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     // 如果对象的底层实现为 ziplist 、intset 而不是哈希表，\n     // 那么这些对象应该只包含了少量元素，\n     // 为了保持不让服务器记录迭代状态的设计\n     // 我们将 ziplist 或者 intset 里面的所有元素都一次返回给调用者\n     // 并向调用者返回游标（cursor） 0\n\n    /* Handle the case of a hash table. */\n    ht = NULL;\n    if (o == NULL) {\n        // 迭代目标为数据库\n        ht = c->db->dict;\n    } else if (o->type == REDIS_SET && o->encoding == REDIS_ENCODING_HT) {\n        // 迭代目标为 HT 编码的集合\n        ht = o->ptr;\n    } else if (o->type == REDIS_HASH && o->encoding == REDIS_ENCODING_HT) {\n        // 迭代目标为 HT 编码的哈希\n        ht = o->ptr;\n        count *= 2; /* We return key / value for this type. */\n    } else if (o->type == REDIS_ZSET && o->encoding == REDIS_ENCODING_SKIPLIST) {\n        // 迭代目标为 HT 编码的跳跃表\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\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        // 我们向回调函数传入两个指针：\n        // 一个是用于记录被迭代元素的列表\n        // 另一个是字典对象\n        // 从而实现类型无关的数据提取操作\n        privdata[0] = keys;\n        privdata[1] = o;\n        do {\n            cursor = dictScan(ht, cursor, scanCallback, privdata);\n        } while (cursor && listLength(keys) < count);\n    } else if (o->type == REDIS_SET) {\n        int pos = 0;\n        int64_t ll;\n\n        while(intsetGet(o->ptr,pos++,&ll))\n            listAddNodeTail(keys,createStringObjectFromLongLong(ll));\n        cursor = 0;\n    } else if (o->type == REDIS_HASH || o->type == REDIS_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            listAddNodeTail(keys,\n                (vstr != NULL) ? createStringObject((char*)vstr,vlen) :\n                                 createStringObjectFromLongLong(vll));\n            p = ziplistNext(o->ptr,p);\n        }\n        cursor = 0;\n    } else {\n        redisPanic(\"Not handled encoding in SCAN.\");\n    }\n\n    /* Step 3: Filter elements. */\n    node = listFirst(keys);\n    while (node) {\n        robj *kobj = listNodeValue(node);\n        nextnode = listNextNode(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[REDIS_LONGSTR_SIZE];\n                int len;\n\n                redisAssert(kobj->encoding == REDIS_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 && expireIfNeeded(c->db, kobj)) filter = 1;\n\n        /* Remove the element and its associted value if needed. */\n        if (filter) {\n            decrRefCount(kobj);\n            listDelNode(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 == REDIS_ZSET || o->type == REDIS_HASH)) {\n            node = nextnode;\n            nextnode = listNextNode(node);\n            if (filter) {\n                kobj = listNodeValue(node);\n                decrRefCount(kobj);\n                listDelNode(keys, node);\n            }\n        }\n        node = nextnode;\n    }\n\n    /* Step 4: Reply to the client. */\n    addReplyMultiBulkLen(c, 2);\n    rv = snprintf(buf, sizeof(buf), \"%lu\", cursor);\n    redisAssert(rv < sizeof(buf));\n    addReplyBulkCBuffer(c, buf, rv);\n\n    addReplyMultiBulkLen(c, listLength(keys));\n    while ((node = listFirst(keys)) != NULL) {\n        robj *kobj = listNodeValue(node);\n        addReplyBulk(c, kobj);\n        decrRefCount(kobj);\n        listDelNode(keys, node);\n    }\n\ncleanup:\n    listSetFreeMethod(keys,decrRefCountVoid);\n    listRelease(keys);\n}\n\n/* The SCAN command completely relies on scanGenericCommand. */\nvoid scanCommand(redisClient *c) {\n    unsigned long cursor;\n    if (parseScanCursorOrReply(c,c->argv[1],&cursor) == REDIS_ERR) return;\n    scanGenericCommand(c,NULL,cursor);\n}\n\nvoid dbsizeCommand(redisClient *c) {\n    addReplyLongLong(c,dictSize(c->db->dict));\n}\n\nvoid lastsaveCommand(redisClient *c) {\n    addReplyLongLong(c,server.lastsave);\n}\n\nvoid typeCommand(redisClient *c) {\n    robj *o;\n    char *type;\n\n    o = lookupKeyRead(c->db,c->argv[1]);\n\n    if (o == NULL) {\n        type = \"none\";\n    } else {\n        switch(o->type) {\n        case REDIS_STRING: type = \"string\"; break;\n        case REDIS_LIST: type = \"list\"; break;\n        case REDIS_SET: type = \"set\"; break;\n        case REDIS_ZSET: type = \"zset\"; break;\n        case REDIS_HASH: type = \"hash\"; break;\n        default: type = \"unknown\"; break;\n        }\n    }\n\n    addReplyStatus(c,type);\n}\n\nvoid shutdownCommand(redisClient *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\n        // 停机时不进行保存\n        if (!strcasecmp(c->argv[1]->ptr,\"nosave\")) {\n            flags |= REDIS_SHUTDOWN_NOSAVE;\n\n        // 停机时进行保存\n        } else if (!strcasecmp(c->argv[1]->ptr,\"save\")) {\n            flags |= REDIS_SHUTDOWN_SAVE;\n\n        } else {\n            addReply(c,shared.syntaxerr);\n            return;\n        }\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 || server.sentinel_mode)\n        flags = (flags & ~REDIS_SHUTDOWN_SAVE) | REDIS_SHUTDOWN_NOSAVE;\n\n    if (prepareForShutdown(flags) == REDIS_OK) exit(0);\n\n    addReplyError(c,\"Errors trying to SHUTDOWN. Check logs.\");\n}\n\nvoid renameGenericCommand(redisClient *c, int nx) {\n    robj *o;\n    long long expire;\n\n    /* To use the same key as src and dst is probably an error */\n    // 来源键和目标键不能相同\n    if (sdscmp(c->argv[1]->ptr,c->argv[2]->ptr) == 0) {\n        addReply(c,shared.sameobjecterr);\n        return;\n    }\n\n    // 取出来源键\n    if ((o = lookupKeyWriteOrReply(c,c->argv[1],shared.nokeyerr)) == NULL)\n        return;\n\n    // 增加引用计数，因为后面目标键也会引用这个对象\n    // 如果不增加的话，当来源键被删除时，这个值对象也会被删除\n    incrRefCount(o);\n\n    // 取出来源键的过期时间\n    expire = getExpire(c->db,c->argv[1]);\n\n    // 检查目标键是否存在\n    if (lookupKeyWrite(c->db,c->argv[2]) != NULL) {\n\n        // 如果目标键存在，并且执行的是 RENAMENX ，那么直接返回\n        if (nx) {\n            decrRefCount(o);\n            addReply(c,shared.czero);\n            return;\n        }\n\n        // 如果执行的是 RENAME ，那么删除已有的目标键\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\n    // 将来源键的值对象和目标键进行关联\n    dbAdd(c->db,c->argv[2],o);\n\n    // 如果有过期时间，那么为目标键设置过期时间\n    if (expire != -1) setExpire(c->db,c->argv[2],expire);\n\n    // 删除来源键\n    dbDelete(c->db,c->argv[1]);\n\n    signalModifiedKey(c->db,c->argv[1]);\n    signalModifiedKey(c->db,c->argv[2]);\n\n    notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,\"rename_from\",\n        c->argv[1],c->db->id);\n    notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,\"rename_to\",\n        c->argv[2],c->db->id);\n\n    server.dirty++;\n\n    addReply(c,nx ? shared.cone : shared.ok);\n}\n\nvoid renameCommand(redisClient *c) {\n    renameGenericCommand(c,0);\n}\n\nvoid renamenxCommand(redisClient *c) {\n    renameGenericCommand(c,1);\n}\n\nvoid moveCommand(redisClient *c) {\n    robj *o;\n    redisDb *src, *dst;\n    int srcid;\n\n    if (server.cluster_enabled) {\n        addReplyError(c,\"MOVE is not allowed in cluster mode\");\n        return;\n    }\n\n    /* Obtain source and target DB pointers */\n    // 源数据库\n    src = c->db;\n    // 源数据库的 id\n    srcid = c->db->id;\n\n    // 切换到目标数据库\n    if (selectDb(c,atoi(c->argv[2]->ptr)) == REDIS_ERR) {\n        addReply(c,shared.outofrangeerr);\n        return;\n    }\n\n    // 目标数据库\n    dst = c->db;\n\n    // 切换回源数据库\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    // 如果源数据库和目标数据库相等，那么返回错误\n    if (src == dst) {\n        addReply(c,shared.sameobjecterr);\n        return;\n    }\n\n    /* Check if the element exists and get a reference */\n    // 取出要移动的对象\n    o = lookupKeyWrite(c->db,c->argv[1]);\n    if (!o) {\n        addReply(c,shared.czero);\n        return;\n    }\n\n    /* Return zero if the key already exists in the target DB */\n    // 如果键已经存在于目标数据库，那么返回\n    if (lookupKeyWrite(dst,c->argv[1]) != NULL) {\n        addReply(c,shared.czero);\n        return;\n    }\n\n    // 将键添加到目标数据库中\n    dbAdd(dst,c->argv[1],o);\n    // 增加对对象的引用计数，避免接下来在源数据库中删除时 o 被清理\n    incrRefCount(o);\n\n    /* OK! key moved, free the entry in the source DB */\n    // 将键从源数据库中返回\n    dbDelete(src,c->argv[1]);\n\n    server.dirty++;\n\n    addReply(c,shared.cone);\n}\n\n/*-----------------------------------------------------------------------------\n * Expires API\n *----------------------------------------------------------------------------*/\n\n/*\n * 移除键 key 的过期时间\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    // 确保键带有过期时间\n    redisAssertWithInfo(NULL,key,dictFind(db->dict,key->ptr) != NULL);\n\n    // 删除过期时间\n    return dictDelete(db->expires,key->ptr) == DICT_OK;\n}\n\n/*\n * 将键 key 的过期时间设为 when\n */\nvoid setExpire(redisDb *db, robj *key, long long when) {\n\n    dictEntry *kde, *de;\n\n    /* Reuse the sds from the main dict in the expire dict */\n    // 取出键\n    kde = dictFind(db->dict,key->ptr);\n\n    redisAssertWithInfo(NULL,key,kde != NULL);\n\n    // 根据键取出键的过期时间\n    de = dictReplaceRaw(db->expires,dictGetKey(kde));\n\n    // 设置键的过期时间\n    // 这里是直接使用整数值来保存过期时间，不是用 INT 编码的 String 对象\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) \n *\n * 返回给定 key 的过期时间。\n *\n * 如果键没有设置过期时间，那么返回 -1 。\n */\nlong long getExpire(redisDb *db, robj *key) {\n    dictEntry *de;\n\n    /* No expire? return ASAP */\n    // 获取键的过期时间\n    // 如果过期时间不存在，那么直接返回\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    redisAssertWithInfo(NULL,key,dictFind(db->dict,key->ptr) != NULL);\n\n    // 返回过期时间\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 * 将过期时间传播到附属节点和 AOF 文件。\n *\n * 当一个键在主节点中过期时，\n * 主节点会向所有附属节点和 AOF 文件传播一个显式的 DEL 命令。\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. \n *\n * 这种做法使得对键的过期可以集中在一处处理，\n * 因为 AOF 以及主节点和附属节点之间的链接，都可以保证操作的执行顺序，\n * 所以即使有写操作对过期键执行，所有数据都还是 consistent 的。\n */\nvoid propagateExpire(redisDb *db, robj *key) {\n    robj *argv[2];\n\n    // 构造一个 DEL key 命令\n    argv[0] = shared.del;\n    argv[1] = key;\n    incrRefCount(argv[0]);\n    incrRefCount(argv[1]);\n\n    // 传播到 AOF \n    if (server.aof_state != REDIS_AOF_OFF)\n        feedAppendOnlyFile(server.delCommand,db->id,argv,2);\n\n    // 传播到所有附属节点\n    replicationFeedSlaves(server.slaves,db->id,argv,2);\n\n    decrRefCount(argv[0]);\n    decrRefCount(argv[1]);\n}\n\n/*\n * 检查 key 是否已经过期，如果是的话，将它从数据库中删除。\n *\n * 返回 0 表示键没有过期时间，或者键未过期。\n *\n * 返回 1 表示键已经因为过期而被删除了。\n */\nint expireIfNeeded(redisDb *db, robj *key) {\n\n    // 取出键的过期时间\n    mstime_t when = getExpire(db,key);\n    mstime_t now;\n\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    // 如果服务器正在进行载入，那么不进行任何过期检查\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 : mstime();\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    // 当服务器运行在 replication 模式时\n    // 附属节点并不主动删除 key\n    // 它只返回一个逻辑上正确的返回值\n    // 真正的删除操作要等待主节点发来删除命令时才执行\n    // 从而保证数据的同步\n    if (server.masterhost != NULL) return now > when;\n\n    // 运行到这里，表示键带有过期时间，并且服务器为主节点\n\n    /* Return when this key has not expired */\n    // 如果未过期，返回 0\n    if (now <= when) return 0;\n\n    /* Delete the key */\n    server.stat_expiredkeys++;\n\n    // 向 AOF 文件和附属节点传播过期信息\n    propagateExpire(db,key);\n\n    // 发送事件通知\n    notifyKeyspaceEvent(REDIS_NOTIFY_EXPIRED,\n        \"expired\",key,db->id);\n\n    // 将过期键从数据库中删除\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 * 这个函数是 EXPIRE 、 PEXPIRE 、 EXPIREAT 和 PEXPIREAT 命令的底层实现函数。\n *\n * 命令的第二个参数可能是绝对值，也可能是相对值。\n * 当执行 *AT 命令时， basetime 为 0 ，在其他情况下，它保存的就是当前的绝对时间。\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. \n *\n * unit 用于指定 argv[2] （传入过期时间）的格式，\n * 它可以是 UNIT_SECONDS 或 UNIT_MILLISECONDS ，\n * basetime 参数则总是毫秒格式的。\n */\nvoid expireGenericCommand(redisClient *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\n    // 取出 when 参数\n    if (getLongLongFromObjectOrReply(c, param, &when, NULL) != REDIS_OK)\n        return;\n\n    // 如果传入的过期时间是以秒为单位的，那么将它转换为毫秒\n    if (unit == UNIT_SECONDS) when *= 1000;\n    when += basetime;\n\n    /* No key, return zero. */\n    // 取出键\n    if (lookupKeyRead(c->db,key) == NULL) {\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     * 在载入数据时，或者服务器为附属节点时，\n     * 即使 EXPIRE 的 TTL 为负数，或者 EXPIREAT 提供的时间戳已经过期，\n     * 服务器也不会主动删除这个键，而是等待主节点发来显式的 DEL 命令。\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     *\n     * 程序会继续将（一个可能已经过期的 TTL）设置为键的过期时间，\n     * 并且等待主节点发来 DEL 命令。\n     */\n    if (when <= mstime() && !server.loading && !server.masterhost) {\n\n        // when 提供的时间已经过期，服务器为主节点，并且没在载入数据\n\n        robj *aux;\n\n        redisAssertWithInfo(c,key,dbDelete(c->db,key));\n        server.dirty++;\n\n        /* Replicate/AOF this as an explicit DEL. */\n        // 传播 DEL 命令\n        aux = createStringObject(\"DEL\",3);\n\n        rewriteClientCommandVector(c,2,aux,key);\n        decrRefCount(aux);\n\n        signalModifiedKey(c->db,key);\n        notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,\"del\",key,c->db->id);\n\n        addReply(c, shared.cone);\n\n        return;\n    } else {\n\n        // 设置键的过期时间\n        // 如果服务器为附属节点，或者服务器正在载入，\n        // 那么这个 when 有可能已经过期的\n        setExpire(c->db,key,when);\n\n        addReply(c,shared.cone);\n\n        signalModifiedKey(c->db,key);\n        notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,\"expire\",key,c->db->id);\n\n        server.dirty++;\n\n        return;\n    }\n}\n\nvoid expireCommand(redisClient *c) {\n    expireGenericCommand(c,mstime(),UNIT_SECONDS);\n}\n\nvoid expireatCommand(redisClient *c) {\n    expireGenericCommand(c,0,UNIT_SECONDS);\n}\n\nvoid pexpireCommand(redisClient *c) {\n    expireGenericCommand(c,mstime(),UNIT_MILLISECONDS);\n}\n\nvoid pexpireatCommand(redisClient *c) {\n    expireGenericCommand(c,0,UNIT_MILLISECONDS);\n}\n\n/*\n * 返回键的剩余生存时间。\n *\n * output_ms 指定返回值的格式：\n *\n *  - 为 1 时，返回毫秒\n *\n *  - 为 0 时，返回秒\n */\nvoid ttlGenericCommand(redisClient *c, int output_ms) {\n    long long expire, ttl = -1;\n\n    /* If the key does not exist at all, return -2 */\n    // 取出键\n    if (lookupKeyRead(c->db,c->argv[1]) == NULL) {\n        addReplyLongLong(c,-2);\n        return;\n    }\n\n    /* The key exists. Return -1 if it has no expire, or the actual\n     * TTL value otherwise. */\n    // 取出过期时间\n    expire = getExpire(c->db,c->argv[1]);\n\n    if (expire != -1) {\n        // 计算剩余生存时间\n        ttl = expire-mstime();\n        if (ttl < 0) ttl = 0;\n    }\n\n    if (ttl == -1) {\n        // 键是持久的\n        addReplyLongLong(c,-1);\n    } else {\n        // 返回 TTL \n        // (ttl+500)/1000 计算的是渐近秒数\n        addReplyLongLong(c,output_ms ? ttl : ((ttl+500)/1000));\n    }\n}\n\nvoid ttlCommand(redisClient *c) {\n    ttlGenericCommand(c, 0);\n}\n\nvoid pttlCommand(redisClient *c) {\n    ttlGenericCommand(c, 1);\n}\n\nvoid persistCommand(redisClient *c) {\n    dictEntry *de;\n\n    // 取出键\n    de = dictFind(c->db->dict,c->argv[1]->ptr);\n\n    if (de == NULL) {\n        // 键没有过期时间\n        addReply(c,shared.czero);\n\n    } else {\n\n        // 键带有过期时间，那么将它移除\n        if (removeExpire(c->db,c->argv[1])) {\n            addReply(c,shared.cone);\n            server.dirty++;\n\n        // 键已经是持久的了\n        } else {\n            addReply(c,shared.czero);\n        }\n    }\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    REDIS_NOTUSED(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 = zmalloc(sizeof(int)*((last - cmd->firstkey)+1));\n    for (j = cmd->firstkey; j <= last; j += cmd->keystep) {\n        redisAssert(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    zfree(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    REDIS_NOTUSED(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 = zmalloc(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    REDIS_NOTUSED(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 = zmalloc(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;\n    REDIS_NOTUSED(cmd);\n\n    num = 0;\n    keys = zmalloc(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                keys[num] = i+1; /* <store-key> */\n                break;\n            }\n        }\n    }\n    *numkeys = num;\n    return keys;\n}\n\n/* Slot to Key API. This is used by Redis Cluster in order to obtain in\n * a fast way a key that belongs to a specified hash slot. This is useful\n * while rehashing the cluster. */\n// 将给定键添加到槽里面，\n// 节点的 slots_to_keys 用跳跃表记录了 slot -> key 之间的映射\n// 这样可以快速地处理槽和键的关系，在 rehash 槽时很有用。\nvoid slotToKeyAdd(robj *key) {\n\n    // 计算出键所属的槽\n    unsigned int hashslot = keyHashSlot(key->ptr,sdslen(key->ptr));\n\n    // 将槽 slot 作为分值，键作为成员，添加到 slots_to_keys 跳跃表里面\n    zslInsert(server.cluster->slots_to_keys,hashslot,key);\n    incrRefCount(key);\n}\n\n// 从槽中删除给定的键 key\nvoid slotToKeyDel(robj *key) {\n    unsigned int hashslot = keyHashSlot(key->ptr,sdslen(key->ptr));\n\n    zslDelete(server.cluster->slots_to_keys,hashslot,key);\n}\n\n// 清空节点所有槽保存的所有键\nvoid slotToKeyFlush(void) {\n    zslFree(server.cluster->slots_to_keys);\n    server.cluster->slots_to_keys = zslCreate();\n}\n\n// 记录 count 个属于 hashslot 槽的键到 keys 数组\n// 并返回被记录键的数量\nunsigned int getKeysInSlot(unsigned int hashslot, robj **keys, unsigned int count) {\n    zskiplistNode *n;\n    zrangespec range;\n    int j = 0;\n\n    range.min = range.max = hashslot;\n    range.minex = range.maxex = 0;\n\n    // 定位到第一个属于指定 slot 的键上面\n    n = zslFirstInRange(server.cluster->slots_to_keys, &range);\n    // 遍历跳跃表，并保存属于指定 slot 的键\n    // n && n->score 检查当前键是否属于指定 slot\n    // && count-- 用来计数\n    while(n && n->score == hashslot && count--) {\n        // 记录键\n        keys[j++] = n->obj;\n        n = n->level[0].forward;\n    }\n    return j;\n}\n\n/* Remove all the keys in the specified hash slot.\n * The number of removed items is returned. */\nunsigned int delKeysInSlot(unsigned int hashslot) {\n    zskiplistNode *n;\n    zrangespec range;\n    int j = 0;\n\n    range.min = range.max = hashslot;\n    range.minex = range.maxex = 0;\n\n    n = zslFirstInRange(server.cluster->slots_to_keys, &range);\n    while(n && n->score == hashslot) {\n        robj *key = n->obj;\n        n = n->level[0].forward; /* Go to the next item before freeing it. */\n        incrRefCount(key); /* Protect the object while freeing it. */\n        dbDelete(&server.db[0],key);\n        decrRefCount(key);\n        j++;\n    }\n    return j;\n}\n\n// 返回指定 slot 包含的键数量\nunsigned int countKeysInSlot(unsigned int hashslot) {\n    zskiplist *zsl = server.cluster->slots_to_keys;\n    zskiplistNode *zn;\n    zrangespec range;\n    int rank, count = 0;\n\n    range.min = range.max = hashslot;\n    range.minex = range.maxex = 0;\n\n    /* Find first element in range */\n    // 定位到第一个在指定 slot 上的键\n    zn = zslFirstInRange(zsl, &range);\n\n    /* Use rank of first element, if any, to determine preliminary count */\n    // 使用第一个指定 slot 键的排位减去最后一个指定 slot 键的排位\n    // 这一方法来计算 slot 键的数量\n    // 类似区间算法\n\n    // 第一个在指定 slot 上的键存在\n    if (zn != NULL) {\n        // 获取第一个键的排位\n        rank = zslGetRank(zsl, zn->score, zn->obj);\n        count = (zsl->length - (rank - 1));\n\n        /* Find last element in range */\n        // 获取最后一个指定 slot 的键\n        zn = zslLastInRange(zsl, &range);\n\n        /* Use rank of last element, if any, to determine the actual count */\n        // 最后一个键存在\n        if (zn != NULL) {\n            // 获取排位\n            rank = zslGetRank(zsl, zn->score, zn->obj);\n            // 计算数量\n            count -= (zsl->length - rank);\n        }\n    }\n\n    return count;\n}\n"
  },
  {
    "path": "src/debug.c",
    "content": "/*\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 \"redis.h\"\n#include \"sha1.h\"   /* SHA1 is used for DEBUG DIGEST */\n#include \"crc64.h\"\n\n#include <arpa/inet.h>\n#include <signal.h>\n\n#ifdef HAVE_BACKTRACE\n#include <execinfo.h>\n#include <ucontext.h>\n#include <fcntl.h>\n#include \"bio.h\"\n#endif /* HAVE_BACKTRACE */\n\n/* ================================= Debugging ============================== */\n\n/* Compute the sha1 of string at 's' with 'len' bytes long.\n * The SHA1 is then xored against the string pointed by digest.\n * Since xor is commutative, this operation is used in order to\n * \"add\" digests relative to unordered elements.\n *\n * So digest(a,b,c,d) will be the same of digest(b,a,c,d) */\nvoid xorDigest(unsigned char *digest, void *ptr, size_t len) {\n    SHA1_CTX ctx;\n    unsigned char hash[20], *s = ptr;\n    int j;\n\n    SHA1Init(&ctx);\n    SHA1Update(&ctx,s,len);\n    SHA1Final(hash,&ctx);\n\n    for (j = 0; j < 20; j++)\n        digest[j] ^= hash[j];\n}\n\nvoid xorObjectDigest(unsigned char *digest, robj *o) {\n    o = getDecodedObject(o);\n    xorDigest(digest,o->ptr,sdslen(o->ptr));\n    decrRefCount(o);\n}\n\n/* This function instead of just computing the SHA1 and xoring it\n * against digest, also perform the digest of \"digest\" itself and\n * replace the old value with the new one.\n *\n * So the final digest will be:\n *\n * digest = SHA1(digest xor SHA1(data))\n *\n * This function is used every time we want to preserve the order so\n * that digest(a,b,c,d) will be different than digest(b,c,d,a)\n *\n * Also note that mixdigest(\"foo\") followed by mixdigest(\"bar\")\n * will lead to a different digest compared to \"fo\", \"obar\".\n */\nvoid mixDigest(unsigned char *digest, void *ptr, size_t len) {\n    SHA1_CTX ctx;\n    char *s = ptr;\n\n    xorDigest(digest,s,len);\n    SHA1Init(&ctx);\n    SHA1Update(&ctx,digest,20);\n    SHA1Final(digest,&ctx);\n}\n\nvoid mixObjectDigest(unsigned char *digest, robj *o) {\n    o = getDecodedObject(o);\n    mixDigest(digest,o->ptr,sdslen(o->ptr));\n    decrRefCount(o);\n}\n\n/* Compute the dataset digest. Since keys, sets elements, hashes elements\n * are not ordered, we use a trick: every aggregate digest is the xor\n * of the digests of their elements. This way the order will not change\n * the result. For list instead we use a feedback entering the output digest\n * as input in order to ensure that a different ordered list will result in\n * a different digest. */\nvoid computeDatasetDigest(unsigned char *final) {\n    unsigned char digest[20];\n    char buf[128];\n    dictIterator *di = NULL;\n    dictEntry *de;\n    int j;\n    uint32_t aux;\n\n    memset(final,0,20); /* Start with a clean result */\n\n    for (j = 0; j < server.dbnum; j++) {\n        redisDb *db = server.db+j;\n\n        if (dictSize(db->dict) == 0) continue;\n        di = dictGetIterator(db->dict);\n\n        /* hash the DB id, so the same dataset moved in a different\n         * DB will lead to a different digest */\n        aux = htonl(j);\n        mixDigest(final,&aux,sizeof(aux));\n\n        /* Iterate this DB writing every entry */\n        while((de = dictNext(di)) != NULL) {\n            sds key;\n            robj *keyobj, *o;\n            long long expiretime;\n\n            memset(digest,0,20); /* This key-val digest */\n            key = dictGetKey(de);\n            keyobj = createStringObject(key,sdslen(key));\n\n            mixDigest(digest,key,sdslen(key));\n\n            o = dictGetVal(de);\n\n            aux = htonl(o->type);\n            mixDigest(digest,&aux,sizeof(aux));\n            expiretime = getExpire(db,keyobj);\n\n            /* Save the key and associated value */\n            if (o->type == REDIS_STRING) {\n                mixObjectDigest(digest,o);\n            } else if (o->type == REDIS_LIST) {\n                listTypeIterator *li = listTypeInitIterator(o,0,REDIS_TAIL);\n                listTypeEntry entry;\n                while(listTypeNext(li,&entry)) {\n                    robj *eleobj = listTypeGet(&entry);\n                    mixObjectDigest(digest,eleobj);\n                    decrRefCount(eleobj);\n                }\n                listTypeReleaseIterator(li);\n            } else if (o->type == REDIS_SET) {\n                setTypeIterator *si = setTypeInitIterator(o);\n                robj *ele;\n                while((ele = setTypeNextObject(si)) != NULL) {\n                    xorObjectDigest(digest,ele);\n                    decrRefCount(ele);\n                }\n                setTypeReleaseIterator(si);\n            } else if (o->type == REDIS_ZSET) {\n                unsigned char eledigest[20];\n\n                if (o->encoding == REDIS_ENCODING_ZIPLIST) {\n                    unsigned char *zl = o->ptr;\n                    unsigned char *eptr, *sptr;\n                    unsigned char *vstr;\n                    unsigned int vlen;\n                    long long vll;\n                    double score;\n\n                    eptr = ziplistIndex(zl,0);\n                    redisAssert(eptr != NULL);\n                    sptr = ziplistNext(zl,eptr);\n                    redisAssert(sptr != NULL);\n\n                    while (eptr != NULL) {\n                        redisAssert(ziplistGet(eptr,&vstr,&vlen,&vll));\n                        score = zzlGetScore(sptr);\n\n                        memset(eledigest,0,20);\n                        if (vstr != NULL) {\n                            mixDigest(eledigest,vstr,vlen);\n                        } else {\n                            ll2string(buf,sizeof(buf),vll);\n                            mixDigest(eledigest,buf,strlen(buf));\n                        }\n\n                        snprintf(buf,sizeof(buf),\"%.17g\",score);\n                        mixDigest(eledigest,buf,strlen(buf));\n                        xorDigest(digest,eledigest,20);\n                        zzlNext(zl,&eptr,&sptr);\n                    }\n                } else if (o->encoding == REDIS_ENCODING_SKIPLIST) {\n                    zset *zs = o->ptr;\n                    dictIterator *di = dictGetIterator(zs->dict);\n                    dictEntry *de;\n\n                    while((de = dictNext(di)) != NULL) {\n                        robj *eleobj = dictGetKey(de);\n                        double *score = dictGetVal(de);\n\n                        snprintf(buf,sizeof(buf),\"%.17g\",*score);\n                        memset(eledigest,0,20);\n                        mixObjectDigest(eledigest,eleobj);\n                        mixDigest(eledigest,buf,strlen(buf));\n                        xorDigest(digest,eledigest,20);\n                    }\n                    dictReleaseIterator(di);\n                } else {\n                    redisPanic(\"Unknown sorted set encoding\");\n                }\n            } else if (o->type == REDIS_HASH) {\n                hashTypeIterator *hi;\n                robj *obj;\n\n                hi = hashTypeInitIterator(o);\n                while (hashTypeNext(hi) != REDIS_ERR) {\n                    unsigned char eledigest[20];\n\n                    memset(eledigest,0,20);\n                    obj = hashTypeCurrentObject(hi,REDIS_HASH_KEY);\n                    mixObjectDigest(eledigest,obj);\n                    decrRefCount(obj);\n                    obj = hashTypeCurrentObject(hi,REDIS_HASH_VALUE);\n                    mixObjectDigest(eledigest,obj);\n                    decrRefCount(obj);\n                    xorDigest(digest,eledigest,20);\n                }\n                hashTypeReleaseIterator(hi);\n            } else {\n                redisPanic(\"Unknown object type\");\n            }\n            /* If the key has an expire, add it to the mix */\n            if (expiretime != -1) xorDigest(digest,\"!!expire!!\",10);\n            /* We can finally xor the key-val digest to the final digest */\n            xorDigest(final,digest,20);\n            decrRefCount(keyobj);\n        }\n        dictReleaseIterator(di);\n    }\n}\n\nvoid debugCommand(redisClient *c) {\n    if (!strcasecmp(c->argv[1]->ptr,\"segfault\")) {\n        *((char*)-1) = 'x';\n    } else if (!strcasecmp(c->argv[1]->ptr,\"oom\")) {\n        void *ptr = zmalloc(ULONG_MAX); /* Should trigger an out of memory. */\n        zfree(ptr);\n        addReply(c,shared.ok);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"assert\")) {\n        if (c->argc >= 3) c->argv[2] = tryObjectEncoding(c->argv[2]);\n        redisAssertWithInfo(c,c->argv[0],1 == 2);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"reload\")) {\n        if (rdbSave(server.rdb_filename) != REDIS_OK) {\n            addReply(c,shared.err);\n            return;\n        }\n        emptyDb(NULL);\n        if (rdbLoad(server.rdb_filename) != REDIS_OK) {\n            addReplyError(c,\"Error trying to load the RDB dump\");\n            return;\n        }\n        redisLog(REDIS_WARNING,\"DB reloaded by DEBUG RELOAD\");\n        addReply(c,shared.ok);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"loadaof\")) {\n        emptyDb(NULL);\n        if (loadAppendOnlyFile(server.aof_filename) != REDIS_OK) {\n            addReply(c,shared.err);\n            return;\n        }\n        server.dirty = 0; /* Prevent AOF / replication */\n        redisLog(REDIS_WARNING,\"Append Only File loaded by DEBUG LOADAOF\");\n        addReply(c,shared.ok);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"object\") && c->argc == 3) {\n        dictEntry *de;\n        robj *val;\n        char *strenc;\n\n        if ((de = dictFind(c->db->dict,c->argv[2]->ptr)) == NULL) {\n            addReply(c,shared.nokeyerr);\n            return;\n        }\n        val = dictGetVal(de);\n        strenc = strEncoding(val->encoding);\n\n        addReplyStatusFormat(c,\n            \"Value at:%p refcount:%d \"\n            \"encoding:%s serializedlength:%lld \"\n            \"lru:%d lru_seconds_idle:%llu\",\n            (void*)val, val->refcount,\n            strenc, (long long) rdbSavedObjectLen(val),\n            val->lru, estimateObjectIdleTime(val));\n    } else if (!strcasecmp(c->argv[1]->ptr,\"sdslen\") && c->argc == 3) {\n        dictEntry *de;\n        robj *val;\n        sds key;\n\n        if ((de = dictFind(c->db->dict,c->argv[2]->ptr)) == NULL) {\n            addReply(c,shared.nokeyerr);\n            return;\n        }\n        val = dictGetVal(de);\n        key = dictGetKey(de);\n\n        if (val->type != REDIS_STRING || !sdsEncodedObject(val)) {\n            addReplyError(c,\"Not an sds encoded string.\");\n        } else {\n            addReplyStatusFormat(c,\n                \"key_sds_len:%lld, key_sds_avail:%lld, \"\n                \"val_sds_len:%lld, val_sds_avail:%lld\",\n                (long long) sdslen(key),\n                (long long) sdsavail(key),\n                (long long) sdslen(val->ptr),\n                (long long) sdsavail(val->ptr));\n        }\n    } else if (!strcasecmp(c->argv[1]->ptr,\"populate\") && c->argc == 3) {\n        long keys, j;\n        robj *key, *val;\n        char buf[128];\n\n        if (getLongFromObjectOrReply(c, c->argv[2], &keys, NULL) != REDIS_OK)\n            return;\n        dictExpand(c->db->dict,keys);\n        for (j = 0; j < keys; j++) {\n            snprintf(buf,sizeof(buf),\"key:%lu\",j);\n            key = createStringObject(buf,strlen(buf));\n            if (lookupKeyRead(c->db,key) != NULL) {\n                decrRefCount(key);\n                continue;\n            }\n            snprintf(buf,sizeof(buf),\"value:%lu\",j);\n            val = createStringObject(buf,strlen(buf));\n            dbAdd(c->db,key,val);\n            decrRefCount(key);\n        }\n        addReply(c,shared.ok);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"digest\") && c->argc == 2) {\n        unsigned char digest[20];\n        sds d = sdsempty();\n        int j;\n\n        computeDatasetDigest(digest);\n        for (j = 0; j < 20; j++)\n            d = sdscatprintf(d, \"%02x\",digest[j]);\n        addReplyStatus(c,d);\n        sdsfree(d);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"sleep\") && c->argc == 3) {\n        double dtime = strtod(c->argv[2]->ptr,NULL);\n        long long utime = dtime*1000000;\n        struct timespec tv;\n\n        tv.tv_sec = utime / 1000000;\n        tv.tv_nsec = (utime % 1000000) * 1000;\n        nanosleep(&tv, NULL);\n        addReply(c,shared.ok);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"set-active-expire\") &&\n               c->argc == 3)\n    {\n        server.active_expire_enabled = atoi(c->argv[2]->ptr);\n        addReply(c,shared.ok);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"cmdkeys\") && c->argc >= 3) {\n        struct redisCommand *cmd = lookupCommand(c->argv[2]->ptr);\n        int *keys, numkeys, j;\n\n        if (!cmd) {\n            addReplyError(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,\"error\") && c->argc == 3) {\n        sds errstr = sdsnewlen(\"-\",1);\n\n        errstr = sdscatsds(errstr,c->argv[2]->ptr);\n        errstr = sdsmapchars(errstr,\"\\n\\r\",\"  \",2); /* no newlines in errors. */\n        errstr = sdscatlen(errstr,\"\\r\\n\",2);\n        addReplySds(c,errstr);\n    } else {\n        addReplyErrorFormat(c, \"Unknown DEBUG subcommand or wrong number of arguments for '%s'\",\n            (char*)c->argv[1]->ptr);\n    }\n}\n\n/* =========================== Crash handling  ============================== */\n\nvoid _redisAssert(char *estr, char *file, int line) {\n    bugReportStart();\n    redisLog(REDIS_WARNING,\"=== ASSERTION FAILED ===\");\n    redisLog(REDIS_WARNING,\"==> %s:%d '%s' is not true\",file,line,estr);\n#ifdef HAVE_BACKTRACE\n    server.assert_failed = estr;\n    server.assert_file = file;\n    server.assert_line = line;\n    redisLog(REDIS_WARNING,\"(forcing SIGSEGV to print the bug report.)\");\n#endif\n    *((char*)-1) = 'x';\n}\n\nvoid _redisAssertPrintClientInfo(redisClient *c) {\n    int j;\n\n    bugReportStart();\n    redisLog(REDIS_WARNING,\"=== ASSERTION FAILED CLIENT CONTEXT ===\");\n    redisLog(REDIS_WARNING,\"client->flags = %d\", c->flags);\n    redisLog(REDIS_WARNING,\"client->fd = %d\", c->fd);\n    redisLog(REDIS_WARNING,\"client->argc = %d\", c->argc);\n    for (j=0; j < c->argc; j++) {\n        char buf[128];\n        char *arg;\n\n        if (c->argv[j]->type == REDIS_STRING && sdsEncodedObject(c->argv[j])) {\n            arg = (char*) c->argv[j]->ptr;\n        } else {\n            snprintf(buf,sizeof(buf),\"Object type: %d, encoding: %d\",\n                c->argv[j]->type, c->argv[j]->encoding);\n            arg = buf;\n        }\n        redisLog(REDIS_WARNING,\"client->argv[%d] = \\\"%s\\\" (refcount: %d)\",\n            j, arg, c->argv[j]->refcount);\n    }\n}\n\nvoid redisLogObjectDebugInfo(robj *o) {\n    redisLog(REDIS_WARNING,\"Object type: %d\", o->type);\n    redisLog(REDIS_WARNING,\"Object encoding: %d\", o->encoding);\n    redisLog(REDIS_WARNING,\"Object refcount: %d\", o->refcount);\n    if (o->type == REDIS_STRING && sdsEncodedObject(o)) {\n        redisLog(REDIS_WARNING,\"Object raw string len: %zu\", sdslen(o->ptr));\n        if (sdslen(o->ptr) < 4096) {\n            sds repr = sdscatrepr(sdsempty(),o->ptr,sdslen(o->ptr));\n            redisLog(REDIS_WARNING,\"Object raw string content: %s\", repr);\n            sdsfree(repr);\n        }\n    } else if (o->type == REDIS_LIST) {\n        redisLog(REDIS_WARNING,\"List length: %d\", (int) listTypeLength(o));\n    } else if (o->type == REDIS_SET) {\n        redisLog(REDIS_WARNING,\"Set size: %d\", (int) setTypeSize(o));\n    } else if (o->type == REDIS_HASH) {\n        redisLog(REDIS_WARNING,\"Hash size: %d\", (int) hashTypeLength(o));\n    } else if (o->type == REDIS_ZSET) {\n        redisLog(REDIS_WARNING,\"Sorted set size: %d\", (int) zsetLength(o));\n        if (o->encoding == REDIS_ENCODING_SKIPLIST)\n            redisLog(REDIS_WARNING,\"Skiplist level: %d\", (int) ((zset*)o->ptr)->zsl->level);\n    }\n}\n\nvoid _redisAssertPrintObject(robj *o) {\n    bugReportStart();\n    redisLog(REDIS_WARNING,\"=== ASSERTION FAILED OBJECT CONTEXT ===\");\n    redisLogObjectDebugInfo(o);\n}\n\nvoid _redisAssertWithInfo(redisClient *c, robj *o, char *estr, char *file, int line) {\n    if (c) _redisAssertPrintClientInfo(c);\n    if (o) _redisAssertPrintObject(o);\n    _redisAssert(estr,file,line);\n}\n\nvoid _redisPanic(char *msg, char *file, int line) {\n    bugReportStart();\n    redisLog(REDIS_WARNING,\"------------------------------------------------\");\n    redisLog(REDIS_WARNING,\"!!! Software Failure. Press left mouse button to continue\");\n    redisLog(REDIS_WARNING,\"Guru Meditation: %s #%s:%d\",msg,file,line);\n#ifdef HAVE_BACKTRACE\n    redisLog(REDIS_WARNING,\"(forcing SIGSEGV in order to print the stack trace)\");\n#endif\n    redisLog(REDIS_WARNING,\"------------------------------------------------\");\n    *((char*)-1) = 'x';\n}\n\nvoid bugReportStart(void) {\n    if (server.bug_report_start == 0) {\n        redisLog(REDIS_WARNING,\n            \"\\n\\n=== REDIS BUG REPORT START: Cut & paste starting from here ===\");\n        server.bug_report_start = 1;\n    }\n}\n\n#ifdef HAVE_BACKTRACE\nstatic void *getMcontextEip(ucontext_t *uc) {\n#if defined(__APPLE__) && !defined(MAC_OS_X_VERSION_10_6)\n    /* OSX < 10.6 */\n    #if defined(__x86_64__)\n    return (void*) uc->uc_mcontext->__ss.__rip;\n    #elif defined(__i386__)\n    return (void*) uc->uc_mcontext->__ss.__eip;\n    #else\n    return (void*) uc->uc_mcontext->__ss.__srr0;\n    #endif\n#elif defined(__APPLE__) && defined(MAC_OS_X_VERSION_10_6)\n    /* OSX >= 10.6 */\n    #if defined(_STRUCT_X86_THREAD_STATE64) && !defined(__i386__)\n    return (void*) uc->uc_mcontext->__ss.__rip;\n    #else\n    return (void*) uc->uc_mcontext->__ss.__eip;\n    #endif\n#elif defined(__linux__)\n    /* Linux */\n    #if defined(__i386__)\n    return (void*) uc->uc_mcontext.gregs[14]; /* Linux 32 */\n    #elif defined(__X86_64__) || defined(__x86_64__)\n    return (void*) uc->uc_mcontext.gregs[16]; /* Linux 64 */\n    #elif defined(__ia64__) /* Linux IA64 */\n    return (void*) uc->uc_mcontext.sc_ip;\n    #endif\n#else\n    return NULL;\n#endif\n}\n\nvoid logStackContent(void **sp) {\n    int i;\n    for (i = 15; i >= 0; i--) {\n        unsigned long addr = (unsigned long) sp+i;\n        unsigned long val = (unsigned long) sp[i];\n\n        if (sizeof(long) == 4)\n            redisLog(REDIS_WARNING, \"(%08lx) -> %08lx\", addr, val);\n        else\n            redisLog(REDIS_WARNING, \"(%016lx) -> %016lx\", addr, val);\n    }\n}\n\nvoid logRegisters(ucontext_t *uc) {\n    redisLog(REDIS_WARNING, \"--- REGISTERS\");\n\n/* OSX */\n#if defined(__APPLE__) && defined(MAC_OS_X_VERSION_10_6)\n  /* OSX AMD64 */\n    #if defined(_STRUCT_X86_THREAD_STATE64) && !defined(__i386__)\n    redisLog(REDIS_WARNING,\n    \"\\n\"\n    \"RAX:%016lx RBX:%016lx\\nRCX:%016lx RDX:%016lx\\n\"\n    \"RDI:%016lx RSI:%016lx\\nRBP:%016lx RSP:%016lx\\n\"\n    \"R8 :%016lx R9 :%016lx\\nR10:%016lx R11:%016lx\\n\"\n    \"R12:%016lx R13:%016lx\\nR14:%016lx R15:%016lx\\n\"\n    \"RIP:%016lx EFL:%016lx\\nCS :%016lx FS:%016lx  GS:%016lx\",\n        (unsigned long) uc->uc_mcontext->__ss.__rax,\n        (unsigned long) uc->uc_mcontext->__ss.__rbx,\n        (unsigned long) uc->uc_mcontext->__ss.__rcx,\n        (unsigned long) uc->uc_mcontext->__ss.__rdx,\n        (unsigned long) uc->uc_mcontext->__ss.__rdi,\n        (unsigned long) uc->uc_mcontext->__ss.__rsi,\n        (unsigned long) uc->uc_mcontext->__ss.__rbp,\n        (unsigned long) uc->uc_mcontext->__ss.__rsp,\n        (unsigned long) uc->uc_mcontext->__ss.__r8,\n        (unsigned long) uc->uc_mcontext->__ss.__r9,\n        (unsigned long) uc->uc_mcontext->__ss.__r10,\n        (unsigned long) uc->uc_mcontext->__ss.__r11,\n        (unsigned long) uc->uc_mcontext->__ss.__r12,\n        (unsigned long) uc->uc_mcontext->__ss.__r13,\n        (unsigned long) uc->uc_mcontext->__ss.__r14,\n        (unsigned long) uc->uc_mcontext->__ss.__r15,\n        (unsigned long) uc->uc_mcontext->__ss.__rip,\n        (unsigned long) uc->uc_mcontext->__ss.__rflags,\n        (unsigned long) uc->uc_mcontext->__ss.__cs,\n        (unsigned long) uc->uc_mcontext->__ss.__fs,\n        (unsigned long) uc->uc_mcontext->__ss.__gs\n    );\n    logStackContent((void**)uc->uc_mcontext->__ss.__rsp);\n    #else\n    /* OSX x86 */\n    redisLog(REDIS_WARNING,\n    \"\\n\"\n    \"EAX:%08lx EBX:%08lx ECX:%08lx EDX:%08lx\\n\"\n    \"EDI:%08lx ESI:%08lx EBP:%08lx ESP:%08lx\\n\"\n    \"SS:%08lx  EFL:%08lx EIP:%08lx CS :%08lx\\n\"\n    \"DS:%08lx  ES:%08lx  FS :%08lx GS :%08lx\",\n        (unsigned long) uc->uc_mcontext->__ss.__eax,\n        (unsigned long) uc->uc_mcontext->__ss.__ebx,\n        (unsigned long) uc->uc_mcontext->__ss.__ecx,\n        (unsigned long) uc->uc_mcontext->__ss.__edx,\n        (unsigned long) uc->uc_mcontext->__ss.__edi,\n        (unsigned long) uc->uc_mcontext->__ss.__esi,\n        (unsigned long) uc->uc_mcontext->__ss.__ebp,\n        (unsigned long) uc->uc_mcontext->__ss.__esp,\n        (unsigned long) uc->uc_mcontext->__ss.__ss,\n        (unsigned long) uc->uc_mcontext->__ss.__eflags,\n        (unsigned long) uc->uc_mcontext->__ss.__eip,\n        (unsigned long) uc->uc_mcontext->__ss.__cs,\n        (unsigned long) uc->uc_mcontext->__ss.__ds,\n        (unsigned long) uc->uc_mcontext->__ss.__es,\n        (unsigned long) uc->uc_mcontext->__ss.__fs,\n        (unsigned long) uc->uc_mcontext->__ss.__gs\n    );\n    logStackContent((void**)uc->uc_mcontext->__ss.__esp);\n    #endif\n/* Linux */\n#elif defined(__linux__)\n    /* Linux x86 */\n    #if defined(__i386__)\n    redisLog(REDIS_WARNING,\n    \"\\n\"\n    \"EAX:%08lx EBX:%08lx ECX:%08lx EDX:%08lx\\n\"\n    \"EDI:%08lx ESI:%08lx EBP:%08lx ESP:%08lx\\n\"\n    \"SS :%08lx EFL:%08lx EIP:%08lx CS:%08lx\\n\"\n    \"DS :%08lx ES :%08lx FS :%08lx GS:%08lx\",\n        (unsigned long) uc->uc_mcontext.gregs[11],\n        (unsigned long) uc->uc_mcontext.gregs[8],\n        (unsigned long) uc->uc_mcontext.gregs[10],\n        (unsigned long) uc->uc_mcontext.gregs[9],\n        (unsigned long) uc->uc_mcontext.gregs[4],\n        (unsigned long) uc->uc_mcontext.gregs[5],\n        (unsigned long) uc->uc_mcontext.gregs[6],\n        (unsigned long) uc->uc_mcontext.gregs[7],\n        (unsigned long) uc->uc_mcontext.gregs[18],\n        (unsigned long) uc->uc_mcontext.gregs[17],\n        (unsigned long) uc->uc_mcontext.gregs[14],\n        (unsigned long) uc->uc_mcontext.gregs[15],\n        (unsigned long) uc->uc_mcontext.gregs[3],\n        (unsigned long) uc->uc_mcontext.gregs[2],\n        (unsigned long) uc->uc_mcontext.gregs[1],\n        (unsigned long) uc->uc_mcontext.gregs[0]\n    );\n    logStackContent((void**)uc->uc_mcontext.gregs[7]);\n    #elif defined(__X86_64__) || defined(__x86_64__)\n    /* Linux AMD64 */\n    redisLog(REDIS_WARNING,\n    \"\\n\"\n    \"RAX:%016lx RBX:%016lx\\nRCX:%016lx RDX:%016lx\\n\"\n    \"RDI:%016lx RSI:%016lx\\nRBP:%016lx RSP:%016lx\\n\"\n    \"R8 :%016lx R9 :%016lx\\nR10:%016lx R11:%016lx\\n\"\n    \"R12:%016lx R13:%016lx\\nR14:%016lx R15:%016lx\\n\"\n    \"RIP:%016lx EFL:%016lx\\nCSGSFS:%016lx\",\n        (unsigned long) uc->uc_mcontext.gregs[13],\n        (unsigned long) uc->uc_mcontext.gregs[11],\n        (unsigned long) uc->uc_mcontext.gregs[14],\n        (unsigned long) uc->uc_mcontext.gregs[12],\n        (unsigned long) uc->uc_mcontext.gregs[8],\n        (unsigned long) uc->uc_mcontext.gregs[9],\n        (unsigned long) uc->uc_mcontext.gregs[10],\n        (unsigned long) uc->uc_mcontext.gregs[15],\n        (unsigned long) uc->uc_mcontext.gregs[0],\n        (unsigned long) uc->uc_mcontext.gregs[1],\n        (unsigned long) uc->uc_mcontext.gregs[2],\n        (unsigned long) uc->uc_mcontext.gregs[3],\n        (unsigned long) uc->uc_mcontext.gregs[4],\n        (unsigned long) uc->uc_mcontext.gregs[5],\n        (unsigned long) uc->uc_mcontext.gregs[6],\n        (unsigned long) uc->uc_mcontext.gregs[7],\n        (unsigned long) uc->uc_mcontext.gregs[16],\n        (unsigned long) uc->uc_mcontext.gregs[17],\n        (unsigned long) uc->uc_mcontext.gregs[18]\n    );\n    logStackContent((void**)uc->uc_mcontext.gregs[15]);\n    #endif\n#else\n    redisLog(REDIS_WARNING,\n        \"  Dumping of registers not supported for this OS/arch\");\n#endif\n}\n\n/* Logs the stack trace using the backtrace() call. This function is designed\n * to be called from signal handlers safely. */\nvoid logStackTrace(ucontext_t *uc) {\n    void *trace[100];\n    int trace_size = 0, fd;\n    int log_to_stdout = server.logfile[0] == '\\0';\n\n    /* Open the log file in append mode. */\n    fd = log_to_stdout ?\n        STDOUT_FILENO :\n        open(server.logfile, O_APPEND|O_CREAT|O_WRONLY, 0644);\n    if (fd == -1) return;\n\n    /* Generate the stack trace */\n    trace_size = backtrace(trace, 100);\n\n    /* overwrite sigaction with caller's address */\n    if (getMcontextEip(uc) != NULL)\n        trace[1] = getMcontextEip(uc);\n\n    /* Write symbols to log file */\n    backtrace_symbols_fd(trace, trace_size, fd);\n\n    /* Cleanup */\n    if (!log_to_stdout) close(fd);\n}\n\n/* Log information about the \"current\" client, that is, the client that is\n * currently being served by Redis. May be NULL if Redis is not serving a\n * client right now. */\nvoid logCurrentClient(void) {\n    if (server.current_client == NULL) return;\n\n    redisClient *cc = server.current_client;\n    sds client;\n    int j;\n\n    redisLog(REDIS_WARNING, \"--- CURRENT CLIENT INFO\");\n    client = catClientInfoString(sdsempty(),cc);\n    redisLog(REDIS_WARNING,\"client: %s\", client);\n    sdsfree(client);\n    for (j = 0; j < cc->argc; j++) {\n        robj *decoded;\n\n        decoded = getDecodedObject(cc->argv[j]);\n        redisLog(REDIS_WARNING,\"argv[%d]: '%s'\", j, (char*)decoded->ptr);\n        decrRefCount(decoded);\n    }\n    /* Check if the first argument, usually a key, is found inside the\n     * selected DB, and if so print info about the associated object. */\n    if (cc->argc >= 1) {\n        robj *val, *key;\n        dictEntry *de;\n\n        key = getDecodedObject(cc->argv[1]);\n        de = dictFind(cc->db->dict, key->ptr);\n        if (de) {\n            val = dictGetVal(de);\n            redisLog(REDIS_WARNING,\"key '%s' found in DB containing the following object:\", (char*)key->ptr);\n            redisLogObjectDebugInfo(val);\n        }\n        decrRefCount(key);\n    }\n}\n\n#if defined(HAVE_PROC_MAPS)\nvoid memtest_non_destructive_invert(void *addr, size_t size);\nvoid memtest_non_destructive_swap(void *addr, size_t size);\n#define MEMTEST_MAX_REGIONS 128\n\nint memtest_test_linux_anonymous_maps(void) {\n    FILE *fp = fopen(\"/proc/self/maps\",\"r\");\n    char line[1024];\n    size_t start_addr, end_addr, size;\n    size_t start_vect[MEMTEST_MAX_REGIONS];\n    size_t size_vect[MEMTEST_MAX_REGIONS];\n    int regions = 0, j;\n    uint64_t crc1 = 0, crc2 = 0, crc3 = 0;\n\n    while(fgets(line,sizeof(line),fp) != NULL) {\n        char *start, *end, *p = line;\n\n        start = p;\n        p = strchr(p,'-');\n        if (!p) continue;\n        *p++ = '\\0';\n        end = p;\n        p = strchr(p,' ');\n        if (!p) continue;\n        *p++ = '\\0';\n        if (strstr(p,\"stack\") ||\n            strstr(p,\"vdso\") ||\n            strstr(p,\"vsyscall\")) continue;\n        if (!strstr(p,\"00:00\")) continue;\n        if (!strstr(p,\"rw\")) continue;\n\n        start_addr = strtoul(start,NULL,16);\n        end_addr = strtoul(end,NULL,16);\n        size = end_addr-start_addr;\n\n        start_vect[regions] = start_addr;\n        size_vect[regions] = size;\n        printf(\"Testing %lx %lu\\n\", (unsigned long) start_vect[regions],\n                                    (unsigned long) size_vect[regions]);\n        regions++;\n    }\n\n    /* Test all the regions as an unique sequential region.\n     * 1) Take the CRC64 of the memory region. */\n    for (j = 0; j < regions; j++) {\n        crc1 = crc64(crc1,(void*)start_vect[j],size_vect[j]);\n    }\n\n    /* 2) Invert bits, swap adjacent words, swap again, invert bits.\n     * This is the error amplification step. */\n    for (j = 0; j < regions; j++)\n        memtest_non_destructive_invert((void*)start_vect[j],size_vect[j]);\n    for (j = 0; j < regions; j++)\n        memtest_non_destructive_swap((void*)start_vect[j],size_vect[j]);\n    for (j = 0; j < regions; j++)\n        memtest_non_destructive_swap((void*)start_vect[j],size_vect[j]);\n    for (j = 0; j < regions; j++)\n        memtest_non_destructive_invert((void*)start_vect[j],size_vect[j]);\n\n    /* 3) Take the CRC64 sum again. */\n    for (j = 0; j < regions; j++)\n        crc2 = crc64(crc2,(void*)start_vect[j],size_vect[j]);\n\n    /* 4) Swap + Swap again */\n    for (j = 0; j < regions; j++)\n        memtest_non_destructive_swap((void*)start_vect[j],size_vect[j]);\n    for (j = 0; j < regions; j++)\n        memtest_non_destructive_swap((void*)start_vect[j],size_vect[j]);\n\n    /* 5) Take the CRC64 sum again. */\n    for (j = 0; j < regions; j++)\n        crc3 = crc64(crc3,(void*)start_vect[j],size_vect[j]);\n\n    /* NOTE: It is very important to close the file descriptor only now\n     * because closing it before may result into unmapping of some memory\n     * region that we are testing. */\n    fclose(fp);\n\n    /* If the two CRC are not the same, we trapped a memory error. */\n    return crc1 != crc2 || crc2 != crc3;\n}\n#endif\n\nvoid sigsegvHandler(int sig, siginfo_t *info, void *secret) {\n    ucontext_t *uc = (ucontext_t*) secret;\n    sds infostring, clients;\n    struct sigaction act;\n    REDIS_NOTUSED(info);\n\n    bugReportStart();\n    redisLog(REDIS_WARNING,\n        \"    Redis %s crashed by signal: %d\", REDIS_VERSION, sig);\n    redisLog(REDIS_WARNING,\n        \"    Failed assertion: %s (%s:%d)\", server.assert_failed,\n                        server.assert_file, server.assert_line);\n\n    /* Log the stack trace */\n    redisLog(REDIS_WARNING, \"--- STACK TRACE\");\n    logStackTrace(uc);\n\n    /* Log INFO and CLIENT LIST */\n    redisLog(REDIS_WARNING, \"--- INFO OUTPUT\");\n    infostring = genRedisInfoString(\"all\");\n    infostring = sdscatprintf(infostring, \"hash_init_value: %u\\n\",\n        dictGetHashFunctionSeed());\n    redisLogRaw(REDIS_WARNING, infostring);\n    redisLog(REDIS_WARNING, \"--- CLIENT LIST OUTPUT\");\n    clients = getAllClientsInfoString();\n    redisLogRaw(REDIS_WARNING, clients);\n    sdsfree(infostring);\n    sdsfree(clients);\n\n    /* Log the current client */\n    logCurrentClient();\n\n    /* Log dump of processor registers */\n    logRegisters(uc);\n\n#if defined(HAVE_PROC_MAPS)\n    /* Test memory */\n    redisLog(REDIS_WARNING, \"--- FAST MEMORY TEST\");\n    bioKillThreads();\n    if (memtest_test_linux_anonymous_maps()) {\n        redisLog(REDIS_WARNING,\n            \"!!! MEMORY ERROR DETECTED! Check your memory ASAP !!!\");\n    } else {\n        redisLog(REDIS_WARNING,\n            \"Fast memory test PASSED, however your memory can still be broken. Please run a memory test for several hours if possible.\");\n    }\n#endif\n\n    redisLog(REDIS_WARNING,\n\"\\n=== REDIS BUG REPORT END. Make sure to include from START to END. ===\\n\\n\"\n\"       Please report the crash opening an issue on github:\\n\\n\"\n\"           http://github.com/antirez/redis/issues\\n\\n\"\n\"  Suspect RAM error? Use redis-server --test-memory to veryfy it.\\n\\n\"\n);\n    /* free(messages); Don't call free() with possibly corrupted memory. */\n    if (server.daemonize) unlink(server.pidfile);\n\n    /* Make sure we exit with the right signal at the end. So for instance\n     * the core will be dumped if enabled. */\n    sigemptyset (&act.sa_mask);\n    act.sa_flags = SA_NODEFER | SA_ONSTACK | SA_RESETHAND;\n    act.sa_handler = SIG_DFL;\n    sigaction (sig, &act, NULL);\n    kill(getpid(),sig);\n}\n#endif /* HAVE_BACKTRACE */\n\n/* ==================== Logging functions for debugging ===================== */\n\nvoid redisLogHexDump(int level, char *descr, void *value, size_t len) {\n    char buf[65], *b;\n    unsigned char *v = value;\n    char charset[] = \"0123456789abcdef\";\n\n    redisLog(level,\"%s (hexdump):\", descr);\n    b = buf;\n    while(len) {\n        b[0] = charset[(*v)>>4];\n        b[1] = charset[(*v)&0xf];\n        b[2] = '\\0';\n        b += 2;\n        len--;\n        v++;\n        if (b-buf == 64 || len == 0) {\n            redisLogRaw(level|REDIS_LOG_RAW,buf);\n            b = buf;\n        }\n    }\n    redisLogRaw(level|REDIS_LOG_RAW,\"\\n\");\n}\n\n/* =========================== Software Watchdog ============================ */\n#include <sys/time.h>\n\nvoid watchdogSignalHandler(int sig, siginfo_t *info, void *secret) {\n#ifdef HAVE_BACKTRACE\n    ucontext_t *uc = (ucontext_t*) secret;\n#endif\n    REDIS_NOTUSED(info);\n    REDIS_NOTUSED(sig);\n\n    redisLogFromHandler(REDIS_WARNING,\"\\n--- WATCHDOG TIMER EXPIRED ---\");\n#ifdef HAVE_BACKTRACE\n    logStackTrace(uc);\n#else\n    redisLogFromHandler(REDIS_WARNING,\"Sorry: no support for backtrace().\");\n#endif\n    redisLogFromHandler(REDIS_WARNING,\"--------\\n\");\n}\n\n/* Schedule a SIGALRM delivery after the specified period in milliseconds.\n * If a timer is already scheduled, this function will re-schedule it to the\n * specified time. If period is 0 the current timer is disabled. */\nvoid watchdogScheduleSignal(int period) {\n    struct itimerval it;\n\n    /* Will stop the timer if period is 0. */\n    it.it_value.tv_sec = period/1000;\n    it.it_value.tv_usec = (period%1000)*1000;\n    /* Don't automatically restart. */\n    it.it_interval.tv_sec = 0;\n    it.it_interval.tv_usec = 0;\n    setitimer(ITIMER_REAL, &it, NULL);\n}\n\n/* Enable the software watchdog with the specified period in milliseconds. */\nvoid enableWatchdog(int period) {\n    int min_period;\n\n    if (server.watchdog_period == 0) {\n        struct sigaction act;\n\n        /* Watchdog was actually disabled, so we have to setup the signal\n         * handler. */\n        sigemptyset(&act.sa_mask);\n        act.sa_flags = SA_NODEFER | SA_ONSTACK | SA_SIGINFO;\n        act.sa_sigaction = watchdogSignalHandler;\n        sigaction(SIGALRM, &act, NULL);\n    }\n    /* If the configured period is smaller than twice the timer period, it is\n     * too short for the software watchdog to work reliably. Fix it now\n     * if needed. */\n    min_period = (1000/server.hz)*2;\n    if (period < min_period) period = min_period;\n    watchdogScheduleSignal(period); /* Adjust the current timer. */\n    server.watchdog_period = period;\n}\n\n/* Disable the software watchdog. */\nvoid disableWatchdog(void) {\n    struct sigaction act;\n    if (server.watchdog_period == 0) return; /* Already disabled. */\n    watchdogScheduleSignal(0); /* Stop the current timer. */\n\n    /* Set the signal handler to SIG_IGN, this will also remove pending\n     * signals from the queue. */\n    sigemptyset(&act.sa_mask);\n    act.sa_flags = 0;\n    act.sa_handler = SIG_IGN;\n    sigaction(SIGALRM, &act, NULL);\n    server.watchdog_period = 0;\n}\n"
  },
  {
    "path": "src/dict.c",
    "content": "/* Hash Tables Implementation.\n *\n * This file implements in memory hash tables with insert/del/replace/find/\n * get-random-element operations. Hash tables will auto resize if needed\n * tables of power of two in size are used, collisions are handled by\n * chaining. See the source code for more information... :)\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#include \"fmacros.h\"\n\n#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 \"dict.h\"\n#include \"zmalloc.h\"\n#include \"redisassert.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 * 通过 dictEnableResize() 和 dictDisableResize() 两个函数，\n * 程序可以手动地允许或阻止哈希表进行 rehash ，\n * 这在 Redis 使用子进程进行保存操作时，可以有效地利用 copy-on-write 机制。\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.\n *\n * 需要注意的是，并非所有 rehash 都会被 dictDisableResize 阻止：\n * 如果已使用节点的数量和字典大小之间的比率，\n * 大于字典强制 rehash 比率 dict_force_resize_ratio ，\n * 那么 rehash 仍然会（强制）进行。\n */\n// 指示字典是否启用 rehash 的标识\nstatic int dict_can_resize = 1;\n// 强制 rehash 的比率\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\n/* Identity hash function for integer keys */\nunsigned int dictIdentityHashFunction(unsigned int key)\n{\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(). */\n/*\n * 重置（或初始化）给定哈希表的各项属性值\n *\n * p.s. 上面的英文注释已经过期\n *\n * T = O(1)\n */\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 */\n/*\n * 创建一个新的字典\n *\n * T = O(1)\n */\ndict *dictCreate(dictType *type,\n        void *privDataPtr)\n{\n    dict *d = zmalloc(sizeof(*d));\n\n    _dictInit(d,type,privDataPtr);\n\n    return d;\n}\n\n/* Initialize the hash table */\n/*\n * 初始化哈希表\n *\n * T = O(1)\n */\nint _dictInit(dict *d, dictType *type,\n        void *privDataPtr)\n{\n    // 初始化两个哈希表的各项属性值\n    // 但暂时还不分配内存给哈希表数组\n    _dictReset(&d->ht[0]);\n    _dictReset(&d->ht[1]);\n\n    // 设置类型特定函数\n    d->type = type;\n\n    // 设置私有数据\n    d->privdata = privDataPtr;\n\n    // 设置哈希表 rehash 状态\n    d->rehashidx = -1;\n\n    // 设置字典的安全迭代器数量\n    d->iterators = 0;\n\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 */\n/*\n * 缩小给定字典\n * 让它的已用节点数和字典大小之间的比率接近 1:1\n *\n * 返回 DICT_ERR 表示字典已经在 rehash ，或者 dict_can_resize 为假。\n *\n * 成功创建体积更小的 ht[1] ，可以开始 resize 时，返回 DICT_OK。\n *\n * T = O(N)\n */\nint dictResize(dict *d)\n{\n    int minimal;\n\n    // 不能在关闭 rehash 或者正在 rehash 的时候调用\n    if (!dict_can_resize || dictIsRehashing(d)) return DICT_ERR;\n\n    // 计算让比率接近 1：1 所需要的最少节点数量\n    minimal = d->ht[0].used;\n    if (minimal < DICT_HT_INITIAL_SIZE)\n        minimal = DICT_HT_INITIAL_SIZE;\n\n    // 调整字典的大小\n    // T = O(N)\n    return dictExpand(d, minimal);\n}\n\n/* Expand or create the hash table */\n/*\n * 创建一个新的哈希表，并根据字典的情况，选择以下其中一个动作来进行：\n *\n * 1) 如果字典的 0 号哈希表为空，那么将新哈希表设置为 0 号哈希表\n * 2) 如果字典的 0 号哈希表非空，那么将新哈希表设置为 1 号哈希表，\n *    并打开字典的 rehash 标识，使得程序可以开始对字典进行 rehash\n *\n * size 参数不够大，或者 rehash 已经在进行时，返回 DICT_ERR 。\n *\n * 成功创建 0 号哈希表，或者 1 号哈希表时，返回 DICT_OK 。\n *\n * T = O(N)\n */\nint dictExpand(dict *d, unsigned long size)\n{\n    // 新哈希表\n    dictht n; /* the new hash table */\n\n    // 根据 size 参数，计算哈希表的大小\n    // T = O(1)\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    // 不能在字典正在 rehash 时进行\n    // size 的值也不能小于 0 号哈希表的当前已使用节点\n    if (dictIsRehashing(d) || d->ht[0].used > size)\n        return DICT_ERR;\n\n    /* Allocate the new hash table and initialize all pointers to NULL */\n    // 为哈希表分配空间，并将所有指针指向 NULL\n    n.size = realsize;\n    n.sizemask = realsize-1;\n    // T = O(N)\n    n.table = zcalloc(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    // 如果 0 号哈希表为空，那么这是一次初始化：\n    // 程序将新哈希表赋给 0 号哈希表的指针，然后字典就可以开始处理键值对了。\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    // 如果 0 号哈希表非空，那么这是一次 rehash ：\n    // 程序将新哈希表设置为 1 号哈希表，\n    // 并将字典的 rehash 标识打开，让程序可以开始对字典进行 rehash\n    d->ht[1] = n;\n    d->rehashidx = 0;\n    return DICT_OK;\n\n    /* 顺带一提，上面的代码可以重构成以下形式：\n    \n    if (d->ht[0].table == NULL) {\n        // 初始化\n        d->ht[0] = n;\n    } else {\n        // rehash \n        d->ht[1] = n;\n        d->rehashidx = 0;\n    }\n\n    return DICT_OK;\n\n    */\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 * 执行 N 步渐进式 rehash 。\n *\n * 返回 1 表示仍有键需要从 0 号哈希表移动到 1 号哈希表，\n * 返回 0 则表示所有键都已经迁移完毕。\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.\n *\n * 注意，每步 rehash 都是以一个哈希表索引（桶）作为单位的，\n * 一个桶里可能会有多个节点，\n * 被 rehash 的桶里的所有节点都会被移动到新哈希表。\n *\n * T = O(N)\n */\nint dictRehash(dict *d, int n) {\n\n    // 只可以在 rehash 进行中时执行\n    if (!dictIsRehashing(d)) return 0;\n\n    // 进行 N 步迁移\n    // T = O(N)\n    while(n--) {\n        dictEntry *de, *nextde;\n\n        /* Check if we already rehashed the whole table... */\n        // 如果 0 号哈希表为空，那么表示 rehash 执行完毕\n        // T = O(1)\n        if (d->ht[0].used == 0) {\n            // 释放 0 号哈希表\n            zfree(d->ht[0].table);\n            // 将原来的 1 号哈希表设置为新的 0 号哈希表\n            d->ht[0] = d->ht[1];\n            // 重置旧的 1 号哈希表\n            _dictReset(&d->ht[1]);\n            // 关闭 rehash 标识\n            d->rehashidx = -1;\n            // 返回 0 ，向调用者表示 rehash 已经完成\n            return 0;\n        }\n\n        /* Note that rehashidx can't overflow as we are sure there are more\n         * elements because ht[0].used != 0 */\n        // 确保 rehashidx 没有越界\n        assert(d->ht[0].size > (unsigned)d->rehashidx);\n\n        // 略过数组中为空的索引，找到下一个非空索引\n        while(d->ht[0].table[d->rehashidx] == NULL) d->rehashidx++;\n\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        // 将链表中的所有节点迁移到新哈希表\n        // T = O(1)\n        while(de) {\n            unsigned int h;\n\n            // 保存下个节点的指针\n            nextde = de->next;\n\n            /* Get the index in the new hash table */\n            // 计算新哈希表的哈希值，以及节点插入的索引位置\n            h = dictHashKey(d, de->key) & d->ht[1].sizemask;\n\n            // 插入节点到新哈希表\n            de->next = d->ht[1].table[h];\n            d->ht[1].table[h] = de;\n\n            // 更新计数器\n            d->ht[0].used--;\n            d->ht[1].used++;\n\n            // 继续处理下个节点\n            de = nextde;\n        }\n        // 将刚迁移完的哈希表索引的指针设为空\n        d->ht[0].table[d->rehashidx] = NULL;\n        // 更新 rehash 索引\n        d->rehashidx++;\n    }\n\n    return 1;\n}\n\n/*\n * 返回以毫秒为单位的 UNIX 时间戳\n *\n * T = O(1)\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 */\n/*\n * 在给定毫秒数内，以 100 步为单位，对字典进行 rehash 。\n *\n * T = O(N)\n */\nint dictRehashMilliseconds(dict *d, int ms) {\n    // 记录开始时间\n    long long start = timeInMilliseconds();\n    int rehashes = 0;\n\n    while(dictRehash(d,100)) {\n        rehashes += 100;\n        // 如果时间已过，跳出\n        if (timeInMilliseconds()-start > ms) break;\n    }\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 * 在字典不存在安全迭代器的情况下，对字典进行单步 rehash 。\n *\n * 字典有安全迭代器的情况下不能进行 rehash ，\n * 因为两种不同的迭代和修改操作可能会弄乱字典。\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. \n *\n * 这个函数被多个通用的查找、更新操作调用，\n * 它可以让字典在被使用的同时进行 rehash 。\n *\n * T = O(1)\n */\nstatic void _dictRehashStep(dict *d) {\n    if (d->iterators == 0) dictRehash(d,1);\n}\n\n/* Add an element to the target hash table */\n/*\n * 尝试将给定键值对添加到字典中\n *\n * 只有给定键 key 不存在于字典时，添加操作才会成功\n *\n * 添加成功返回 DICT_OK ，失败返回 DICT_ERR\n *\n * 最坏 T = O(N) ，平滩 O(1) \n */\nint dictAdd(dict *d, void *key, void *val)\n{\n    // 尝试添加键到字典，并返回包含了这个键的新哈希节点\n    // T = O(N)\n    dictEntry *entry = dictAddRaw(d,key);\n\n    // 键已存在，添加失败\n    if (!entry) return DICT_ERR;\n\n    // 键不存在，设置节点的值\n    // T = O(1)\n    dictSetVal(d, entry, val);\n\n    // 添加成功\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 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 */\n/*\n * 尝试将键插入到字典中\n *\n * 如果键已经在字典存在，那么返回 NULL\n *\n * 如果键不存在，那么程序创建新的哈希节点，\n * 将节点和键关联，并插入到字典，然后返回节点本身。\n *\n * T = O(N)\n */\ndictEntry *dictAddRaw(dict *d, void *key)\n{\n    int index;\n    dictEntry *entry;\n    dictht *ht;\n\n    // 如果条件允许的话，进行单步 rehash\n    // T = O(1)\n    if (dictIsRehashing(d)) _dictRehashStep(d);\n\n    /* Get the index of the new element, or -1 if\n     * the element already exists. */\n    // 计算键在哈希表中的索引值\n    // 如果值为 -1 ，那么表示键已经存在\n    // T = O(N)\n    if ((index = _dictKeyIndex(d, key)) == -1)\n        return NULL;\n\n    // T = O(1)\n    /* Allocate the memory and store the new entry */\n    // 如果字典正在 rehash ，那么将新键添加到 1 号哈希表\n    // 否则，将新键添加到 0 号哈希表\n    ht = dictIsRehashing(d) ? &d->ht[1] : &d->ht[0];\n    // 为新节点分配空间\n    entry = zmalloc(sizeof(*entry));\n    // 将新节点插入到链表表头\n    entry->next = ht->table[index];\n    ht->table[index] = entry;\n    // 更新哈希表已使用节点数量\n    ht->used++;\n\n    /* Set the hash entry fields. */\n    // 设置新节点的键\n    // T = O(1)\n    dictSetKey(d, entry, key);\n\n    return entry;\n}\n\n/* Add an element, discarding the old if the key already exists.\n *\n * 将给定的键值对添加到字典中，如果键已经存在，那么删除旧有的键值对。\n *\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. \n *\n * 如果键值对为全新添加，那么返回 1 。\n * 如果键值对是通过对原有的键值对更新得来的，那么返回 0 。\n *\n * T = O(N)\n */\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    // 尝试直接将键值对添加到字典\n    // 如果键 key 不存在的话，添加会成功\n    // T = O(N)\n    if (dictAdd(d, key, val) == DICT_OK)\n        return 1;\n\n    /* It already exists, get the entry */\n    // 运行到这里，说明键 key 已经存在，那么找出包含这个 key 的节点\n    // T = O(1)\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    // 先保存原有的值的指针\n    auxentry = *entry;\n    // 然后设置新的值\n    // T = O(1)\n    dictSetVal(d, entry, val);\n    // 然后释放旧值\n    // T = O(1)\n    dictFreeVal(d, &auxentry);\n\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. */\n/*\n * dictAddRaw() 根据给定 key 释放存在，执行以下动作：\n *\n * 1) key 已经存在，返回包含该 key 的字典节点\n * 2) key 不存在，那么将 key 添加到字典\n *\n * 不论发生以上的哪一种情况，\n * dictAddRaw() 都总是返回包含给定 key 的字典节点。\n *\n * T = O(N)\n */\ndictEntry *dictReplaceRaw(dict *d, void *key) {\n    \n    // 使用 key 在字典中查找节点\n    // T = O(1)\n    dictEntry *entry = dictFind(d,key);\n\n    // 如果节点找到了直接返回节点，否则添加并返回一个新节点\n    // T = O(N)\n    return entry ? entry : dictAddRaw(d,key);\n}\n\n/* Search and remove an element */\n/*\n * 查找并删除包含给定键的节点\n *\n * 参数 nofree 决定是否调用键和值的释放函数\n * 0 表示调用，1 表示不调用\n *\n * 找到并成功删除返回 DICT_OK ，没找到则返回 DICT_ERR\n *\n * T = O(1)\n */\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    // 字典（的哈希表）为空\n    if (d->ht[0].size == 0) return DICT_ERR; /* d->ht[0].table is NULL */\n\n    // 进行单步 rehash ，T = O(1)\n    if (dictIsRehashing(d)) _dictRehashStep(d);\n\n    // 计算哈希值\n    h = dictHashKey(d, key);\n\n    // 遍历哈希表\n    // T = O(1)\n    for (table = 0; table <= 1; table++) {\n\n        // 计算索引值 \n        idx = h & d->ht[table].sizemask;\n        // 指向该索引上的链表\n        he = d->ht[table].table[idx];\n        prevHe = NULL;\n        // 遍历链表上的所有节点\n        // T = O(1)\n        while(he) {\n        \n            if (dictCompareKeys(d, key, he->key)) {\n                // 超找目标节点\n\n                /* Unlink the element from the list */\n                // 从链表中删除\n                if (prevHe)\n                    prevHe->next = he->next;\n                else\n                    d->ht[table].table[idx] = he->next;\n\n                // 释放调用键和值的释放函数？\n                if (!nofree) {\n                    dictFreeKey(d, he);\n                    dictFreeVal(d, he);\n                }\n                \n                // 释放节点本身\n                zfree(he);\n\n                // 更新已使用节点数量\n                d->ht[table].used--;\n\n                // 返回已找到信号\n                return DICT_OK;\n            }\n\n            prevHe = he;\n            he = he->next;\n        }\n\n        // 如果执行到这里，说明在 0 号哈希表中找不到给定键\n        // 那么根据字典是否正在进行 rehash ，决定要不要查找 1 号哈希表\n        if (!dictIsRehashing(d)) break;\n    }\n\n    // 没找到\n    return DICT_ERR; /* not found */\n}\n\n/*\n * 从字典中删除包含给定键的节点\n * \n * 并且调用键值的释放函数来删除键值\n *\n * 找到并成功删除返回 DICT_OK ，没找到则返回 DICT_ERR\n * T = O(1)\n */\nint dictDelete(dict *ht, const void *key) {\n    return dictGenericDelete(ht,key,0);\n}\n\n/*\n * 从字典中删除包含给定键的节点\n * \n * 但不调用键值的释放函数来删除键值\n *\n * 找到并成功删除返回 DICT_OK ，没找到则返回 DICT_ERR\n * T = O(1)\n */\nint dictDeleteNoFree(dict *ht, const void *key) {\n    return dictGenericDelete(ht,key,1);\n}\n\n/* Destroy an entire dictionary */\n/*\n * 删除哈希表上的所有节点，并重置哈希表的各项属性\n *\n * T = O(N)\n */\nint _dictClear(dict *d, dictht *ht, void(callback)(void *)) {\n    unsigned long i;\n\n    /* Free all the elements */\n    // 遍历整个哈希表\n    // T = O(N)\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        // 跳过空索引\n        if ((he = ht->table[i]) == NULL) continue;\n\n        // 遍历整个链表\n        // T = O(1)\n        while(he) {\n            nextHe = he->next;\n            // 删除键\n            dictFreeKey(d, he);\n            // 删除值\n            dictFreeVal(d, he);\n            // 释放节点\n            zfree(he);\n\n            // 更新已使用节点计数\n            ht->used--;\n\n            // 处理下个节点\n            he = nextHe;\n        }\n    }\n\n    /* Free the table and the allocated cache structure */\n    // 释放哈希表结构\n    zfree(ht->table);\n\n    /* Re-initialize the table */\n    // 重置哈希表属性\n    _dictReset(ht);\n\n    return DICT_OK; /* never fails */\n}\n\n/* Clear & Release the hash table */\n/*\n * 删除并释放整个字典\n *\n * T = O(N)\n */\nvoid dictRelease(dict *d)\n{\n    // 删除并清空两个哈希表\n    _dictClear(d,&d->ht[0],NULL);\n    _dictClear(d,&d->ht[1],NULL);\n    // 释放节点结构\n    zfree(d);\n}\n\n/*\n * 返回字典中包含键 key 的节点\n *\n * 找到返回节点，找不到返回 NULL\n *\n * T = O(1)\n */\ndictEntry *dictFind(dict *d, const void *key)\n{\n    dictEntry *he;\n    unsigned int h, idx, table;\n\n    // 字典（的哈希表）为空\n    if (d->ht[0].size == 0) return NULL; /* We don't have a table at all */\n\n    // 如果条件允许的话，进行单步 rehash\n    if (dictIsRehashing(d)) _dictRehashStep(d);\n\n    // 计算键的哈希值\n    h = dictHashKey(d, key);\n    // 在字典的哈希表中查找这个键\n    // T = O(1)\n    for (table = 0; table <= 1; table++) {\n\n        // 计算索引值\n        idx = h & d->ht[table].sizemask;\n\n        // 遍历给定索引上的链表的所有节点，查找 key\n        he = d->ht[table].table[idx];\n        // T = O(1)\n        while(he) {\n\n            if (dictCompareKeys(d, key, he->key))\n                return he;\n\n            he = he->next;\n        }\n\n        // 如果程序遍历完 0 号哈希表，仍然没找到指定的键的节点\n        // 那么程序会检查字典是否在进行 rehash ，\n        // 然后才决定是直接返回 NULL ，还是继续查找 1 号哈希表\n        if (!dictIsRehashing(d)) return NULL;\n    }\n\n    // 进行到这里时，说明两个哈希表都没找到\n    return NULL;\n}\n\n/*\n * 获取包含给定键的节点的值\n *\n * 如果节点不为空，返回节点的值\n * 否则返回 NULL\n *\n * T = O(1)\n */\nvoid *dictFetchValue(dict *d, const void *key) {\n    dictEntry *he;\n\n    // T = O(1)\n    he = dictFind(d,key);\n\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\n/*\n * 创建并返回给定字典的不安全迭代器\n *\n * T = O(1)\n */\ndictIterator *dictGetIterator(dict *d)\n{\n    dictIterator *iter = zmalloc(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\n    return iter;\n}\n\n/*\n * 创建并返回给定节点的安全迭代器\n *\n * T = O(1)\n */\ndictIterator *dictGetSafeIterator(dict *d) {\n    dictIterator *i = dictGetIterator(d);\n\n    // 设置安全迭代器标识\n    i->safe = 1;\n\n    return i;\n}\n\n/*\n * 返回迭代器指向的当前节点\n *\n * 字典迭代完毕时，返回 NULL\n *\n * T = O(1)\n */\ndictEntry *dictNext(dictIterator *iter)\n{\n    while (1) {\n\n        // 进入这个循环有两种可能：\n        // 1) 这是迭代器第一次运行\n        // 2) 当前索引链表中的节点已经迭代完（NULL 为链表的表尾）\n        if (iter->entry == NULL) {\n\n            // 指向被迭代的哈希表\n            dictht *ht = &iter->d->ht[iter->table];\n\n            // 初次迭代时执行\n            if (iter->index == -1 && iter->table == 0) {\n                // 如果是安全迭代器，那么更新安全迭代器计数器\n                if (iter->safe)\n                    iter->d->iterators++;\n                // 如果是不安全迭代器，那么计算指纹\n                else\n                    iter->fingerprint = dictFingerprint(iter->d);\n            }\n            // 更新索引\n            iter->index++;\n\n            // 如果迭代器的当前索引大于当前被迭代的哈希表的大小\n            // 那么说明这个哈希表已经迭代完毕\n            if (iter->index >= (signed) ht->size) {\n                // 如果正在 rehash 的话，那么说明 1 号哈希表也正在使用中\n                // 那么继续对 1 号哈希表进行迭代\n                if (dictIsRehashing(iter->d) && iter->table == 0) {\n                    iter->table++;\n                    iter->index = 0;\n                    ht = &iter->d->ht[1];\n                // 如果没有 rehash ，那么说明迭代已经完成\n                } else {\n                    break;\n                }\n            }\n\n            // 如果进行到这里，说明这个哈希表并未迭代完\n            // 更新节点指针，指向下个索引链表的表头节点\n            iter->entry = ht->table[iter->index];\n        } else {\n            // 执行到这里，说明程序正在迭代某个链表\n            // 将节点指针指向链表的下个节点\n            iter->entry = iter->nextEntry;\n        }\n\n        // 如果当前节点不为空，那么也记录下该节点的下个节点\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\n    // 迭代完毕\n    return NULL;\n}\n\n/*\n * 释放给定字典迭代器\n *\n * T = O(1)\n */\nvoid dictReleaseIterator(dictIterator *iter)\n{\n\n    if (!(iter->index == -1 && iter->table == 0)) {\n        // 释放安全迭代器时，安全迭代器计数器减一\n        if (iter->safe)\n            iter->d->iterators--;\n        // 释放不安全迭代器时，验证指纹是否有变化\n        else\n            assert(iter->fingerprint == dictFingerprint(iter->d));\n    }\n    zfree(iter);\n}\n\n/* Return a random entry from the hash table. Useful to\n * implement randomized algorithms */\n/*\n * 随机返回字典中任意一个节点。\n *\n * 可用于实现随机化算法。\n *\n * 如果字典为空，返回 NULL 。\n *\n * T = O(N)\n*/\ndictEntry *dictGetRandomKey(dict *d)\n{\n    dictEntry *he, *orighe;\n    unsigned int h;\n    int listlen, listele;\n\n    // 字典为空\n    if (dictSize(d) == 0) return NULL;\n\n    // 进行单步 rehash\n    if (dictIsRehashing(d)) _dictRehashStep(d);\n\n    // 如果正在 rehash ，那么将 1 号哈希表也作为随机查找的目标\n    if (dictIsRehashing(d)) {\n        // T = O(N)\n        do {\n            h = random() % (d->ht[0].size+d->ht[1].size);\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    // 否则，只从 0 号哈希表中查找节点\n    } else {\n        // T = O(N)\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    // 目前 he 已经指向一个非空的节点链表\n    // 程序将从这个链表随机返回一个节点\n    listlen = 0;\n    orighe = he;\n    // 计算节点数量, T = O(1)\n    while(he) {\n        he = he->next;\n        listlen++;\n    }\n    // 取模，得出随机节点的索引\n    listele = random() % listlen;\n    he = orighe;\n    // 按索引查找节点\n    // T = O(1)\n    while(listele--) he = he->next;\n\n    // 返回随机节点\n    return he;\n}\n\n/* This is a version of dictGetRandomKey() that is modified in order to\n * return multiple entries by jumping at a random place of the hash table\n * and scanning linearly for entries.\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.\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, and the elements are guaranteed to be non\n * repeating. */\nint dictGetRandomKeys(dict *d, dictEntry **des, int count) {\n    int j; /* internal hash table id, 0 or 1. */\n    int stored = 0;\n\n    if (dictSize(d) < count) count = dictSize(d);\n    while(stored < count) {\n        for (j = 0; j < 2; j++) {\n            /* Pick a random point inside the hash table 0 or 1. */\n            unsigned int i = random() & d->ht[j].sizemask;\n            int size = d->ht[j].size;\n\n            /* Make sure to visit every bucket by iterating 'size' times. */\n            while(size--) {\n                dictEntry *he = d->ht[j].table[i];\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                i = (i+1) & d->ht[j].sizemask;\n            }\n            /* If there is only one table and we iterated it all, we should\n             * already have 'count' elements. Assert this condition. */\n            assert(dictIsRehashing(d) != 0);\n        }\n    }\n    return stored; /* Never reached. */\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 * dictScan() 函数用于迭代给定字典中的元素。\n *\n * Iterating works in the following way:\n *\n * 迭代按以下方式执行：\n *\n * 1) Initially you call the function using a cursor (v) value of 0.\n *    一开始，你使用 0 作为游标来调用函数。\n * 2) The function performs one step of the iteration, and returns the\n *    new cursor value that you must use in the next call.\n *    函数执行一步迭代操作，\n *    并返回一个下次迭代时使用的新游标。\n * 3) When the returned cursor is 0, the iteration is complete.\n *    当函数返回的游标为 0 时，迭代完成。\n *\n * The function guarantees that all the elements that are present in the\n * dictionary from the start to the end of the iteration are returned.\n * However it is possible that some element is returned multiple time.\n *\n * 函数保证，在迭代从开始到结束期间，一直存在于字典的元素肯定会被迭代到，\n * 但一个元素可能会被返回多次。\n *\n * For every element returned, the callback 'fn' passed as argument is\n * called, with 'privdata' as first argument and the dictionar entry\n * 'de' as second argument.\n *\n * 每当一个元素被返回时，回调函数 fn 就会被执行，\n * fn 函数的第一个参数是 privdata ，而第二个参数则是字典节点 de 。\n *\n * HOW IT WORKS.\n * 工作原理\n *\n * The algorithm used in the iteration 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 * 迭代所使用的算法是由 Pieter Noordhuis 设计的，\n * 算法的主要思路是在二进制高位上对游标进行加法计算\n * 也即是说，不是按正常的办法来对游标进行加法计算，\n * 而是首先将游标的二进制位翻转（reverse）过来，\n * 然后对翻转后的值进行加法计算，\n * 最后再次对加法计算之后的结果进行翻转。\n *\n * This strategy is needed because the hash table may be resized from one\n * call to the other call of the same iteration.\n *\n * 这一策略是必要的，因为在一次完整的迭代过程中，\n * 哈希表的大小有可能在两次迭代之间发生改变。\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 * always 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 * 哈希表的大小总是 2 的某个次方，并且哈希表使用链表来解决冲突，\n * 因此一个给定元素在一个给定表的位置总可以通过 Hash(key) & SIZE-1\n * 公式来计算得出，\n * 其中 SIZE-1 是哈希表的最大索引值，\n * 这个最大索引值就是哈希表的 mask （掩码）。\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 be always\n * the last four bits of the hash output, and so forth.\n *\n * 举个例子，如果当前哈希表的大小为 16 ，\n * 那么它的掩码就是二进制值 1111 ，\n * 这个哈希表的所有位置都可以使用哈希值的最后四个二进制位来记录。\n *\n * WHAT HAPPENS IF THE TABLE CHANGES IN SIZE?\n * 如果哈希表的大小改变了怎么办？\n *\n * If the hash table grows, elements can go anyway in one multiple of\n * the old bucket: for example let's say that we already iterated with\n * a 4 bit cursor 1100, since the mask is 1111 (hash table size = 16).\n *\n * 当对哈希表进行扩展时，元素可能会从一个槽移动到另一个槽，\n * 举个例子，假设我们刚好迭代至 4 位游标 1100 ，\n * 而哈希表的 mask 为 1111 （哈希表的大小为 16 ）。\n *\n * If the hash table will be resized to 64 elements, and the new mask will\n * be 111111, the new buckets that you obtain substituting in ??1100\n * either 0 or 1, can be targeted only by keys that we already visited\n * when scanning the bucket 1100 in the smaller hash table.\n *\n * 如果这时哈希表将大小改为 64 ，那么哈希表的 mask 将变为 111111 ，\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, and will\n * just continue iterating with cursors that don't have '1100' at the end,\n * nor any other combination of 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) was already completely explored, it will not be visited again\n * as we are sure that, 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 * 等等。。。在 rehash 的时候可是会出现两个哈希表的阿！\n *\n * Yes, this is true, but we always iterate the smaller one of the tables,\n * testing also all the expansions of the current cursor into the larger\n * table. So 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 exists, is just an expansion of the smaller one.\n *\n * LIMITATIONS\n * 限制\n *\n * This iterator is completely stateless, and this is a huge advantage,\n * including no additional memory used.\n * 这个迭代器是完全无状态的，这是一个巨大的优势，\n * 因为迭代可以在不使用任何额外内存的情况下进行。\n *\n * The disadvantages resulting from this design are:\n * 这个设计的缺陷在于：\n *\n * 1) It is possible that we return duplicated elements. However this is usually\n *    easy to deal with in the application level.\n *    函数可能会返回重复的元素，不过这个问题可以很容易在应用层解决。\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.\n *    为了不错过任何元素，\n *    迭代器需要返回给定桶上的所有键，\n *    以及因为扩展哈希表而产生出来的新表，\n *    所以迭代器必须在一次迭代中返回多个元素。\n * 3) The reverse cursor is somewhat hard to understand at first, but this\n *    comment is supposed to help.\n *    对游标进行翻转（reverse）的原因初看上去比较难以理解，\n *    不过阅读这份注释应该会有所帮助。\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    // 跳过空字典\n    if (dictSize(d) == 0) return 0;\n\n    // 迭代只有一个哈希表的字典\n    if (!dictIsRehashing(d)) {\n\n        // 指向哈希表\n        t0 = &(d->ht[0]);\n\n        // 记录 mask\n        m0 = t0->sizemask;\n\n        /* Emit entries at cursor */\n        // 指向哈希桶\n        de = t0->table[v & m0];\n        // 遍历桶中的所有节点\n        while (de) {\n            fn(privdata, de);\n            de = de->next;\n        }\n\n    // 迭代有两个哈希表的字典\n    } else {\n\n        // 指向两个哈希表\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        // 确保 t0 比 t1 要小\n        if (t0->size > t1->size) {\n            t0 = &d->ht[1];\n            t1 = &d->ht[0];\n        }\n\n        // 记录掩码\n        m0 = t0->sizemask;\n        m1 = t1->sizemask;\n\n        /* Emit entries at cursor */\n        // 指向桶，并迭代桶中的所有节点\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        // Iterate over indices in larger table             // 迭代大表中的桶\n        // that are the expansion of the index pointed to   // 这些桶被索引的 expansion 所指向\n        // by the cursor in the smaller table               //\n        do {\n            /* Emit entries at cursor */\n            // 指向桶，并迭代桶中的所有节点\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 */\n/*\n * 根据需要，初始化字典（的哈希表），或者对字典（的现有哈希表）进行扩展\n *\n * T = O(N)\n */\nstatic int _dictExpandIfNeeded(dict *d)\n{\n    /* Incremental rehashing already in progress. Return. */\n    // 渐进式 rehash 已经在进行了，直接返回\n    if (dictIsRehashing(d)) return DICT_OK;\n\n    /* If the hash table is empty expand it to the initial size. */\n    // 如果字典（的 0 号哈希表）为空，那么创建并返回初始化大小的 0 号哈希表\n    // T = O(1)\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    // 一下两个条件之一为真时，对字典进行扩展\n    // 1）字典已使用节点数和字典大小之间的比率接近 1：1\n    //    并且 dict_can_resize 为真\n    // 2）已使用节点数和字典大小之间的比率超过 dict_force_resize_ratio\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        // 新哈希表的大小至少是目前已使用节点数的两倍\n        // T = O(N)\n        return dictExpand(d, d->ht[0].used*2);\n    }\n\n    return DICT_OK;\n}\n\n/* Our hash table capability is a power of two */\n/*\n * 计算第一个大于等于 size 的 2 的 N 次方，用作哈希表的值\n *\n * T = O(1)\n */\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 * 返回可以将 key 插入到哈希表的索引位置\n * 如果 key 已经存在于哈希表，那么返回 -1\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. \n *\n * 注意，如果字典正在进行 rehash ，那么总是返回 1 号哈希表的索引。\n * 因为在字典进行 rehash 时，新节点总是插入到 1 号哈希表。\n *\n * T = O(N)\n */\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    // 单步 rehash\n    // T = O(N)\n    if (_dictExpandIfNeeded(d) == DICT_ERR)\n        return -1;\n\n    /* Compute the key hash value */\n    // 计算 key 的哈希值\n    h = dictHashKey(d, key);\n    // T = O(1)\n    for (table = 0; table <= 1; table++) {\n\n        // 计算索引值\n        idx = h & d->ht[table].sizemask;\n\n        /* Search if this slot does not already contain the given key */\n        // 查找 key 是否存在\n        // T = O(1)\n        he = d->ht[table].table[idx];\n        while(he) {\n            if (dictCompareKeys(d, key, he->key))\n                return -1;\n            he = he->next;\n        }\n\n        // 如果运行到这里时，说明 0 号哈希表中所有节点都不包含 key\n        // 如果这时 rehahs 正在进行，那么继续对 1 号哈希表进行 rehash\n        if (!dictIsRehashing(d)) break;\n    }\n\n    // 返回索引值\n    return idx;\n}\n\n/*\n * 清空字典上的所有哈希表节点，并重置字典属性\n *\n * T = O(N)\n */\nvoid dictEmpty(dict *d, void(callback)(void*)) {\n\n    // 删除两个哈希表上的所有节点\n    // T = O(N)\n    _dictClear(d,&d->ht[0],callback);\n    _dictClear(d,&d->ht[1],callback);\n    // 重置属性 \n    d->rehashidx = -1;\n    d->iterators = 0;\n}\n\n/*\n * 开启自动 rehash\n *\n * T = O(1)\n */\nvoid dictEnableResize(void) {\n    dict_can_resize = 1;\n}\n\n/*\n * 关闭自动 rehash\n *\n * T = O(1)\n */\nvoid dictDisableResize(void) {\n    dict_can_resize = 0;\n}\n\n#if 0\n\n/* The following is code that we don't use for Redis currently, but that is part\nof the library. */\n\n/* ----------------------- Debugging ------------------------*/\n\n#define DICT_STATS_VECTLEN 50\nstatic void _dictPrintStatsHt(dictht *ht) {\n    unsigned long i, slots = 0, chainlen, maxchainlen = 0;\n    unsigned long totchainlen = 0;\n    unsigned long clvector[DICT_STATS_VECTLEN];\n\n    if (ht->used == 0) {\n        printf(\"No stats available for empty dictionaries\\n\");\n        return;\n    }\n\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    printf(\"Hash table stats:\\n\");\n    printf(\" table size: %ld\\n\", ht->size);\n    printf(\" number of elements: %ld\\n\", ht->used);\n    printf(\" different slots: %ld\\n\", slots);\n    printf(\" max chain length: %ld\\n\", maxchainlen);\n    printf(\" avg chain length (counted): %.02f\\n\", (float)totchainlen/slots);\n    printf(\" avg chain length (computed): %.02f\\n\", (float)ht->used/slots);\n    printf(\" Chain length distribution:\\n\");\n    for (i = 0; i < DICT_STATS_VECTLEN-1; i++) {\n        if (clvector[i] == 0) continue;\n        printf(\"   %s%ld: %ld (%.02f%%)\\n\",(i == DICT_STATS_VECTLEN-1)?\">= \":\"\", i, clvector[i], ((float)clvector[i]/ht->size)*100);\n    }\n}\n\nvoid dictPrintStats(dict *d) {\n    _dictPrintStatsHt(&d->ht[0]);\n    if (dictIsRehashing(d)) {\n        printf(\"-- Rehashing into ht[1]:\\n\");\n        _dictPrintStatsHt(&d->ht[1]);\n    }\n}\n\n/* ----------------------- StringCopy Hash Table Type ------------------------*/\n\nstatic unsigned int _dictStringCopyHTHashFunction(const void *key)\n{\n    return dictGenHashFunction(key, strlen(key));\n}\n\nstatic void *_dictStringDup(void *privdata, const void *key)\n{\n    int len = strlen(key);\n    char *copy = zmalloc(len+1);\n    DICT_NOTUSED(privdata);\n\n    memcpy(copy, key, len);\n    copy[len] = '\\0';\n    return copy;\n}\n\nstatic int _dictStringCopyHTKeyCompare(void *privdata, const void *key1,\n        const void *key2)\n{\n    DICT_NOTUSED(privdata);\n\n    return strcmp(key1, key2) == 0;\n}\n\nstatic void _dictStringDestructor(void *privdata, void *key)\n{\n    DICT_NOTUSED(privdata);\n\n    zfree(key);\n}\n\ndictType dictTypeHeapStringCopyKey = {\n    _dictStringCopyHTHashFunction, /* hash function */\n    _dictStringDup,                /* key dup */\n    NULL,                          /* val dup */\n    _dictStringCopyHTKeyCompare,   /* key compare */\n    _dictStringDestructor,         /* key destructor */\n    NULL                           /* val destructor */\n};\n\n/* This is like StringCopy but does not auto-duplicate the key.\n * It's used for intepreter's shared strings. */\ndictType dictTypeHeapStrings = {\n    _dictStringCopyHTHashFunction, /* hash function */\n    NULL,                          /* key dup */\n    NULL,                          /* val dup */\n    _dictStringCopyHTKeyCompare,   /* key compare */\n    _dictStringDestructor,         /* key destructor */\n    NULL                           /* val destructor */\n};\n\n/* This is like StringCopy but also automatically handle dynamic\n * allocated C strings as values. */\ndictType dictTypeHeapStringCopyKeyValue = {\n    _dictStringCopyHTHashFunction, /* hash function */\n    _dictStringDup,                /* key dup */\n    _dictStringDup,                /* val dup */\n    _dictStringCopyHTKeyCompare,   /* key compare */\n    _dictStringDestructor,         /* key destructor */\n    _dictStringDestructor,         /* val destructor */\n};\n#endif\n"
  },
  {
    "path": "src/dict.h",
    "content": "/* Hash Tables Implementation.\n *\n * This file implements in-memory hash tables with insert/del/replace/find/\n * get-random-element operations. Hash tables will auto-resize if needed\n * tables of power of two in size are used, collisions are handled by\n * chaining. See the source code for more information... :)\n *\n * 这个文件实现了一个内存哈希表，\n * 它支持插入、删除、替换、查找和获取随机元素等操作。\n *\n * 哈希表会自动在表的大小的二次方之间进行调整。\n *\n * 键的冲突通过链表来解决。\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#include <stdint.h>\n\n#ifndef __DICT_H\n#define __DICT_H\n\n/*\n * 字典的操作状态\n */\n// 操作成功\n#define DICT_OK 0\n// 操作失败（或出错）\n#define DICT_ERR 1\n\n/* Unused arguments generate annoying warnings... */\n// 如果字典的私有数据不使用时\n// 用这个宏来避免编译器错误\n#define DICT_NOTUSED(V) ((void) V)\n\n/*\n * 哈希表节点\n */\ntypedef struct dictEntry {\n    \n    // 键\n    void *key;\n\n    // 值\n    union {\n        void *val;\n        uint64_t u64;\n        int64_t s64;\n    } v;\n\n    // 指向下个哈希表节点，形成链表\n    struct dictEntry *next;\n\n} dictEntry;\n\n\n/*\n * 字典类型特定函数\n */\ntypedef struct dictType {\n\n    // 计算哈希值的函数\n    unsigned int (*hashFunction)(const void *key);\n\n    // 复制键的函数\n    void *(*keyDup)(void *privdata, const void *key);\n\n    // 复制值的函数\n    void *(*valDup)(void *privdata, const void *obj);\n\n    // 对比键的函数\n    int (*keyCompare)(void *privdata, const void *key1, const void *key2);\n\n    // 销毁键的函数\n    void (*keyDestructor)(void *privdata, void *key);\n    \n    // 销毁值的函数\n    void (*valDestructor)(void *privdata, void *obj);\n\n} dictType;\n\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. */\n/*\n * 哈希表\n *\n * 每个字典都使用两个哈希表，从而实现渐进式 rehash 。\n */\ntypedef struct dictht {\n    \n    // 哈希表数组\n    dictEntry **table;\n\n    // 哈希表大小\n    unsigned long size;\n    \n    // 哈希表大小掩码，用于计算索引值\n    // 总是等于 size - 1\n    unsigned long sizemask;\n\n    // 该哈希表已有节点的数量\n    unsigned long used;\n\n} dictht;\n\n/*\n * 字典\n */\ntypedef struct dict {\n\n    // 类型特定函数\n    dictType *type;\n\n    // 私有数据\n    void *privdata;\n\n    // 哈希表\n    dictht ht[2];\n\n    // rehash 索引\n    // 当 rehash 不在进行时，值为 -1\n    int rehashidx; /* rehashing not in progress if rehashidx == -1 */\n\n    // 目前正在运行的安全迭代器的数量\n    int iterators; /* number of iterators currently running */\n\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. */\n/*\n * 字典迭代器\n *\n * 如果 safe 属性的值为 1 ，那么在迭代进行的过程中，\n * 程序仍然可以执行 dictAdd 、 dictFind 和其他函数，对字典进行修改。\n *\n * 如果 safe 不为 1 ，那么程序只会调用 dictNext 对字典进行迭代，\n * 而不对字典进行修改。\n */\ntypedef struct dictIterator {\n        \n    // 被迭代的字典\n    dict *d;\n\n    // table ：正在被迭代的哈希表号码，值可以是 0 或 1 。\n    // index ：迭代器当前所指向的哈希表索引位置。\n    // safe ：标识这个迭代器是否安全\n    int table, index, safe;\n\n    // entry ：当前迭代到的节点的指针\n    // nextEntry ：当前迭代节点的下一个节点\n    //             因为在安全迭代器运作时， entry 所指向的节点可能会被修改，\n    //             所以需要一个额外的指针来保存下一节点的位置，\n    //             从而防止指针丢失\n    dictEntry *entry, *nextEntry;\n\n    long long fingerprint; /* unsafe iterator fingerprint for misuse detection */\n} dictIterator;\n\ntypedef void (dictScanFunction)(void *privdata, const dictEntry *de);\n\n/* This is the initial size of every hash table */\n/*\n * 哈希表的初始大小\n */\n#define DICT_HT_INITIAL_SIZE     4\n\n/* ------------------------------- Macros ------------------------------------*/\n// 释放给定字典节点的值\n#define dictFreeVal(d, entry) \\\n    if ((d)->type->valDestructor) \\\n        (d)->type->valDestructor((d)->privdata, (entry)->v.val)\n\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// 将一个有符号整数设为节点的值\n#define dictSetSignedIntegerVal(entry, _val_) \\\n    do { entry->v.s64 = _val_; } while(0)\n\n// 将一个无符号整数设为节点的值\n#define dictSetUnsignedIntegerVal(entry, _val_) \\\n    do { entry->v.u64 = _val_; } while(0)\n\n// 释放给定字典节点的键\n#define dictFreeKey(d, entry) \\\n    if ((d)->type->keyDestructor) \\\n        (d)->type->keyDestructor((d)->privdata, (entry)->key)\n\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// 比对两个键\n#define dictCompareKeys(d, key1, key2) \\\n    (((d)->type->keyCompare) ? \\\n        (d)->type->keyCompare((d)->privdata, key1, key2) : \\\n        (key1) == (key2))\n\n// 计算给定键的哈希值\n#define dictHashKey(d, key) (d)->type->hashFunction(key)\n// 返回获取给定节点的键\n#define dictGetKey(he) ((he)->key)\n// 返回获取给定节点的值\n#define dictGetVal(he) ((he)->v.val)\n// 返回获取给定节点的有符号整数值\n#define dictGetSignedIntegerVal(he) ((he)->v.s64)\n// 返回给定节点的无符号整数值\n#define dictGetUnsignedIntegerVal(he) ((he)->v.u64)\n// 返回给定字典的大小\n#define dictSlots(d) ((d)->ht[0].size+(d)->ht[1].size)\n// 返回字典的已有节点数量\n#define dictSize(d) ((d)->ht[0].used+(d)->ht[1].used)\n// 查看字典是否正在 rehash\n#define dictIsRehashing(ht) ((ht)->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);\nint dictGetRandomKeys(dict *d, dictEntry **des, int count);\nvoid dictPrintStats(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 /* __DICT_H */\n"
  },
  {
    "path": "src/endianconv.c",
    "content": "/* endinconv.c -- Endian conversions utilities.\n *\n * This functions are never called directly, but always using the macros\n * defined into endianconv.h, this way we define everything is a non-operation\n * if the arch is already little endian.\n *\n * Redis tries to encode everything as little endian (but a few things that need\n * to be backward compatible are still in big endian) because most of the\n * production environments are little endian, and we have a lot of conversions\n * in a few places because ziplists, intsets, zipmaps, need to be endian-neutral\n * even in memory, since they are serialied on RDB files directly with a single\n * write(2) without other additional steps.\n *\n * ----------------------------------------------------------------------------\n *\n * Copyright (c) 2011-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 <stdint.h>\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#ifdef TESTMAIN\n#include <stdio.h>\n\nint main(void) {\n    char buf[32];\n\n    sprintf(buf,\"ciaoroma\");\n    memrev16(buf);\n    printf(\"%s\\n\", buf);\n\n    sprintf(buf,\"ciaoroma\");\n    memrev32(buf);\n    printf(\"%s\\n\", buf);\n\n    sprintf(buf,\"ciaoroma\");\n    memrev64(buf);\n    printf(\"%s\\n\", buf);\n\n    return 0;\n}\n#endif\n"
  },
  {
    "path": "src/endianconv.h",
    "content": "/* See endianconv.c top comments for more information\n *\n * ----------------------------------------------------------------------------\n *\n * Copyright (c) 2011-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 __ENDIANCONV_H\n#define __ENDIANCONV_H\n\n#include \"config.h\"\n#include <stdint.h>\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#if (BYTE_ORDER == 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\n/* The functions htonu64() and ntohu64() convert the specified value to\n * network byte ordering and back. In big endian systems they are no-ops. */\n#if (BYTE_ORDER == BIG_ENDIAN)\n#define htonu64(v) (v)\n#define ntohu64(v) (v)\n#else\n#define htonu64(v) intrev64(v)\n#define ntohu64(v) intrev64(v)\n#endif\n\n#endif\n"
  },
  {
    "path": "src/fmacros.h",
    "content": "/*\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#ifndef _REDIS_FMACRO_H\n#define _REDIS_FMACRO_H\n\n#define _BSD_SOURCE\n\n#if defined(__linux__)\n#define _GNU_SOURCE\n#endif\n\n#if defined(__linux__) || defined(__OpenBSD__)\n#define _XOPEN_SOURCE 700\n/*\n * On NetBSD, _XOPEN_SOURCE undefines _NETBSD_SOURCE and\n * thus hides inet_aton etc.\n */\n#elif !defined(__NetBSD__)\n#define _XOPEN_SOURCE\n#endif\n\n#define _LARGEFILE_SOURCE\n#define _FILE_OFFSET_BITS 64\n\n#endif\n"
  },
  {
    "path": "src/help.h",
    "content": "/* Automatically generated by utils/generate-command-help.rb, do not edit. */\n\n#ifndef __REDIS_HELP_H\n#define __REDIS_HELP_H\n\nstatic char *commandGroups[] = {\n    \"generic\",\n    \"string\",\n    \"list\",\n    \"set\",\n    \"sorted_set\",\n    \"hash\",\n    \"pubsub\",\n    \"transactions\",\n    \"connection\",\n    \"server\",\n    \"scripting\",\n    \"hyperloglog\"\n};\n\nstruct commandHelp {\n  char *name;\n  char *params;\n  char *summary;\n  int group;\n  char *since;\n} commandHelp[] = {\n    { \"APPEND\",\n    \"key value\",\n    \"Append a value to a key\",\n    1,\n    \"2.0.0\" },\n    { \"AUTH\",\n    \"password\",\n    \"Authenticate to the server\",\n    8,\n    \"1.0.0\" },\n    { \"BGREWRITEAOF\",\n    \"-\",\n    \"Asynchronously rewrite the append-only file\",\n    9,\n    \"1.0.0\" },\n    { \"BGSAVE\",\n    \"-\",\n    \"Asynchronously save the dataset to disk\",\n    9,\n    \"1.0.0\" },\n    { \"BITCOUNT\",\n    \"key [start] [end]\",\n    \"Count set bits in a string\",\n    1,\n    \"2.6.0\" },\n    { \"BITOP\",\n    \"operation destkey key [key ...]\",\n    \"Perform bitwise operations between strings\",\n    1,\n    \"2.6.0\" },\n    { \"BITPOS\",\n    \"key bit [start] [end]\",\n    \"Find first bit set or clear in a string\",\n    1,\n    \"2.8.7\" },\n    { \"BLPOP\",\n    \"key [key ...] timeout\",\n    \"Remove and get the first element in a list, or block until one is available\",\n    2,\n    \"2.0.0\" },\n    { \"BRPOP\",\n    \"key [key ...] timeout\",\n    \"Remove and get the last element in a list, or block until one is available\",\n    2,\n    \"2.0.0\" },\n    { \"BRPOPLPUSH\",\n    \"source destination timeout\",\n    \"Pop a value from a list, push it to another list and return it; or block until one is available\",\n    2,\n    \"2.2.0\" },\n    { \"CLIENT GETNAME\",\n    \"-\",\n    \"Get the current connection name\",\n    9,\n    \"2.6.9\" },\n    { \"CLIENT KILL\",\n    \"ip:port\",\n    \"Kill the connection of a client\",\n    9,\n    \"2.4.0\" },\n    { \"CLIENT LIST\",\n    \"-\",\n    \"Get the list of client connections\",\n    9,\n    \"2.4.0\" },\n    { \"CLIENT PAUSE\",\n    \"timeout\",\n    \"Stop processing commands from clients for some time\",\n    9,\n    \"2.9.50\" },\n    { \"CLIENT SETNAME\",\n    \"connection-name\",\n    \"Set the current connection name\",\n    9,\n    \"2.6.9\" },\n    { \"CONFIG GET\",\n    \"parameter\",\n    \"Get the value of a configuration parameter\",\n    9,\n    \"2.0.0\" },\n    { \"CONFIG RESETSTAT\",\n    \"-\",\n    \"Reset the stats returned by INFO\",\n    9,\n    \"2.0.0\" },\n    { \"CONFIG REWRITE\",\n    \"-\",\n    \"Rewrite the configuration file with the in memory configuration\",\n    9,\n    \"2.8.0\" },\n    { \"CONFIG SET\",\n    \"parameter value\",\n    \"Set a configuration parameter to the given value\",\n    9,\n    \"2.0.0\" },\n    { \"DBSIZE\",\n    \"-\",\n    \"Return the number of keys in the selected database\",\n    9,\n    \"1.0.0\" },\n    { \"DEBUG OBJECT\",\n    \"key\",\n    \"Get debugging information about a key\",\n    9,\n    \"1.0.0\" },\n    { \"DEBUG SEGFAULT\",\n    \"-\",\n    \"Make the server crash\",\n    9,\n    \"1.0.0\" },\n    { \"DECR\",\n    \"key\",\n    \"Decrement the integer value of a key by one\",\n    1,\n    \"1.0.0\" },\n    { \"DECRBY\",\n    \"key decrement\",\n    \"Decrement the integer value of a key by the given number\",\n    1,\n    \"1.0.0\" },\n    { \"DEL\",\n    \"key [key ...]\",\n    \"Delete a key\",\n    0,\n    \"1.0.0\" },\n    { \"DISCARD\",\n    \"-\",\n    \"Discard all commands issued after MULTI\",\n    7,\n    \"2.0.0\" },\n    { \"DUMP\",\n    \"key\",\n    \"Return a serialized version of the value stored at the specified key.\",\n    0,\n    \"2.6.0\" },\n    { \"ECHO\",\n    \"message\",\n    \"Echo the given string\",\n    8,\n    \"1.0.0\" },\n    { \"EVAL\",\n    \"script numkeys key [key ...] arg [arg ...]\",\n    \"Execute a Lua script server side\",\n    10,\n    \"2.6.0\" },\n    { \"EVALSHA\",\n    \"sha1 numkeys key [key ...] arg [arg ...]\",\n    \"Execute a Lua script server side\",\n    10,\n    \"2.6.0\" },\n    { \"EXEC\",\n    \"-\",\n    \"Execute all commands issued after MULTI\",\n    7,\n    \"1.2.0\" },\n    { \"EXISTS\",\n    \"key\",\n    \"Determine if a key exists\",\n    0,\n    \"1.0.0\" },\n    { \"EXPIRE\",\n    \"key seconds\",\n    \"Set a key's time to live in seconds\",\n    0,\n    \"1.0.0\" },\n    { \"EXPIREAT\",\n    \"key timestamp\",\n    \"Set the expiration for a key as a UNIX timestamp\",\n    0,\n    \"1.2.0\" },\n    { \"FLUSHALL\",\n    \"-\",\n    \"Remove all keys from all databases\",\n    9,\n    \"1.0.0\" },\n    { \"FLUSHDB\",\n    \"-\",\n    \"Remove all keys from the current database\",\n    9,\n    \"1.0.0\" },\n    { \"GET\",\n    \"key\",\n    \"Get the value of a key\",\n    1,\n    \"1.0.0\" },\n    { \"GETBIT\",\n    \"key offset\",\n    \"Returns the bit value at offset in the string value stored at key\",\n    1,\n    \"2.2.0\" },\n    { \"GETRANGE\",\n    \"key start end\",\n    \"Get a substring of the string stored at a key\",\n    1,\n    \"2.4.0\" },\n    { \"GETSET\",\n    \"key value\",\n    \"Set the string value of a key and return its old value\",\n    1,\n    \"1.0.0\" },\n    { \"HDEL\",\n    \"key field [field ...]\",\n    \"Delete one or more hash fields\",\n    5,\n    \"2.0.0\" },\n    { \"HEXISTS\",\n    \"key field\",\n    \"Determine if a hash field exists\",\n    5,\n    \"2.0.0\" },\n    { \"HGET\",\n    \"key field\",\n    \"Get the value of a hash field\",\n    5,\n    \"2.0.0\" },\n    { \"HGETALL\",\n    \"key\",\n    \"Get all the fields and values in a hash\",\n    5,\n    \"2.0.0\" },\n    { \"HINCRBY\",\n    \"key field increment\",\n    \"Increment the integer value of a hash field by the given number\",\n    5,\n    \"2.0.0\" },\n    { \"HINCRBYFLOAT\",\n    \"key field increment\",\n    \"Increment the float value of a hash field by the given amount\",\n    5,\n    \"2.6.0\" },\n    { \"HKEYS\",\n    \"key\",\n    \"Get all the fields in a hash\",\n    5,\n    \"2.0.0\" },\n    { \"HLEN\",\n    \"key\",\n    \"Get the number of fields in a hash\",\n    5,\n    \"2.0.0\" },\n    { \"HMGET\",\n    \"key field [field ...]\",\n    \"Get the values of all the given hash fields\",\n    5,\n    \"2.0.0\" },\n    { \"HMSET\",\n    \"key field value [field value ...]\",\n    \"Set multiple hash fields to multiple values\",\n    5,\n    \"2.0.0\" },\n    { \"HSCAN\",\n    \"key cursor [MATCH pattern] [COUNT count]\",\n    \"Incrementally iterate hash fields and associated values\",\n    5,\n    \"2.8.0\" },\n    { \"HSET\",\n    \"key field value\",\n    \"Set the string value of a hash field\",\n    5,\n    \"2.0.0\" },\n    { \"HSETNX\",\n    \"key field value\",\n    \"Set the value of a hash field, only if the field does not exist\",\n    5,\n    \"2.0.0\" },\n    { \"HVALS\",\n    \"key\",\n    \"Get all the values in a hash\",\n    5,\n    \"2.0.0\" },\n    { \"INCR\",\n    \"key\",\n    \"Increment the integer value of a key by one\",\n    1,\n    \"1.0.0\" },\n    { \"INCRBY\",\n    \"key increment\",\n    \"Increment the integer value of a key by the given amount\",\n    1,\n    \"1.0.0\" },\n    { \"INCRBYFLOAT\",\n    \"key increment\",\n    \"Increment the float value of a key by the given amount\",\n    1,\n    \"2.6.0\" },\n    { \"INFO\",\n    \"[section]\",\n    \"Get information and statistics about the server\",\n    9,\n    \"1.0.0\" },\n    { \"KEYS\",\n    \"pattern\",\n    \"Find all keys matching the given pattern\",\n    0,\n    \"1.0.0\" },\n    { \"LASTSAVE\",\n    \"-\",\n    \"Get the UNIX time stamp of the last successful save to disk\",\n    9,\n    \"1.0.0\" },\n    { \"LINDEX\",\n    \"key index\",\n    \"Get an element from a list by its index\",\n    2,\n    \"1.0.0\" },\n    { \"LINSERT\",\n    \"key BEFORE|AFTER pivot value\",\n    \"Insert an element before or after another element in a list\",\n    2,\n    \"2.2.0\" },\n    { \"LLEN\",\n    \"key\",\n    \"Get the length of a list\",\n    2,\n    \"1.0.0\" },\n    { \"LPOP\",\n    \"key\",\n    \"Remove and get the first element in a list\",\n    2,\n    \"1.0.0\" },\n    { \"LPUSH\",\n    \"key value [value ...]\",\n    \"Prepend one or multiple values to a list\",\n    2,\n    \"1.0.0\" },\n    { \"LPUSHX\",\n    \"key value\",\n    \"Prepend a value to a list, only if the list exists\",\n    2,\n    \"2.2.0\" },\n    { \"LRANGE\",\n    \"key start stop\",\n    \"Get a range of elements from a list\",\n    2,\n    \"1.0.0\" },\n    { \"LREM\",\n    \"key count value\",\n    \"Remove elements from a list\",\n    2,\n    \"1.0.0\" },\n    { \"LSET\",\n    \"key index value\",\n    \"Set the value of an element in a list by its index\",\n    2,\n    \"1.0.0\" },\n    { \"LTRIM\",\n    \"key start stop\",\n    \"Trim a list to the specified range\",\n    2,\n    \"1.0.0\" },\n    { \"MGET\",\n    \"key [key ...]\",\n    \"Get the values of all the given keys\",\n    1,\n    \"1.0.0\" },\n    { \"MIGRATE\",\n    \"host port key destination-db timeout [COPY] [REPLACE]\",\n    \"Atomically transfer a key from a Redis instance to another one.\",\n    0,\n    \"2.6.0\" },\n    { \"MONITOR\",\n    \"-\",\n    \"Listen for all requests received by the server in real time\",\n    9,\n    \"1.0.0\" },\n    { \"MOVE\",\n    \"key db\",\n    \"Move a key to another database\",\n    0,\n    \"1.0.0\" },\n    { \"MSET\",\n    \"key value [key value ...]\",\n    \"Set multiple keys to multiple values\",\n    1,\n    \"1.0.1\" },\n    { \"MSETNX\",\n    \"key value [key value ...]\",\n    \"Set multiple keys to multiple values, only if none of the keys exist\",\n    1,\n    \"1.0.1\" },\n    { \"MULTI\",\n    \"-\",\n    \"Mark the start of a transaction block\",\n    7,\n    \"1.2.0\" },\n    { \"OBJECT\",\n    \"subcommand [arguments [arguments ...]]\",\n    \"Inspect the internals of Redis objects\",\n    0,\n    \"2.2.3\" },\n    { \"PERSIST\",\n    \"key\",\n    \"Remove the expiration from a key\",\n    0,\n    \"2.2.0\" },\n    { \"PEXPIRE\",\n    \"key milliseconds\",\n    \"Set a key's time to live in milliseconds\",\n    0,\n    \"2.6.0\" },\n    { \"PEXPIREAT\",\n    \"key milliseconds-timestamp\",\n    \"Set the expiration for a key as a UNIX timestamp specified in milliseconds\",\n    0,\n    \"2.6.0\" },\n    { \"PFADD\",\n    \"key element [element ...]\",\n    \"Adds the specified elements to the specified HyperLogLog.\",\n    11,\n    \"2.8.9\" },\n    { \"PFCOUNT\",\n    \"key [key ...]\",\n    \"Return the approximated cardinality of the set(s) observed by the HyperLogLog at key(s).\",\n    11,\n    \"2.8.9\" },\n    { \"PFMERGE\",\n    \"destkey sourcekey [sourcekey ...]\",\n    \"Merge N different HyperLogLogs into a single one.\",\n    11,\n    \"2.8.9\" },\n    { \"PING\",\n    \"-\",\n    \"Ping the server\",\n    8,\n    \"1.0.0\" },\n    { \"PSETEX\",\n    \"key milliseconds value\",\n    \"Set the value and expiration in milliseconds of a key\",\n    1,\n    \"2.6.0\" },\n    { \"PSUBSCRIBE\",\n    \"pattern [pattern ...]\",\n    \"Listen for messages published to channels matching the given patterns\",\n    6,\n    \"2.0.0\" },\n    { \"PTTL\",\n    \"key\",\n    \"Get the time to live for a key in milliseconds\",\n    0,\n    \"2.6.0\" },\n    { \"PUBLISH\",\n    \"channel message\",\n    \"Post a message to a channel\",\n    6,\n    \"2.0.0\" },\n    { \"PUBSUB\",\n    \"subcommand [argument [argument ...]]\",\n    \"Inspect the state of the Pub/Sub subsystem\",\n    6,\n    \"2.8.0\" },\n    { \"PUNSUBSCRIBE\",\n    \"[pattern [pattern ...]]\",\n    \"Stop listening for messages posted to channels matching the given patterns\",\n    6,\n    \"2.0.0\" },\n    { \"QUIT\",\n    \"-\",\n    \"Close the connection\",\n    8,\n    \"1.0.0\" },\n    { \"RANDOMKEY\",\n    \"-\",\n    \"Return a random key from the keyspace\",\n    0,\n    \"1.0.0\" },\n    { \"RENAME\",\n    \"key newkey\",\n    \"Rename a key\",\n    0,\n    \"1.0.0\" },\n    { \"RENAMENX\",\n    \"key newkey\",\n    \"Rename a key, only if the new key does not exist\",\n    0,\n    \"1.0.0\" },\n    { \"RESTORE\",\n    \"key ttl serialized-value\",\n    \"Create a key using the provided serialized value, previously obtained using DUMP.\",\n    0,\n    \"2.6.0\" },\n    { \"RPOP\",\n    \"key\",\n    \"Remove and get the last element in a list\",\n    2,\n    \"1.0.0\" },\n    { \"RPOPLPUSH\",\n    \"source destination\",\n    \"Remove the last element in a list, append it to another list and return it\",\n    2,\n    \"1.2.0\" },\n    { \"RPUSH\",\n    \"key value [value ...]\",\n    \"Append one or multiple values to a list\",\n    2,\n    \"1.0.0\" },\n    { \"RPUSHX\",\n    \"key value\",\n    \"Append a value to a list, only if the list exists\",\n    2,\n    \"2.2.0\" },\n    { \"SADD\",\n    \"key member [member ...]\",\n    \"Add one or more members to a set\",\n    3,\n    \"1.0.0\" },\n    { \"SAVE\",\n    \"-\",\n    \"Synchronously save the dataset to disk\",\n    9,\n    \"1.0.0\" },\n    { \"SCAN\",\n    \"cursor [MATCH pattern] [COUNT count]\",\n    \"Incrementally iterate the keys space\",\n    0,\n    \"2.8.0\" },\n    { \"SCARD\",\n    \"key\",\n    \"Get the number of members in a set\",\n    3,\n    \"1.0.0\" },\n    { \"SCRIPT EXISTS\",\n    \"script [script ...]\",\n    \"Check existence of scripts in the script cache.\",\n    10,\n    \"2.6.0\" },\n    { \"SCRIPT FLUSH\",\n    \"-\",\n    \"Remove all the scripts from the script cache.\",\n    10,\n    \"2.6.0\" },\n    { \"SCRIPT KILL\",\n    \"-\",\n    \"Kill the script currently in execution.\",\n    10,\n    \"2.6.0\" },\n    { \"SCRIPT LOAD\",\n    \"script\",\n    \"Load the specified Lua script into the script cache.\",\n    10,\n    \"2.6.0\" },\n    { \"SDIFF\",\n    \"key [key ...]\",\n    \"Subtract multiple sets\",\n    3,\n    \"1.0.0\" },\n    { \"SDIFFSTORE\",\n    \"destination key [key ...]\",\n    \"Subtract multiple sets and store the resulting set in a key\",\n    3,\n    \"1.0.0\" },\n    { \"SELECT\",\n    \"index\",\n    \"Change the selected database for the current connection\",\n    8,\n    \"1.0.0\" },\n    { \"SET\",\n    \"key value [EX seconds] [PX milliseconds] [NX|XX]\",\n    \"Set the string value of a key\",\n    1,\n    \"1.0.0\" },\n    { \"SETBIT\",\n    \"key offset value\",\n    \"Sets or clears the bit at offset in the string value stored at key\",\n    1,\n    \"2.2.0\" },\n    { \"SETEX\",\n    \"key seconds value\",\n    \"Set the value and expiration of a key\",\n    1,\n    \"2.0.0\" },\n    { \"SETNX\",\n    \"key value\",\n    \"Set the value of a key, only if the key does not exist\",\n    1,\n    \"1.0.0\" },\n    { \"SETRANGE\",\n    \"key offset value\",\n    \"Overwrite part of a string at key starting at the specified offset\",\n    1,\n    \"2.2.0\" },\n    { \"SHUTDOWN\",\n    \"[NOSAVE] [SAVE]\",\n    \"Synchronously save the dataset to disk and then shut down the server\",\n    9,\n    \"1.0.0\" },\n    { \"SINTER\",\n    \"key [key ...]\",\n    \"Intersect multiple sets\",\n    3,\n    \"1.0.0\" },\n    { \"SINTERSTORE\",\n    \"destination key [key ...]\",\n    \"Intersect multiple sets and store the resulting set in a key\",\n    3,\n    \"1.0.0\" },\n    { \"SISMEMBER\",\n    \"key member\",\n    \"Determine if a given value is a member of a set\",\n    3,\n    \"1.0.0\" },\n    { \"SLAVEOF\",\n    \"host port\",\n    \"Make the server a slave of another instance, or promote it as master\",\n    9,\n    \"1.0.0\" },\n    { \"SLOWLOG\",\n    \"subcommand [argument]\",\n    \"Manages the Redis slow queries log\",\n    9,\n    \"2.2.12\" },\n    { \"SMEMBERS\",\n    \"key\",\n    \"Get all the members in a set\",\n    3,\n    \"1.0.0\" },\n    { \"SMOVE\",\n    \"source destination member\",\n    \"Move a member from one set to another\",\n    3,\n    \"1.0.0\" },\n    { \"SORT\",\n    \"key [BY pattern] [LIMIT offset count] [GET pattern [GET pattern ...]] [ASC|DESC] [ALPHA] [STORE destination]\",\n    \"Sort the elements in a list, set or sorted set\",\n    0,\n    \"1.0.0\" },\n    { \"SPOP\",\n    \"key\",\n    \"Remove and return a random member from a set\",\n    3,\n    \"1.0.0\" },\n    { \"SRANDMEMBER\",\n    \"key [count]\",\n    \"Get one or multiple random members from a set\",\n    3,\n    \"1.0.0\" },\n    { \"SREM\",\n    \"key member [member ...]\",\n    \"Remove one or more members from a set\",\n    3,\n    \"1.0.0\" },\n    { \"SSCAN\",\n    \"key cursor [MATCH pattern] [COUNT count]\",\n    \"Incrementally iterate Set elements\",\n    3,\n    \"2.8.0\" },\n    { \"STRLEN\",\n    \"key\",\n    \"Get the length of the value stored in a key\",\n    1,\n    \"2.2.0\" },\n    { \"SUBSCRIBE\",\n    \"channel [channel ...]\",\n    \"Listen for messages published to the given channels\",\n    6,\n    \"2.0.0\" },\n    { \"SUNION\",\n    \"key [key ...]\",\n    \"Add multiple sets\",\n    3,\n    \"1.0.0\" },\n    { \"SUNIONSTORE\",\n    \"destination key [key ...]\",\n    \"Add multiple sets and store the resulting set in a key\",\n    3,\n    \"1.0.0\" },\n    { \"SYNC\",\n    \"-\",\n    \"Internal command used for replication\",\n    9,\n    \"1.0.0\" },\n    { \"TIME\",\n    \"-\",\n    \"Return the current server time\",\n    9,\n    \"2.6.0\" },\n    { \"TTL\",\n    \"key\",\n    \"Get the time to live for a key\",\n    0,\n    \"1.0.0\" },\n    { \"TYPE\",\n    \"key\",\n    \"Determine the type stored at key\",\n    0,\n    \"1.0.0\" },\n    { \"UNSUBSCRIBE\",\n    \"[channel [channel ...]]\",\n    \"Stop listening for messages posted to the given channels\",\n    6,\n    \"2.0.0\" },\n    { \"UNWATCH\",\n    \"-\",\n    \"Forget about all watched keys\",\n    7,\n    \"2.2.0\" },\n    { \"WATCH\",\n    \"key [key ...]\",\n    \"Watch the given keys to determine execution of the MULTI/EXEC block\",\n    7,\n    \"2.2.0\" },\n    { \"ZADD\",\n    \"key score member [score member ...]\",\n    \"Add one or more members to a sorted set, or update its score if it already exists\",\n    4,\n    \"1.2.0\" },\n    { \"ZCARD\",\n    \"key\",\n    \"Get the number of members in a sorted set\",\n    4,\n    \"1.2.0\" },\n    { \"ZCOUNT\",\n    \"key min max\",\n    \"Count the members in a sorted set with scores within the given values\",\n    4,\n    \"2.0.0\" },\n    { \"ZINCRBY\",\n    \"key increment member\",\n    \"Increment the score of a member in a sorted set\",\n    4,\n    \"1.2.0\" },\n    { \"ZINTERSTORE\",\n    \"destination numkeys key [key ...] [WEIGHTS weight] [AGGREGATE SUM|MIN|MAX]\",\n    \"Intersect multiple sorted sets and store the resulting sorted set in a new key\",\n    4,\n    \"2.0.0\" },\n    { \"ZLEXCOUNT\",\n    \"key min max\",\n    \"Count the number of members in a sorted set between a given lexicographical range\",\n    4,\n    \"2.8.9\" },\n    { \"ZRANGE\",\n    \"key start stop [WITHSCORES]\",\n    \"Return a range of members in a sorted set, by index\",\n    4,\n    \"1.2.0\" },\n    { \"ZRANGEBYLEX\",\n    \"key min max [LIMIT offset count]\",\n    \"Return a range of members in a sorted set, by lexicographical range\",\n    4,\n    \"2.8.9\" },\n    { \"ZRANGEBYSCORE\",\n    \"key min max [WITHSCORES] [LIMIT offset count]\",\n    \"Return a range of members in a sorted set, by score\",\n    4,\n    \"1.0.5\" },\n    { \"ZRANK\",\n    \"key member\",\n    \"Determine the index of a member in a sorted set\",\n    4,\n    \"2.0.0\" },\n    { \"ZREM\",\n    \"key member [member ...]\",\n    \"Remove one or more members from a sorted set\",\n    4,\n    \"1.2.0\" },\n    { \"ZREMRANGEBYLEX\",\n    \"key min max\",\n    \"Remove all members in a sorted set between the given lexicographical range\",\n    4,\n    \"2.8.9\" },\n    { \"ZREMRANGEBYRANK\",\n    \"key start stop\",\n    \"Remove all members in a sorted set within the given indexes\",\n    4,\n    \"2.0.0\" },\n    { \"ZREMRANGEBYSCORE\",\n    \"key min max\",\n    \"Remove all members in a sorted set within the given scores\",\n    4,\n    \"1.2.0\" },\n    { \"ZREVRANGE\",\n    \"key start stop [WITHSCORES]\",\n    \"Return a range of members in a sorted set, by index, with scores ordered from high to low\",\n    4,\n    \"1.2.0\" },\n    { \"ZREVRANGEBYSCORE\",\n    \"key max min [WITHSCORES] [LIMIT offset count]\",\n    \"Return a range of members in a sorted set, by score, with scores ordered from high to low\",\n    4,\n    \"2.2.0\" },\n    { \"ZREVRANK\",\n    \"key member\",\n    \"Determine the index of a member in a sorted set, with scores ordered from high to low\",\n    4,\n    \"2.0.0\" },\n    { \"ZSCAN\",\n    \"key cursor [MATCH pattern] [COUNT count]\",\n    \"Incrementally iterate sorted sets elements and associated scores\",\n    4,\n    \"2.8.0\" },\n    { \"ZSCORE\",\n    \"key member\",\n    \"Get the score associated with the given member in a sorted set\",\n    4,\n    \"1.2.0\" },\n    { \"ZUNIONSTORE\",\n    \"destination numkeys key [key ...] [WEIGHTS weight] [AGGREGATE SUM|MIN|MAX]\",\n    \"Add multiple sorted sets and store the resulting sorted set in a new key\",\n    4,\n    \"2.0.0\" }\n};\n\n#endif\n"
  },
  {
    "path": "src/hyperloglog.c",
    "content": "/* hyperloglog.c - Redis HyperLogLog probabilistic cardinality approximation.\n * This file implements the algorithm and the exported Redis commands.\n *\n * Copyright (c) 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 \"redis.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[0] |= (1<<7)\n#define HLL_VALID_CACHE(hdr) (((hdr)->card[0] & (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 REDIS_OK if the sparse representation was valid,\n * otherwise REDIS_ERR 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 REDIS_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 REDIS_ERR;\n    }\n\n    /* Free the old representation and set the new one. */\n    sdsfree(o->ptr);\n    o->ptr = dense;\n    return REDIS_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) == REDIS_ERR) 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    redisAssert(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 armonic\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). */\nuint64_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        redisPanic(\"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, REDIS_ERR\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 REDIS_ERR;\n    }\n    return REDIS_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    redisAssert((p-(uint8_t*)s) == sparselen);\n\n    /* Create the actual object. */\n    o = createObject(REDIS_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 REDIS_OK if this is true, otherwise reply to the client\n * with an error and return REDIS_ERR. */\nint isHLLObjectOrReply(redisClient *c, robj *o) {\n    struct hllhdr *hdr;\n\n    /* Key exists, check type */\n    if (checkType(c,o,REDIS_STRING))\n        return REDIS_ERR; /* 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 REDIS_OK;\n\ninvalid:\n    addReplySds(c,\n        sdsnew(\"-WRONGTYPE Key is not a valid \"\n               \"HyperLogLog string value.\\r\\n\"));\n    return REDIS_ERR;\n}\n\n/* PFADD var ele ele ele ... ele => :0 or :1 */\nvoid pfaddCommand(redisClient *c) {\n    robj *o = lookupKeyWrite(c->db,c->argv[1]);\n    struct hllhdr *hdr;\n    int updated = 0, j;\n\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) != REDIS_OK) return;\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            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(REDIS_NOTIFY_STRING,\"pfadd\",c->argv[1],c->db->id);\n        server.dirty++;\n        HLL_INVALIDATE_CACHE(hdr);\n    }\n    addReply(c, updated ? shared.cone : shared.czero);\n}\n\n/* PFCOUNT var -> approximated cardinality of set. */\nvoid pfcountCommand(redisClient *c) {\n    robj *o;\n    struct hllhdr *hdr;\n    uint64_t card;\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            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) != REDIS_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(registers,o) == REDIS_ERR) {\n                addReplySds(c,sdsnew(invalid_hll_err));\n                return;\n            }\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    o = lookupKeyRead(c->db,c->argv[1]);\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) != REDIS_OK) return;\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                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        }\n        addReplyLongLong(c,card);\n    }\n}\n\n/* PFMERGE dest src1 src2 src3 ... srcN => OK */\nvoid pfmergeCommand(redisClient *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) != REDIS_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) == REDIS_ERR) {\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]);\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) == REDIS_ERR) {\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(REDIS_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(redisClient *c) {\n    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 4 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            if (abserr < 0) abserr = -abserr;\n            if (abserr > (uint64_t)(relerr*4*checkpoint)) {\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(redisClient *c) {\n    char *cmd = c->argv[1]->ptr;\n    struct hllhdr *hdr;\n    robj *o;\n    int j;\n\n    o = lookupKeyRead(c->db,c->argv[2]);\n    if (o == NULL) {\n        addReplyError(c,\"The specified key does not exist\");\n        return;\n    }\n    if (isHLLObjectOrReply(c,o) != REDIS_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) == REDIS_ERR) {\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) == REDIS_ERR) {\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/intset.c",
    "content": "/*\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 \"intset.h\"\n#include \"zmalloc.h\"\n#include \"endianconv.h\"\n\n/* Note that these encodings are ordered, so:\n * INTSET_ENC_INT16 < INTSET_ENC_INT32 < INTSET_ENC_INT64. */\n/*\n * intset 的编码方式\n */\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. \n *\n * 返回适用于传入值 v 的编码方式\n *\n * T = O(1)\n */\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. \n *\n * 根据给定的编码方式 enc ，返回集合的底层数组在 pos 索引上的元素。\n *\n * T = O(1)\n */\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    // ((ENCODING*)is->contents) 首先将数组转换回被编码的类型\n    // 然后 ((ENCODING*)is->contents)+pos 计算出元素在数组中的正确位置\n    // 之后 member(&vEnc, ..., sizeof(vEnc)) 再从数组中拷贝出正确数量的字节\n    // 如果有需要的话， memrevEncifbe(&vEnc) 会对拷贝出的字节进行大小端转换\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. \n *\n * 根据集合的编码方式，返回底层数组在 pos 索引上的值\n *\n * T = O(1)\n */\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. \n *\n * 根据集合的编码方式，将底层数组在 pos 位置上的值设为 value 。\n *\n * T = O(1)\n */\nstatic void _intsetSet(intset *is, int pos, int64_t value) {\n\n    // 取出集合的编码方式\n    uint32_t encoding = intrev32ifbe(is->encoding);\n\n    // 根据编码 ((Enc_t*)is->contents) 将数组转换回正确的类型\n    // 然后 ((Enc_t*)is->contents)[pos] 定位到数组索引上\n    // 接着 ((Enc_t*)is->contents)[pos] = value 将值赋给数组\n    // 最后， ((Enc_t*)is->contents)+pos 定位到刚刚设置的新值上 \n    // 如果有需要的话， memrevEncifbe 将对值进行大小端转换\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. \n *\n * 创建并返回一个新的空整数集合\n *\n * T = O(1)\n */\nintset *intsetNew(void) {\n\n    // 为整数集合结构分配空间\n    intset *is = zmalloc(sizeof(intset));\n\n    // 设置初始编码\n    is->encoding = intrev32ifbe(INTSET_ENC_INT16);\n\n    // 初始化元素数量\n    is->length = 0;\n\n    return is;\n}\n\n/* Resize the intset \n *\n * 调整整数集合的内存空间大小\n *\n * 如果调整后的大小要比集合原来的大小要大，\n * 那么集合中原有元素的值不会被改变。\n *\n * 返回值：调整大小后的整数集合\n *\n * T = O(N)\n */\nstatic intset *intsetResize(intset *is, uint32_t len) {\n\n    // 计算数组的空间大小\n    uint32_t size = len*intrev32ifbe(is->encoding);\n\n    // 根据空间大小，重新分配空间\n    // 注意这里使用的是 zrealloc ，\n    // 所以如果新空间大小比原来的空间大小要大，\n    // 那么数组原有的数据会被保留\n    is = zrealloc(is,sizeof(intset)+size);\n\n    return is;\n}\n\n/* Search for the position of \"value\".\n * \n * 在集合 is 的底层数组中查找值 value 所在的索引。\n *\n * Return 1 when the value was found and \n * sets \"pos\" to the position of the value within the intset. \n *\n * 成功找到 value 时，函数返回 1 ，并将 *pos 的值设为 value 所在的索引。\n *\n * Return 0 when the value is not present in the intset \n * and sets \"pos\" to the position where \"value\" can be inserted. \n *\n * 当在数组中没找到 value 时，返回 0 。\n * 并将 *pos 的值设为 value 可以插入到数组中的位置。\n *\n * T = O(log N)\n */\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    // 处理 is 为空时的情况\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        // 因为底层数组是有序的，如果 value 比数组中最后一个值都要大\n        // 那么 value 肯定不存在于集合中，\n        // 并且应该将 value 添加到底层数组的最末端\n        if (value > _intsetGet(is,intrev32ifbe(is->length)-1)) {\n            if (pos) *pos = intrev32ifbe(is->length);\n            return 0;\n        // 因为底层数组是有序的，如果 value 比数组中最前一个值都要小\n        // 那么 value 肯定不存在于集合中，\n        // 并且应该将它添加到底层数组的最前端\n        } else if (value < _intsetGet(is,0)) {\n            if (pos) *pos = 0;\n            return 0;\n        }\n    }\n\n    // 在有序数组中进行二分查找\n    // T = O(log N)\n    while(max >= min) {\n        mid = (min+max)/2;\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    // 检查是否已经找到了 value\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. \n *\n * 根据值 value 所使用的编码方式，对整数集合的编码进行升级，\n * 并将值 value 添加到升级后的整数集合中。\n *\n * 返回值：添加新元素之后的整数集合\n *\n * T = O(N)\n */\nstatic intset *intsetUpgradeAndAdd(intset *is, int64_t value) {\n    \n    // 当前的编码方式\n    uint8_t curenc = intrev32ifbe(is->encoding);\n\n    // 新值所需的编码方式\n    uint8_t newenc = _intsetValueEncoding(value);\n\n    // 当前集合的元素数量\n    int length = intrev32ifbe(is->length);\n\n    // 根据 value 的值，决定是将它添加到底层数组的最前端还是最后端\n    // 注意，因为 value 的编码比集合原有的其他元素的编码都要大\n    // 所以 value 要么大于集合中的所有元素，要么小于集合中的所有元素\n    // 因此，value 只能添加到底层数组的最前端或最后端\n    int prepend = value < 0 ? 1 : 0;\n\n    /* First set new encoding and resize */\n    // 更新集合的编码方式\n    is->encoding = intrev32ifbe(newenc);\n    // 根据新编码对集合（的底层数组）进行空间调整\n    // T = O(N)\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    // 根据集合原来的编码方式，从底层数组中取出集合元素\n    // 然后再将元素以新编码的方式添加到集合中\n    // 当完成了这个步骤之后，集合中所有原有的元素就完成了从旧编码到新编码的转换\n    // 因为新分配的空间都放在数组的后端，所以程序先从后端向前端移动元素\n    // 举个例子，假设原来有 curenc 编码的三个元素，它们在数组中排列如下：\n    // | x | y | z | \n    // 当程序对数组进行重分配之后，数组就被扩容了（符号 ？ 表示未使用的内存）：\n    // | x | y | z | ? |   ?   |   ?   |\n    // 这时程序从数组后端开始，重新插入元素：\n    // | x | y | z | ? |   z   |   ?   |\n    // | x | y |   y   |   z   |   ?   |\n    // |   x   |   y   |   z   |   ?   |\n    // 最后，程序可以将新元素添加到最后 ？ 号标示的位置中：\n    // |   x   |   y   |   z   |  new  |\n    // 上面演示的是新元素比原来的所有元素都大的情况，也即是 prepend == 0\n    // 当新元素比原来的所有元素都小时（prepend == 1），调整的过程如下：\n    // | x | y | z | ? |   ?   |   ?   |\n    // | x | y | z | ? |   ?   |   z   |\n    // | x | y | z | ? |   y   |   z   |\n    // | x | y |   x   |   y   |   z   |\n    // 当添加新值时，原本的 | x | y | 的数据将被新值代替\n    // |  new  |   x   |   y   |   z   |\n    // T = O(N)\n    while(length--)\n        _intsetSet(is,length+prepend,_intsetGetEncoded(is,length,curenc));\n\n    /* Set the value at the beginning or the end. */\n    // 设置新值，根据 prepend 的值来决定是添加到数组头还是数组尾\n    if (prepend)\n        _intsetSet(is,0,value);\n    else\n        _intsetSet(is,intrev32ifbe(is->length),value);\n\n    // 更新整数集合的元素数量\n    is->length = intrev32ifbe(intrev32ifbe(is->length)+1);\n\n    return is;\n}\n\n/*\n * 向前或先后移动指定索引范围内的数组元素\n *\n * 函数名中的 MoveTail 其实是一个有误导性的名字，\n * 这个函数可以向前或向后移动元素，\n * 而不仅仅是向后\n *\n * 在添加新元素到数组时，就需要进行向后移动，\n * 如果数组表示如下（？表示一个未设置新值的空间）：\n * | x | y | z | ? |\n *     |<----->|\n * 而新元素 n 的 pos 为 1 ，那么数组将移动 y 和 z 两个元素\n * | x | y | y | z |\n *         |<----->|\n * 接着就可以将新元素 n 设置到 pos 上了：\n * | x | n | y | z |\n *\n * 当从数组中删除元素时，就需要进行向前移动，\n * 如果数组表示如下，并且 b 为要删除的目标：\n * | a | b | c | d |\n *         |<----->|\n * 那么程序就会移动 b 后的所有元素向前一个元素的位置，\n * 从而覆盖 b 的数据：\n * | a | c | d | d |\n *     |<----->|\n * 最后，程序再从数组末尾删除一个元素的空间：\n * | a | c | d |\n * 这样就完成了删除操作。\n *\n * T = O(N)\n */\nstatic void intsetMoveTail(intset *is, uint32_t from, uint32_t to) {\n\n    void *src, *dst;\n\n    // 要移动的元素个数\n    uint32_t bytes = intrev32ifbe(is->length)-from;\n\n    // 集合的编码方式\n    uint32_t encoding = intrev32ifbe(is->encoding);\n\n    // 根据不同的编码\n    // src = (Enc_t*)is->contents+from 记录移动开始的位置\n    // dst = (Enc_t*)is_.contents+to 记录移动结束的位置\n    // bytes *= sizeof(Enc_t) 计算一共要移动多少字节\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\n    // 进行移动\n    // T = O(N)\n    memmove(dst,src,bytes);\n}\n\n/* Insert an integer in the intset \n * \n * 尝试将元素 value 添加到整数集合中。\n *\n * *success 的值指示添加是否成功：\n * - 如果添加成功，那么将 *success 的值设为 1 。\n * - 因为元素已存在而造成添加失败时，将 *success 的值设为 0 。\n *\n * T = O(N)\n */\nintset *intsetAdd(intset *is, int64_t value, uint8_t *success) {\n\n    // 计算编码 value 所需的长度\n    uint8_t valenc = _intsetValueEncoding(value);\n    uint32_t pos;\n\n    // 默认设置插入为成功\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    // 如果 value 的编码比整数集合现在的编码要大\n    // 那么表示 value 必然可以添加到整数集合中\n    // 并且整数集合需要对自身进行升级，才能满足 value 所需的编码\n    if (valenc > intrev32ifbe(is->encoding)) {\n        /* This always succeeds, so we don't need to curry *success. */\n        // T = O(N)\n        return intsetUpgradeAndAdd(is,value);\n    } else {\n        // 运行到这里，表示整数集合现有的编码方式适用于 value\n\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        // 在整数集合中查找 value ，看他是否存在：\n        // - 如果存在，那么将 *success 设置为 0 ，并返回未经改动的整数集合\n        // - 如果不存在，那么可以插入 value 的位置将被保存到 pos 指针中\n        //   等待后续程序使用\n        if (intsetSearch(is,value,&pos)) {\n            if (success) *success = 0;\n            return is;\n        }\n\n        // 运行到这里，表示 value 不存在于集合中\n        // 程序需要将 value 添加到整数集合中\n    \n        // 为 value 在集合中分配空间\n        is = intsetResize(is,intrev32ifbe(is->length)+1);\n        // 如果新元素不是被添加到底层数组的末尾\n        // 那么需要对现有元素的数据进行移动，空出 pos 上的位置，用于设置新值\n        // 举个例子\n        // 如果数组为：\n        // | x | y | z | ? |\n        //     |<----->|\n        // 而新元素 n 的 pos 为 1 ，那么数组将移动 y 和 z 两个元素\n        // | x | y | y | z |\n        //         |<----->|\n        // 这样就可以将新元素设置到 pos 上了：\n        // | x | n | y | z |\n        // T = O(N)\n        if (pos < intrev32ifbe(is->length)) intsetMoveTail(is,pos,pos+1);\n    }\n\n    // 将新值设置到底层数组的指定位置中\n    _intsetSet(is,pos,value);\n\n    // 增一集合元素数量的计数器\n    is->length = intrev32ifbe(intrev32ifbe(is->length)+1);\n\n    // 返回添加新元素后的整数集合\n    return is;\n\n    /* p.s. 上面的代码可以重构成以下更简单的形式：\n    \n    if (valenc > intrev32ifbe(is->encoding)) {\n        return intsetUpgradeAndAdd(is,value);\n    }\n     \n    if (intsetSearch(is,value,&pos)) {\n        if (success) *success = 0;\n        return is;\n    } else {\n        is = intsetResize(is,intrev32ifbe(is->length)+1);\n        if (pos < intrev32ifbe(is->length)) intsetMoveTail(is,pos,pos+1);\n        _intsetSet(is,pos,value);\n\n        is->length = intrev32ifbe(intrev32ifbe(is->length)+1);\n        return is;\n    }\n    */\n}\n\n/* Delete integer from intset \n *\n * 从整数集合中删除值 value 。\n *\n * *success 的值指示删除是否成功：\n * - 因值不存在而造成删除失败时该值为 0 。\n * - 删除成功时该值为 1 。\n *\n * T = O(N)\n */\nintset *intsetRemove(intset *is, int64_t value, int *success) {\n\n    // 计算 value 的编码方式\n    uint8_t valenc = _intsetValueEncoding(value);\n    uint32_t pos;\n\n    // 默认设置标识值为删除失败\n    if (success) *success = 0;\n\n    // 当 value 的编码大小小于或等于集合的当前编码方式（说明 value 有可能存在于集合）\n    // 并且 intsetSearch 的结果为真，那么执行删除\n    // T = O(log N)\n    if (valenc <= intrev32ifbe(is->encoding) && intsetSearch(is,value,&pos)) {\n\n        // 取出集合当前的元素数量\n        uint32_t len = intrev32ifbe(is->length);\n\n        /* We know we can delete */\n        // 设置标识值为删除成功\n        if (success) *success = 1;\n\n        /* Overwrite value with tail and update length */\n        // 如果 value 不是位于数组的末尾\n        // 那么需要对原本位于 value 之后的元素进行移动\n        //\n        // 举个例子，如果数组表示如下，而 b 为删除的目标\n        // | a | b | c | d |\n        // 那么 intsetMoveTail 将 b 之后的所有数据向前移动一个元素的空间，\n        // 覆盖 b 原来的数据\n        // | a | c | d | d |\n        // 之后 intsetResize 缩小内存大小时，\n        // 数组末尾多出来的一个元素的空间将被移除\n        // | a | c | d |\n        if (pos < (len-1)) intsetMoveTail(is,pos+1,pos);\n        // 缩小数组的大小，移除被删除元素占用的空间\n        // T = O(N)\n        is = intsetResize(is,len-1);\n        // 更新集合的元素数量\n        is->length = intrev32ifbe(len-1);\n    }\n\n    return is;\n}\n\n/* Determine whether a value belongs to this set \n *\n * 检查给定值 value 是否集合中的元素。\n *\n * 是返回 1 ，不是返回 0 。\n *\n * T = O(log N)\n */\nuint8_t intsetFind(intset *is, int64_t value) {\n\n    // 计算 value 的编码\n    uint8_t valenc = _intsetValueEncoding(value);\n\n    // 如果 value 的编码大于集合的当前编码，那么 value 一定不存在于集合\n    // 当 value 的编码小于等于集合的当前编码时，\n    // 才再使用 intsetSearch 进行查找\n    return valenc <= intrev32ifbe(is->encoding) && intsetSearch(is,value,NULL);\n}\n\n/* Return random member \n *\n * 从整数集合中随机返回一个元素\n *\n * 只能在集合非空时使用\n *\n * T = O(1)\n */\nint64_t intsetRandom(intset *is) {\n    // intrev32ifbe(is->length) 取出集合的元素数量\n    // 而 rand() % intrev32ifbe(is->length) 根据元素数量计算一个随机索引\n    // 然后 _intsetGet 负责根据随机索引来查找值\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. */\n/* \n * 取出集合底层数组指定位置中的值，并将它保存到 value 指针中。\n *\n * 如果 pos 没超出数组的索引范围，那么返回 1 ，如果超出索引，那么返回 0 。\n *\n * p.s. 上面原文的文档说这个函数用于设置值，这是错误的。\n *\n * T = O(1)\n */\nuint8_t intsetGet(intset *is, uint32_t pos, int64_t *value) {\n\n    // pos < intrev32ifbe(is->length) \n    // 检查 pos 是否符合数组的范围\n    if (pos < intrev32ifbe(is->length)) {\n\n        // 保存值到指针\n        *value = _intsetGet(is,pos);\n\n        // 返回成功指示值\n        return 1;\n    }\n\n    // 超出索引范围\n    return 0;\n}\n\n/* Return intset length \n *\n * 返回整数集合现有的元素个数\n *\n * T = O(1)\n */\nuint32_t intsetLen(intset *is) {\n    return intrev32ifbe(is->length);\n}\n\n/* Return intset blob size in bytes. \n *\n * 返回整数集合现在占用的字节总数量\n * 这个数量包括整数集合的结构大小，以及整数集合所有元素的总大小\n *\n * T = O(1)\n */\nsize_t intsetBlobLen(intset *is) {\n    return sizeof(intset)+intrev32ifbe(is->length)*intrev32ifbe(is->encoding);\n}\n\n#ifdef INTSET_TEST_MAIN\n#include <sys/time.h>\n\nvoid intsetRepr(intset *is) {\n    int i;\n    for (i = 0; i < intrev32ifbe(is->length); i++) {\n        printf(\"%lld\\n\", (uint64_t)_intsetGet(is,i));\n    }\n    printf(\"\\n\");\n}\n\nvoid error(char *err) {\n    printf(\"%s\\n\", err);\n    exit(1);\n}\n\nvoid ok(void) {\n    printf(\"OK\\n\");\n}\n\nlong long usec(void) {\n    struct timeval tv;\n    gettimeofday(&tv,NULL);\n    return (((long long)tv.tv_sec)*1000000)+tv.tv_usec;\n}\n\n#define assert(_e) ((_e)?(void)0:(_assert(#_e,__FILE__,__LINE__),exit(1)))\nvoid _assert(char *estr, char *file, int line) {\n    printf(\"\\n\\n=== ASSERTION FAILED ===\\n\");\n    printf(\"==> %s:%d '%s' is not true\\n\",file,line,estr);\n}\n\nintset *createSet(int bits, int size) {\n    uint64_t mask = (1<<bits)-1;\n    uint64_t i, value;\n    intset *is = intsetNew();\n\n    for (i = 0; i < size; i++) {\n        if (bits > 32) {\n            value = (rand()*rand()) & mask;\n        } else {\n            value = rand() & mask;\n        }\n        is = intsetAdd(is,value,NULL);\n    }\n    return is;\n}\n\nvoid checkConsistency(intset *is) {\n    int i;\n\n    for (i = 0; i < (intrev32ifbe(is->length)-1); i++) {\n        uint32_t encoding = intrev32ifbe(is->encoding);\n\n        if (encoding == INTSET_ENC_INT16) {\n            int16_t *i16 = (int16_t*)is->contents;\n            assert(i16[i] < i16[i+1]);\n        } else if (encoding == INTSET_ENC_INT32) {\n            int32_t *i32 = (int32_t*)is->contents;\n            assert(i32[i] < i32[i+1]);\n        } else {\n            int64_t *i64 = (int64_t*)is->contents;\n            assert(i64[i] < i64[i+1]);\n        }\n    }\n}\n\nint main(int argc, char **argv) {\n    uint8_t success;\n    int i;\n    intset *is;\n    sranddev();\n\n    printf(\"Value encodings: \"); {\n        assert(_intsetValueEncoding(-32768) == INTSET_ENC_INT16);\n        assert(_intsetValueEncoding(+32767) == INTSET_ENC_INT16);\n        assert(_intsetValueEncoding(-32769) == INTSET_ENC_INT32);\n        assert(_intsetValueEncoding(+32768) == INTSET_ENC_INT32);\n        assert(_intsetValueEncoding(-2147483648) == INTSET_ENC_INT32);\n        assert(_intsetValueEncoding(+2147483647) == INTSET_ENC_INT32);\n        assert(_intsetValueEncoding(-2147483649) == INTSET_ENC_INT64);\n        assert(_intsetValueEncoding(+2147483648) == INTSET_ENC_INT64);\n        assert(_intsetValueEncoding(-9223372036854775808ull) == INTSET_ENC_INT64);\n        assert(_intsetValueEncoding(+9223372036854775807ull) == INTSET_ENC_INT64);\n        ok();\n    }\n\n    printf(\"Basic adding: \"); {\n        is = intsetNew();\n        is = intsetAdd(is,5,&success); assert(success);\n        is = intsetAdd(is,6,&success); assert(success);\n        is = intsetAdd(is,4,&success); assert(success);\n        is = intsetAdd(is,4,&success); assert(!success);\n        ok();\n    }\n\n    printf(\"Large number of random adds: \"); {\n        int inserts = 0;\n        is = intsetNew();\n        for (i = 0; i < 1024; i++) {\n            is = intsetAdd(is,rand()%0x800,&success);\n            if (success) inserts++;\n        }\n        assert(intrev32ifbe(is->length) == inserts);\n        checkConsistency(is);\n        ok();\n    }\n\n    printf(\"Upgrade from int16 to int32: \"); {\n        is = intsetNew();\n        is = intsetAdd(is,32,NULL);\n        assert(intrev32ifbe(is->encoding) == INTSET_ENC_INT16);\n        is = intsetAdd(is,65535,NULL);\n        assert(intrev32ifbe(is->encoding) == INTSET_ENC_INT32);\n        assert(intsetFind(is,32));\n        assert(intsetFind(is,65535));\n        checkConsistency(is);\n\n        is = intsetNew();\n        is = intsetAdd(is,32,NULL);\n        assert(intrev32ifbe(is->encoding) == INTSET_ENC_INT16);\n        is = intsetAdd(is,-65535,NULL);\n        assert(intrev32ifbe(is->encoding) == INTSET_ENC_INT32);\n        assert(intsetFind(is,32));\n        assert(intsetFind(is,-65535));\n        checkConsistency(is);\n        ok();\n    }\n\n    printf(\"Upgrade from int16 to int64: \"); {\n        is = intsetNew();\n        is = intsetAdd(is,32,NULL);\n        assert(intrev32ifbe(is->encoding) == INTSET_ENC_INT16);\n        is = intsetAdd(is,4294967295,NULL);\n        assert(intrev32ifbe(is->encoding) == INTSET_ENC_INT64);\n        assert(intsetFind(is,32));\n        assert(intsetFind(is,4294967295));\n        checkConsistency(is);\n\n        is = intsetNew();\n        is = intsetAdd(is,32,NULL);\n        assert(intrev32ifbe(is->encoding) == INTSET_ENC_INT16);\n        is = intsetAdd(is,-4294967295,NULL);\n        assert(intrev32ifbe(is->encoding) == INTSET_ENC_INT64);\n        assert(intsetFind(is,32));\n        assert(intsetFind(is,-4294967295));\n        checkConsistency(is);\n        ok();\n    }\n\n    printf(\"Upgrade from int32 to int64: \"); {\n        is = intsetNew();\n        is = intsetAdd(is,65535,NULL);\n        assert(intrev32ifbe(is->encoding) == INTSET_ENC_INT32);\n        is = intsetAdd(is,4294967295,NULL);\n        assert(intrev32ifbe(is->encoding) == INTSET_ENC_INT64);\n        assert(intsetFind(is,65535));\n        assert(intsetFind(is,4294967295));\n        checkConsistency(is);\n\n        is = intsetNew();\n        is = intsetAdd(is,65535,NULL);\n        assert(intrev32ifbe(is->encoding) == INTSET_ENC_INT32);\n        is = intsetAdd(is,-4294967295,NULL);\n        assert(intrev32ifbe(is->encoding) == INTSET_ENC_INT64);\n        assert(intsetFind(is,65535));\n        assert(intsetFind(is,-4294967295));\n        checkConsistency(is);\n        ok();\n    }\n\n    printf(\"Stress lookups: \"); {\n        long num = 100000, size = 10000;\n        int i, bits = 20;\n        long long start;\n        is = createSet(bits,size);\n        checkConsistency(is);\n\n        start = usec();\n        for (i = 0; i < num; i++) intsetSearch(is,rand() % ((1<<bits)-1),NULL);\n        printf(\"%ld lookups, %ld element set, %lldusec\\n\",num,size,usec()-start);\n    }\n\n    printf(\"Stress add+delete: \"); {\n        int i, v1, v2;\n        is = intsetNew();\n        for (i = 0; i < 0xffff; i++) {\n            v1 = rand() % 0xfff;\n            is = intsetAdd(is,v1,NULL);\n            assert(intsetFind(is,v1));\n\n            v2 = rand() % 0xfff;\n            is = intsetRemove(is,v2,NULL);\n            assert(!intsetFind(is,v2));\n        }\n        checkConsistency(is);\n        ok();\n    }\n}\n#endif\n"
  },
  {
    "path": "src/intset.h",
    "content": "/*\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#ifndef __INTSET_H\n#define __INTSET_H\n#include <stdint.h>\n\ntypedef struct intset {\n    \n    // 编码方式\n    uint32_t encoding;\n\n    // 集合包含的元素数量\n    uint32_t length;\n\n    // 保存元素的数组\n    int8_t contents[];\n\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 // __INTSET_H\n"
  },
  {
    "path": "src/lzf.h",
    "content": "/*\n * Copyright (c) 2000-2008 Marc Alexander Lehmann <schmorp@schmorp.de>\n * \n * Redistribution and use in source and binary forms, with or without modifica-\n * tion, are permitted provided that the following conditions are met:\n * \n *   1.  Redistributions of source code must retain the above copyright notice,\n *       this list of conditions and the following disclaimer.\n * \n *   2.  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 * \n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED\n * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER-\n * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO\n * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE-\n * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;\n * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\n * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH-\n * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED\n * OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n * Alternatively, the contents of this file may be used under the terms of\n * the GNU General Public License (\"GPL\") version 2 or any later version,\n * in which case the provisions of the GPL are applicable instead of\n * the above. If you wish to allow the use of your version of this file\n * only under the terms of the GPL and not to allow others to use your\n * version of this file under the BSD license, indicate your decision\n * by deleting the provisions above and replace them with the notice\n * and other provisions required by the GPL. If you do not delete the\n * provisions above, a recipient may use your version of this file under\n * either the BSD or the GPL.\n */\n\n#ifndef LZF_H\n#define 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/lzfP.h",
    "content": "/*\n * Copyright (c) 2000-2007 Marc Alexander Lehmann <schmorp@schmorp.de>\n * \n * Redistribution and use in source and binary forms, with or without modifica-\n * tion, are permitted provided that the following conditions are met:\n * \n *   1.  Redistributions of source code must retain the above copyright notice,\n *       this list of conditions and the following disclaimer.\n * \n *   2.  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 * \n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED\n * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER-\n * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO\n * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE-\n * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;\n * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\n * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH-\n * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED\n * OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n * Alternatively, the contents of this file may be used under the terms of\n * the GNU General Public License (\"GPL\") version 2 or any later version,\n * in which case the provisions of the GPL are applicable instead of\n * the above. If you wish to allow the use of your version of this file\n * only under the terms of the GPL and not to allow others to use your\n * version of this file under the BSD license, indicate your decision\n * by deleting the provisions above and replace them with the notice\n * and other provisions required by the GPL. If you do not delete the\n * provisions above, a recipient may use your version of this file under\n * either the BSD or the GPL.\n */\n\n#ifndef LZFP_h\n#define LZFP_h\n\n#define STANDALONE 1 /* at the moment, this is ok. */\n\n#ifndef STANDALONE\n# include \"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 23).\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.\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/* nothing should be changed below */\n\ntypedef unsigned char u8;\n\ntypedef const u8 *LZF_STATE[1 << (HLOG)];\n\n#if !STRICT_ALIGN\n/* for unaligned accesses we need a 16 bit datatype. */\n# include <limits.h>\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# if defined(VERY_FAST)\n#  undef VERY_FAST\n# endif\n#endif\n\n#if INIT_HTAB\n# ifdef __cplusplus\n#  include <cstring>\n# else\n#  include <string.h>\n# endif\n#endif\n\n#endif\n\n"
  },
  {
    "path": "src/lzf_c.c",
    "content": "/*\n * Copyright (c) 2000-2008 Marc Alexander Lehmann <schmorp@schmorp.de>\n * \n * Redistribution and use in source and binary forms, with or without modifica-\n * tion, are permitted provided that the following conditions are met:\n * \n *   1.  Redistributions of source code must retain the above copyright notice,\n *       this list of conditions and the following disclaimer.\n * \n *   2.  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 * \n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED\n * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER-\n * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO\n * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE-\n * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;\n * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\n * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH-\n * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED\n * OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n * Alternatively, the contents of this file may be used under the terms of\n * the GNU General Public License (\"GPL\") version 2 or any later version,\n * in which case the provisions of the GPL are applicable instead of\n * the above. If you wish to allow the use of your version of this file\n * only under the terms of the GPL and not to allow others to use your\n * version of this file under the BSD license, indicate your decision\n * by deleting the provisions above and replace them with the notice\n * and other provisions required by the GPL. If you do not delete the\n * provisions above, a recipient may use your version of this file under\n * either the BSD or the GPL.\n */\n\n#include \"lzfP.h\"\n\n#define HSIZE (1 << (HLOG))\n\n/*\n * don't play with this unless you benchmark!\n * decompression is not dependent on the hash function\n * the hashing 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\n * LLLooooo oooooooo ; backref L\n * 111ooooo LLLLLLLL oooooooo ; backref L+7\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 **hslot;\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# if 0\n  for (hslot = htab; hslot < htab + HSIZE; hslot++)\n    *hslot++ = ip;\n# endif\n#endif\n\n  lit = 0; op++; /* start run */\n\n  hval = FRST (ip);\n  while (ip < in_end - 2)\n    {\n      hval = NEXT (hval, ip);\n      hslot = htab + IDX (hval);\n      ref = *hslot; *hslot = ip;\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          && ip + 4 < in_end\n          && ref > (u8 *)in_data\n#if STRICT_ALIGN\n          && ref[0] == ip[0]\n          && ref[1] == ip[1]\n          && ref[2] == ip[2]\n#else\n          && *(u16 *)ref == *(u16 *)ip\n          && ref[2] == ip[2]\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          op [- lit - 1] = lit - 1; /* stop run */\n          op -= !lit; /* undo run if length is zero */\n\n          if (expect_false (op + 3 + 1 >= out_end))\n            return 0;\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          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;\n          ip++;\n\n# if VERY_FAST && !ULTRA_FAST\n          hval = NEXT (hval, ip);\n          htab[IDX (hval)] = ip;\n          ip++;\n# endif\n#else\n          ip -= len + 1;\n\n          do\n            {\n              hval = NEXT (hval, ip);\n              htab[IDX (hval)] = ip;\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/lzf_d.c",
    "content": "/*\n * Copyright (c) 2000-2007 Marc Alexander Lehmann <schmorp@schmorp.de>\n * \n * Redistribution and use in source and binary forms, with or without modifica-\n * tion, are permitted provided that the following conditions are met:\n * \n *   1.  Redistributions of source code must retain the above copyright notice,\n *       this list of conditions and the following disclaimer.\n * \n *   2.  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 * \n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED\n * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER-\n * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO\n * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE-\n * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;\n * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\n * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH-\n * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED\n * OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n * Alternatively, the contents of this file may be used under the terms of\n * the GNU General Public License (\"GPL\") version 2 or any later version,\n * in which case the provisions of the GPL are applicable instead of\n * the above. If you wish to allow the use of your version of this file\n * only under the terms of the GPL and not to allow others to use your\n * version of this file under the BSD license, indicate your decision\n * by deleting the provisions above and replace them with the notice\n * and other provisions required by the GPL. If you do not delete the\n * provisions above, a recipient may use your version of this file under\n * either the BSD or the GPL.\n */\n\n#include \"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/*\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*/\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          do\n            *op++ = *ip++;\n          while (--ctrl);\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          *op++ = *ref++;\n          *op++ = *ref++;\n\n          do\n            *op++ = *ref++;\n          while (--len);\n#endif\n        }\n    }\n  while (ip < in_end);\n\n  return op - (u8 *)out_data;\n}\n\n"
  },
  {
    "path": "src/memtest.c",
    "content": "/*\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 <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n#include <assert.h>\n#include <limits.h>\n#include <errno.h>\n#include <termios.h>\n#include <sys/ioctl.h>\n#include \"config.h\"\n\n#if (ULONG_MAX == 4294967295UL)\n#define MEMTEST_32BIT\n#elif (ULONG_MAX == 18446744073709551615ULL)\n#define MEMTEST_64BIT\n#else\n#error \"ULONG_MAX value not supported.\"\n#endif\n\n#ifdef MEMTEST_32BIT\n#define ULONG_ONEZERO 0xaaaaaaaaUL\n#define ULONG_ZEROONE 0x55555555UL\n#else\n#define ULONG_ONEZERO 0xaaaaaaaaaaaaaaaaUL\n#define ULONG_ZEROONE 0x5555555555555555UL\n#endif\n\nstatic struct winsize ws;\nsize_t progress_printed; /* Printed chars in screen-wide progress bar. */\nsize_t progress_full; /* How many chars to write to fill the progress bar. */\n\nvoid memtest_progress_start(char *title, int pass) {\n    int j;\n\n    printf(\"\\x1b[H\\x1b[2J\");    /* Cursor home, clear screen. */\n    /* Fill with dots. */\n    for (j = 0; j < ws.ws_col*(ws.ws_row-2); j++) printf(\".\");\n    printf(\"Please keep the test running several minutes per GB of memory.\\n\");\n    printf(\"Also check http://www.memtest86.com/ and http://pyropus.ca/software/memtester/\");\n    printf(\"\\x1b[H\\x1b[2K\");          /* Cursor home, clear current line.  */\n    printf(\"%s [%d]\\n\", title, pass); /* Print title. */\n    progress_printed = 0;\n    progress_full = ws.ws_col*(ws.ws_row-3);\n    fflush(stdout);\n}\n\nvoid memtest_progress_end(void) {\n    printf(\"\\x1b[H\\x1b[2J\");    /* Cursor home, clear screen. */\n}\n\nvoid memtest_progress_step(size_t curr, size_t size, char c) {\n    size_t chars = ((unsigned long long)curr*progress_full)/size, j;\n\n    for (j = 0; j < chars-progress_printed; j++) printf(\"%c\",c);\n    progress_printed = chars;\n    fflush(stdout);\n}\n\n/* Test that addressing is fine. Every location is populated with its own\n * address, and finally verified. This test is very fast but may detect\n * ASAP big issues with the memory subsystem. */\nvoid memtest_addressing(unsigned long *l, size_t bytes) {\n    unsigned long words = bytes/sizeof(unsigned long);\n    unsigned long j, *p;\n\n    /* Fill */\n    p = l;\n    for (j = 0; j < words; j++) {\n        *p = (unsigned long)p;\n        p++;\n        if ((j & 0xffff) == 0) memtest_progress_step(j,words*2,'A');\n    }\n    /* Test */\n    p = l;\n    for (j = 0; j < words; j++) {\n        if (*p != (unsigned long)p) {\n            printf(\"\\n*** MEMORY ADDRESSING ERROR: %p contains %lu\\n\",\n                (void*) p, *p);\n            exit(1);\n        }\n        p++;\n        if ((j & 0xffff) == 0) memtest_progress_step(j+words,words*2,'A');\n    }\n}\n\n/* Fill words stepping a single page at every write, so we continue to\n * touch all the pages in the smallest amount of time reducing the\n * effectiveness of caches, and making it hard for the OS to transfer\n * pages on the swap. */\nvoid memtest_fill_random(unsigned long *l, size_t bytes) {\n    unsigned long step = 4096/sizeof(unsigned long);\n    unsigned long words = bytes/sizeof(unsigned long)/2;\n    unsigned long iwords = words/step;  /* words per iteration */\n    unsigned long off, w, *l1, *l2;\n\n    assert((bytes & 4095) == 0);\n    for (off = 0; off < step; off++) {\n        l1 = l+off;\n        l2 = l1+words;\n        for (w = 0; w < iwords; w++) {\n#ifdef MEMTEST_32BIT\n            *l1 = *l2 = ((unsigned long)     (rand()&0xffff)) |\n                        (((unsigned long)    (rand()&0xffff)) << 16);\n#else\n            *l1 = *l2 = ((unsigned long)     (rand()&0xffff)) |\n                        (((unsigned long)    (rand()&0xffff)) << 16) |\n                        (((unsigned long)    (rand()&0xffff)) << 32) |\n                        (((unsigned long)    (rand()&0xffff)) << 48);\n#endif\n            l1 += step;\n            l2 += step;\n            if ((w & 0xffff) == 0)\n                memtest_progress_step(w+iwords*off,words,'R');\n        }\n    }\n}\n\n/* Like memtest_fill_random() but uses the two specified values to fill\n * memory, in an alternated way (v1|v2|v1|v2|...) */\nvoid memtest_fill_value(unsigned long *l, size_t bytes, unsigned long v1,\n                        unsigned long v2, char sym)\n{\n    unsigned long step = 4096/sizeof(unsigned long);\n    unsigned long words = bytes/sizeof(unsigned long)/2;\n    unsigned long iwords = words/step;  /* words per iteration */\n    unsigned long off, w, *l1, *l2, v;\n\n    assert((bytes & 4095) == 0);\n    for (off = 0; off < step; off++) {\n        l1 = l+off;\n        l2 = l1+words;\n        v = (off & 1) ? v2 : v1;\n        for (w = 0; w < iwords; w++) {\n#ifdef MEMTEST_32BIT\n            *l1 = *l2 = ((unsigned long)     v) |\n                        (((unsigned long)    v) << 16);\n#else\n            *l1 = *l2 = ((unsigned long)     v) |\n                        (((unsigned long)    v) << 16) |\n                        (((unsigned long)    v) << 32) |\n                        (((unsigned long)    v) << 48);\n#endif\n            l1 += step;\n            l2 += step;\n            if ((w & 0xffff) == 0)\n                memtest_progress_step(w+iwords*off,words,sym);\n        }\n    }\n}\n\nvoid memtest_compare(unsigned long *l, size_t bytes) {\n    unsigned long words = bytes/sizeof(unsigned long)/2;\n    unsigned long w, *l1, *l2;\n\n    assert((bytes & 4095) == 0);\n    l1 = l;\n    l2 = l1+words;\n    for (w = 0; w < words; w++) {\n        if (*l1 != *l2) {\n            printf(\"\\n*** MEMORY ERROR DETECTED: %p != %p (%lu vs %lu)\\n\",\n                (void*)l1, (void*)l2, *l1, *l2);\n            exit(1);\n        }\n        l1 ++;\n        l2 ++;\n        if ((w & 0xffff) == 0) memtest_progress_step(w,words,'=');\n    }\n}\n\nvoid memtest_compare_times(unsigned long *m, size_t bytes, int pass, int times) {\n    int j;\n\n    for (j = 0; j < times; j++) {\n        memtest_progress_start(\"Compare\",pass);\n        memtest_compare(m,bytes);\n        memtest_progress_end();\n    }\n}\n\nvoid memtest_test(size_t megabytes, int passes) {\n    size_t bytes = megabytes*1024*1024;\n    unsigned long *m = malloc(bytes);\n    int pass = 0;\n\n    if (m == NULL) {\n        fprintf(stderr,\"Unable to allocate %zu megabytes: %s\",\n            megabytes, strerror(errno));\n        exit(1);\n    }\n    while (pass != passes) {\n        pass++;\n\n        memtest_progress_start(\"Addressing test\",pass);\n        memtest_addressing(m,bytes);\n        memtest_progress_end();\n\n        memtest_progress_start(\"Random fill\",pass);\n        memtest_fill_random(m,bytes);\n        memtest_progress_end();\n        memtest_compare_times(m,bytes,pass,4);\n\n        memtest_progress_start(\"Solid fill\",pass);\n        memtest_fill_value(m,bytes,0,(unsigned long)-1,'S');\n        memtest_progress_end();\n        memtest_compare_times(m,bytes,pass,4);\n\n        memtest_progress_start(\"Checkerboard fill\",pass);\n        memtest_fill_value(m,bytes,ULONG_ONEZERO,ULONG_ZEROONE,'C');\n        memtest_progress_end();\n        memtest_compare_times(m,bytes,pass,4);\n    }\n}\n\nvoid memtest_non_destructive_invert(void *addr, size_t size) {\n    volatile unsigned long *p = addr;\n    size_t words = size / sizeof(unsigned long);\n    size_t j;\n\n    /* Invert */\n    for (j = 0; j < words; j++)\n        p[j] = ~p[j];\n}\n\nvoid memtest_non_destructive_swap(void *addr, size_t size) {\n    volatile unsigned long *p = addr;\n    size_t words = size / sizeof(unsigned long);\n    size_t j;\n\n    /* Swap */\n    for (j = 0; j < words; j += 2) {\n        unsigned long a, b;\n\n        a = p[j];\n        b = p[j+1];\n        p[j] = b;\n        p[j+1] = a;\n    }\n}\n\nvoid memtest(size_t megabytes, int passes) {\n    if (ioctl(1, TIOCGWINSZ, &ws) == -1) {\n        ws.ws_col = 80;\n        ws.ws_row = 20;\n    }\n    memtest_test(megabytes,passes);\n    printf(\"\\nYour memory passed this test.\\n\");\n    printf(\"Please if you are still in doubt use the following two tools:\\n\");\n    printf(\"1) memtest86: http://www.memtest86.com/\\n\");\n    printf(\"2) memtester: http://pyropus.ca/software/memtester/\\n\");\n    exit(0);\n}\n"
  },
  {
    "path": "src/mkreleasehdr.sh",
    "content": "#!/bin/sh\nGIT_SHA1=`(git show-ref --head --hash=8 2> /dev/null || echo 00000000) | head -n1`\nGIT_DIRTY=`git diff --no-ext-diff 2> /dev/null | wc -l`\nBUILD_ID=`uname -n`\"-\"`date +%s`\ntest -f release.h || touch release.h\n(cat release.h | grep SHA1 | grep $GIT_SHA1) && \\\n(cat release.h | grep DIRTY | grep $GIT_DIRTY) && exit 0 # Already up-to-date\necho \"#define REDIS_GIT_SHA1 \\\"$GIT_SHA1\\\"\" > release.h\necho \"#define REDIS_GIT_DIRTY \\\"$GIT_DIRTY\\\"\" >> release.h\necho \"#define REDIS_BUILD_ID \\\"$BUILD_ID\\\"\" >> release.h\ntouch release.c # Force recompile of release.c\n"
  },
  {
    "path": "src/multi.c",
    "content": "/*\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 \"redis.h\"\n\n/* ================================ MULTI/EXEC ============================== */\n\n/* Client state initialization for MULTI/EXEC \n *\n * 初始化客户端的事务状态\n */\nvoid initClientMultiState(redisClient *c) {\n\n    // 命令队列\n    c->mstate.commands = NULL;\n\n    // 命令计数\n    c->mstate.count = 0;\n}\n\n/* Release all the resources associated with MULTI/EXEC state \n *\n * 释放所有事务状态相关的资源\n */\nvoid freeClientMultiState(redisClient *c) {\n    int j;\n\n    // 遍历事务队列\n    for (j = 0; j < c->mstate.count; j++) {\n        int i;\n        multiCmd *mc = c->mstate.commands+j;\n\n        // 释放所有命令参数\n        for (i = 0; i < mc->argc; i++)\n            decrRefCount(mc->argv[i]);\n\n        // 释放参数数组本身\n        zfree(mc->argv);\n    }\n\n    // 释放事务队列\n    zfree(c->mstate.commands);\n}\n\n/* Add a new command into the MULTI commands queue \n *\n * 将一个新命令添加到事务队列中\n */\nvoid queueMultiCommand(redisClient *c) {\n    multiCmd *mc;\n    int j;\n\n    // 为新数组元素分配空间\n    c->mstate.commands = zrealloc(c->mstate.commands,\n            sizeof(multiCmd)*(c->mstate.count+1));\n\n    // 指向新元素\n    mc = c->mstate.commands+c->mstate.count;\n\n    // 设置事务的命令、命令参数数量，以及命令的参数\n    mc->cmd = c->cmd;\n    mc->argc = c->argc;\n    mc->argv = zmalloc(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\n    // 事务命令数量计数器增一\n    c->mstate.count++;\n}\n\nvoid discardTransaction(redisClient *c) {\n\n    // 重置事务状态\n    freeClientMultiState(c);\n    initClientMultiState(c);\n\n    // 屏蔽事务状态\n    c->flags &= ~(REDIS_MULTI|REDIS_DIRTY_CAS|REDIS_DIRTY_EXEC);;\n\n    // 取消对所有键的监视\n    unwatchAllKeys(c);\n}\n\n/* Flag the transacation as DIRTY_EXEC so that EXEC will fail.\n *\n * 将事务状态设为 DIRTY_EXEC ，让之后的 EXEC 命令失败。\n *\n * Should be called every time there is an error while queueing a command. \n *\n * 每次在入队命令出错时调用\n */\nvoid flagTransaction(redisClient *c) {\n    if (c->flags & REDIS_MULTI)\n        c->flags |= REDIS_DIRTY_EXEC;\n}\n\nvoid multiCommand(redisClient *c) {\n\n    // 不能在事务中嵌套事务\n    if (c->flags & REDIS_MULTI) {\n        addReplyError(c,\"MULTI calls can not be nested\");\n        return;\n    }\n\n    // 打开事务 FLAG\n    c->flags |= REDIS_MULTI;\n\n    addReply(c,shared.ok);\n}\n\nvoid discardCommand(redisClient *c) {\n\n    // 不能在客户端未进行事务状态之前使用\n    if (!(c->flags & REDIS_MULTI)) {\n        addReplyError(c,\"DISCARD without MULTI\");\n        return;\n    }\n\n    discardTransaction(c);\n\n    addReply(c,shared.ok);\n}\n\n/* Send a MULTI command to all the slaves and AOF file. Check the execCommand\n * implementation for more information. \n *\n * 向所有附属节点和 AOF 文件传播 MULTI 命令。\n */\nvoid execCommandPropagateMulti(redisClient *c) {\n    robj *multistring = createStringObject(\"MULTI\",5);\n\n    propagate(server.multiCommand,c->db->id,&multistring,1,\n              REDIS_PROPAGATE_AOF|REDIS_PROPAGATE_REPL);\n\n    decrRefCount(multistring);\n}\n\nvoid execCommand(redisClient *c) {\n    int j;\n    robj **orig_argv;\n    int orig_argc;\n    struct redisCommand *orig_cmd;\n    int must_propagate = 0; /* Need to propagate MULTI/EXEC to AOF / slaves? */\n\n    // 客户端没有执行事务\n    if (!(c->flags & REDIS_MULTI)) {\n        addReplyError(c,\"EXEC without MULTI\");\n        return;\n    }\n\n    /* Check if we need to abort the EXEC because:\n     *\n     * 检查是否需要阻止事务执行，因为：\n     *\n     * 1) Some WATCHed key was touched.\n     *    有被监视的键已经被修改了\n     *\n     * 2) There was a previous error while queueing commands.\n     *    命令在入队时发生错误\n     *    （注意这个行为是 2.6.4 以后才修改的，之前是静默处理入队出错命令）\n     *\n     * A failed EXEC in the first case returns a multi bulk nil object\n     * (technically it is not an error but a special behavior), while\n     * in the second an EXECABORT error is returned. \n     *\n     * 第一种情况返回多个批量回复的空对象\n     * 而第二种情况则返回一个 EXECABORT 错误\n     */\n    if (c->flags & (REDIS_DIRTY_CAS|REDIS_DIRTY_EXEC)) {\n\n        addReply(c, c->flags & REDIS_DIRTY_EXEC ? shared.execaborterr :\n                                                  shared.nullmultibulk);\n\n        // 取消事务\n        discardTransaction(c);\n\n        goto handle_monitor;\n    }\n\n    /* Exec all the queued commands */\n    // 已经可以保证安全性了，取消客户端对所有键的监视\n    unwatchAllKeys(c); /* Unwatch ASAP otherwise we'll waste CPU cycles */\n\n    // 因为事务中的命令在执行时可能会修改命令和命令的参数\n    // 所以为了正确地传播命令，需要现备份这些命令和参数\n    orig_argv = c->argv;\n    orig_argc = c->argc;\n    orig_cmd = c->cmd;\n\n    addReplyMultiBulkLen(c,c->mstate.count);\n\n    // 执行事务中的命令\n    for (j = 0; j < c->mstate.count; j++) {\n\n        // 因为 Redis 的命令必须在客户端的上下文中执行\n        // 所以要将事务队列中的命令、命令参数等设置给客户端\n        c->argc = c->mstate.commands[j].argc;\n        c->argv = c->mstate.commands[j].argv;\n        c->cmd = c->mstate.commands[j].cmd;\n\n        /* Propagate a MULTI request once we encounter the first write op.\n         *\n         * 当遇上第一个写命令时，传播 MULTI 命令。\n         *\n         * This way we'll deliver the MULTI/..../EXEC block as a whole and\n         * both the AOF and the replication link will have the same consistency\n         * and atomicity guarantees. \n         *\n         * 这可以确保服务器和 AOF 文件以及附属节点的数据一致性。\n         */\n        if (!must_propagate && !(c->cmd->flags & REDIS_CMD_READONLY)) {\n\n            // 传播 MULTI 命令\n            execCommandPropagateMulti(c);\n\n            // 计数器，只发送一次\n            must_propagate = 1;\n        }\n\n        // 执行命令\n        call(c,REDIS_CALL_FULL);\n\n        /* Commands may alter argc/argv, restore mstate. */\n        // 因为执行后命令、命令参数可能会被改变\n        // 比如 SPOP 会被改写为 SREM\n        // 所以这里需要更新事务队列中的命令和参数\n        // 确保附属节点和 AOF 的数据一致性\n        c->mstate.commands[j].argc = c->argc;\n        c->mstate.commands[j].argv = c->argv;\n        c->mstate.commands[j].cmd = c->cmd;\n    }\n\n    // 还原命令、命令参数\n    c->argv = orig_argv;\n    c->argc = orig_argc;\n    c->cmd = orig_cmd;\n\n    // 清理事务状态\n    discardTransaction(c);\n\n    /* Make sure the EXEC command will be propagated as well if MULTI\n     * was already propagated. */\n    // 将服务器设为脏，确保 EXEC 命令也会被传播\n    if (must_propagate) server.dirty++;\n\nhandle_monitor:\n    /* Send EXEC to clients waiting data from MONITOR. We do it here\n     * since the natural order of commands execution is actually:\n     * MUTLI, EXEC, ... commands inside transaction ...\n     * Instead EXEC is flagged as REDIS_CMD_SKIP_MONITOR in the command\n     * table, and we do it here with correct ordering. */\n    if (listLength(server.monitors) && !server.loading)\n        replicationFeedMonitors(c,server.monitors,c->db->id,c->argv,c->argc);\n}\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 * 这个实现为每个数据库都设置了一个将 key 映射为 list 的字典，\n * list 中保存的是所有监视这个 key 的客户端，\n * 这样就可以在这个 key 被修改的时候，\n * 方便地对所有监视这个 key 的客户端进行处理。\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 * 另外，每个客户端都会保存一个带有所有被监视键的列表，\n * 这样就可以方便地对所有被监视键进行 UNWATCH 。\n */\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 \n *\n * 在监视一个键时，\n * 我们既需要保存被监视的键，\n * 还需要保存该键所在的数据库。\n */\ntypedef struct watchedKey {\n\n    // 被监视的键\n    robj *key;\n\n    // 键所在的数据库\n    redisDb *db;\n\n} watchedKey;\n\n/* Watch for the specified key \n *\n * 让客户端 c 监视给定的键 key\n */\nvoid watchForKey(redisClient *c, robj *key) {\n\n    list *clients = NULL;\n    listIter li;\n    listNode *ln;\n    watchedKey *wk;\n\n    /* Check if we are already watching for this key */\n    // 检查 key 是否已经保存在 watched_keys 链表中，\n    // 如果是的话，直接返回\n    listRewind(c->watched_keys,&li);\n    while((ln = listNext(&li))) {\n        wk = listNodeValue(ln);\n        if (wk->db == c->db && equalStringObjects(key,wk->key))\n            return; /* Key already watched */\n    }\n\n    // 键不存在于 watched_keys ，添加它\n\n    // 以下是一个 key 不存在于字典的例子：\n    // before :\n    // {\n    //  'key-1' : [c1, c2, c3],\n    //  'key-2' : [c1, c2],\n    // }\n    // after c-10086 WATCH key-1 and key-3:\n    // {\n    //  'key-1' : [c1, c2, c3, c-10086],\n    //  'key-2' : [c1, c2],\n    //  'key-3' : [c-10086]\n    // }\n\n    /* This key is not already watched in this DB. Let's add it */\n    // 检查 key 是否存在于数据库的 watched_keys 字典中\n    clients = dictFetchValue(c->db->watched_keys,key);\n    // 如果不存在的话，添加它\n    if (!clients) { \n        // 值为链表\n        clients = listCreate();\n        // 关联键值对到字典\n        dictAdd(c->db->watched_keys,key,clients);\n        incrRefCount(key);\n    }\n    // 将客户端添加到链表的末尾\n    listAddNodeTail(clients,c);\n\n    /* Add the new key to the list of keys watched by this client */\n    // 将新 watchedKey 结构添加到客户端 watched_keys 链表的表尾\n    // 以下是一个添加 watchedKey 结构的例子\n    // before:\n    // [\n    //  {\n    //   'key': 'key-1',\n    //   'db' : 0\n    //  }\n    // ]\n    // after client watch key-123321 in db 0:\n    // [\n    //  {\n    //   'key': 'key-1',\n    //   'db' : 0\n    //  }\n    //  ,\n    //  {\n    //   'key': 'key-123321',\n    //   'db': 0\n    //  }\n    // ]\n    wk = zmalloc(sizeof(*wk));\n    wk->key = key;\n    wk->db = c->db;\n    incrRefCount(key);\n    listAddNodeTail(c->watched_keys,wk);\n}\n\n/* Unwatch all the keys watched by this client. To clean the EXEC dirty\n * flag is up to the caller. \n *\n * 取消客户端对所有键的监视。\n *\n * 清除客户端事务状态的任务由调用者执行。\n */\nvoid unwatchAllKeys(redisClient *c) {\n    listIter li;\n    listNode *ln;\n\n    // 没有键被监视，直接返回\n    if (listLength(c->watched_keys) == 0) return;\n\n    // 遍历链表中所有被客户端监视的键\n    listRewind(c->watched_keys,&li);\n    while((ln = listNext(&li))) {\n        list *clients;\n        watchedKey *wk;\n\n        /* Lookup the watched key -> clients list and remove the client\n         * from the list */\n        // 从数据库的 watched_keys 字典的 key 键中\n        // 删除链表里包含的客户端节点\n        wk = listNodeValue(ln);\n        // 取出客户端链表\n        clients = dictFetchValue(wk->db->watched_keys, wk->key);\n        redisAssertWithInfo(c,NULL,clients != NULL);\n        // 删除链表中的客户端节点\n        listDelNode(clients,listSearchKey(clients,c));\n\n        /* Kill the entry at all if this was the only client */\n        // 如果链表已经被清空，那么删除这个键\n        if (listLength(clients) == 0)\n            dictDelete(wk->db->watched_keys, wk->key);\n\n        /* Remove this watched key from the client->watched list */\n        // 从链表中移除 key 节点\n        listDelNode(c->watched_keys,ln);\n\n        decrRefCount(wk->key);\n        zfree(wk);\n    }\n}\n\n/* \"Touch\" a key, so that if this key is being WATCHed by some client the\n * next EXEC will fail. \n *\n * “触碰”一个键，如果这个键正在被某个/某些客户端监视着，\n * 那么这个/这些客户端在执行 EXEC 时事务将失败。\n */\nvoid touchWatchedKey(redisDb *db, robj *key) {\n    list *clients;\n    listIter li;\n    listNode *ln;\n\n    // 字典为空，没有任何键被监视\n    if (dictSize(db->watched_keys) == 0) return;\n\n    // 获取所有监视这个键的客户端\n    clients = dictFetchValue(db->watched_keys, key);\n    if (!clients) return;\n\n    /* Mark all the clients watching this key as REDIS_DIRTY_CAS */\n    /* Check if we are already watching for this key */\n    // 遍历所有客户端，打开他们的 REDIS_DIRTY_CAS 标识\n    listRewind(clients,&li);\n    while((ln = listNext(&li))) {\n        redisClient *c = listNodeValue(ln);\n\n        c->flags |= REDIS_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). \n *\n * 当一个数据库被 FLUSHDB 或者 FLUSHALL 清空时，\n * 它数据库内的所有 key 都应该被触碰。\n *\n * dbid 参数指定要被 FLUSH 的数据库。\n *\n * 如果 dbid 为 -1 ，那么表示执行的是 FLUSHALL ，\n * 所有数据库都将被 FLUSH\n */\nvoid touchWatchedKeysOnFlush(int dbid) {\n    listIter li1, li2;\n    listNode *ln;\n\n    // 这里的思路挺有趣的，不是遍历数据库的所有 key 来让客户端变为 DIRTY\n    // 而是遍历所有客户端，然后遍历客户端监视的键，再让相应的客户端变为 DIRTY\n    // 后者要比前者高效很多\n\n    /* For every client, check all the waited keys */\n    // 遍历所有客户端\n    listRewind(server.clients,&li1);\n    while((ln = listNext(&li1))) {\n\n        redisClient *c = listNodeValue(ln);\n\n        // 遍历客户端监视的键\n        listRewind(c->watched_keys,&li2);\n        while((ln = listNext(&li2))) {\n\n            // 取出监视的键和键的数据库\n            watchedKey *wk = listNodeValue(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            // 如果数据库号码相同，或者执行的命令为 FLUSHALL\n            // 那么将客户端设置为 REDIS_DIRTY_CAS\n            if (dbid == -1 || wk->db->id == dbid) {\n                if (dictFind(wk->db->dict, wk->key->ptr) != NULL)\n                    c->flags |= REDIS_DIRTY_CAS;\n            }\n        }\n    }\n}\n\nvoid watchCommand(redisClient *c) {\n    int j;\n\n    // 不能在事务开始后执行\n    if (c->flags & REDIS_MULTI) {\n        addReplyError(c,\"WATCH inside MULTI is not allowed\");\n        return;\n    }\n\n    // 监视输入的任意个键\n    for (j = 1; j < c->argc; j++)\n        watchForKey(c,c->argv[j]);\n\n    addReply(c,shared.ok);\n}\n\nvoid unwatchCommand(redisClient *c) {\n\n    // 取消客户端对所有键的监视\n    unwatchAllKeys(c);\n    \n    // 重置状态\n    c->flags &= (~REDIS_DIRTY_CAS);\n\n    addReply(c,shared.ok);\n}\n"
  },
  {
    "path": "src/networking.c",
    "content": "/*\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 \"redis.h\"\n#include <sys/uio.h>\n#include <math.h>\n\nstatic void setProtocolError(redisClient *c, int pos);\n\n/* To evaluate the output buffer size of a client we need to get size of\n * allocated objects, however we can't used zmalloc_size() directly on sds\n * strings because of the trick they use to work (the header is before the\n * returned pointer), so we use this helper function. */\n// 计算输出缓冲区的大小 \nsize_t zmalloc_size_sds(sds s) {\n    return zmalloc_size(s-sizeof(struct sdshdr));\n}\n\n/* Return the amount of memory used by the sds string at object->ptr\n * for a string object. */\n// 返回 object->ptr 所指向的字符串对象所使用的内存数量。\nsize_t getStringObjectSdsUsedMemory(robj *o) {\n    redisAssertWithInfo(NULL,o,o->type == REDIS_STRING);\n    switch(o->encoding) {\n    case REDIS_ENCODING_RAW: return zmalloc_size_sds(o->ptr);\n    case REDIS_ENCODING_EMBSTR: return sdslen(o->ptr);\n    default: return 0; /* Just integer encoding for now. */\n    }\n}\n\n/*\n * 回复内容复制函数\n */\nvoid *dupClientReplyValue(void *o) {\n    incrRefCount((robj*)o);\n    return o;\n}\n\n/*\n * 订阅模式对比函数\n */\nint listMatchObjects(void *a, void *b) {\n    return equalStringObjects(a,b);\n}\n\n/*\n * 创建一个新客户端\n */\nredisClient *createClient(int fd) {\n\n    // 分配空间\n    redisClient *c = zmalloc(sizeof(redisClient));\n\n    /* passing -1 as fd it is possible to create a non connected client.\n     * This is useful since all the Redis 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    // 当 fd 不为 -1 时，创建带网络连接的客户端\n    // 如果 fd 为 -1 ，那么创建无网络连接的伪客户端\n    // 因为 Redis 的命令必须在客户端的上下文中使用，所以在执行 Lua 环境中的命令时\n    // 需要用到这种伪终端\n    if (fd != -1) {\n        // 非阻塞\n        anetNonBlock(NULL,fd);\n        // 禁用 Nagle 算法\n        anetEnableTcpNoDelay(NULL,fd);\n        // 设置 keep alive\n        if (server.tcpkeepalive)\n            anetKeepAlive(NULL,fd,server.tcpkeepalive);\n        // 绑定读事件到事件 loop （开始接收命令请求）\n        if (aeCreateFileEvent(server.el,fd,AE_READABLE,\n            readQueryFromClient, c) == AE_ERR)\n        {\n            close(fd);\n            zfree(c);\n            return NULL;\n        }\n    }\n\n    // 初始化各个属性\n\n    // 默认数据库\n    selectDb(c,0);\n    // 套接字\n    c->fd = fd;\n    // 名字\n    c->name = NULL;\n    // 回复缓冲区的偏移量\n    c->bufpos = 0;\n    // 查询缓冲区\n    c->querybuf = sdsempty();\n    // 查询缓冲区峰值\n    c->querybuf_peak = 0;\n    // 命令请求的类型\n    c->reqtype = 0;\n    // 命令参数数量\n    c->argc = 0;\n    // 命令参数\n    c->argv = NULL;\n    // 当前执行的命令和最近一次执行的命令\n    c->cmd = c->lastcmd = NULL;\n    // 查询缓冲区中未读入的命令内容数量\n    c->multibulklen = 0;\n    // 读入的参数的长度\n    c->bulklen = -1;\n    // 已发送字节数\n    c->sentlen = 0;\n    // 状态 FLAG\n    c->flags = 0;\n    // 创建时间和最后一次互动时间\n    c->ctime = c->lastinteraction = server.unixtime;\n    // 认证状态\n    c->authenticated = 0;\n    // 复制状态\n    c->replstate = REDIS_REPL_NONE;\n    // 复制偏移量\n    c->reploff = 0;\n    // 通过 ACK 命令接收到的偏移量\n    c->repl_ack_off = 0;\n    // 通过 AKC 命令接收到偏移量的时间\n    c->repl_ack_time = 0;\n    // 客户端为从服务器时使用，记录了从服务器所使用的端口号\n    c->slave_listening_port = 0;\n    // 回复链表\n    c->reply = listCreate();\n    // 回复链表的字节量\n    c->reply_bytes = 0;\n    // 回复缓冲区大小达到软限制的时间\n    c->obuf_soft_limit_reached_time = 0;\n    // 回复链表的释放和复制函数\n    listSetFreeMethod(c->reply,decrRefCountVoid);\n    listSetDupMethod(c->reply,dupClientReplyValue);\n    // 阻塞类型\n    c->btype = REDIS_BLOCKED_NONE;\n    // 阻塞超时\n    c->bpop.timeout = 0;\n    // 造成客户端阻塞的列表键\n    c->bpop.keys = dictCreate(&setDictType,NULL);\n    // 在解除阻塞时将元素推入到 target 指定的键中\n    // BRPOPLPUSH 命令时使用\n    c->bpop.target = NULL;\n    c->bpop.numreplicas = 0;\n    c->bpop.reploffset = 0;\n    c->woff = 0;\n    // 进行事务时监视的键\n    c->watched_keys = listCreate();\n    // 订阅的频道和模式\n    c->pubsub_channels = dictCreate(&setDictType,NULL);\n    c->pubsub_patterns = listCreate();\n    c->peerid = NULL;\n    listSetFreeMethod(c->pubsub_patterns,decrRefCountVoid);\n    listSetMatchMethod(c->pubsub_patterns,listMatchObjects);\n    // 如果不是伪客户端，那么添加到服务器的客户端链表中\n    if (fd != -1) listAddNodeTail(server.clients,c);\n    // 初始化客户端的事务状态\n    initClientMultiState(c);\n\n    // 返回客户端\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 * 这个函数在每次向客户端发送数据时都会被调用。函数的行为如下：\n *\n * If the client should receive new data (normal clients will) the function\n * returns REDIS_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 * 当客户端可以接收新数据时（通常情况下都是这样），函数返回 REDIS_OK ，\n * 并将写处理器（write handler）安装到事件循环中，\n * 这样当套接字可写时，新数据就会被写入。\n *\n * If the client should not receive new data, because it is a fake client,\n * a master, a slave not yet online, or because the setup of the write handler\n * failed, the function returns REDIS_ERR.\n *\n * 对于那些不应该接收新数据的客户端，\n * 比如伪客户端、 master 以及 未 ONLINE 的 slave ，\n * 或者写处理器安装失败时，\n * 函数返回 REDIS_ERR 。\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 REDIS_ERR no\n * data should be appended to the output buffers. \n *\n * 通常在每个回复被创建时调用，如果函数返回 REDIS_ERR ，\n * 那么没有数据会被追加到输出缓冲区。\n */\nint prepareClientToWrite(redisClient *c) {\n\n    // LUA 脚本环境所使用的伪客户端总是可写的\n    if (c->flags & REDIS_LUA_CLIENT) return REDIS_OK;\n    \n    // 客户端是主服务器并且不接受查询，\n    // 那么它是不可写的，出错\n    if ((c->flags & REDIS_MASTER) &&\n        !(c->flags & REDIS_MASTER_FORCE_REPLY)) return REDIS_ERR;\n\n    // 无连接的伪客户端总是不可写的\n    if (c->fd <= 0) return REDIS_ERR; /* Fake client */\n\n    // 一般情况，为客户端套接字安装写处理器到事件循环\n    if (c->bufpos == 0 && listLength(c->reply) == 0 &&\n        (c->replstate == REDIS_REPL_NONE ||\n         c->replstate == REDIS_REPL_ONLINE) &&\n        aeCreateFileEvent(server.el, c->fd, AE_WRITABLE,\n        sendReplyToClient, c) == AE_ERR) return REDIS_ERR;\n\n    return REDIS_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. */\n// 当回复列表中的最后一个对象并非属于回复的一部分时\n// 创建该对象的一个复制品\nrobj *dupLastObjectIfNeeded(list *reply) {\n    robj *new, *cur;\n    listNode *ln;\n    redisAssert(listLength(reply) > 0);\n    ln = listLast(reply);\n    cur = listNodeValue(ln);\n    if (cur->refcount > 1) {\n        new = dupStringObject(cur);\n        decrRefCount(cur);\n        listNodeValue(ln) = new;\n    }\n    return listNodeValue(ln);\n}\n\n/* -----------------------------------------------------------------------------\n * Low level functions to add more data to output buffers.\n * -------------------------------------------------------------------------- */\n\n/*\n * 尝试将回复添加到 c->buf 中\n */\nint _addReplyToBuffer(redisClient *c, char *s, size_t len) {\n    size_t available = sizeof(c->buf)-c->bufpos;\n\n    // 正准备关闭客户端，无须再发送内容\n    if (c->flags & REDIS_CLOSE_AFTER_REPLY) return REDIS_OK;\n\n    /* If there already are entries in the reply list, we cannot\n     * add anything more to the static buffer. */\n    // 回复链表里已经有内容，再添加内容到 c->buf 里面就是错误了\n    if (listLength(c->reply) > 0) return REDIS_ERR;\n\n    /* Check that the buffer has enough space available for this string. */\n    // 空间必须满足\n    if (len > available) return REDIS_ERR;\n\n    // 复制内容到 c->buf 里面\n    memcpy(c->buf+c->bufpos,s,len);\n    c->bufpos+=len;\n\n    return REDIS_OK;\n}\n\n/*\n * 将回复对象（一个 SDS ）添加到 c->reply 回复链表中\n */\nvoid _addReplyObjectToList(redisClient *c, robj *o) {\n    robj *tail;\n\n    // 客户端即将被关闭，无须再发送回复\n    if (c->flags & REDIS_CLOSE_AFTER_REPLY) return;\n\n    // 链表中无缓冲块，直接将对象追加到链表中\n    if (listLength(c->reply) == 0) {\n        incrRefCount(o);\n        listAddNodeTail(c->reply,o);\n\n        // 链表中已有缓冲块，尝试将回复添加到块内\n        // 如果当前的块不能容纳回复的话，那么新建一个块\n        c->reply_bytes += getStringObjectSdsUsedMemory(o);\n    } else {\n\n        // 取出表尾的 SDS\n        tail = listNodeValue(listLast(c->reply));\n\n        /* Append to this object when possible. */\n        // 如果表尾 SDS 的已用空间加上对象的长度，小于 REDIS_REPLY_CHUNK_BYTES\n        // 那么将新对象的内容拼接到表尾 SDS 的末尾\n        if (tail->ptr != NULL &&\n            tail->encoding == REDIS_ENCODING_RAW &&\n            sdslen(tail->ptr)+sdslen(o->ptr) <= REDIS_REPLY_CHUNK_BYTES)\n        {\n            c->reply_bytes -= zmalloc_size_sds(tail->ptr);\n            tail = dupLastObjectIfNeeded(c->reply);\n            // 拼接\n            tail->ptr = sdscatlen(tail->ptr,o->ptr,sdslen(o->ptr));\n            c->reply_bytes += zmalloc_size_sds(tail->ptr);\n\n        // 直接将对象追加到末尾\n        } else {\n            incrRefCount(o);\n            listAddNodeTail(c->reply,o);\n            c->reply_bytes += getStringObjectSdsUsedMemory(o);\n        }\n    }\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. */\n// 和 _addReplyObjectToList 类似，但会负责 SDS 的释放功能（如果需要的话）\nvoid _addReplySdsToList(redisClient *c, sds s) {\n    robj *tail;\n\n    if (c->flags & REDIS_CLOSE_AFTER_REPLY) {\n        sdsfree(s);\n        return;\n    }\n\n    if (listLength(c->reply) == 0) {\n        listAddNodeTail(c->reply,createObject(REDIS_STRING,s));\n        c->reply_bytes += zmalloc_size_sds(s);\n    } else {\n        tail = listNodeValue(listLast(c->reply));\n\n        /* Append to this object when possible. */\n        if (tail->ptr != NULL && tail->encoding == REDIS_ENCODING_RAW &&\n            sdslen(tail->ptr)+sdslen(s) <= REDIS_REPLY_CHUNK_BYTES)\n        {\n            c->reply_bytes -= zmalloc_size_sds(tail->ptr);\n            tail = dupLastObjectIfNeeded(c->reply);\n            tail->ptr = sdscatlen(tail->ptr,s,sdslen(s));\n            c->reply_bytes += zmalloc_size_sds(tail->ptr);\n            sdsfree(s);\n        } else {\n            listAddNodeTail(c->reply,createObject(REDIS_STRING,s));\n            c->reply_bytes += zmalloc_size_sds(s);\n        }\n    }\n    asyncCloseClientOnOutputBufferLimitReached(c);\n}\n\nvoid _addReplyStringToList(redisClient *c, char *s, size_t len) {\n    robj *tail;\n\n    if (c->flags & REDIS_CLOSE_AFTER_REPLY) return;\n\n    if (listLength(c->reply) == 0) {\n        // 为字符串创建字符串对象并追加到回复链表末尾\n        robj *o = createStringObject(s,len);\n\n        listAddNodeTail(c->reply,o);\n        c->reply_bytes += getStringObjectSdsUsedMemory(o);\n    } else {\n        tail = listNodeValue(listLast(c->reply));\n\n        /* Append to this object when possible. */\n        if (tail->ptr != NULL && tail->encoding == REDIS_ENCODING_RAW &&\n            sdslen(tail->ptr)+len <= REDIS_REPLY_CHUNK_BYTES)\n        {\n            c->reply_bytes -= zmalloc_size_sds(tail->ptr);\n            tail = dupLastObjectIfNeeded(c->reply);\n            // 将字符串拼接到一个 SDS 之后\n            tail->ptr = sdscatlen(tail->ptr,s,len);\n            c->reply_bytes += zmalloc_size_sds(tail->ptr);\n        } else {\n            // 为字符串创建字符串对象并追加到回复链表末尾\n            robj *o = createStringObject(s,len);\n\n            listAddNodeTail(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(redisClient *c, robj *obj) {\n\n    // 为客户端安装写处理器到事件循环\n    if (prepareClientToWrite(c) != REDIS_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     * 如果在使用子进程，那么尽可能地避免修改对象的 refcount 域。\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     *\n     * 如果对象的编码为 RAW ，并且静态缓冲区中有空间\n     * 那么就可以在不弄乱内存页的情况下，将对象发送给客户端。\n     */\n    if (sdsEncodedObject(obj)) {\n        // 首先尝试复制内容到 c->buf 中，这样可以避免内存分配\n        if (_addReplyToBuffer(c,obj->ptr,sdslen(obj->ptr)) != REDIS_OK)\n            // 如果 c->buf 中的空间不够，就复制到 c->reply 链表中\n            // 可能会引起内存分配\n            _addReplyObjectToList(c,obj);\n    } else if (obj->encoding == REDIS_ENCODING_INT) {\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        // 优化，如果 c->buf 中有等于或多于 32 个字节的空间\n        // 那么将整数直接以字符串的形式复制到 c->buf 中\n        if (listLength(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) == REDIS_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        // 执行到这里，代表对象是整数，并且长度大于 32 位\n        // 将它转换为字符串\n        obj = getDecodedObject(obj);\n        // 保存到缓存中\n        if (_addReplyToBuffer(c,obj->ptr,sdslen(obj->ptr)) != REDIS_OK)\n            _addReplyObjectToList(c,obj);\n        decrRefCount(obj);\n    } else {\n        redisPanic(\"Wrong obj->encoding in addReply()\");\n    }\n}\n\n/*\n * 将 SDS 中的内容复制到回复缓冲区\n */\nvoid addReplySds(redisClient *c, sds s) {\n    if (prepareClientToWrite(c) != REDIS_OK) {\n        /* The caller expects the sds to be free'd. */\n        sdsfree(s);\n        return;\n    }\n    if (_addReplyToBuffer(c,s,sdslen(s)) == REDIS_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\n/*\n * 将 C 字符串中的内容复制到回复缓冲区\n */\nvoid addReplyString(redisClient *c, char *s, size_t len) {\n    if (prepareClientToWrite(c) != REDIS_OK) return;\n    if (_addReplyToBuffer(c,s,len) != REDIS_OK)\n        _addReplyStringToList(c,s,len);\n}\n\nvoid addReplyErrorLength(redisClient *c, char *s, size_t len) {\n    addReplyString(c,\"-ERR \",5);\n    addReplyString(c,s,len);\n    addReplyString(c,\"\\r\\n\",2);\n}\n\n/*\n * 返回一个错误回复\n *\n * 例子 -ERR unknown command 'foobar'\n */\nvoid addReplyError(redisClient *c, char *err) {\n    addReplyErrorLength(c,err,strlen(err));\n}\n\nvoid addReplyErrorFormat(redisClient *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(redisClient *c, char *s, size_t len) {\n    addReplyString(c,\"+\",1);\n    addReplyString(c,s,len);\n    addReplyString(c,\"\\r\\n\",2);\n}\n\n/*\n * 返回一个状态回复\n *\n * 例子 +OK\\r\\n\n */\nvoid addReplyStatus(redisClient *c, char *status) {\n    addReplyStatusLength(c,status,strlen(status));\n}\n\nvoid addReplyStatusFormat(redisClient *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. */\n// 当发送 Multi Bulk 回复时，先创建一个空的链表，之后再用实际的回复填充它\nvoid *addDeferredMultiBulkLength(redisClient *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) != REDIS_OK) return NULL;\n    listAddNodeTail(c->reply,createObject(REDIS_STRING,NULL));\n    return listLast(c->reply);\n}\n\n/* Populate the length object and try gluing it to the next chunk. */\n// 设置 Multi Bulk 回复的长度\nvoid setDeferredMultiBulkLength(redisClient *c, void *node, long length) {\n    listNode *ln = (listNode*)node;\n    robj *len, *next;\n\n    /* Abort when *node is NULL (see addDeferredMultiBulkLength). */\n    if (node == NULL) return;\n\n    len = listNodeValue(ln);\n    len->ptr = sdscatprintf(sdsempty(),\"*%ld\\r\\n\",length);\n    len->encoding = REDIS_ENCODING_RAW; /* in case it was an EMBSTR. */\n    c->reply_bytes += zmalloc_size_sds(len->ptr);\n    if (ln->next != NULL) {\n        next = listNodeValue(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 -= zmalloc_size_sds(len->ptr);\n            c->reply_bytes -= getStringObjectSdsUsedMemory(next);\n            len->ptr = sdscatlen(len->ptr,next->ptr,sdslen(next->ptr));\n            c->reply_bytes += zmalloc_size_sds(len->ptr);\n            listDelNode(c->reply,ln->next);\n        }\n    }\n    asyncCloseClientOnOutputBufferLimitReached(c);\n}\n\n/* Add a double as a bulk reply */\n/*\n * 以 bulk 回复的形式，返回一个双精度浮点数\n *\n * 例子 $4\\r\\n3.14\\r\\n\n */\nvoid addReplyDouble(redisClient *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 long as integer reply or bulk len / multi bulk count.\n * \n * 添加一个 long long 为整数回复，或者 bulk 或 multi bulk 的数目\n *\n * Basically this is used to output <prefix><long long><crlf>. \n *\n * 输出格式为 <prefix><long long><crlf>\n *\n * 例子:\n *\n * *5\\r\\n10086\\r\\n\n *\n * $5\\r\\n10086\\r\\n\n */\nvoid addReplyLongLongWithPrefix(redisClient *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 < REDIS_SHARED_BULKHDR_LEN) {\n        // 多条批量回复\n        addReply(c,shared.mbulkhdr[ll]);\n        return;\n    } else if (prefix == '$' && ll < REDIS_SHARED_BULKHDR_LEN) {\n        // 批量回复\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\n/*\n * 返回一个整数回复\n * \n * 格式为 :10086\\r\\n\n */\nvoid addReplyLongLong(redisClient *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(redisClient *c, long length) {\n    if (length < REDIS_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(redisClient *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 < REDIS_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 \n *\n * 返回一个 Redis 对象作为回复\n */\nvoid addReplyBulk(redisClient *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 \n *\n * 返回一个 C 缓冲区作为回复\n */\nvoid addReplyBulkCBuffer(redisClient *c, void *p, size_t len) {\n    addReplyLongLongWithPrefix(c,len,'$');\n    addReplyString(c,p,len);\n    addReply(c,shared.crlf);\n}\n\n/* Add a C nul term string as bulk reply \n *\n * 返回一个 C 字符串作为回复\n */\nvoid addReplyBulkCString(redisClient *c, 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 \n *\n * 返回一个 long long 值作为回复\n */\nvoid addReplyBulkLongLong(redisClient *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. */\n// 释放 dst 客户端原有的输出内容，并将 src 客户端的输出内容复制给 dst\nvoid copyClientOutputBuffer(redisClient *dst, redisClient *src) {\n\n    // 释放 dst 原有的回复链表\n    listRelease(dst->reply);\n    // 复制新链表到 dst\n    dst->reply = listDup(src->reply);\n\n    // 复制内容到回复 buf\n    memcpy(dst->buf,src->buf,src->bufpos);\n\n    // 同步偏移量和字节数\n    dst->bufpos = src->bufpos;\n    dst->reply_bytes = src->reply_bytes;\n}\n\n/*\n * TCP 连接 accept 处理器\n */\n#define MAX_ACCEPTS_PER_CALL 1000\nstatic void acceptCommonHandler(int fd, int flags) {\n\n    // 创建客户端\n    redisClient *c;\n    if ((c = createClient(fd)) == NULL) {\n        redisLog(REDIS_WARNING,\n            \"Error registering fd event for the new client: %s (fd=%d)\",\n            strerror(errno),fd);\n        close(fd); /* May be already closed, just ignore errors */\n        return;\n    }\n\n    /* If maxclient directive is set and this is one client more... close the\n     * connection. Note that we create the client instead to check before\n     * for this condition, since now the socket is already set in non-blocking\n     * mode and we can send an error for free using the Kernel I/O */\n    // 如果新添加的客户端令服务器的最大客户端数量达到了\n    // 那么向新客户端写入错误信息，并关闭新客户端\n    // 先创建客户端，再进行数量检查是为了方便地进行错误信息写入\n    if (listLength(server.clients) > server.maxclients) {\n        char *err = \"-ERR max number of clients reached\\r\\n\";\n\n        /* That's a best effort error message, don't check write errors */\n        if (write(c->fd,err,strlen(err)) == -1) {\n            /* Nothing to do, Just to avoid the warning... */\n        }\n        // 更新拒绝连接数\n        server.stat_rejected_conn++;\n        freeClient(c);\n        return;\n    }\n\n    // 更新连接次数\n    server.stat_numconnections++;\n\n    // 设置 FLAG\n    c->flags |= flags;\n}\n\n/* \n * 创建一个 TCP 连接处理器\n */\nvoid acceptTcpHandler(aeEventLoop *el, int fd, void *privdata, int mask) {\n    int cport, cfd, max = MAX_ACCEPTS_PER_CALL;\n    char cip[REDIS_IP_STR_LEN];\n    REDIS_NOTUSED(el);\n    REDIS_NOTUSED(mask);\n    REDIS_NOTUSED(privdata);\n\n    while(max--) {\n        // accept 客户端连接\n        cfd = anetTcpAccept(server.neterr, fd, cip, sizeof(cip), &cport);\n        if (cfd == ANET_ERR) {\n            if (errno != EWOULDBLOCK)\n                redisLog(REDIS_WARNING,\n                    \"Accepting client connection: %s\", server.neterr);\n            return;\n        }\n        redisLog(REDIS_VERBOSE,\"Accepted %s:%d\", cip, cport);\n        // 为客户端创建客户端状态（redisClient）\n        acceptCommonHandler(cfd,0);\n    }\n}\n\n/*\n * 创建一个本地连接处理器\n */\nvoid acceptUnixHandler(aeEventLoop *el, int fd, void *privdata, int mask) {\n    int cfd, max = MAX_ACCEPTS_PER_CALL;\n    REDIS_NOTUSED(el);\n    REDIS_NOTUSED(mask);\n    REDIS_NOTUSED(privdata);\n\n    while(max--) {\n        // accept 本地客户端连接\n        cfd = anetUnixAccept(server.neterr, fd);\n        if (cfd == ANET_ERR) {\n            if (errno != EWOULDBLOCK)\n                redisLog(REDIS_WARNING,\n                    \"Accepting client connection: %s\", server.neterr);\n            return;\n        }\n        redisLog(REDIS_VERBOSE,\"Accepted connection to %s\", server.unixsocket);\n        // 为本地客户端创建客户端状态\n        acceptCommonHandler(cfd,REDIS_UNIX_SOCKET);\n    }\n}\n\n/*\n * 清空所有命令参数\n */\nstatic void freeClientArgv(redisClient *c) {\n    int j;\n    for (j = 0; j < c->argc; j++)\n        decrRefCount(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. */\n// 断开所有从服务器的连接，强制所有从服务器执行重同步\nvoid disconnectSlaves(void) {\n    while (listLength(server.slaves)) {\n        listNode *ln = listFirst(server.slaves);\n        freeClient((redisClient*)ln->value);\n    }\n}\n\n/* This function is called when the slave lose the connection with the\n * master into an unexpected way. */\n// 这个函数在从服务器以外地和主服务器失去联系时调用\nvoid replicationHandleMasterDisconnection(void) {\n    server.master = NULL;\n    server.repl_state = REDIS_REPL_CONNECT;\n    server.repl_down_since = server.unixtime;\n    /* We lost connection with our master, force our slaves to resync\n     * with us as well to load the new data set.\n     *\n     * 和主服务器失联，强制所有这个服务器的从服务器 resync ，\n     * 等待载入新数据。\n     *\n     * If server.masterhost is NULL the user called SLAVEOF NO ONE so\n     * slave resync is not needed. \n     *\n     * 如果 masterhost 不存在（怎么会这样呢？）\n     * 那么调用 SLAVEOF NO ONE ，避免 slave resync\n     */\n    if (server.masterhost != NULL) disconnectSlaves();\n}\n\n/*\n * 释放客户端\n */\nvoid freeClient(redisClient *c) {\n    listNode *ln;\n\n    /* If this is marked as current client unset it */\n    if (server.current_client == c) server.current_client = NULL;\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 (server.master && c->flags & REDIS_MASTER) {\n        redisLog(REDIS_WARNING,\"Connection with master lost.\");\n        if (!(c->flags & (REDIS_CLOSE_AFTER_REPLY|\n                          REDIS_CLOSE_ASAP|\n                          REDIS_BLOCKED|\n                          REDIS_UNBLOCKED)))\n        {\n            replicationCacheMaster(c);\n            return;\n        }\n    }\n\n    /* Log link disconnection with slave */\n    if ((c->flags & REDIS_SLAVE) && !(c->flags & REDIS_MONITOR)) {\n        char ip[REDIS_IP_STR_LEN];\n\n        if (anetPeerToString(c->fd,ip,sizeof(ip),NULL) != -1) {\n            redisLog(REDIS_WARNING,\"Connection with slave %s:%d lost.\",\n                ip, c->slave_listening_port);\n        }\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 & REDIS_BLOCKED) unblockClient(c);\n    dictRelease(c->bpop.keys);\n\n    /* UNWATCH all the keys */\n    // 清空 WATCH 信息\n    unwatchAllKeys(c);\n    listRelease(c->watched_keys);\n\n    /* Unsubscribe from all the pubsub channels */\n    // 退订所有频道和模式\n    pubsubUnsubscribeAllChannels(c,0);\n    pubsubUnsubscribeAllPatterns(c,0);\n    dictRelease(c->pubsub_channels);\n    listRelease(c->pubsub_patterns);\n\n    /* Close socket, unregister events, and remove list of replies and\n     * accumulated arguments. */\n    // 关闭套接字，并从事件处理器中删除该套接字的事件\n    if (c->fd != -1) {\n        aeDeleteFileEvent(server.el,c->fd,AE_READABLE);\n        aeDeleteFileEvent(server.el,c->fd,AE_WRITABLE);\n        close(c->fd);\n    }\n\n    // 清空回复缓冲区\n    listRelease(c->reply);\n\n    // 清空命令参数\n    freeClientArgv(c);\n\n    /* Remove from the list of clients */\n    // 从服务器的客户端链表中删除自身\n    if (c->fd != -1) {\n        ln = listSearchKey(server.clients,c);\n        redisAssert(ln != NULL);\n        listDelNode(server.clients,ln);\n    }\n\n    /* When client was just unblocked because of a blocking operation,\n     * remove it from the list of unblocked clients. */\n    // 删除客户端的阻塞信息\n    if (c->flags & REDIS_UNBLOCKED) {\n        ln = listSearchKey(server.unblocked_clients,c);\n        redisAssert(ln != NULL);\n        listDelNode(server.unblocked_clients,ln);\n    }\n\n    /* Master/slave cleanup Case 1:\n     * we lost the connection with a slave. */\n    if (c->flags & REDIS_SLAVE) {\n        if (c->replstate == REDIS_REPL_SEND_BULK) {\n            if (c->repldbfd != -1) close(c->repldbfd);\n            if (c->replpreamble) sdsfree(c->replpreamble);\n        }\n        list *l = (c->flags & REDIS_MONITOR) ? server.monitors : server.slaves;\n        ln = listSearchKey(l,c);\n        redisAssert(ln != NULL);\n        listDelNode(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 & REDIS_SLAVE && listLength(server.slaves) == 0)\n            server.repl_no_slaves_since = server.unixtime;\n        refreshGoodSlavesCount();\n    }\n\n    /* Master/slave cleanup Case 2:\n     * we lost the connection with the master. */\n    if (c->flags & REDIS_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 & REDIS_CLOSE_ASAP) {\n        ln = listSearchKey(server.clients_to_close,c);\n        redisAssert(ln != NULL);\n        listDelNode(server.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) decrRefCount(c->name);\n    // 清除参数空间\n    zfree(c->argv);\n    // 清除事务状态信息\n    freeClientMultiState(c);\n    sdsfree(c->peerid);\n    // 释放客户端 redisClient 结构本身\n    zfree(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. */\n// 异步地释放给定的客户端\nvoid freeClientAsync(redisClient *c) {\n    if (c->flags & REDIS_CLOSE_ASAP) return;\n    c->flags |= REDIS_CLOSE_ASAP;\n    listAddNodeTail(server.clients_to_close,c);\n}\n\n// 关闭需要异步关闭的客户端\nvoid freeClientsInAsyncFreeQueue(void) {\n    \n    // 遍历所有要关闭的客户端\n    while (listLength(server.clients_to_close)) {\n        listNode *ln = listFirst(server.clients_to_close);\n        redisClient *c = listNodeValue(ln);\n\n        c->flags &= ~REDIS_CLOSE_ASAP;\n        // 关闭客户端\n        freeClient(c);\n        // 从客户端链表中删除被关闭的客户端\n        listDelNode(server.clients_to_close,ln);\n    }\n}\n\n/*\n * 负责传送命令回复的写处理器\n */\nvoid sendReplyToClient(aeEventLoop *el, int fd, void *privdata, int mask) {\n    redisClient *c = privdata;\n    int nwritten = 0, totwritten = 0, objlen;\n    size_t objmem;\n    robj *o;\n    REDIS_NOTUSED(el);\n    REDIS_NOTUSED(mask);\n\n    // 一直循环，直到回复缓冲区为空\n    // 或者指定条件满足为止\n    while(c->bufpos > 0 || listLength(c->reply)) {\n\n        if (c->bufpos > 0) {\n\n            // c->bufpos > 0\n\n            // 写入内容到套接字\n            // c->sentlen 是用来处理 short write 的\n            // 当出现 short write ，导致写入未能一次完成时，\n            // c->buf+c->sentlen 就会偏移到正确（未写入）内容的位置上。\n            nwritten = write(fd,c->buf+c->sentlen,c->bufpos-c->sentlen);\n            // 出错则跳出\n            if (nwritten <= 0) break;\n            // 成功写入则更新写入计数器变量\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            // 如果缓冲区中的内容已经全部写入完毕\n            // 那么清空客户端的两个计数器变量\n            if (c->sentlen == c->bufpos) {\n                c->bufpos = 0;\n                c->sentlen = 0;\n            }\n        } else {\n\n            // listLength(c->reply) != 0\n\n            // 取出位于链表最前面的对象\n            o = listNodeValue(listFirst(c->reply));\n            objlen = sdslen(o->ptr);\n            objmem = getStringObjectSdsUsedMemory(o);\n\n            // 略过空对象\n            if (objlen == 0) {\n                listDelNode(c->reply,listFirst(c->reply));\n                c->reply_bytes -= objmem;\n                continue;\n            }\n\n            // 写入内容到套接字\n            // c->sentlen 是用来处理 short write 的\n            // 当出现 short write ，导致写入未能一次完成时，\n            // c->buf+c->sentlen 就会偏移到正确（未写入）内容的位置上。\n            nwritten = write(fd, ((char*)o->ptr)+c->sentlen,objlen-c->sentlen);\n            // 写入出错则跳出\n            if (nwritten <= 0) break;\n            // 成功写入则更新写入计数器变量\n            c->sentlen += nwritten;\n            totwritten += nwritten;\n\n            /* If we fully sent the object on head go to the next one */\n            // 如果缓冲区内容全部写入完毕，那么删除已写入完毕的节点\n            if (c->sentlen == objlen) {\n                listDelNode(c->reply,listFirst(c->reply));\n                c->sentlen = 0;\n                c->reply_bytes -= objmem;\n            }\n        }\n        /* Note that we avoid to send more than REDIS_MAX_WRITE_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         * 为了避免一个非常大的回复独占服务器，\n         * 当写入的总数量大于 REDIS_MAX_WRITE_PER_EVENT ，\n         * 临时中断写入，将处理时间让给其他客户端，\n         * 剩余的内容等下次写入就绪再继续写入\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         *\n         * 不过，如果服务器的内存占用已经超过了限制，\n         * 那么为了将回复缓冲区中的内容尽快写入给客户端，\n         * 然后释放回复缓冲区的空间来回收内存，\n         * 这时即使写入量超过了 REDIS_MAX_WRITE_PER_EVENT ，\n         * 程序也继续进行写入\n         */\n        if (totwritten > REDIS_MAX_WRITE_PER_EVENT &&\n            (server.maxmemory == 0 ||\n             zmalloc_used_memory() < server.maxmemory)) break;\n    }\n\n    // 写入出错检查\n    if (nwritten == -1) {\n        if (errno == EAGAIN) {\n            nwritten = 0;\n        } else {\n            redisLog(REDIS_VERBOSE,\n                \"Error writing to client: %s\", strerror(errno));\n            freeClient(c);\n            return;\n        }\n    }\n\n    if (totwritten > 0) {\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 & REDIS_MASTER)) c->lastinteraction = server.unixtime;\n    }\n    if (c->bufpos == 0 && listLength(c->reply) == 0) {\n        c->sentlen = 0;\n\n        // 删除 write handler\n        aeDeleteFileEvent(server.el,c->fd,AE_WRITABLE);\n\n        /* Close connection after entire reply has been sent. */\n        // 如果指定了写入之后关闭客户端 FLAG ，那么关闭客户端\n        if (c->flags & REDIS_CLOSE_AFTER_REPLY) freeClient(c);\n    }\n}\n\n/* resetClient prepare the client to process the next command */\n// 在客户端执行完命令之后执行：重置客户端以准备执行下个命令\nvoid resetClient(redisClient *c) {\n    redisCommandProc *prevcmd = c->cmd ? c->cmd->proc : NULL;\n\n    freeClientArgv(c);\n    c->reqtype = 0;\n    c->multibulklen = 0;\n    c->bulklen = -1;\n    /* We clear the ASKING flag as well if we are not inside a MULTI, and\n     * if what we just executed is not the ASKING command itself. */\n    if (!(c->flags & REDIS_MULTI) && prevcmd != askingCommand)\n        c->flags &= (~REDIS_ASKING);\n}\n\n/*\n * 处理内联命令，并创建参数对象\n *\n * 内联命令的各个参数以空格分开，并以 \\r\\n 结尾\n * 例子：\n *\n * <arg0> <arg1> <arg...> <argN>\\r\\n\n *\n * 这些内容会被用于创建参数对象，\n * 比如\n *\n * argv[0] = arg0\n * argv[1] = arg1\n * argv[2] = arg2\n */\nint processInlineBuffer(redisClient *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    // 收到的查询内容不符合协议格式，出错\n    if (newline == NULL) {\n        if (sdslen(c->querybuf) > REDIS_INLINE_MAX_SIZE) {\n            addReplyError(c,\"Protocol error: too big inline request\");\n            setProtocolError(c,0);\n        }\n        return REDIS_ERR;\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    // 根据空格，分割命令的参数\n    // 比如说 SET msg hello \\r\\n 将分割为\n    // argv[0] = SET\n    // argv[1] = msg\n    // argv[2] = hello\n    // argc = 3\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 REDIS_ERR;\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 & REDIS_SLAVE)\n        c->repl_ack_time = server.unixtime;\n\n    /* Leave data after the first line of the query in the buffer */\n\n    // 从缓冲区中删除已 argv 已读取的内容\n    // 剩余的内容是未读取的\n    sdsrange(c->querybuf,querylen+2,-1);\n\n    /* Setup argv array on client structure */\n    // 为客户端的参数分配空间\n    if (c->argv) zfree(c->argv);\n    c->argv = zmalloc(sizeof(robj*)*argc);\n\n    /* Create redis objects for all arguments. */\n    // 为每个参数创建一个字符串对象\n    for (c->argc = 0, j = 0; j < argc; j++) {\n        if (sdslen(argv[j])) {\n            // argv[j] 已经是 SDS 了\n            // 所以创建的字符串对象直接指向该 SDS\n            c->argv[c->argc] = createObject(REDIS_STRING,argv[j]);\n            c->argc++;\n        } else {\n            sdsfree(argv[j]);\n        }\n    }\n\n    zfree(argv);\n\n    return REDIS_OK;\n}\n\n/* Helper function. Trims query buffer to make the function that processes\n * multi bulk requests idempotent. */\n// 如果在读入协议内容时，发现内容不符合协议，那么异步地关闭这个客户端。\nstatic void setProtocolError(redisClient *c, int pos) {\n    if (server.verbosity >= REDIS_VERBOSE) {\n        sds client = catClientInfoString(sdsempty(),c);\n        redisLog(REDIS_VERBOSE,\n            \"Protocol error from client: %s\", client);\n        sdsfree(client);\n    }\n    c->flags |= REDIS_CLOSE_AFTER_REPLY;\n    sdsrange(c->querybuf,pos,-1);\n}\n\n/*\n * 将 c->querybuf 中的协议内容转换成 c->argv 中的参数对象\n * \n * 比如 *3\\r\\n$3\\r\\nSET\\r\\n$3\\r\\nMSG\\r\\n$5\\r\\nHELLO\\r\\n\n * 将被转换为：\n * argv[0] = SET\n * argv[1] = MSG\n * argv[2] = HELLO\n */\nint processMultibulkBuffer(redisClient *c) {\n    char *newline = NULL;\n    int pos = 0, ok;\n    long long ll;\n\n    // 读入命令的参数个数\n    // 比如 *3\\r\\n$3\\r\\nSET\\r\\n... 将令 c->multibulklen = 3\n    if (c->multibulklen == 0) {\n        /* The client should have been reset */\n        redisAssertWithInfo(c,NULL,c->argc == 0);\n\n        /* Multi bulk length cannot be read without a \\r\\n */\n        // 检查缓冲区的内容第一个 \"\\r\\n\"\n        newline = strchr(c->querybuf,'\\r');\n        if (newline == NULL) {\n            if (sdslen(c->querybuf) > REDIS_INLINE_MAX_SIZE) {\n                addReplyError(c,\"Protocol error: too big mbulk count string\");\n                setProtocolError(c,0);\n            }\n            return REDIS_ERR;\n        }\n        /* Buffer should also contain \\n */\n        if (newline-(c->querybuf) > ((signed)sdslen(c->querybuf)-2))\n            return REDIS_ERR;\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        // 协议的第一个字符必须是 '*'\n        redisAssertWithInfo(c,NULL,c->querybuf[0] == '*');\n        // 将参数个数，也即是 * 之后， \\r\\n 之前的数字取出并保存到 ll 中\n        // 比如对于 *3\\r\\n ，那么 ll 将等于 3\n        ok = string2ll(c->querybuf+1,newline-(c->querybuf+1),&ll);\n        // 参数的数量超出限制\n        if (!ok || ll > 1024*1024) {\n            addReplyError(c,\"Protocol error: invalid multibulk length\");\n            setProtocolError(c,pos);\n            return REDIS_ERR;\n        }\n\n        // 参数数量之后的位置\n        // 比如对于 *3\\r\\n$3\\r\\n$SET\\r\\n... 来说，\n        // pos 指向 *3\\r\\n$3\\r\\n$SET\\r\\n...\n        //                ^\n        //                |\n        //               pos\n        pos = (newline-c->querybuf)+2;\n        // 如果 ll <= 0 ，那么这个命令是一个空白命令\n        // 那么将这段内容从查询缓冲区中删除，只保留未阅读的那部分内容\n        // 为什么参数可以是空的呢？\n        // processInputBuffer 中有注释到 \"Multibulk processing could see a <= 0 length\"\n        // 但并没有详细说明原因\n        if (ll <= 0) {\n            sdsrange(c->querybuf,pos,-1);\n            return REDIS_OK;\n        }\n\n        // 设置参数数量\n        c->multibulklen = ll;\n\n        /* Setup argv array on client structure */\n        // 根据参数数量，为各个参数对象分配空间\n        if (c->argv) zfree(c->argv);\n        c->argv = zmalloc(sizeof(robj*)*c->multibulklen);\n    }\n\n    redisAssertWithInfo(c,NULL,c->multibulklen > 0);\n\n    // 从 c->querybuf 中读入参数，并创建各个参数对象到 c->argv\n    while(c->multibulklen) {\n\n        /* Read bulk length if unknown */\n        // 读入参数长度\n        if (c->bulklen == -1) {\n\n            // 确保 \"\\r\\n\" 存在\n            newline = strchr(c->querybuf+pos,'\\r');\n            if (newline == NULL) {\n                if (sdslen(c->querybuf) > REDIS_INLINE_MAX_SIZE) {\n                    addReplyError(c,\n                        \"Protocol error: too big bulk count string\");\n                    setProtocolError(c,0);\n                    return REDIS_ERR;\n                }\n                break;\n            }\n            /* Buffer should also contain \\n */\n            if (newline-(c->querybuf) > ((signed)sdslen(c->querybuf)-2))\n                break;\n\n            // 确保协议符合参数格式，检查其中的 $...\n            // 比如 $3\\r\\nSET\\r\\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 REDIS_ERR;\n            }\n\n            // 读取长度\n            // 比如 $3\\r\\nSET\\r\\n 将会让 ll 的值设置 3\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 REDIS_ERR;\n            }\n\n            // 定位到参数的开头\n            // 比如 \n            // $3\\r\\nSET\\r\\n...\n            //       ^\n            //       |\n            //      pos\n            pos += newline-(c->querybuf+pos)+2;\n            // 如果参数非常长，那么做一些预备措施来优化接下来的参数复制操作\n            if (ll >= REDIS_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 < ll+2)\n                    c->querybuf = sdsMakeRoomFor(c->querybuf,ll+2-qblen);\n            }\n            // 参数的长度\n            c->bulklen = ll;\n        }\n\n        /* Read bulk argument */\n        // 读入参数\n        if (sdslen(c->querybuf)-pos < (unsigned)(c->bulklen+2)) {\n            // 确保内容符合协议格式\n            // 比如 $3\\r\\nSET\\r\\n 就检查 SET 之后的 \\r\\n\n            /* Not enough data (+2 == trailing \\r\\n) */\n            break;\n        } else {\n            // 为参数创建字符串对象  \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 >= REDIS_MBULK_BIG_ARG &&\n                (signed) sdslen(c->querybuf) == c->bulklen+2)\n            {\n                c->argv[c->argc++] = createObject(REDIS_STRING,c->querybuf);\n                sdsIncrLen(c->querybuf,-2); /* remove CRLF */\n                c->querybuf = sdsempty();\n                /* Assume that if we saw a fat argument we'll see another one\n                 * likely... */\n                c->querybuf = sdsMakeRoomFor(c->querybuf,c->bulklen+2);\n                pos = 0;\n            } else {\n                c->argv[c->argc++] =\n                    createStringObject(c->querybuf+pos,c->bulklen);\n                pos += c->bulklen+2;\n            }\n\n            // 清空参数长度\n            c->bulklen = -1;\n\n            // 减少还需读入的参数个数\n            c->multibulklen--;\n        }\n    }\n\n    /* Trim to pos */\n    // 从 querybuf 中删除已被读取的内容\n    if (pos) sdsrange(c->querybuf,pos,-1);\n\n    /* We're done when c->multibulk == 0 */\n    // 如果本条命令的所有参数都已读取完，那么返回\n    if (c->multibulklen == 0) return REDIS_OK;\n\n    /* Still not read to process the command */\n    // 如果还有参数未读取完，那么就协议内容有错\n    return REDIS_ERR;\n}\n\n// 处理客户端输入的命令内容\nvoid processInputBuffer(redisClient *c) {\n\n    /* Keep processing while there is something in the input buffer */\n    // 尽可能地处理查询缓冲区中的内容\n    // 如果读取出现 short read ，那么可能会有内容滞留在读取缓冲区里面\n    // 这些滞留内容也许不能完整构成一个符合协议的命令，\n    // 需要等待下次读事件的就绪\n    while(sdslen(c->querybuf)) {\n\n        /* Return if clients are paused. */\n        // 如果客户端正处于暂停状态，那么直接返回\n        if (!(c->flags & REDIS_SLAVE) && clientsArePaused()) return;\n\n        /* Immediately abort if the client is in the middle of something. */\n        // REDIS_BLOCKED 状态表示客户端正在被阻塞\n        if (c->flags & REDIS_BLOCKED) return;\n\n        /* REDIS_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        // 客户端已经设置了关闭 FLAG ，没有必要处理命令了\n        if (c->flags & REDIS_CLOSE_AFTER_REPLY) return;\n\n        /* Determine request type when unknown. */\n        // 判断请求的类型\n        // 两种类型的区别可以在 Redis 的通讯协议上查到：\n        // http://redis.readthedocs.org/en/latest/topic/protocol.html\n        // 简单来说，多条查询是一般客户端发送来的，\n        // 而内联查询则是 TELNET 发送来的\n        if (!c->reqtype) {\n            if (c->querybuf[0] == '*') {\n                // 多条查询\n                c->reqtype = REDIS_REQ_MULTIBULK;\n            } else {\n                // 内联查询\n                c->reqtype = REDIS_REQ_INLINE;\n            }\n        }\n\n        // 将缓冲区中的内容转换成命令，以及命令参数\n        if (c->reqtype == REDIS_REQ_INLINE) {\n            if (processInlineBuffer(c) != REDIS_OK) break;\n        } else if (c->reqtype == REDIS_REQ_MULTIBULK) {\n            if (processMultibulkBuffer(c) != REDIS_OK) break;\n        } else {\n            redisPanic(\"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            // 执行命令，并重置客户端\n            if (processCommand(c) == REDIS_OK)\n                resetClient(c);\n        }\n    }\n}\n\n/*\n * 读取客户端的查询缓冲区内容\n */\nvoid readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask) {\n    redisClient *c = (redisClient*) privdata;\n    int nread, readlen;\n    size_t qblen;\n    REDIS_NOTUSED(el);\n    REDIS_NOTUSED(mask);\n\n    // 设置服务器的当前客户端\n    server.current_client = c;\n    \n    // 读入长度（默认为 16 MB）\n    readlen = REDIS_IOBUF_LEN;\n\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 == REDIS_REQ_MULTIBULK && c->multibulklen && c->bulklen != -1\n        && c->bulklen >= REDIS_MBULK_BIG_ARG)\n    {\n        int remaining = (unsigned)(c->bulklen+2)-sdslen(c->querybuf);\n\n        if (remaining < readlen) readlen = remaining;\n    }\n\n    // 获取查询缓冲区当前内容的长度\n    // 如果读取出现 short read ，那么可能会有内容滞留在读取缓冲区里面\n    // 这些滞留内容也许不能完整构成一个符合协议的命令，\n    qblen = sdslen(c->querybuf);\n    // 如果有需要，更新缓冲区内容长度的峰值（peak）\n    if (c->querybuf_peak < qblen) c->querybuf_peak = qblen;\n    // 为查询缓冲区分配空间\n    c->querybuf = sdsMakeRoomFor(c->querybuf, readlen);\n    // 读入内容到查询缓存\n    nread = read(fd, c->querybuf+qblen, readlen);\n\n    // 读入出错\n    if (nread == -1) {\n        if (errno == EAGAIN) {\n            nread = 0;\n        } else {\n            redisLog(REDIS_VERBOSE, \"Reading from client: %s\",strerror(errno));\n            freeClient(c);\n            return;\n        }\n    // 遇到 EOF\n    } else if (nread == 0) {\n        redisLog(REDIS_VERBOSE, \"Client closed connection\");\n        freeClient(c);\n        return;\n    }\n\n    if (nread) {\n        // 根据内容，更新查询缓冲区（SDS） free 和 len 属性\n        // 并将 '\\0' 正确地放到内容的最后\n        sdsIncrLen(c->querybuf,nread);\n        // 记录服务器和客户端最后一次互动的时间\n        c->lastinteraction = server.unixtime;\n        // 如果客户端是 master 的话，更新它的复制偏移量\n        if (c->flags & REDIS_MASTER) c->reploff += nread;\n    } else {\n        // 在 nread == -1 且 errno == EAGAIN 时运行\n        server.current_client = NULL;\n        return;\n    }\n\n    // 查询缓冲区长度超出服务器最大缓冲区长度\n    // 清空缓冲区并释放客户端\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        redisLog(REDIS_WARNING,\"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\n    // 从查询缓存重读取内容，创建参数，并执行命令\n    // 函数会执行到缓存中的所有内容都被处理完为止\n    processInputBuffer(c);\n\n    server.current_client = NULL;\n}\n\n// 获取客户端目前最大的一块缓冲区的大小\nvoid getClientsMaxBuffers(unsigned long *longest_output_list,\n                          unsigned long *biggest_input_buffer) {\n    redisClient *c;\n    listNode *ln;\n    listIter li;\n    unsigned long lol = 0, bib = 0;\n\n    listRewind(server.clients,&li);\n    while ((ln = listNext(&li)) != NULL) {\n        c = listNodeValue(ln);\n\n        if (listLength(c->reply) > lol) lol = listLength(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/* This is a helper function for genClientPeerId().\n * It writes the specified ip/port to \"peerid\" as a null termiated string\n * in the form ip:port if ip does not contain \":\" itself, otherwise\n * [ip]:port format is used (for IPv6 addresses basically). */\nvoid formatPeerId(char *peerid, size_t peerid_len, char *ip, int port) {\n    if (strchr(ip,':'))\n        snprintf(peerid,peerid_len,\"[%s]:%d\",ip,port);\n    else\n        snprintf(peerid,peerid_len,\"%s:%d\",ip,port);\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:pork, example: \"127.0.0.1:1234\".\n * For IPv6 addresses we use [] around the IP part, like in \"[::1]:1234\".\n * For Unix socekts we use path:0, like in \"/tmp/redis:0\".\n *\n * A Peer ID always fits inside a buffer of REDIS_PEER_ID_LEN bytes, including\n * the null term.\n *\n * The function returns REDIS_OK on succcess, and REDIS_ERR on failure.\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). */\nint genClientPeerId(redisClient *client, char *peerid, size_t peerid_len) {\n    char ip[REDIS_IP_STR_LEN];\n    int port;\n\n    if (client->flags & REDIS_UNIX_SOCKET) {\n        /* Unix socket client. */\n        snprintf(peerid,peerid_len,\"%s:0\",server.unixsocket);\n        return REDIS_OK;\n    } else {\n        /* TCP client. */\n        int retval = anetPeerToString(client->fd,ip,sizeof(ip),&port);\n        formatPeerId(peerid,peerid_len,ip,port);\n        return (retval == -1) ? REDIS_ERR : REDIS_OK;\n    }\n}\n\n/* This function returns the client peer id, by creating and caching it\n * if client->perrid 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(redisClient *c) {\n    char peerid[REDIS_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'. */\n// 获取客户端的各项信息，将它们储存到 sds 值 s 里面，并返回。\nsds catClientInfoString(sds s, redisClient *client) {\n    char flags[16], events[3], *p;\n    int emask;\n\n    p = flags;\n    if (client->flags & REDIS_SLAVE) {\n        if (client->flags & REDIS_MONITOR)\n            *p++ = 'O';\n        else\n            *p++ = 'S';\n    }\n    if (client->flags & REDIS_MASTER) *p++ = 'M';\n    if (client->flags & REDIS_MULTI) *p++ = 'x';\n    if (client->flags & REDIS_BLOCKED) *p++ = 'b';\n    if (client->flags & REDIS_DIRTY_CAS) *p++ = 'd';\n    if (client->flags & REDIS_CLOSE_AFTER_REPLY) *p++ = 'c';\n    if (client->flags & REDIS_UNBLOCKED) *p++ = 'u';\n    if (client->flags & REDIS_CLOSE_ASAP) *p++ = 'A';\n    if (client->flags & REDIS_UNIX_SOCKET) *p++ = 'U';\n    if (client->flags & REDIS_READONLY) *p++ = 'r';\n    if (p == flags) *p++ = 'N';\n    *p++ = '\\0';\n\n    emask = client->fd == -1 ? 0 : aeGetFileEvents(server.el,client->fd);\n    p = events;\n    if (emask & AE_READABLE) *p++ = 'r';\n    if (emask & AE_WRITABLE) *p++ = 'w';\n    *p = '\\0';\n    return sdscatfmt(s,\n        \"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        getClientPeerId(client),\n        client->fd,\n        client->name ? (char*)client->name->ptr : \"\",\n        (long long)(server.unixtime - client->ctime),\n        (long long)(server.unixtime - client->lastinteraction),\n        flags,\n        client->db->id,\n        (int) dictSize(client->pubsub_channels),\n        (int) listLength(client->pubsub_patterns),\n        (client->flags & REDIS_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) listLength(client->reply),\n        (unsigned long long) getClientOutputBufferMemoryUsage(client),\n        events,\n        client->lastcmd ? client->lastcmd->name : \"NULL\");\n}\n\n/*\n * 打印出所有连接到服务器的客户端的信息\n */\nsds getAllClientsInfoString(void) {\n    listNode *ln;\n    listIter li;\n    redisClient *client;\n    sds o = sdsempty();\n\n    o = sdsMakeRoomFor(o,200*listLength(server.clients));\n    listRewind(server.clients,&li);\n    while ((ln = listNext(&li)) != NULL) {\n        client = listNodeValue(ln);\n        o = catClientInfoString(o,client);\n        o = sdscatlen(o,\"\\n\",1);\n    }\n    return o;\n}\n\n/*\n * CLIENT 命令的实现\n */\nvoid clientCommand(redisClient *c) {\n    listNode *ln;\n    listIter li;\n    redisClient *client;\n\n    // CLIENT list\n    if (!strcasecmp(c->argv[1]->ptr,\"list\") && c->argc == 2) {\n        sds o = getAllClientsInfoString();\n        addReplyBulkCBuffer(c,o,sdslen(o));\n        sdsfree(o);\n\n    // CLIENT kill\n    } else if (!strcasecmp(c->argv[1]->ptr,\"kill\") && c->argc == 3) {\n\n        // 遍历客户端链表，并杀死指定地址的客户端\n        listRewind(server.clients,&li);\n        while ((ln = listNext(&li)) != NULL) {\n            char *peerid;\n\n            client = listNodeValue(ln);\n            peerid = getClientPeerId(client);\n            if (strcmp(peerid,c->argv[2]->ptr) == 0) {\n                addReply(c,shared.ok);\n                if (c == client) {\n                    client->flags |= REDIS_CLOSE_AFTER_REPLY;\n                } else {\n                    freeClient(client);\n                }\n                return;\n            }\n        }\n        addReplyError(c,\"No such client\");\n\n    // CLIENT setname 设置客户端名字\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        // 名字为空时，清空客户端的名字\n        if (len == 0) {\n            if (c->name) decrRefCount(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) decrRefCount(c->name);\n        c->name = c->argv[2];\n        incrRefCount(c->name);\n        addReply(c,shared.ok);\n\n    // CLIENT getname 获取客户端的名字\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    } 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                                        != REDIS_OK) return;\n        pauseClients(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 ref count\n * is incremented. The old command vector is freed, and the old objects\n * ref count is decremented. */\n// 修改客户端的参数数组\nvoid rewriteClientCommandVector(redisClient *c, int argc, ...) {\n    va_list ap;\n    int j;\n    robj **argv; /* The new argument vector */\n\n    // 创建新参数\n    argv = zmalloc(sizeof(robj*)*argc);\n    va_start(ap,argc);\n    for (j = 0; j < argc; j++) {\n        robj *a;\n        \n        a = va_arg(ap, robj*);\n        argv[j] = a;\n        incrRefCount(a);\n    }\n    /* We free the objects in the original vector at the end, so we are\n     * sure that if the same objects are reused in the new vector the\n     * refcount gets incremented before it gets decremented. */\n    // 释放旧参数\n    for (j = 0; j < c->argc; j++) decrRefCount(c->argv[j]);\n    zfree(c->argv);\n\n    /* Replace argv and argc with our new versions. */\n    // 用新参数替换\n    c->argv = argv;\n    c->argc = argc;\n    c->cmd = lookupCommandOrOriginal(c->argv[0]->ptr);\n    redisAssertWithInfo(c,NULL,c->cmd != NULL);\n    va_end(ap);\n}\n\n/* Rewrite a single item in the command vector.\n * The new val ref count is incremented, and the old decremented. */\n// 修改单个参数\nvoid rewriteClientCommandArgument(redisClient *c, int i, robj *newval) {\n    robj *oldval;\n   \n    redisAssertWithInfo(c,NULL,i < c->argc);\n    oldval = c->argv[i];\n    c->argv[i] = newval;\n    incrRefCount(newval);\n    decrRefCount(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        redisAssertWithInfo(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 * 函数返回客用于保存目前仍未返回给客户端的回复的虚拟大小（以字节为单位）。\n * 之所以说是虚拟大小，因为回复列表中可能有包含共享的对象。\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 * 函数返回回复列表中所包含的全部对象的体积总和，\n * 加上列表节点所分配的空间。\n * 静态回复缓冲区不会被计算在内，因为它总是会被分配的。\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. \n *\n * 注意：这个函数的速度很快，所以它可以被随意地调用多次。\n * 这个函数目前的主要作用就是用来强制客户端输出长度限制。\n */\nunsigned long getClientOutputBufferMemoryUsage(redisClient *c) {\n    unsigned long list_item_size = sizeof(listNode)+sizeof(robj);\n\n    return c->reply_bytes + (list_item_size*listLength(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 * 获取客户端的类型，用于对不同类型的客户端应用不同的限制。\n *\n * The function will return one of the following:\n * \n * 函数将返回以下三个值的其中一个：\n *\n * REDIS_CLIENT_LIMIT_CLASS_NORMAL -> Normal client\n *                                    普通客户端\n *\n * REDIS_CLIENT_LIMIT_CLASS_SLAVE  -> Slave or client executing MONITOR command\n *                                    从服务器，或者正在执行 MONITOR 命令的客户端\n *\n * REDIS_CLIENT_LIMIT_CLASS_PUBSUB -> Client subscribed to Pub/Sub channels\n *                                    正在进行订阅操作（SUBSCRIBE/PSUBSCRIBE）的客户端\n */\nint getClientLimitClass(redisClient *c) {\n    if (c->flags & REDIS_SLAVE) return REDIS_CLIENT_LIMIT_CLASS_SLAVE;\n    if (dictSize(c->pubsub_channels) || listLength(c->pubsub_patterns))\n        return REDIS_CLIENT_LIMIT_CLASS_PUBSUB;\n    return REDIS_CLIENT_LIMIT_CLASS_NORMAL;\n}\n\n// 根据名字，获取客户端的类型常量\nint getClientLimitClassByName(char *name) {\n    if (!strcasecmp(name,\"normal\")) return REDIS_CLIENT_LIMIT_CLASS_NORMAL;\n    else if (!strcasecmp(name,\"slave\")) return REDIS_CLIENT_LIMIT_CLASS_SLAVE;\n    else if (!strcasecmp(name,\"pubsub\")) return REDIS_CLIENT_LIMIT_CLASS_PUBSUB;\n    else return -1;\n}\n\n// 根据客户端的类型，获取名字\nchar *getClientLimitClassName(int class) {\n    switch(class) {\n    case REDIS_CLIENT_LIMIT_CLASS_NORMAL:   return \"normal\";\n    case REDIS_CLIENT_LIMIT_CLASS_SLAVE:    return \"slave\";\n    case REDIS_CLIENT_LIMIT_CLASS_PUBSUB:   return \"pubsub\";\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 * 这个函数检查客户端是否达到了输出缓冲区的软性（soft）限制或者硬性（hard）限制，\n * 并在到达软限制时，对客户端进行标记。\n *\n * Return value: non-zero if the client reached the soft or the hard limit.\n *               Otherwise zero is returned. \n *\n * 返回值：到达软性限制或者硬性限制时，返回非 0 值。\n *         否则返回 0 。\n */\nint checkClientOutputBufferLimits(redisClient *c) {\n    int soft = 0, hard = 0, class;\n\n    // 获取客户端回复缓冲区的大小\n    unsigned long used_mem = getClientOutputBufferMemoryUsage(c);\n\n    // 获取客户端的限制大小\n    class = getClientLimitClass(c);\n\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\n    // 检查硬性限制\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    // 达到软性限制\n    if (soft) {\n\n        // 第一次达到软性限制\n        if (c->obuf_soft_limit_reached_time == 0) {\n            // 记录时间\n            c->obuf_soft_limit_reached_time = server.unixtime;\n            // 关闭软性限制 flag\n            soft = 0; /* First time we see the soft limit reached */\n\n        // 再次达到软性限制\n        } else {\n            // 软性限制的连续时长\n            time_t elapsed = server.unixtime - c->obuf_soft_limit_reached_time;\n\n            // 如果没有超过最大连续时长的话，那么关闭软性限制 flag\n            // 如果超过了最大连续时长的话，软性限制 flag 就会被保留\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        // 未达到软性限制，或者已脱离软性限制，那么清空软性限制的进入时间\n        c->obuf_soft_limit_reached_time = 0;\n    }\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 REDIS_CLOSE_ASAP flag is set.\n *\n * 如果客户端达到缓冲区大小的软性或者硬性限制，那么打开客户端的 ``REDIS_CLOSE_ASAP`` 状态，\n * 让服务器异步地关闭客户端。\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. \n *\n * 注意：\n * 我们不能直接关闭客户端，而要异步关闭的原因是客户端正处于一个不能被安全地关闭的上下文中。\n * 比如说，可能有底层函数正在推入数据到客户端的输出缓冲区里面。      \n */\nvoid asyncCloseClientOnOutputBufferLimitReached(redisClient *c) {\n    redisAssert(c->reply_bytes < ULONG_MAX-(1024*64));\n\n    // 已经被标记了\n    if (c->reply_bytes == 0 || c->flags & REDIS_CLOSE_ASAP) return;\n\n    // 检查限制\n    if (checkClientOutputBufferLimits(c)) {\n        sds client = catClientInfoString(sdsempty(),c);\n\n        // 异步关闭\n        freeClientAsync(c);\n        redisLog(REDIS_WARNING,\"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// freeMemoryIfNeeded() 函数的辅助函数，\n// 用于在不进入事件循环的情况下，冲洗所有从服务器的输出缓冲区。\nvoid flushSlavesOutputBuffers(void) {\n    listIter li;\n    listNode *ln;\n\n    listRewind(server.slaves,&li);\n    while((ln = listNext(&li))) {\n        redisClient *slave = listNodeValue(ln);\n        int events;\n\n        events = aeGetFileEvents(server.el,slave->fd);\n        if (events & AE_WRITABLE &&\n            slave->replstate == REDIS_REPL_ONLINE &&\n            listLength(slave->reply))\n        {\n            sendReplyToClient(server.el,slave->fd,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. */\n// 暂停客户端，让服务器在指定的时间内不再接受被暂停客户端发来的命令\n// 可以用于系统更新，并在内部由 CLUSTER FAILOVER 命令使用。\nvoid pauseClients(mstime_t end) {\n\n    // 设置暂停时间\n    if (!server.clients_paused || end > server.clients_pause_end_time)\n        server.clients_pause_end_time = end;\n\n    // 打开客户端的“已被暂停”标志\n    server.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. */\n // 判断服务器目前被暂停客户端的数量，没有任何客户端被暂停时，返回 0 。\nint clientsArePaused(void) {\n    if (server.clients_paused && server.clients_pause_end_time < server.mstime) {\n        listNode *ln;\n        listIter li;\n        redisClient *c;\n\n        server.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        listRewind(server.clients,&li);\n        while ((ln = listNext(&li)) != NULL) {\n            c = listNodeValue(ln);\n\n            if (c->flags & REDIS_SLAVE) continue;\n            listAddNodeTail(server.unblocked_clients,c);\n        }\n    }\n    return server.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 for 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. */\n// 让服务器在被阻塞的情况下，仍然处理某些事件。\nint processEventsWhileBlocked(void) {\n    int iterations = 4; /* See the function top-comment. */\n    int count = 0;\n    while (iterations--) {\n        int events = aeProcessEvents(server.el, AE_FILE_EVENTS|AE_DONT_WAIT);\n        if (!events) break;\n        count += events;\n    }\n    return count;\n}\n"
  },
  {
    "path": "src/notify.c",
    "content": "/*\n * Copyright (c) 2013, 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 \"redis.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 * 对传入的字符串参数进行分析， 给出相应的 flags 值\n *\n * The function returns -1 if the input contains characters not mapping to\n * any class. \n *\n * 如果传入的字符串中有不能识别的字符串，那么返回 -1 。\n */\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 |= REDIS_NOTIFY_ALL; break;\n        case 'g': flags |= REDIS_NOTIFY_GENERIC; break;\n        case '$': flags |= REDIS_NOTIFY_STRING; break;\n        case 'l': flags |= REDIS_NOTIFY_LIST; break;\n        case 's': flags |= REDIS_NOTIFY_SET; break;\n        case 'h': flags |= REDIS_NOTIFY_HASH; break;\n        case 'z': flags |= REDIS_NOTIFY_ZSET; break;\n        case 'x': flags |= REDIS_NOTIFY_EXPIRED; break;\n        case 'e': flags |= REDIS_NOTIFY_EVICTED; break;\n        case 'K': flags |= REDIS_NOTIFY_KEYSPACE; break;\n        case 'E': flags |= REDIS_NOTIFY_KEYEVENT; break;\n        // 不能识别\n        default: return -1;\n        }\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(). */\n/*\n * 根据 flags 值还原设置这个 flags 所需的字符串\n */\nsds keyspaceEventsFlagsToString(int flags) {\n    sds res;\n\n    res = sdsempty();\n    if ((flags & REDIS_NOTIFY_ALL) == REDIS_NOTIFY_ALL) {\n        res = sdscatlen(res,\"A\",1);\n    } else {\n        if (flags & REDIS_NOTIFY_GENERIC) res = sdscatlen(res,\"g\",1);\n        if (flags & REDIS_NOTIFY_STRING) res = sdscatlen(res,\"$\",1);\n        if (flags & REDIS_NOTIFY_LIST) res = sdscatlen(res,\"l\",1);\n        if (flags & REDIS_NOTIFY_SET) res = sdscatlen(res,\"s\",1);\n        if (flags & REDIS_NOTIFY_HASH) res = sdscatlen(res,\"h\",1);\n        if (flags & REDIS_NOTIFY_ZSET) res = sdscatlen(res,\"z\",1);\n        if (flags & REDIS_NOTIFY_EXPIRED) res = sdscatlen(res,\"x\",1);\n        if (flags & REDIS_NOTIFY_EVICTED) res = sdscatlen(res,\"e\",1);\n    }\n    if (flags & REDIS_NOTIFY_KEYSPACE) res = sdscatlen(res,\"K\",1);\n    if (flags & REDIS_NOTIFY_KEYEVENT) res = sdscatlen(res,\"E\",1);\n\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 *\n * event 参数是一个字符串表示的事件名\n *\n * 'key' is a Redis object representing the key name.\n *\n * key 参数是一个 Redis 对象表示的键名\n *\n * 'dbid' is the database ID where the key lives.  \n *\n * dbid 参数为键所在的数据库\n */\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    // 如果服务器配置为不发送 type 类型的通知，那么直接返回\n    if (!(server.notify_keyspace_events & type)) return;\n\n    // 事件的名字\n    eventobj = createStringObject(event,strlen(event));\n\n    /* __keyspace@<db>__:<key> <event> notifications. */\n    // 发送键空间通知\n    if (server.notify_keyspace_events & REDIS_NOTIFY_KEYSPACE) {\n\n        // 构建频道对象\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\n        chanobj = createObject(REDIS_STRING, chan);\n\n        // 通过 publish 命令发送通知\n        pubsubPublishMessage(chanobj, eventobj);\n\n        // 释放频道对象\n        decrRefCount(chanobj);\n    }\n\n    /* __keyevente@<db>__:<event> <key> notifications. */\n    // 发送键事件通知\n    if (server.notify_keyspace_events & REDIS_NOTIFY_KEYEVENT) {\n\n        // 构建频道对象\n        chan = sdsnewlen(\"__keyevent@\",11);\n        // 如果在前面发送键空间通知的时候计算了 len ，那么它就不会是 -1\n        // 这可以避免计算两次 buf 的长度\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\n        chanobj = createObject(REDIS_STRING, chan);\n\n        // 通过 publish 命令发送通知\n        pubsubPublishMessage(chanobj, key);\n\n        // 释放频道对象\n        decrRefCount(chanobj);\n    }\n\n    // 释放事件对象\n    decrRefCount(eventobj);\n}\n"
  },
  {
    "path": "src/object.c",
    "content": "/* Redis Object implementation.\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#include \"redis.h\"\n#include <math.h>\n#include <ctype.h>\n\n/*\n * 创建一个新 robj 对象\n */\nrobj *createObject(int type, void *ptr) {\n\n    robj *o = zmalloc(sizeof(*o));\n\n    o->type = type;\n    o->encoding = REDIS_ENCODING_RAW;\n    o->ptr = ptr;\n    o->refcount = 1;\n\n    /* Set the LRU to the current lruclock (minutes resolution). */\n    o->lru = LRU_CLOCK();\n    return o;\n}\n\n/* Create a string object with encoding REDIS_ENCODING_RAW, that is a plain\n * string object where o->ptr points to a proper sds string. */\n// 创建一个 REDIS_ENCODING_RAW 编码的字符对象\n// 对象的指针指向一个 sds 结构\nrobj *createRawStringObject(char *ptr, size_t len) {\n    return createObject(REDIS_STRING,sdsnewlen(ptr,len));\n}\n\n/* Create a string object with encoding REDIS_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. */\n// 创建一个 REDIS_ENCODING_EMBSTR 编码的字符对象\n// 这个字符串对象中的 sds 会和字符串对象的 redisObject 结构一起分配\n// 因此这个字符也是不可修改的\nrobj *createEmbeddedStringObject(char *ptr, size_t len) {\n    robj *o = zmalloc(sizeof(robj)+sizeof(struct sdshdr)+len+1);\n    struct sdshdr *sh = (void*)(o+1);\n\n    o->type = REDIS_STRING;\n    o->encoding = REDIS_ENCODING_EMBSTR;\n    o->ptr = sh+1;\n    o->refcount = 1;\n    o->lru = LRU_CLOCK();\n\n    sh->len = len;\n    sh->free = 0;\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 REDIS_ENCODING_EMBSTR_SIZE_LIMIT 39\nrobj *createStringObject(char *ptr, size_t len) {\n    if (len <= REDIS_ENCODING_EMBSTR_SIZE_LIMIT)\n        return createEmbeddedStringObject(ptr,len);\n    else\n        return createRawStringObject(ptr,len);\n}\n\n/*\n * 根据传入的整数值，创建一个字符串对象\n *\n * 这个字符串的对象保存的可以是 INT 编码的 long 值，\n * 也可以是 RAW 编码的、被转换成字符串的 long long 值。\n */\nrobj *createStringObjectFromLongLong(long long value) {\n\n    robj *o;\n\n    // value 的大小符合 REDIS 共享整数的范围\n    // 那么返回一个共享对象\n    if (value >= 0 && value < REDIS_SHARED_INTEGERS) {\n        incrRefCount(shared.integers[value]);\n        o = shared.integers[value];\n\n    // 不符合共享范围，创建一个新的整数对象\n    } else {\n        // 值可以用 long 类型保存，\n        // 创建一个 REDIS_ENCODING_INT 编码的字符串对象\n        if (value >= LONG_MIN && value <= LONG_MAX) {\n            o = createObject(REDIS_STRING, NULL);\n            o->encoding = REDIS_ENCODING_INT;\n            o->ptr = (void*)((long)value);\n\n        // 值不能用 long 类型保存（long long 类型），将值转换为字符串，\n        // 并创建一个 REDIS_ENCODING_RAW 的字符串对象来保存值\n        } else {\n            o = createObject(REDIS_STRING,sdsfromlonglong(value));\n        }\n    }\n\n    return o;\n}\n\n/* Note: this function is defined into object.c since here it is where it\n * belongs but it is actually designed to be used just for INCRBYFLOAT */\n/*\n * 根据传入的 long double 值，为它创建一个字符串对象\n *\n * 对象将 long double 转换为字符串来保存\n */\nrobj *createStringObjectFromLongDouble(long double value) {\n    char buf[256];\n    int len;\n\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 way\n     * that is \"non surprising\" for the user (that is, most small decimal\n     * numbers will be represented in a way that when converted back into\n     * a string are exactly the same as what the user typed.) */\n    // 使用 17 位小数精度，这种精度可以在大部分机器上被 rounding 而不改变\n    len = snprintf(buf,sizeof(buf),\"%.17Lf\", value);\n\n    /* Now remove trailing zeroes after the '.' */\n    // 移除尾部的 0 \n    // 比如 3.1400000 将变成 3.14\n    // 而 3.00000 将变成 3\n    if (strchr(buf,'.') != NULL) {\n        char *p = buf+len-1;\n        while(*p == '0') {\n            p--;\n            len--;\n        }\n        // 如果不需要小数点，那么移除它\n        if (*p == '.') len--;\n    }\n\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 * 复制一个字符串对象，复制出的对象和输入对象拥有相同编码。\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 * 另外，\n * 这个函数在复制一个包含整数值的字符串对象时，总是产生一个非共享的对象。\n *\n * The resulting object always has refcount set to 1. \n *\n * 输出对象的 refcount 总为 1 。\n */\nrobj *dupStringObject(robj *o) {\n    robj *d;\n\n    redisAssert(o->type == REDIS_STRING);\n\n    switch(o->encoding) {\n\n    case REDIS_ENCODING_RAW:\n        return createRawStringObject(o->ptr,sdslen(o->ptr));\n\n    case REDIS_ENCODING_EMBSTR:\n        return createEmbeddedStringObject(o->ptr,sdslen(o->ptr));\n\n    case REDIS_ENCODING_INT:\n        d = createObject(REDIS_STRING, NULL);\n        d->encoding = REDIS_ENCODING_INT;\n        d->ptr = o->ptr;\n        return d;\n\n    default:\n        redisPanic(\"Wrong encoding.\");\n        break;\n    }\n}\n\n/*\n * 创建一个 LINKEDLIST 编码的列表对象\n */\nrobj *createListObject(void) {\n\n    list *l = listCreate();\n\n    robj *o = createObject(REDIS_LIST,l);\n\n    listSetFreeMethod(l,decrRefCountVoid);\n\n    o->encoding = REDIS_ENCODING_LINKEDLIST;\n\n    return o;\n}\n\n/*\n * 创建一个 ZIPLIST 编码的列表对象\n */\nrobj *createZiplistObject(void) {\n\n    unsigned char *zl = ziplistNew();\n\n    robj *o = createObject(REDIS_LIST,zl);\n\n    o->encoding = REDIS_ENCODING_ZIPLIST;\n\n    return o;\n}\n\n/*\n * 创建一个 SET 编码的集合对象\n */\nrobj *createSetObject(void) {\n\n    dict *d = dictCreate(&setDictType,NULL);\n\n    robj *o = createObject(REDIS_SET,d);\n\n    o->encoding = REDIS_ENCODING_HT;\n\n    return o;\n}\n\n/*\n * 创建一个 INTSET 编码的集合对象\n */\nrobj *createIntsetObject(void) {\n\n    intset *is = intsetNew();\n\n    robj *o = createObject(REDIS_SET,is);\n\n    o->encoding = REDIS_ENCODING_INTSET;\n\n    return o;\n}\n\n/*\n * 创建一个 ZIPLIST 编码的哈希对象\n */\nrobj *createHashObject(void) {\n\n    unsigned char *zl = ziplistNew();\n\n    robj *o = createObject(REDIS_HASH, zl);\n\n    o->encoding = REDIS_ENCODING_ZIPLIST;\n\n    return o;\n}\n\n/*\n * 创建一个 SKIPLIST 编码的有序集合\n */\nrobj *createZsetObject(void) {\n\n    zset *zs = zmalloc(sizeof(*zs));\n\n    robj *o;\n\n    zs->dict = dictCreate(&zsetDictType,NULL);\n    zs->zsl = zslCreate();\n\n    o = createObject(REDIS_ZSET,zs);\n\n    o->encoding = REDIS_ENCODING_SKIPLIST;\n\n    return o;\n}\n\n/*\n * 创建一个 ZIPLIST 编码的有序集合\n */\nrobj *createZsetZiplistObject(void) {\n\n    unsigned char *zl = ziplistNew();\n\n    robj *o = createObject(REDIS_ZSET,zl);\n\n    o->encoding = REDIS_ENCODING_ZIPLIST;\n\n    return o;\n}\n\n/*\n * 释放字符串对象\n */\nvoid freeStringObject(robj *o) {\n    if (o->encoding == REDIS_ENCODING_RAW) {\n        sdsfree(o->ptr);\n    }\n}\n\n/*\n * 释放列表对象\n */\nvoid freeListObject(robj *o) {\n\n    switch (o->encoding) {\n\n    case REDIS_ENCODING_LINKEDLIST:\n        listRelease((list*) o->ptr);\n        break;\n\n    case REDIS_ENCODING_ZIPLIST:\n        zfree(o->ptr);\n        break;\n\n    default:\n        redisPanic(\"Unknown list encoding type\");\n    }\n}\n\n/*\n * 释放集合对象\n */\nvoid freeSetObject(robj *o) {\n\n    switch (o->encoding) {\n\n    case REDIS_ENCODING_HT:\n        dictRelease((dict*) o->ptr);\n        break;\n\n    case REDIS_ENCODING_INTSET:\n        zfree(o->ptr);\n        break;\n\n    default:\n        redisPanic(\"Unknown set encoding type\");\n    }\n}\n\n/*\n * 释放有序集合对象\n */\nvoid freeZsetObject(robj *o) {\n\n    zset *zs;\n\n    switch (o->encoding) {\n\n    case REDIS_ENCODING_SKIPLIST:\n        zs = o->ptr;\n        dictRelease(zs->dict);\n        zslFree(zs->zsl);\n        zfree(zs);\n        break;\n\n    case REDIS_ENCODING_ZIPLIST:\n        zfree(o->ptr);\n        break;\n\n    default:\n        redisPanic(\"Unknown sorted set encoding\");\n    }\n}\n\n/*\n * 释放哈希对象\n */\nvoid freeHashObject(robj *o) {\n\n    switch (o->encoding) {\n\n    case REDIS_ENCODING_HT:\n        dictRelease((dict*) o->ptr);\n        break;\n\n    case REDIS_ENCODING_ZIPLIST:\n        zfree(o->ptr);\n        break;\n\n    default:\n        redisPanic(\"Unknown hash encoding type\");\n        break;\n    }\n}\n\n/*\n * 为对象的引用计数增一\n */\nvoid incrRefCount(robj *o) {\n    o->refcount++;\n}\n\n/*\n * 为对象的引用计数减一\n *\n * 当对象的引用计数降为 0 时，释放对象。\n */\nvoid decrRefCount(robj *o) {\n\n    if (o->refcount <= 0) redisPanic(\"decrRefCount against refcount <= 0\");\n\n    // 释放对象\n    if (o->refcount == 1) {\n        switch(o->type) {\n        case REDIS_STRING: freeStringObject(o); break;\n        case REDIS_LIST: freeListObject(o); break;\n        case REDIS_SET: freeSetObject(o); break;\n        case REDIS_ZSET: freeZsetObject(o); break;\n        case REDIS_HASH: freeHashObject(o); break;\n        default: redisPanic(\"Unknown object type\"); break;\n        }\n        zfree(o);\n\n    // 减少计数\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. \n *\n * 作用于特定数据结构的释放函数包装\n */\nvoid decrRefCountVoid(void *o) {\n    decrRefCount(o);\n}\n\n/* This function set the ref count to zero without freeing the object.\n *\n * 这个函数将对象的引用计数设为 0 ，但并不释放对象。\n *\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 * 这个函数在将一个对象传入一个会增加引用计数的函数中时，非常有用。\n * 就像这样：\n *\n *    functionThatWillIncrementRefCount(resetRefCount(CreateObject(...)));\n *\n * Otherwise you need to resort to the less elegant pattern:\n *\n * 没有这个函数的话，事情就会比较麻烦了：\n *\n *    *obj = createObject(...);\n *    functionThatWillIncrementRefCount(obj);\n *    decrRefCount(obj);\n */\nrobj *resetRefCount(robj *obj) {\n    obj->refcount = 0;\n    return obj;\n}\n\n/*\n * 检查对象 o 的类型是否和 type 相同：\n *\n *  - 相同返回 0 \n *\n *  - 不相同返回 1 ，并向客户端回复一个错误\n */\nint checkType(redisClient *c, robj *o, int type) {\n\n    if (o->type != type) {\n        addReply(c,shared.wrongtypeerr);\n        return 1;\n    }\n\n    return 0;\n}\n\n/*\n * 检查对象 o 中的值能否表示为 long long 类型：\n *\n *  - 可以则返回 REDIS_OK ，并将 long long 值保存到 *llval 中。\n *\n *  - 不可以则返回 REDIS_ERR\n */\nint isObjectRepresentableAsLongLong(robj *o, long long *llval) {\n\n    redisAssertWithInfo(NULL,o,o->type == REDIS_STRING);\n\n    // INT 编码的 long 值总是能保存为 long long\n    if (o->encoding == REDIS_ENCODING_INT) {\n        if (llval) *llval = (long) o->ptr;\n        return REDIS_OK;\n\n    // 如果是字符串的话，那么尝试将它转换为 long long\n    } else {\n        return string2ll(o->ptr,sdslen(o->ptr),llval) ? REDIS_OK : REDIS_ERR;\n    }\n}\n\n/* Try to encode a string object in order to save space */\n// 尝试对字符串对象进行编码，以节约内存。\nrobj *tryObjectEncoding(robj *o) {\n    long value;\n\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    redisAssertWithInfo(NULL,o,o->type == REDIS_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    // 只在字符串的编码为 RAW 或者 EMBSTR 时尝试进行编码\n    if (!sdsEncodedObject(o)) return o;\n\n    /* It's not safe to encode shared objects: shared objects can be shared\n     * everywhere in the \"object space\" of Redis and may end in places where\n     * they are not handled. We handle them only as values in the keyspace. */\n     // 不对共享对象进行编码\n     if (o->refcount > 1) 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    // 对字符串进行检查\n    // 只对长度小于或等于 21 字节，并且可以被解释为整数的字符串进行编码\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        if (server.maxmemory == 0 &&\n            value >= 0 &&\n            value < REDIS_SHARED_INTEGERS)\n        {\n            decrRefCount(o);\n            incrRefCount(shared.integers[value]);\n            return shared.integers[value];\n        } else {\n            if (o->encoding == REDIS_ENCODING_RAW) sdsfree(o->ptr);\n            o->encoding = REDIS_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    // 尝试将 RAW 编码的字符串编码为 EMBSTR 编码\n    if (len <= REDIS_ENCODING_EMBSTR_SIZE_LIMIT) {\n        robj *emb;\n\n        if (o->encoding == REDIS_ENCODING_EMBSTR) return o;\n        emb = createEmbeddedStringObject(s,sdslen(s));\n        decrRefCount(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     * REDIS_ENCODING_EMBSTR_SIZE_LIMIT. */\n    // 这个对象没办法进行编码，尝试从 SDS 中移除所有空余空间\n    if (o->encoding == REDIS_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 *\n * 以新对象的形式，返回一个输入对象的解码版本（RAW 编码）。\n *\n * If the object is already raw-encoded just increment the ref count. \n *\n * 如果对象已经是 RAW 编码的，那么对输入对象的引用计数增一，\n * 然后返回输入对象。\n */\nrobj *getDecodedObject(robj *o) {\n    robj *dec;\n\n    if (sdsEncodedObject(o)) {\n        incrRefCount(o);\n        return o;\n    }\n\n    // 解码对象，将对象的值从整数转换为字符串\n    if (o->type == REDIS_STRING && o->encoding == REDIS_ENCODING_INT) {\n        char buf[32];\n\n        ll2string(buf,32,(long)o->ptr);\n        dec = createStringObject(buf,strlen(buf));\n        return dec;\n\n    } else {\n        redisPanic(\"Unknown encoding type\");\n    }\n}\n\n/* Compare two string objects via strcmp() or strcoll() depending on flags.\n *\n * 根据 flags 的值，决定是使用 strcmp() 或者 strcoll() 来对比字符串对象。\n *\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 * 注意，因为字符串对象可能实际上保存的是整数值，\n * 如果出现这种情况，那么函数先将整数转换为字符串，\n * 然后再对比两个字符串，\n * 这种做法比调用 getDecodedObject() 更快\n *\n * Important note: when REDIS_COMPARE_BINARY is used a binary-safe comparison\n * is used. \n * 当 flags 为 REDIS_COMPARE_BINARY 时，\n * 对比以二进制安全的方式进行。\n */\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    redisAssertWithInfo(NULL,a,a->type == REDIS_STRING && b->type == REDIS_STRING);\n\n    char bufa[128], bufb[128], *astr, *bstr;\n    size_t alen, blen, minlen;\n\n    if (a == b) return 0;\n\n\t// 指向字符串值，并在有需要时，将整数转换为字符串 a\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\n\t// 同样处理字符串 b\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\n\n\t// 对比\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. \n *\n * 如果两个对象的值在字符串的形式上相等，那么返回 1 ， 否则返回 0 。\n *\n * Note that this function is faster then checking for (compareStringObject(a,b) == 0)\n * because it can perform some more optimization. \n *\n * 这个函数做了相应的优化，所以比 (compareStringObject(a, b) == 0) 更快一些。\n */\nint equalStringObjects(robj *a, robj *b) {\n\n    // 对象的编码为 INT ，直接对比值\n    // 这里避免了将整数值转换为字符串，所以效率更高\n    if (a->encoding == REDIS_ENCODING_INT &&\n        b->encoding == REDIS_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\n    // 进行字符串对象\n    } else {\n        return compareStringObjects(a,b) == 0;\n    }\n}\n\n/*\n * 返回字符串对象中字符串值的长度\n */\nsize_t stringObjectLen(robj *o) {\n\n    redisAssertWithInfo(NULL,o,o->type == REDIS_STRING);\n\n    if (sdsEncodedObject(o)) {\n        return sdslen(o->ptr);\n\n    // INT 编码，计算将这个值转换为字符串要多少字节\n    // 相当于返回它的长度\n    } else {\n        char buf[32];\n        return ll2string(buf,32,(long)o->ptr);\n    }\n}\n\n/*\n * 尝试从对象中取出 double 值\n *\n *  - 转换成功则将值保存在 *target 中，函数返回 REDIS_OK\n *\n *  - 否则，函数返回 REDIS_ERR\n */\nint getDoubleFromObject(robj *o, double *target) {\n    double value;\n    char *eptr;\n\n    if (o == NULL) {\n        value = 0;\n\n    } else {\n        redisAssertWithInfo(NULL,o,o->type == REDIS_STRING);\n\n        // 尝试从字符串中转换 double 值\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 REDIS_ERR;\n\n        // INT 编码\n        } else if (o->encoding == REDIS_ENCODING_INT) {\n            value = (long)o->ptr;\n\n        } else {\n            redisPanic(\"Unknown string encoding\");\n        }\n    }\n\n    // 返回值\n    *target = value;\n    return REDIS_OK;\n}\n\n/*\n * 尝试从对象 o 中取出 double 值：\n *\n *  - 如果尝试失败的话，就返回指定的回复 msg 给客户端，函数返回 REDIS_ERR 。\n *\n *  - 取出成功的话，将值保存在 *target 中，函数返回 REDIS_OK 。\n */\nint getDoubleFromObjectOrReply(redisClient *c, robj *o, double *target, const char *msg) {\n\n    double value;\n\n    if (getDoubleFromObject(o, &value) != REDIS_OK) {\n        if (msg != NULL) {\n            addReplyError(c,(char*)msg);\n        } else {\n            addReplyError(c,\"value is not a valid float\");\n        }\n        return REDIS_ERR;\n    }\n\n    *target = value;\n    return REDIS_OK;\n}\n\n/*\n * 尝试从对象中取出 long double 值\n *\n *  - 转换成功则将值保存在 *target 中，函数返回 REDIS_OK\n *\n *  - 否则，函数返回 REDIS_ERR\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\n        redisAssertWithInfo(NULL,o,o->type == REDIS_STRING);\n\n        // RAW 编码，尝试从字符串中转换 long double\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 REDIS_ERR;\n\n        // INT 编码，直接保存\n        } else if (o->encoding == REDIS_ENCODING_INT) {\n            value = (long)o->ptr;\n\n        } else {\n            redisPanic(\"Unknown string encoding\");\n        }\n    }\n\n    *target = value;\n    return REDIS_OK;\n}\n\n/*\n * 尝试从对象 o 中取出 long double 值：\n *\n *  - 如果尝试失败的话，就返回指定的回复 msg 给客户端，函数返回 REDIS_ERR 。\n *\n *  - 取出成功的话，将值保存在 *target 中，函数返回 REDIS_OK 。\n */\nint getLongDoubleFromObjectOrReply(redisClient *c, robj *o, long double *target, const char *msg) {\n\n    long double value;\n\n    if (getLongDoubleFromObject(o, &value) != REDIS_OK) {\n        if (msg != NULL) {\n            addReplyError(c,(char*)msg);\n        } else {\n            addReplyError(c,\"value is not a valid float\");\n        }\n        return REDIS_ERR;\n    }\n\n    *target = value;\n    return REDIS_OK;\n}\n\n/*\n * 尝试从对象 o 中取出整数值，\n * 或者尝试将对象 o 所保存的值转换为整数值，\n * 并将这个整数值保存到 *target 中。\n *\n * 如果 o 为 NULL ，那么将 *target 设为 0 。\n *\n * 如果对象 o 中的值不是整数，并且不能转换为整数，那么函数返回 REDIS_ERR 。\n *\n * 成功取出或者成功进行转换时，返回 REDIS_OK 。\n *\n * T = O(N)\n */\nint getLongLongFromObject(robj *o, long long *target) {\n    long long value;\n    char *eptr;\n\n    if (o == NULL) {\n        // o 为 NULL 时，将值设为 0 。\n        value = 0;\n    } else {\n\n        // 确保对象为 REDIS_STRING 类型\n        redisAssertWithInfo(NULL,o,o->type == REDIS_STRING);\n        if (sdsEncodedObject(o)) {\n            errno = 0;\n            // T = O(N)\n            value = strtoll(o->ptr, &eptr, 10);\n            if (isspace(((char*)o->ptr)[0]) || eptr[0] != '\\0' ||\n                errno == ERANGE)\n                return REDIS_ERR;\n        } else if (o->encoding == REDIS_ENCODING_INT) {\n            // 对于 REDIS_ENCODING_INT 编码的整数值\n            // 直接将它的值保存到 value 中\n            value = (long)o->ptr;\n        } else {\n            redisPanic(\"Unknown string encoding\");\n        }\n    }\n\n    // 保存值到指针\n    if (target) *target = value;\n\n    // 返回结果标识符\n    return REDIS_OK;\n}\n\n/*\n * 尝试从对象 o 中取出整数值，\n * 或者尝试将对象 o 中的值转换为整数值，\n * 并将这个得出的整数值保存到 *target 。\n *\n * 如果取出/转换成功的话，返回 REDIS_OK 。\n * 否则，返回 REDIS_ERR ，并向客户端发送一条出错回复。\n *\n * T = O(N)\n */\nint getLongLongFromObjectOrReply(redisClient *c, robj *o, long long *target, const char *msg) {\n\n    long long value;\n\n    // T = O(N)\n    if (getLongLongFromObject(o, &value) != REDIS_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 REDIS_ERR;\n    }\n\n    *target = value;\n\n    return REDIS_OK;\n}\n\n/*\n * 尝试从对象 o 中取出 long 类型值，\n * 或者尝试将对象 o 中的值转换为 long 类型值，\n * 并将这个得出的整数值保存到 *target 。\n *\n * 如果取出/转换成功的话，返回 REDIS_OK 。\n * 否则，返回 REDIS_ERR ，并向客户端发送一条 msg 出错回复。\n */\nint getLongFromObjectOrReply(redisClient *c, robj *o, long *target, const char *msg) {\n    long long value;\n\n    // 先尝试以 long long 类型取出值\n    if (getLongLongFromObjectOrReply(c, o, &value, msg) != REDIS_OK) return REDIS_ERR;\n\n    // 然后检查值是否在 long 类型的范围之内\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 REDIS_ERR;\n    }\n\n    *target = value;\n    return REDIS_OK;\n}\n\n/*\n * 返回编码的字符串表示\n */\nchar *strEncoding(int encoding) {\n\n    switch(encoding) {\n\n    case REDIS_ENCODING_RAW: return \"raw\";\n    case REDIS_ENCODING_INT: return \"int\";\n    case REDIS_ENCODING_HT: return \"hashtable\";\n    case REDIS_ENCODING_LINKEDLIST: return \"linkedlist\";\n    case REDIS_ENCODING_ZIPLIST: return \"ziplist\";\n    case REDIS_ENCODING_INTSET: return \"intset\";\n    case REDIS_ENCODING_SKIPLIST: return \"skiplist\";\n    case REDIS_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. */\n// 使用近似 LRU 算法，计算出给定对象的闲置时长\nunsigned long long estimateObjectIdleTime(robj *o) {\n    unsigned long long lruclock = LRU_CLOCK();\n    if (lruclock >= o->lru) {\n        return (lruclock - o->lru) * REDIS_LRU_CLOCK_RESOLUTION;\n    } else {\n        return (lruclock + (REDIS_LRU_CLOCK_MAX - o->lru)) *\n                    REDIS_LRU_CLOCK_RESOLUTION;\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.\n *\n * OBJECT 命令的辅助函数，用于在不修改 LRU 时间的情况下，尝试获取 key 对象\n */\nrobj *objectCommandLookup(redisClient *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\n/*\n * 在不修改 LRU 时间的情况下，获取 key 对应的对象。\n *\n * 如果对象不存在，那么向客户端发送回复 reply 。\n */\nrobj *objectCommandLookupOrReply(redisClient *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 <verb> ... arguments ... */\nvoid objectCommand(redisClient *c) {\n    robj *o;\n\n    // 返回对戏哪个的引用计数\n    if (!strcasecmp(c->argv[1]->ptr,\"refcount\") && c->argc == 3) {\n        if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk))\n                == NULL) return;\n        addReplyLongLong(c,o->refcount);\n\n    // 返回对象的编码\n    } else if (!strcasecmp(c->argv[1]->ptr,\"encoding\") && c->argc == 3) {\n        if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk))\n                == NULL) return;\n        addReplyBulkCString(c,strEncoding(o->encoding));\n    \n    // 返回对象的空闲时间\n    } else if (!strcasecmp(c->argv[1]->ptr,\"idletime\") && c->argc == 3) {\n        if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk))\n                == NULL) return;\n        addReplyLongLong(c,estimateObjectIdleTime(o)/1000);\n    } else {\n        addReplyError(c,\"Syntax error. Try OBJECT (refcount|encoding|idletime)\");\n    }\n}\n"
  },
  {
    "path": "src/pqsort.c",
    "content": "/* The following is the NetBSD libc qsort implementation modified in order to\n * support partial sorting of ranges for Redis.\n *\n * Copyright(C) 2009-2012 Salvatore Sanfilippo. All rights reserved.\n *\n * The original copyright notice follows. */\n\n\n/*\t$NetBSD: qsort.c,v 1.19 2009/01/30 23:38:44 lukem Exp $\t*/\n\n/*-\n * Copyright (c) 1992, 1993\n *\tThe Regents of the University of California.  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\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. 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 * 3. Neither the name of the University nor the names of its contributors\n *    may be used to endorse or promote products derived from this software\n *    without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND\n * 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 REGENTS OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#include <sys/types.h>\n\n#include <errno.h>\n#include <stdlib.h>\n\nstatic inline char\t*med3 (char *, char *, char *,\n    int (*)(const void *, const void *));\nstatic inline void\t swapfunc (char *, char *, size_t, int);\n\n#define min(a, b)\t(a) < (b) ? a : b\n\n/*\n * Qsort routine from Bentley & McIlroy's \"Engineering a Sort Function\".\n */\n#define swapcode(TYPE, parmi, parmj, n) { \t\t\\\n\tsize_t i = (n) / sizeof (TYPE); \t\t\\\n\tTYPE *pi = (TYPE *)(void *)(parmi); \t\t\\\n\tTYPE *pj = (TYPE *)(void *)(parmj); \t\t\\\n\tdo { \t\t\t\t\t\t\\\n\t\tTYPE\tt = *pi;\t\t\t\\\n\t\t*pi++ = *pj;\t\t\t\t\\\n\t\t*pj++ = t;\t\t\t\t\\\n        } while (--i > 0);\t\t\t\t\\\n}\n\n#define SWAPINIT(a, es) swaptype = ((char *)a - (char *)0) % sizeof(long) || \\\n\tes % sizeof(long) ? 2 : es == sizeof(long)? 0 : 1;\n\nstatic inline void\nswapfunc(char *a, char *b, size_t n, int swaptype)\n{\n\n\tif (swaptype <= 1) \n\t\tswapcode(long, a, b, n)\n\telse\n\t\tswapcode(char, a, b, n)\n}\n\n#define swap(a, b)\t\t\t\t\t\t\\\n\tif (swaptype == 0) {\t\t\t\t\t\\\n\t\tlong t = *(long *)(void *)(a);\t\t\t\\\n\t\t*(long *)(void *)(a) = *(long *)(void *)(b);\t\\\n\t\t*(long *)(void *)(b) = t;\t\t\t\\\n\t} else\t\t\t\t\t\t\t\\\n\t\tswapfunc(a, b, es, swaptype)\n\n#define vecswap(a, b, n) if ((n) > 0) swapfunc((a), (b), (size_t)(n), swaptype)\n\nstatic inline char *\nmed3(char *a, char *b, char *c,\n    int (*cmp) (const void *, const void *))\n{\n\n\treturn cmp(a, b) < 0 ?\n\t       (cmp(b, c) < 0 ? b : (cmp(a, c) < 0 ? c : a ))\n              :(cmp(b, c) > 0 ? b : (cmp(a, c) < 0 ? a : c ));\n}\n\nstatic void\n_pqsort(void *a, size_t n, size_t es,\n    int (*cmp) (const void *, const void *), void *lrange, void *rrange)\n{\n\tchar *pa, *pb, *pc, *pd, *pl, *pm, *pn;\n\tsize_t d, r;\n\tint swaptype, cmp_result;\n\nloop:\tSWAPINIT(a, es);\n\tif (n < 7) {\n\t\tfor (pm = (char *) a + es; pm < (char *) a + n * es; pm += es)\n\t\t\tfor (pl = pm; pl > (char *) a && cmp(pl - es, pl) > 0;\n\t\t\t     pl -= es)\n\t\t\t\tswap(pl, pl - es);\n\t\treturn;\n\t}\n\tpm = (char *) a + (n / 2) * es;\n\tif (n > 7) {\n\t\tpl = (char *) a;\n\t\tpn = (char *) a + (n - 1) * es;\n\t\tif (n > 40) {\n\t\t\td = (n / 8) * es;\n\t\t\tpl = med3(pl, pl + d, pl + 2 * d, cmp);\n\t\t\tpm = med3(pm - d, pm, pm + d, cmp);\n\t\t\tpn = med3(pn - 2 * d, pn - d, pn, cmp);\n\t\t}\n\t\tpm = med3(pl, pm, pn, cmp);\n\t}\n\tswap(a, pm);\n\tpa = pb = (char *) a + es;\n\n\tpc = pd = (char *) a + (n - 1) * es;\n\tfor (;;) {\n\t\twhile (pb <= pc && (cmp_result = cmp(pb, a)) <= 0) {\n\t\t\tif (cmp_result == 0) {\n\t\t\t\tswap(pa, pb);\n\t\t\t\tpa += es;\n\t\t\t}\n\t\t\tpb += es;\n\t\t}\n\t\twhile (pb <= pc && (cmp_result = cmp(pc, a)) >= 0) {\n\t\t\tif (cmp_result == 0) {\n\t\t\t\tswap(pc, pd);\n\t\t\t\tpd -= es;\n\t\t\t}\n\t\t\tpc -= es;\n\t\t}\n\t\tif (pb > pc)\n\t\t\tbreak;\n\t\tswap(pb, pc);\n\t\tpb += es;\n\t\tpc -= es;\n\t}\n\n\tpn = (char *) a + n * es;\n\tr = min(pa - (char *) a, pb - pa);\n\tvecswap(a, pb - r, r);\n\tr = min((size_t)(pd - pc), pn - pd - es);\n\tvecswap(pb, pn - r, r);\n\tif ((r = pb - pa) > es) {\n                void *_l = a, *_r = ((unsigned char*)a)+r-1;\n                if (!((lrange < _l && rrange < _l) ||\n                    (lrange > _r && rrange > _r)))\n\t\t    _pqsort(a, r / es, es, cmp, lrange, rrange);\n        }\n\tif ((r = pd - pc) > es) { \n                void *_l, *_r;\n                \n\t\t/* Iterate rather than recurse to save stack space */\n\t\ta = pn - r;\n\t\tn = r / es;\n\n                _l = a;\n                _r = ((unsigned char*)a)+r-1;\n                if (!((lrange < _l && rrange < _l) ||\n                    (lrange > _r && rrange > _r)))\n\t\t    goto loop;\n\t}\n/*\t\tqsort(pn - r, r / es, es, cmp);*/\n}\n\nvoid\npqsort(void *a, size_t n, size_t es,\n    int (*cmp) (const void *, const void *), size_t lrange, size_t rrange)\n{\n    _pqsort(a,n,es,cmp,((unsigned char*)a)+(lrange*es),\n                       ((unsigned char*)a)+((rrange+1)*es)-1);\n}\n"
  },
  {
    "path": "src/pqsort.h",
    "content": "/* The following is the NetBSD libc qsort implementation modified in order to\n * support partial sorting of ranges for Redis.\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 * See the pqsort.c file for the original copyright notice. */\n\n#ifndef __PQSORT_H\n#define __PQSORT_H\n\nvoid\npqsort(void *a, size_t n, size_t es,\n    int (*cmp) (const void *, const void *), size_t lrange, size_t rrange);\n\n#endif\n"
  },
  {
    "path": "src/pubsub.c",
    "content": "/*\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 \"redis.h\"\n\n/*-----------------------------------------------------------------------------\n * Pubsub low level API\n *----------------------------------------------------------------------------*/\n\n/*\n * 释放给定的模式 p\n */\nvoid freePubsubPattern(void *p) {\n    pubsubPattern *pat = p;\n\n    decrRefCount(pat->pattern);\n    zfree(pat);\n}\n\n/*\n * 对比模式 a 和 b 是否相同，相同返回 1 ，不相同返回 0 。\n */\nint listMatchPubsubPattern(void *a, void *b) {\n    pubsubPattern *pa = a, *pb = b;\n\n    return (pa->client == pb->client) &&\n           (equalStringObjects(pa->pattern,pb->pattern));\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. \n *\n * 设置客户端 c 订阅频道 channel 。\n *\n * 订阅成功返回 1 ，如果客户端已经订阅了该频道，那么返回 0 。\n */\nint pubsubSubscribeChannel(redisClient *c, robj *channel) {\n    dictEntry *de;\n    list *clients = NULL;\n    int retval = 0;\n\n    /* Add the channel to the client -> channels hash table */\n    // 将 channels 填接到 c->pubsub_channels 的集合中（值为 NULL 的字典视为集合）\n    if (dictAdd(c->pubsub_channels,channel,NULL) == DICT_OK) {\n        retval = 1;\n        incrRefCount(channel);\n\n        // 关联示意图\n        // {\n        //  频道名        订阅频道的客户端\n        //  'channel-a' : [c1, c2, c3],\n        //  'channel-b' : [c5, c2, c1],\n        //  'channel-c' : [c10, c2, c1]\n        // }\n        /* Add the client to the channel -> list of clients hash table */\n        // 从 pubsub_channels 字典中取出保存着所有订阅了 channel 的客户端的链表\n        // 如果 channel 不存在于字典，那么添加进去\n        de = dictFind(server.pubsub_channels,channel);\n        if (de == NULL) {\n            clients = listCreate();\n            dictAdd(server.pubsub_channels,channel,clients);\n            incrRefCount(channel);\n        } else {\n            clients = dictGetVal(de);\n        }\n\n        // before:\n        // 'channel' : [c1, c2]\n        // after:\n        // 'channel' : [c1, c2, c3]\n        // 将客户端添加到链表的末尾\n        listAddNodeTail(clients,c);\n    }\n\n    /* Notify the client */\n    // 回复客户端。\n    // 示例：\n    // redis 127.0.0.1:6379> SUBSCRIBE xxx\n    // Reading messages... (press Ctrl-C to quit)\n    // 1) \"subscribe\"\n    // 2) \"xxx\"\n    // 3) (integer) 1\n    addReply(c,shared.mbulkhdr[3]);\n    // \"subscribe\\n\" 字符串\n    addReply(c,shared.subscribebulk);\n    // 被订阅的客户端\n    addReplyBulk(c,channel);\n    // 客户端订阅的频道和模式总数\n    addReplyLongLong(c,dictSize(c->pubsub_channels)+listLength(c->pubsub_patterns));\n\n    return retval;\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. \n *\n * 客户端 c 退订频道 channel 。\n *\n * 如果取消成功返回 1 ，如果因为客户端未订阅频道，而造成取消失败，返回 0 。\n */\nint pubsubUnsubscribeChannel(redisClient *c, robj *channel, int notify) {\n    dictEntry *de;\n    list *clients;\n    listNode *ln;\n    int retval = 0;\n\n    /* Remove the channel from the client -> channels hash table */\n    // 将频道 channel 从 client->channels 字典中移除\n    incrRefCount(channel); /* channel may be just a pointer to the same object\n                            we have in the hash tables. Protect it... */\n    // 示意图：\n    // before:\n    // {\n    //  'channel-x': NULL,\n    //  'channel-y': NULL,\n    //  'channel-z': NULL,\n    // }\n    // after unsubscribe channel-y ：\n    // {\n    //  'channel-x': NULL,\n    //  'channel-z': NULL,\n    // }\n    if (dictDelete(c->pubsub_channels,channel) == DICT_OK) {\n\n        // channel 移除成功，表示客户端订阅了这个频道，执行以下代码\n\n        retval = 1;\n        /* Remove the client from the channel -> clients list hash table */\n        // 从 channel->clients 的 clients 链表中，移除 client \n        // 示意图：\n        // before:\n        // {\n        //  'channel-x' : [c1, c2, c3],\n        // }\n        // after c2 unsubscribe channel-x:\n        // {\n        //  'channel-x' : [c1, c3]\n        // }\n        de = dictFind(server.pubsub_channels,channel);\n        redisAssertWithInfo(c,NULL,de != NULL);\n        clients = dictGetVal(de);\n        ln = listSearchKey(clients,c);\n        redisAssertWithInfo(c,NULL,ln != NULL);\n        listDelNode(clients,ln);\n\n        // 如果移除 client 之后链表为空，那么删除这个 channel 键\n        // 示意图：\n        // before\n        // {\n        //  'channel-x' : [c1]\n        // }\n        // after c1 ubsubscribe channel-x\n        // then also delete 'channel-x' key in dict\n        // {\n        //  // nothing here\n        // }\n        if (listLength(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(server.pubsub_channels,channel);\n        }\n    }\n\n    /* Notify the client */\n    // 回复客户端\n    if (notify) {\n        addReply(c,shared.mbulkhdr[3]);\n        // \"ubsubscribe\" 字符串\n        addReply(c,shared.unsubscribebulk);\n        // 被退订的频道\n        addReplyBulk(c,channel);\n        // 退订频道之后客户端仍在订阅的频道和模式的总数\n        addReplyLongLong(c,dictSize(c->pubsub_channels)+\n                       listLength(c->pubsub_patterns));\n\n    }\n\n    decrRefCount(channel); /* it is finally safe to release it */\n\n    return retval;\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. \n *\n * 设置客户端 c 订阅模式 pattern 。\n *\n * 订阅成功返回 1 ，如果客户端已经订阅了该模式，那么返回 0 。\n */\nint pubsubSubscribePattern(redisClient *c, robj *pattern) {\n    int retval = 0;\n\n    // 在链表中查找模式，看客户端是否已经订阅了这个模式\n    // 这里为什么不像 channel 那样，用字典来进行检测呢？\n    // 虽然 pattern 的数量一般来说并不多\n    if (listSearchKey(c->pubsub_patterns,pattern) == NULL) {\n        \n        // 如果没有的话，执行以下代码\n\n        retval = 1;\n\n        pubsubPattern *pat;\n\n        // 将 pattern 添加到 c->pubsub_patterns 链表中\n        listAddNodeTail(c->pubsub_patterns,pattern);\n\n        incrRefCount(pattern);\n\n        // 创建并设置新的 pubsubPattern 结构\n        pat = zmalloc(sizeof(*pat));\n        pat->pattern = getDecodedObject(pattern);\n        pat->client = c;\n\n        // 添加到末尾\n        listAddNodeTail(server.pubsub_patterns,pat);\n    }\n\n    /* Notify the client */\n    // 回复客户端。\n    // 示例：\n    // redis 127.0.0.1:6379> PSUBSCRIBE xxx*\n    // Reading messages... (press Ctrl-C to quit)\n    // 1) \"psubscribe\"\n    // 2) \"xxx*\"\n    // 3) (integer) 1\n    addReply(c,shared.mbulkhdr[3]);\n    // 回复 \"psubscribe\" 字符串\n    addReply(c,shared.psubscribebulk);\n    // 回复被订阅的模式\n    addReplyBulk(c,pattern);\n    // 回复客户端订阅的频道和模式的总数\n    addReplyLongLong(c,dictSize(c->pubsub_channels)+listLength(c->pubsub_patterns));\n\n    return retval;\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. \n *\n * 取消客户端 c 对模式 pattern 的订阅。\n *\n * 取消成功返回 1 ，因为客户端未订阅 pattern 而造成取消失败，返回 0 。\n */\nint pubsubUnsubscribePattern(redisClient *c, robj *pattern, int notify) {\n    listNode *ln;\n    pubsubPattern pat;\n    int retval = 0;\n\n    incrRefCount(pattern); /* Protect the object. May be the same we remove */\n\n    // 先确认一下，客户端是否订阅了这个模式\n    if ((ln = listSearchKey(c->pubsub_patterns,pattern)) != NULL) {\n\n        retval = 1;\n\n        // 将模式从客户端的订阅列表中删除\n        listDelNode(c->pubsub_patterns,ln);\n\n        // 设置 pubsubPattern 结构\n        pat.client = c;\n        pat.pattern = pattern;\n\n        // 在服务器中查找\n        ln = listSearchKey(server.pubsub_patterns,&pat);\n        listDelNode(server.pubsub_patterns,ln);\n    }\n\n    /* Notify the client */\n    // 回复客户端\n    if (notify) {\n        addReply(c,shared.mbulkhdr[3]);\n        // \"punsubscribe\" 字符串\n        addReply(c,shared.punsubscribebulk);\n        // 被退订的模式\n        addReplyBulk(c,pattern);\n        // 退订频道之后客户端仍在订阅的频道和模式的总数\n        addReplyLongLong(c,dictSize(c->pubsub_channels)+\n                       listLength(c->pubsub_patterns));\n    }\n\n    decrRefCount(pattern);\n\n    return retval;\n}\n\n/* Unsubscribe from all the channels. Return the number of channels the\n * client was subscribed from. \n *\n * 退订客户端 c 订阅的所有频道。\n *\n * 返回被退订频道的总数。\n */\nint pubsubUnsubscribeAllChannels(redisClient *c, int notify) {\n\n    // 频道迭代器\n    dictIterator *di = dictGetSafeIterator(c->pubsub_channels);\n    dictEntry *de;\n    int count = 0;\n\n    // 退订\n    while((de = dictNext(di)) != NULL) {\n        robj *channel = dictGetKey(de);\n\n        count += pubsubUnsubscribeChannel(c,channel,notify);\n    }\n\n    /* We were subscribed to nothing? Still reply to the client. */\n    // 如果在执行这个函数时，客户端没有订阅任何频道，\n    // 那么向客户端发送回复\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                       listLength(c->pubsub_patterns));\n    }\n\n    dictReleaseIterator(di);\n\n    // 被退订的频道的数量\n    return count;\n}\n\n/* Unsubscribe from all the patterns. Return the number of patterns the\n * client was subscribed from. \n *\n * 退订客户端 c 订阅的所有模式。\n *\n * 返回被退订模式的数量。\n */\nint pubsubUnsubscribeAllPatterns(redisClient *c, int notify) {\n    listNode *ln;\n    listIter li;\n    int count = 0;\n\n    // 迭代客户端订阅模式的链表\n    listRewind(c->pubsub_patterns,&li);\n    while ((ln = listNext(&li)) != NULL) {\n        robj *pattern = ln->value;\n\n        // 退订，并计算退订数\n        count += pubsubUnsubscribePattern(c,pattern,notify);\n    }\n\n    // 如果在执行这个函数时，客户端没有订阅任何模式，\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                       listLength(c->pubsub_patterns));\n    }\n\n    // 退订总数\n    return count;\n}\n\n/* Publish a message \n *\n * 将 message 发送到所有订阅频道 channel 的客户端，\n * 以及所有订阅了和 channel 频道匹配的模式的客户端。\n */\nint pubsubPublishMessage(robj *channel, robj *message) {\n    int receivers = 0;\n    dictEntry *de;\n    listNode *ln;\n    listIter li;\n\n    /* Send to clients listening for that channel */\n    // 取出包含所有订阅频道 channel 的客户端的链表\n    // 并将消息发送给它们\n    de = dictFind(server.pubsub_channels,channel);\n    if (de) {\n        list *list = dictGetVal(de);\n        listNode *ln;\n        listIter li;\n\n        // 遍历客户端链表，将 message 发送给它们\n        listRewind(list,&li);\n        while ((ln = listNext(&li)) != NULL) {\n            redisClient *c = ln->value;\n\n            // 回复客户端。\n            // 示例：\n            // 1) \"message\"\n            // 2) \"xxx\"\n            // 3) \"hello\"\n            addReply(c,shared.mbulkhdr[3]);\n            // \"message\" 字符串\n            addReply(c,shared.messagebulk);\n            // 消息的来源频道\n            addReplyBulk(c,channel);\n            // 消息内容\n            addReplyBulk(c,message);\n\n            // 接收客户端计数\n            receivers++;\n        }\n    }\n\n    /* Send to clients listening to matching channels */\n    // 将消息也发送给那些和频道匹配的模式\n    if (listLength(server.pubsub_patterns)) {\n\n        // 遍历模式链表\n        listRewind(server.pubsub_patterns,&li);\n        channel = getDecodedObject(channel);\n        while ((ln = listNext(&li)) != NULL) {\n\n            // 取出 pubsubPattern\n            pubsubPattern *pat = ln->value;\n\n            // 如果 channel 和 pattern 匹配\n            // 就给所有订阅该 pattern 的客户端发送消息\n            if (stringmatchlen((char*)pat->pattern->ptr,\n                                sdslen(pat->pattern->ptr),\n                                (char*)channel->ptr,\n                                sdslen(channel->ptr),0)) {\n\n                // 回复客户端\n                // 示例：\n                // 1) \"pmessage\"\n                // 2) \"*\"\n                // 3) \"xxx\"\n                // 4) \"hello\"\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\n                // 对接收消息的客户端进行计数\n                receivers++;\n            }\n        }\n\n        decrRefCount(channel);\n    }\n\n    // 返回计数\n    return receivers;\n}\n\n/*-----------------------------------------------------------------------------\n * Pubsub commands implementation\n *----------------------------------------------------------------------------*/\n\nvoid subscribeCommand(redisClient *c) {\n    int j;\n\n    for (j = 1; j < c->argc; j++)\n        pubsubSubscribeChannel(c,c->argv[j]);\n}\n\nvoid unsubscribeCommand(redisClient *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}\n\nvoid psubscribeCommand(redisClient *c) {\n    int j;\n\n    for (j = 1; j < c->argc; j++)\n        pubsubSubscribePattern(c,c->argv[j]);\n}\n\nvoid punsubscribeCommand(redisClient *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}\n\nvoid publishCommand(redisClient *c) {\n\n    int receivers = pubsubPublishMessage(c->argv[1],c->argv[2]);\n    if (server.cluster_enabled)\n        clusterPropagatePublish(c->argv[1],c->argv[2]);\n    else\n        forceCommandPropagation(c,REDIS_PROPAGATE_REPL);\n    addReplyLongLong(c,receivers);\n}\n\n/* PUBSUB command for Pub/Sub introspection. */\nvoid pubsubCommand(redisClient *c) {\n\n    // PUBSUB CHANNELS [pattern] 子命令\n    if (!strcasecmp(c->argv[1]->ptr,\"channels\") &&\n        (c->argc == 2 || c->argc ==3))\n    {\n        /* PUBSUB CHANNELS [<pattern>] */\n        // 检查命令请求是否给定了 pattern 参数\n        // 如果没有给定的话，就设为 NULL\n        sds pat = (c->argc == 2) ? NULL : c->argv[2]->ptr;\n\n        // 创建 pubsub_channels 的字典迭代器\n        // 该字典的键为频道，值为链表\n        // 链表中保存了所有订阅键所对应的频道的客户端\n        dictIterator *di = dictGetIterator(server.pubsub_channels);\n        dictEntry *de;\n        long mblen = 0;\n        void *replylen;\n\n        replylen = addDeferredMultiBulkLength(c);\n        // 从迭代器中获取一个客户端\n        while((de = dictNext(di)) != NULL) {\n\n            // 从字典中取出客户端所订阅的频道\n            robj *cobj = dictGetKey(de);\n            sds channel = cobj->ptr;\n\n            // 顺带一提\n            // 因为 Redis 的字典实现只能遍历字典的值（客户端）\n            // 所以这里才会有遍历字典值然后通过字典值取出字典键（频道）的蹩脚用法\n\n            // 如果没有给定 pattern 参数，那么打印所有找到的频道\n            // 如果给定了 pattern 参数，那么只打印和 pattern 相匹配的频道\n            if (!pat || stringmatchlen(pat, sdslen(pat),\n                                       channel, sdslen(channel),0))\n            {\n                // 向客户端输出频道\n                addReplyBulk(c,cobj);\n                mblen++;\n            }\n        }\n        // 释放字典迭代器\n        dictReleaseIterator(di);\n        setDeferredMultiBulkLength(c,replylen,mblen);\n\n    // PUBSUB NUMSUB [channel-1 channel-2 ... channel-N] 子命令\n    } else if (!strcasecmp(c->argv[1]->ptr,\"numsub\") && c->argc >= 2) {\n        /* PUBSUB NUMSUB [Channel_1 ... Channel_N] */\n        int j;\n\n        addReplyMultiBulkLen(c,(c->argc-2)*2);\n        for (j = 2; j < c->argc; j++) {\n\n            // c->argv[j] 也即是客户端输入的第 N 个频道名字\n            // pubsub_channels 的字典为频道名字\n            // 而值则是保存了 c->argv[j] 频道所有订阅者的链表\n            // 而调用 dictFetchValue 也就是取出所有订阅给定频道的客户端\n            list *l = dictFetchValue(server.pubsub_channels,c->argv[j]);\n\n            addReplyBulk(c,c->argv[j]);\n            // 向客户端返回链表的长度属性\n            // 这个属性就是某个频道的订阅者数量\n            // 例如：如果一个频道有三个订阅者，那么链表的长度就是 3\n            // 而返回给客户端的数字也是三\n            addReplyBulkLongLong(c,l ? listLength(l) : 0);\n        }\n\n    // PUBSUB NUMPAT 子命令\n    } else if (!strcasecmp(c->argv[1]->ptr,\"numpat\") && c->argc == 2) {\n        /* PUBSUB NUMPAT */\n\n        // pubsub_patterns 链表保存了服务器中所有被订阅的模式\n        // pubsub_patterns 的长度就是服务器中被订阅模式的数量\n        addReplyLongLong(c,listLength(server.pubsub_patterns));\n\n    // 错误处理\n    } else {\n        addReplyErrorFormat(c,\n            \"Unknown PUBSUB subcommand or wrong number of arguments for '%s'\",\n            (char*)c->argv[1]->ptr);\n    }\n}\n"
  },
  {
    "path": "src/rand.c",
    "content": "/* Pseudo random number generation functions derived from the drand48()\n * function obtained from pysam source code.\n *\n * This functions are used in order to replace the default math.random()\n * Lua implementation with something having exactly the same behavior\n * across different systems (by default Lua uses libc's rand() that is not\n * required to implement a specific PRNG generating the same sequence\n * in different systems if seeded with the same integer).\n *\n * The original code appears to be under the public domain.\n * I modified it removing the non needed functions and all the\n * 1960-style C coding stuff...\n *\n * ----------------------------------------------------------------------------\n *\n * Copyright (c) 2010-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 <stdint.h>\n\n#define N\t16\n#define MASK\t((1 << (N - 1)) + (1 << (N - 1)) - 1)\n#define LOW(x)\t((unsigned)(x) & MASK)\n#define HIGH(x)\tLOW((x) >> N)\n#define MUL(x, y, z)\t{ int32_t l = (long)(x) * (long)(y); \\\n\t\t(z)[0] = LOW(l); (z)[1] = HIGH(l); }\n#define CARRY(x, y)\t((int32_t)(x) + (long)(y) > MASK)\n#define ADDEQU(x, y, z)\t(z = CARRY(x, (y)), x = LOW(x + (y)))\n#define X0\t0x330E\n#define X1\t0xABCD\n#define X2\t0x1234\n#define A0\t0xE66D\n#define A1\t0xDEEC\n#define A2\t0x5\n#define C\t0xB\n#define SET3(x, x0, x1, x2)\t((x)[0] = (x0), (x)[1] = (x1), (x)[2] = (x2))\n#define SETLOW(x, y, n) SET3(x, LOW((y)[n]), LOW((y)[(n)+1]), LOW((y)[(n)+2]))\n#define SEED(x0, x1, x2) (SET3(x, x0, x1, x2), SET3(a, A0, A1, A2), c = C)\n#define REST(v)\tfor (i = 0; i < 3; i++) { xsubi[i] = x[i]; x[i] = temp[i]; } \\\n\t\treturn (v);\n#define HI_BIT\t(1L << (2 * N - 1))\n\nstatic uint32_t x[3] = { X0, X1, X2 }, a[3] = { A0, A1, A2 }, c = C;\nstatic void next();\n\nint32_t redisLrand48() {\n    next();\n    return (((int32_t)x[2] << (N - 1)) + (x[1] >> 1));\n}\n\nvoid redisSrand48(int32_t seedval) {\n    SEED(X0, LOW(seedval), HIGH(seedval));\n}\n\nstatic void next() {\n    uint32_t p[2], q[2], r[2], carry0, carry1;\n\n    MUL(a[0], x[0], p);\n    ADDEQU(p[0], c, carry0);\n    ADDEQU(p[1], carry0, carry1);\n    MUL(a[0], x[1], q);\n    ADDEQU(p[1], q[0], carry0);\n    MUL(a[1], x[0], r);\n    x[2] = LOW(carry0 + carry1 + CARRY(p[1], r[0]) + q[1] + r[1] +\n            a[0] * x[2] + a[1] * x[1] + a[2] * x[0]);\n    x[1] = LOW(p[1] + r[0]);\n    x[0] = LOW(p[0]);\n}\n"
  },
  {
    "path": "src/rand.h",
    "content": "/*\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#ifndef REDIS_RANDOM_H\n#define REDIS_RANDOM_H\n\nint32_t redisLrand48();\nvoid redisSrand48(int32_t seedval);\n\n#define REDIS_LRAND48_MAX INT32_MAX\n\n#endif\n"
  },
  {
    "path": "src/rdb.c",
    "content": "/*\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 \"redis.h\"\n#include \"lzf.h\"    /* LZF compression library */\n#include \"zipmap.h\"\n#include \"endianconv.h\"\n\n#include <math.h>\n#include <sys/types.h>\n#include <sys/time.h>\n#include <sys/resource.h>\n#include <sys/wait.h>\n#include <arpa/inet.h>\n#include <sys/stat.h>\n\n/*\n * 将长度为 len 的字符数组 p 写入到 rdb 中。\n *\n * 写入成功返回 len ，失败返回 -1 。\n */\nstatic int rdbWriteRaw(rio *rdb, void *p, size_t len) {\n    if (rdb && rioWrite(rdb,p,len) == 0)\n        return -1;\n    return len;\n}\n\n/*\n * 将长度为 1 字节的字符 type 写入到 rdb 文件中。\n */\nint rdbSaveType(rio *rdb, unsigned char type) {\n    return rdbWriteRaw(rdb,&type,1);\n}\n\n/* Load a \"type\" in RDB format, that is a one byte unsigned integer.\n *\n * 从 rdb 中载入 1 字节长的 type 数据。\n *\n * This function is not only used to load object types, but also special\n * \"types\" like the end-of-file type, the EXPIRE type, and so forth. \n *\n * 函数即可以用于载入键的类型（rdb.h/REDIS_RDB_TYPE_*），\n * 也可以用于载入特殊标识号（rdb.h/REDIS_RDB_OPCODE_*）\n */\nint rdbLoadType(rio *rdb) {\n    unsigned char type;\n\n    if (rioRead(rdb,&type,1) == 0) return -1;\n\n    return type;\n}\n\n/*\n * 载入以秒为单位的过期时间，长度为 4 字节\n */\ntime_t rdbLoadTime(rio *rdb) {\n    int32_t t32;\n    if (rioRead(rdb,&t32,4) == 0) return -1;\n    return (time_t)t32;\n}\n\n/*\n * 将长度为 8 字节的毫秒过期时间写入到 rdb 中。\n */\nint rdbSaveMillisecondTime(rio *rdb, long long t) {\n    int64_t t64 = (int64_t) t;\n    return rdbWriteRaw(rdb,&t64,8);\n}\n\n/*\n * 从 rdb 中载入 8 字节长的毫秒过期时间。\n */\nlong long rdbLoadMillisecondTime(rio *rdb) {\n    int64_t t64;\n    if (rioRead(rdb,&t64,8) == 0) return -1;\n    return (long long)t64;\n}\n\n/* Saves an encoded length. The first two bits in the first byte are used to\n * hold the encoding type. See the REDIS_RDB_* definitions for more information\n * on the types of encoding. \n *\n * 对 len 进行特殊编码之后写入到 rdb 。\n *\n * 写入成功返回保存编码后的 len 所需的字节数。\n */\nint rdbSaveLen(rio *rdb, uint32_t len) {\n    unsigned char buf[2];\n    size_t nwritten;\n\n    if (len < (1<<6)) {\n        /* Save a 6 bit len */\n        buf[0] = (len&0xFF)|(REDIS_RDB_6BITLEN<<6);\n        if (rdbWriteRaw(rdb,buf,1) == -1) return -1;\n        nwritten = 1;\n\n    } else if (len < (1<<14)) {\n        /* Save a 14 bit len */\n        buf[0] = ((len>>8)&0xFF)|(REDIS_RDB_14BITLEN<<6);\n        buf[1] = len&0xFF;\n        if (rdbWriteRaw(rdb,buf,2) == -1) return -1;\n        nwritten = 2;\n\n    } else {\n        /* Save a 32 bit len */\n        buf[0] = (REDIS_RDB_32BITLEN<<6);\n        if (rdbWriteRaw(rdb,buf,1) == -1) return -1;\n        len = htonl(len);\n        if (rdbWriteRaw(rdb,&len,4) == -1) return -1;\n        nwritten = 1+4;\n    }\n\n    return nwritten;\n}\n\n/* Load an encoded length. The \"isencoded\" argument is set to 1 if the length\n * is not actually a length but an \"encoding type\". See the REDIS_RDB_ENC_*\n * definitions in rdb.h for more information. \n *\n * 读入一个被编码的长度值。\n *\n * 如果 length 值不是整数，而是一个被编码后值，那么 isencoded 将被设为 1 。\n *\n * 查看 rdb./hREDIS_RDB_ENC_* 定义以获得更多信息。\n */\nuint32_t rdbLoadLen(rio *rdb, int *isencoded) {\n    unsigned char buf[2];\n    uint32_t len;\n    int type;\n\n    if (isencoded) *isencoded = 0;\n\n    // 读入 length ，这个值可能已经被编码，也可能没有\n    if (rioRead(rdb,buf,1) == 0) return REDIS_RDB_LENERR;\n\n    type = (buf[0]&0xC0)>>6;\n\n    // 编码值，进行解码\n    if (type == REDIS_RDB_ENCVAL) {\n        /* Read a 6 bit encoding type. */\n        if (isencoded) *isencoded = 1;\n        return buf[0]&0x3F;\n\n    // 6 位整数\n    } else if (type == REDIS_RDB_6BITLEN) {\n        /* Read a 6 bit len. */\n        return buf[0]&0x3F;\n\n    // 14 位整数\n    } else if (type == REDIS_RDB_14BITLEN) {\n        /* Read a 14 bit len. */\n        if (rioRead(rdb,buf+1,1) == 0) return REDIS_RDB_LENERR;\n        return ((buf[0]&0x3F)<<8)|buf[1];\n\n    // 32 位整数\n    } else {\n        /* Read a 32 bit len. */\n        if (rioRead(rdb,&len,4) == 0) return REDIS_RDB_LENERR;\n        return ntohl(len);\n    }\n}\n\n/* Encodes the \"value\" argument as integer when it fits in the supported ranges\n * for encoded types. If the function successfully encodes the integer, the\n * representation is stored in the buffer pointer to by \"enc\" and the string\n * length is returned. Otherwise 0 is returned. \n *\n * 尝试使用特殊的整数编码来保存 value ，这要求它的值必须在给定范围之内。\n *\n * 如果可以编码的话，将编码后的值保存在 enc 指针中，\n * 并返回值在编码后所需的长度。\n *\n * 如果不能编码的话，返回 0 。\n */\nint rdbEncodeInteger(long long value, unsigned char *enc) {\n\n    if (value >= -(1<<7) && value <= (1<<7)-1) {\n        enc[0] = (REDIS_RDB_ENCVAL<<6)|REDIS_RDB_ENC_INT8;\n        enc[1] = value&0xFF;\n        return 2;\n\n    } else if (value >= -(1<<15) && value <= (1<<15)-1) {\n        enc[0] = (REDIS_RDB_ENCVAL<<6)|REDIS_RDB_ENC_INT16;\n        enc[1] = value&0xFF;\n        enc[2] = (value>>8)&0xFF;\n        return 3;\n\n    } else if (value >= -((long long)1<<31) && value <= ((long long)1<<31)-1) {\n        enc[0] = (REDIS_RDB_ENCVAL<<6)|REDIS_RDB_ENC_INT32;\n        enc[1] = value&0xFF;\n        enc[2] = (value>>8)&0xFF;\n        enc[3] = (value>>16)&0xFF;\n        enc[4] = (value>>24)&0xFF;\n        return 5;\n\n    } else {\n        return 0;\n    }\n}\n\n/* Loads an integer-encoded object with the specified encoding type \"enctype\".\n *\n * 载入被编码成指定类型的编码整数对象。\n *\n * If the \"encode\" argument is set the function may return an integer-encoded\n * string object, otherwise it always returns a raw string object. \n *\n * 如果 encoded 参数被设置了的话，那么可能会返回一个整数编码的字符串对象，\n * 否则，字符串总是未编码的。\n */\nrobj *rdbLoadIntegerObject(rio *rdb, int enctype, int encode) {\n    unsigned char enc[4];\n    long long val;\n\n    // 整数编码\n    if (enctype == REDIS_RDB_ENC_INT8) {\n        if (rioRead(rdb,enc,1) == 0) return NULL;\n        val = (signed char)enc[0];\n    } else if (enctype == REDIS_RDB_ENC_INT16) {\n        uint16_t v;\n        if (rioRead(rdb,enc,2) == 0) return NULL;\n        v = enc[0]|(enc[1]<<8);\n        val = (int16_t)v;\n    } else if (enctype == REDIS_RDB_ENC_INT32) {\n        uint32_t v;\n        if (rioRead(rdb,enc,4) == 0) return NULL;\n        v = enc[0]|(enc[1]<<8)|(enc[2]<<16)|(enc[3]<<24);\n        val = (int32_t)v;\n    } else {\n        val = 0; /* anti-warning */\n        redisPanic(\"Unknown RDB integer encoding type\");\n    }\n\n    \n    if (encode)\n        // 整数编码的字符串\n        return createStringObjectFromLongLong(val);\n    else\n        // 未编码\n        return createObject(REDIS_STRING,sdsfromlonglong(val));\n}\n\n/* String objects in the form \"2391\" \"-100\" without any space and with a\n * range of values that can fit in an 8, 16 or 32 bit signed value can be\n * encoded as integers to save space \n *\n * 那些保存像是 \"2391\" 、 \"-100\" 这样的字符串的字符串对象，\n * 可以将它们的值保存到 8 位、16 位或 32 位的带符号整数值中，\n * 从而节省一些内存。\n *\n * 这个函数就是尝试将字符串编码成整数，\n * 如果成功的话，返回保存整数值所需的字节数，这个值必然大于 0 。\n *\n * 如果转换失败，那么返回 0 。\n */\nint rdbTryIntegerEncoding(char *s, size_t len, unsigned char *enc) {\n    long long value;\n    char *endptr, buf[32];\n\n    /* Check if it's possible to encode this value as a number */\n    // 尝试将值转换为整数\n    value = strtoll(s, &endptr, 10);\n    if (endptr[0] != '\\0') return 0;\n\n    // 尝试将转换后的整数转换回字符串\n    ll2string(buf,32,value);\n\n    /* If the number converted back into a string is not identical\n     * then it's not possible to encode the string as integer */\n    // 检查两次转换后的整数值能否还原回原来的字符串\n    // 如果不行的话，那么转换失败\n    if (strlen(buf) != len || memcmp(buf,s,len)) return 0;\n\n    // 转换成功，对转换所得的整数进行特殊编码\n    return rdbEncodeInteger(value,enc);\n}\n\n/*\n * 尝试对输入字符串 s 进行压缩，\n * 如果压缩成功，那么将压缩后的字符串保存到 rdb 中。\n *\n * 函数在成功时返回保存压缩后的 s 所需的字节数，\n * 压缩失败或者内存不足时返回 0 ，\n * 写入失败时返回 -1 。\n */\nint rdbSaveLzfStringObject(rio *rdb, unsigned char *s, size_t len) {\n    size_t comprlen, outlen;\n    unsigned char byte;\n    int n, nwritten = 0;\n    void *out;\n\n    /* We require at least four bytes compression for this to be worth it */\n    // 压缩字符串\n    if (len <= 4) return 0;\n    outlen = len-4;\n    if ((out = zmalloc(outlen+1)) == NULL) return 0;\n    comprlen = lzf_compress(s, len, out, outlen);\n    if (comprlen == 0) {\n        zfree(out);\n        return 0;\n    }\n\n    /* Data compressed! Let's save it on disk \n     *\n     * 保存压缩后的字符串到 rdb 。\n     */\n\n    // 写入类型，说明这是一个 LZF 压缩字符串\n    byte = (REDIS_RDB_ENCVAL<<6)|REDIS_RDB_ENC_LZF;\n    if ((n = rdbWriteRaw(rdb,&byte,1)) == -1) goto writeerr;\n    nwritten += n;\n\n    // 写入字符串压缩后的长度\n    if ((n = rdbSaveLen(rdb,comprlen)) == -1) goto writeerr;\n    nwritten += n;\n    \n    // 写入字符串未压缩时的长度\n    if ((n = rdbSaveLen(rdb,len)) == -1) goto writeerr;\n    nwritten += n;\n\n    // 写入压缩后的字符串\n    if ((n = rdbWriteRaw(rdb,out,comprlen)) == -1) goto writeerr;\n    nwritten += n;\n\n    zfree(out);\n\n    return nwritten;\n\nwriteerr:\n    zfree(out);\n    return -1;\n}\n\n/*\n * 从 rdb 中载入被 LZF 压缩的字符串，解压它，并创建相应的字符串对象。\n */\nrobj *rdbLoadLzfStringObject(rio *rdb) {\n    unsigned int len, clen;\n    unsigned char *c = NULL;\n    sds val = NULL;\n\n    // 读入压缩后的缓存长度\n    if ((clen = rdbLoadLen(rdb,NULL)) == REDIS_RDB_LENERR) return NULL;\n    // 读入字符串未压缩前的长度\n    if ((len = rdbLoadLen(rdb,NULL)) == REDIS_RDB_LENERR) return NULL;\n    // 压缩缓存空间\n    if ((c = zmalloc(clen)) == NULL) goto err;\n    // 字符串空间\n    if ((val = sdsnewlen(NULL,len)) == NULL) goto err;\n\n    // 读入压缩后的缓存\n    if (rioRead(rdb,c,clen) == 0) goto err;\n\n    // 解压缓存，得出字符串\n    if (lzf_decompress(c,clen,val,len) == 0) goto err;\n    zfree(c);\n\n    // 创建字符串对象\n    return createObject(REDIS_STRING,val);\nerr:\n    zfree(c);\n    sdsfree(val);\n    return NULL;\n}\n\n/* Save a string object as [len][data] on disk. If the object is a string\n * representation of an integer value we try to save it in a special form \n *\n * 以 [len][data] 的形式将字符串对象写入到 rdb 中。\n *\n * 如果对象是字符串表示的整数值，那么程序尝试以特殊的形式来保存它。\n *\n * 函数返回保存字符串所需的空间字节数。\n */\nint rdbSaveRawString(rio *rdb, unsigned char *s, size_t len) {\n    int enclen;\n    int n, nwritten = 0;\n\n    /* Try integer encoding \n     *\n     * 尝试进行整数值编码\n     */\n    if (len <= 11) {\n        unsigned char buf[5];\n        if ((enclen = rdbTryIntegerEncoding((char*)s,len,buf)) > 0) {\n            // 整数转换成功，写入\n            if (rdbWriteRaw(rdb,buf,enclen) == -1) return -1;\n            // 返回字节数\n            return enclen;\n        }\n    }\n\n    /* Try LZF compression - under 20 bytes it's unable to compress even\n     * aaaaaaaaaaaaaaaaaa so skip it \n     *\n     * 如果字符串长度大于 20 ，并且服务器开启了 LZF 压缩，\n     * 那么在保存字符串到数据库之前，先对字符串进行 LZF 压缩。\n     */\n    if (server.rdb_compression && len > 20) {\n\n        // 尝试压缩\n        n = rdbSaveLzfStringObject(rdb,s,len);\n\n        if (n == -1) return -1;\n        if (n > 0) return n;\n        /* Return value of 0 means data can't be compressed, save the old way */\n    }\n\n    // 执行到这里，说明值 s 既不能编码为整数\n    // 也不能被压缩\n    // 那么直接将它写入到 rdb 中\n    /* Store verbatim */\n\n    // 写入长度\n    if ((n = rdbSaveLen(rdb,len)) == -1) return -1;\n    nwritten += n;\n\n    // 写入内容\n    if (len > 0) {\n        if (rdbWriteRaw(rdb,s,len) == -1) return -1;\n        nwritten += len;\n    }\n\n    return nwritten;\n}\n\n/* Save a long long value as either an encoded string or a string. \n *\n * 将输入的 long long 类型的 value 转换成一个特殊编码的字符串，\n * 或者是一个普通的字符串表示的整数，\n * 然后将它写入到 rdb 中。\n *\n * 函数返回在 rdb 中保存 value 所需的字节数。\n */\nint rdbSaveLongLongAsStringObject(rio *rdb, long long value) {\n    unsigned char buf[32];\n    int n, nwritten = 0;\n\n    // 尝试以节省空间的方式编码整数值 value \n    int enclen = rdbEncodeInteger(value,buf);\n\n    // 编码成功，直接写入编码后的缓存\n    // 比如，值 1 可以编码为 11 00 0001\n    if (enclen > 0) {\n        return rdbWriteRaw(rdb,buf,enclen);\n\n    // 编码失败，将整数值转换成对应的字符串来保存\n    // 比如，值 999999999 要编码成 \"999999999\" ，\n    // 因为这个值没办法用节省空间的方式编码\n    } else {\n        /* Encode as string */\n        // 转换成字符串表示\n        enclen = ll2string((char*)buf,32,value);\n        redisAssert(enclen < 32);\n        // 写入字符串长度\n        if ((n = rdbSaveLen(rdb,enclen)) == -1) return -1;\n        nwritten += n;\n        // 写入字符串\n        if ((n = rdbWriteRaw(rdb,buf,enclen)) == -1) return -1;\n        nwritten += n;\n    }\n\n    // 返回长度\n    return nwritten;\n}\n\n/* Like rdbSaveStringObjectRaw() but handle encoded objects */\n/*\n * 将给定的字符串对象 obj 保存到 rdb 中。\n *\n * 函数返回 rdb 保存字符串对象所需的字节数。\n *\n * p.s. 代码原本的注释 rdbSaveStringObjectRaw() 函数已经不存在了。\n */\nint rdbSaveStringObject(rio *rdb, robj *obj) {\n\n    /* Avoid to decode the object, then encode it again, if the\n     * object is already integer encoded. */\n    // 尝试对 INT 编码的字符串进行特殊编码\n    if (obj->encoding == REDIS_ENCODING_INT) {\n        return rdbSaveLongLongAsStringObject(rdb,(long)obj->ptr);\n\n    // 保存 STRING 编码的字符串\n    } else {\n        redisAssertWithInfo(NULL,obj,sdsEncodedObject(obj));\n        return rdbSaveRawString(rdb,obj->ptr,sdslen(obj->ptr));\n    }\n}\n\n/*\n * 从 rdb 中载入一个字符串对象\n *\n * encode 不为 0 时，它指定了字符串所使用的编码。\n */ \nrobj *rdbGenericLoadStringObject(rio *rdb, int encode) {\n    int isencoded;\n    uint32_t len;\n    sds val;\n\n    // 长度\n    len = rdbLoadLen(rdb,&isencoded);\n\n    // 这是一个特殊编码字符串\n    if (isencoded) {\n\n        switch(len) {\n\n        // 整数编码\n        case REDIS_RDB_ENC_INT8:\n        case REDIS_RDB_ENC_INT16:\n        case REDIS_RDB_ENC_INT32:\n            return rdbLoadIntegerObject(rdb,len,encode);\n\n        // LZF 压缩\n        case REDIS_RDB_ENC_LZF:\n            return rdbLoadLzfStringObject(rdb);\n\n        default:\n            redisPanic(\"Unknown RDB encoding type\");\n        }\n    }\n\n    if (len == REDIS_RDB_LENERR) return NULL;\n\n    // 执行到这里，说明这个字符串即没有被压缩，也不是整数\n    // 那么直接从 rdb 中读入它\n    val = sdsnewlen(NULL,len);\n    if (len && rioRead(rdb,val,len) == 0) {\n        sdsfree(val);\n        return NULL;\n    }\n\n    return createObject(REDIS_STRING,val);\n}\n\nrobj *rdbLoadStringObject(rio *rdb) {\n    return rdbGenericLoadStringObject(rdb,0);\n}\n\nrobj *rdbLoadEncodedStringObject(rio *rdb) {\n    return rdbGenericLoadStringObject(rdb,1);\n}\n\n/* Save a double value. Doubles are saved as strings prefixed by an unsigned\n * 8 bit integer specifying the length of the representation.\n *\n * 以字符串形式来保存一个双精度浮点数。\n * 字符串的前面是一个 8 位长的无符号整数值，\n * 它指定了浮点数表示的长度。\n *\n * This 8 bit integer has special values in order to specify the following\n * conditions:\n *\n * 其中， 8 位整数中的以下值用作特殊值，来指示一些特殊情况：\n *\n * 253: not a number\n *      输入不是数\n * 254: + inf\n *      输入为正无穷\n * 255: - inf\n *      输入为负无穷\n */\nint rdbSaveDoubleValue(rio *rdb, double val) {\n    unsigned char buf[128];\n    int len;\n\n    // 不是数\n    if (isnan(val)) {\n        buf[0] = 253;\n        len = 1;\n\n    // 无穷\n    } else if (!isfinite(val)) {\n        len = 1;\n        buf[0] = (val < 0) ? 255 : 254;\n\n    // 转换为整数\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 (val > min && val < max && val == ((double)((long long)val)))\n            ll2string((char*)buf+1,sizeof(buf)-1,(long long)val);\n        else\n#endif\n            snprintf((char*)buf+1,sizeof(buf)-1,\"%.17g\",val);\n        buf[0] = strlen((char*)buf+1);\n        len = buf[0]+1;\n    }\n\n    // 将字符串写入到 rdb\n    return rdbWriteRaw(rdb,buf,len);\n}\n\n/* For information about double serialization check rdbSaveDoubleValue() \n *\n * 载入字符串表示的双精度浮点数\n */\nint rdbLoadDoubleValue(rio *rdb, double *val) {\n    char buf[256];\n    unsigned char len;\n\n    // 载入字符串长度\n    if (rioRead(rdb,&len,1) == 0) return -1;\n\n    switch(len) {\n    // 特殊值\n    case 255: *val = R_NegInf; return 0;\n    case 254: *val = R_PosInf; return 0;\n    case 253: *val = R_Nan; return 0;\n    // 载入字符串\n    default:\n        if (rioRead(rdb,buf,len) == 0) return -1;\n        buf[len] = '\\0';\n        sscanf(buf, \"%lg\", val);\n        return 0;\n    }\n}\n\n/* Save the object type of object \"o\". \n *\n * 将对象 o 的类型写入到 rdb 中\n */\nint rdbSaveObjectType(rio *rdb, robj *o) {\n\n    switch (o->type) {\n\n    case REDIS_STRING:\n        return rdbSaveType(rdb,REDIS_RDB_TYPE_STRING);\n\n    case REDIS_LIST:\n        if (o->encoding == REDIS_ENCODING_ZIPLIST)\n            return rdbSaveType(rdb,REDIS_RDB_TYPE_LIST_ZIPLIST);\n        else if (o->encoding == REDIS_ENCODING_LINKEDLIST)\n            return rdbSaveType(rdb,REDIS_RDB_TYPE_LIST);\n        else\n            redisPanic(\"Unknown list encoding\");\n\n    case REDIS_SET:\n        if (o->encoding == REDIS_ENCODING_INTSET)\n            return rdbSaveType(rdb,REDIS_RDB_TYPE_SET_INTSET);\n        else if (o->encoding == REDIS_ENCODING_HT)\n            return rdbSaveType(rdb,REDIS_RDB_TYPE_SET);\n        else\n            redisPanic(\"Unknown set encoding\");\n\n    case REDIS_ZSET:\n        if (o->encoding == REDIS_ENCODING_ZIPLIST)\n            return rdbSaveType(rdb,REDIS_RDB_TYPE_ZSET_ZIPLIST);\n        else if (o->encoding == REDIS_ENCODING_SKIPLIST)\n            return rdbSaveType(rdb,REDIS_RDB_TYPE_ZSET);\n        else\n            redisPanic(\"Unknown sorted set encoding\");\n\n    case REDIS_HASH:\n        if (o->encoding == REDIS_ENCODING_ZIPLIST)\n            return rdbSaveType(rdb,REDIS_RDB_TYPE_HASH_ZIPLIST);\n        else if (o->encoding == REDIS_ENCODING_HT)\n            return rdbSaveType(rdb,REDIS_RDB_TYPE_HASH);\n        else\n            redisPanic(\"Unknown hash encoding\");\n\n    default:\n        redisPanic(\"Unknown object type\");\n    }\n\n    return -1; /* avoid warning */\n}\n\n/* Use rdbLoadType() to load a TYPE in RDB format, but returns -1 if the\n * type is not specifically a valid Object Type. */\nint rdbLoadObjectType(rio *rdb) {\n    int type;\n    if ((type = rdbLoadType(rdb)) == -1) return -1;\n    if (!rdbIsObjectType(type)) return -1;\n    return type;\n}\n\n/* Save a Redis object. Returns -1 on error, 0 on success. \n *\n * 将给定对象 o 保存到 rdb 中。\n *\n * 保存成功返回 rdb 保存该对象所需的字节数 ，失败返回 0 。\n *\n * p.s.上面原文注释所说的返回值是不正确的\n */\nint rdbSaveObject(rio *rdb, robj *o) {\n    int n, nwritten = 0;\n\n    // 保存字符串对象\n    if (o->type == REDIS_STRING) {\n        /* Save a string value */\n        if ((n = rdbSaveStringObject(rdb,o)) == -1) return -1;\n        nwritten += n;\n\n    // 保存列表对象\n    } else if (o->type == REDIS_LIST) {\n        /* Save a list value */\n        if (o->encoding == REDIS_ENCODING_ZIPLIST) {\n            size_t l = ziplistBlobLen((unsigned char*)o->ptr);\n\n            // 以字符串对象的形式保存整个 ZIPLIST 列表\n            if ((n = rdbSaveRawString(rdb,o->ptr,l)) == -1) return -1;\n            nwritten += n;\n        } else if (o->encoding == REDIS_ENCODING_LINKEDLIST) {\n            list *list = o->ptr;\n            listIter li;\n            listNode *ln;\n\n            if ((n = rdbSaveLen(rdb,listLength(list))) == -1) return -1;\n            nwritten += n;\n\n            // 遍历所有列表项\n            listRewind(list,&li);\n            while((ln = listNext(&li))) {\n                robj *eleobj = listNodeValue(ln);\n                // 以字符串对象的形式保存列表项\n                if ((n = rdbSaveStringObject(rdb,eleobj)) == -1) return -1;\n                nwritten += n;\n            }\n        } else {\n            redisPanic(\"Unknown list encoding\");\n        }\n\n    // 保存集合对象\n    } else if (o->type == REDIS_SET) {\n        /* Save a set value */\n        if (o->encoding == REDIS_ENCODING_HT) {\n            dict *set = o->ptr;\n            dictIterator *di = dictGetIterator(set);\n            dictEntry *de;\n\n            if ((n = rdbSaveLen(rdb,dictSize(set))) == -1) return -1;\n            nwritten += n;\n\n            // 遍历集合成员\n            while((de = dictNext(di)) != NULL) {\n                robj *eleobj = dictGetKey(de);\n                // 以字符串对象的方式保存成员\n                if ((n = rdbSaveStringObject(rdb,eleobj)) == -1) return -1;\n                nwritten += n;\n            }\n            dictReleaseIterator(di);\n        } else if (o->encoding == REDIS_ENCODING_INTSET) {\n            size_t l = intsetBlobLen((intset*)o->ptr);\n\n            // 以字符串对象的方式保存整个 INTSET 集合\n            if ((n = rdbSaveRawString(rdb,o->ptr,l)) == -1) return -1;\n            nwritten += n;\n        } else {\n            redisPanic(\"Unknown set encoding\");\n        }\n\n    // 保存有序集对象\n    } else if (o->type == REDIS_ZSET) {\n        /* Save a sorted set value */\n        if (o->encoding == REDIS_ENCODING_ZIPLIST) {\n            size_t l = ziplistBlobLen((unsigned char*)o->ptr);\n\n            // 以字符串对象的形式保存整个 ZIPLIST 有序集\n            if ((n = rdbSaveRawString(rdb,o->ptr,l)) == -1) return -1;\n            nwritten += n;\n        } else if (o->encoding == REDIS_ENCODING_SKIPLIST) {\n            zset *zs = o->ptr;\n            dictIterator *di = dictGetIterator(zs->dict);\n            dictEntry *de;\n\n            if ((n = rdbSaveLen(rdb,dictSize(zs->dict))) == -1) return -1;\n            nwritten += n;\n\n            // 遍历有序集\n            while((de = dictNext(di)) != NULL) {\n                robj *eleobj = dictGetKey(de);\n                double *score = dictGetVal(de);\n\n                // 以字符串对象的形式保存集合成员\n                if ((n = rdbSaveStringObject(rdb,eleobj)) == -1) return -1;\n                nwritten += n;\n\n                // 成员分值（一个双精度浮点数）会被转换成字符串\n                // 然后保存到 rdb 中\n                if ((n = rdbSaveDoubleValue(rdb,*score)) == -1) return -1;\n                nwritten += n;\n            }\n            dictReleaseIterator(di);\n        } else {\n            redisPanic(\"Unknown sorted set encoding\");\n        }\n\n    // 保存哈希表\n    } else if (o->type == REDIS_HASH) {\n\n        /* Save a hash value */\n        if (o->encoding == REDIS_ENCODING_ZIPLIST) {\n            size_t l = ziplistBlobLen((unsigned char*)o->ptr);\n\n            // 以字符串对象的形式保存整个 ZIPLIST 哈希表\n            if ((n = rdbSaveRawString(rdb,o->ptr,l)) == -1) return -1;\n            nwritten += n;\n\n        } else if (o->encoding == REDIS_ENCODING_HT) {\n            dictIterator *di = dictGetIterator(o->ptr);\n            dictEntry *de;\n\n            if ((n = rdbSaveLen(rdb,dictSize((dict*)o->ptr))) == -1) return -1;\n            nwritten += n;\n\n            // 迭代字典\n            while((de = dictNext(di)) != NULL) {\n                robj *key = dictGetKey(de);\n                robj *val = dictGetVal(de);\n\n                // 键和值都以字符串对象的形式来保存\n                if ((n = rdbSaveStringObject(rdb,key)) == -1) return -1;\n                nwritten += n;\n                if ((n = rdbSaveStringObject(rdb,val)) == -1) return -1;\n                nwritten += n;\n            }\n            dictReleaseIterator(di);\n\n        } else {\n            redisPanic(\"Unknown hash encoding\");\n        }\n\n    } else {\n        redisPanic(\"Unknown object type\");\n    }\n\n    return nwritten;\n}\n\n/* Return the length the object will have on disk if saved with\n * the rdbSaveObject() function. Currently we use a trick to get\n * this length with very little changes to the code. In the future\n * we could switch to a faster solution. */\n// 未使用，可能已经废弃\noff_t rdbSavedObjectLen(robj *o) {\n    int len = rdbSaveObject(NULL,o);\n    redisAssertWithInfo(NULL,o,len != -1);\n    return len;\n}\n\n/* Save a key-value pair, with expire time, type, key, value.\n *\n * 将键值对的键、值、过期时间和类型写入到 RDB 中。\n *\n * On error -1 is returned.\n *\n * 出错返回 -1 。\n *\n * On success if the key was actually saved 1 is returned, otherwise 0\n * is returned (the key was already expired). \n *\n * 成功保存返回 1 ，当键已经过期时，返回 0 。\n */\nint rdbSaveKeyValuePair(rio *rdb, robj *key, robj *val,\n                        long long expiretime, long long now)\n{\n    /* Save the expire time \n     *\n     * 保存键的过期时间\n     */\n    if (expiretime != -1) {\n        /* If this key is already expired skip it \n         *\n         * 不写入已经过期的键\n         */\n        if (expiretime < now) return 0;\n\n        if (rdbSaveType(rdb,REDIS_RDB_OPCODE_EXPIRETIME_MS) == -1) return -1;\n        if (rdbSaveMillisecondTime(rdb,expiretime) == -1) return -1;\n    }\n\n    /* Save type, key, value \n     *\n     * 保存类型，键，值\n     */\n    if (rdbSaveObjectType(rdb,val) == -1) return -1;\n    if (rdbSaveStringObject(rdb,key) == -1) return -1;\n    if (rdbSaveObject(rdb,val) == -1) return -1;\n\n    return 1;\n}\n\n/* Save the DB on disk. Return REDIS_ERR on error, REDIS_OK on success \n *\n * 将数据库保存到磁盘上。\n *\n * 保存成功返回 REDIS_OK ，出错/失败返回 REDIS_ERR 。\n */\nint rdbSave(char *filename) {\n    dictIterator *di = NULL;\n    dictEntry *de;\n    char tmpfile[256];\n    char magic[10];\n    int j;\n    long long now = mstime();\n    FILE *fp;\n    rio rdb;\n    uint64_t cksum;\n\n    // 创建临时文件\n    snprintf(tmpfile,256,\"temp-%d.rdb\", (int) getpid());\n    fp = fopen(tmpfile,\"w\");\n    if (!fp) {\n        redisLog(REDIS_WARNING, \"Failed opening .rdb for saving: %s\",\n            strerror(errno));\n        return REDIS_ERR;\n    }\n\n    // 初始化 I/O\n    rioInitWithFile(&rdb,fp);\n\n    // 设置校验和函数\n    if (server.rdb_checksum)\n        rdb.update_cksum = rioGenericUpdateChecksum;\n\n    // 写入 RDB 版本号\n    snprintf(magic,sizeof(magic),\"REDIS%04d\",REDIS_RDB_VERSION);\n    if (rdbWriteRaw(&rdb,magic,9) == -1) goto werr;\n\n    // 遍历所有数据库\n    for (j = 0; j < server.dbnum; j++) {\n\n        // 指向数据库\n        redisDb *db = server.db+j;\n\n        // 指向数据库键空间\n        dict *d = db->dict;\n\n        // 跳过空数据库\n        if (dictSize(d) == 0) continue;\n\n        // 创建键空间迭代器\n        di = dictGetSafeIterator(d);\n        if (!di) {\n            fclose(fp);\n            return REDIS_ERR;\n        }\n\n        /* Write the SELECT DB opcode \n         *\n         * 写入 DB 选择器\n         */\n        if (rdbSaveType(&rdb,REDIS_RDB_OPCODE_SELECTDB) == -1) goto werr;\n        if (rdbSaveLen(&rdb,j) == -1) goto werr;\n\n        /* Iterate this DB writing every entry \n         *\n         * 遍历数据库，并写入每个键值对的数据\n         */\n        while((de = dictNext(di)) != NULL) {\n            sds keystr = dictGetKey(de);\n            robj key, *o = dictGetVal(de);\n            long long expire;\n            \n            // 根据 keystr ，在栈中创建一个 key 对象\n            initStaticStringObject(key,keystr);\n\n            // 获取键的过期时间\n            expire = getExpire(db,&key);\n\n            // 保存键值对数据\n            if (rdbSaveKeyValuePair(&rdb,&key,o,expire,now) == -1) goto werr;\n        }\n        dictReleaseIterator(di);\n    }\n    di = NULL; /* So that we don't release it again on error. */\n\n    /* EOF opcode \n     *\n     * 写入 EOF 代码\n     */\n    if (rdbSaveType(&rdb,REDIS_RDB_OPCODE_EOF) == -1) goto werr;\n\n    /* CRC64 checksum. It will be zero if checksum computation is disabled, the\n     * loading code skips the check in this case. \n     *\n     * CRC64 校验和。\n     *\n     * 如果校验和功能已关闭，那么 rdb.cksum 将为 0 ，\n     * 在这种情况下， RDB 载入时会跳过校验和检查。\n     */\n    cksum = rdb.cksum;\n    memrev64ifbe(&cksum);\n    rioWrite(&rdb,&cksum,8);\n\n    /* Make sure data will not remain on the OS's output buffers */\n    // 冲洗缓存，确保数据已写入磁盘\n    if (fflush(fp) == EOF) goto werr;\n    if (fsync(fileno(fp)) == -1) goto werr;\n    if (fclose(fp) == EOF) goto werr;\n\n    /* Use RENAME to make sure the DB file is changed atomically only\n     * if the generate DB file is ok. \n     *\n     * 使用 RENAME ，原子性地对临时文件进行改名，覆盖原来的 RDB 文件。\n     */\n    if (rename(tmpfile,filename) == -1) {\n        redisLog(REDIS_WARNING,\"Error moving temp DB file on the final destination: %s\", strerror(errno));\n        unlink(tmpfile);\n        return REDIS_ERR;\n    }\n\n    // 写入完成，打印日志\n    redisLog(REDIS_NOTICE,\"DB saved on disk\");\n\n    // 清零数据库脏状态\n    server.dirty = 0;\n\n    // 记录最后一次完成 SAVE 的时间\n    server.lastsave = time(NULL);\n\n    // 记录最后一次执行 SAVE 的状态\n    server.lastbgsave_status = REDIS_OK;\n\n    return REDIS_OK;\n\nwerr:\n    // 关闭文件\n    fclose(fp);\n    // 删除文件\n    unlink(tmpfile);\n\n    redisLog(REDIS_WARNING,\"Write error saving DB on disk: %s\", strerror(errno));\n\n    if (di) dictReleaseIterator(di);\n\n    return REDIS_ERR;\n}\n\nint rdbSaveBackground(char *filename) {\n    pid_t childpid;\n    long long start;\n\n    // 如果 BGSAVE 已经在执行，那么出错\n    if (server.rdb_child_pid != -1) return REDIS_ERR;\n\n    // 记录 BGSAVE 执行前的数据库被修改次数\n    server.dirty_before_bgsave = server.dirty;\n\n    // 最近一次尝试执行 BGSAVE 的时间\n    server.lastbgsave_try = time(NULL);\n\n    // fork() 开始前的时间，记录 fork() 返回耗时用\n    start = ustime();\n\n    if ((childpid = fork()) == 0) {\n        int retval;\n\n        /* Child */\n\n        // 关闭网络连接 fd\n        closeListeningSockets(0);\n\n        // 设置进程的标题，方便识别\n        redisSetProcTitle(\"redis-rdb-bgsave\");\n\n        // 执行保存操作\n        retval = rdbSave(filename);\n\n        // 打印 copy-on-write 时使用的内存数\n        if (retval == REDIS_OK) {\n            size_t private_dirty = zmalloc_get_private_dirty();\n\n            if (private_dirty) {\n                redisLog(REDIS_NOTICE,\n                    \"RDB: %zu MB of memory used by copy-on-write\",\n                    private_dirty/(1024*1024));\n            }\n        }\n\n        // 向父进程发送信号\n        exitFromChild((retval == REDIS_OK) ? 0 : 1);\n\n    } else {\n\n        /* Parent */\n\n        // 计算 fork() 执行的时间\n        server.stat_fork_time = ustime()-start;\n\n        // 如果 fork() 出错，那么报告错误\n        if (childpid == -1) {\n            server.lastbgsave_status = REDIS_ERR;\n            redisLog(REDIS_WARNING,\"Can't save in background: fork: %s\",\n                strerror(errno));\n            return REDIS_ERR;\n        }\n\n        // 打印 BGSAVE 开始的日志\n        redisLog(REDIS_NOTICE,\"Background saving started by pid %d\",childpid);\n\n        // 记录数据库开始 BGSAVE 的时间\n        server.rdb_save_time_start = time(NULL);\n\n        // 记录负责执行 BGSAVE 的子进程 ID\n        server.rdb_child_pid = childpid;\n\n        // 关闭自动 rehash\n        updateDictResizePolicy();\n\n        return REDIS_OK;\n    }\n\n    return REDIS_OK; /* unreached */\n}\n\n/*\n * 移除 BGSAVE 所产生的临时文件\n *\n * BGSAVE 执行被中断时使用\n */\nvoid rdbRemoveTempFile(pid_t childpid) {\n    char tmpfile[256];\n\n    snprintf(tmpfile,256,\"temp-%d.rdb\", (int) childpid);\n    unlink(tmpfile);\n}\n\n/* Load a Redis object of the specified type from the specified file.\n *\n * 从 rdb 文件中载入指定类型的对象。\n *\n * On success a newly allocated object is returned, otherwise NULL. \n *\n * 读入成功返回一个新对象，否则返回 NULL 。\n */\nrobj *rdbLoadObject(int rdbtype, rio *rdb) {\n    robj *o, *ele, *dec;\n    size_t len;\n    unsigned int i;\n\n    // 载入字符串对象\n    if (rdbtype == REDIS_RDB_TYPE_STRING) {\n        /* Read string value */\n        if ((o = rdbLoadEncodedStringObject(rdb)) == NULL) return NULL;\n        o = tryObjectEncoding(o);\n\n    // 载入列表对象\n    } else if (rdbtype == REDIS_RDB_TYPE_LIST) {\n\n        /* Read list value \n         *\n         * 读入列表的节点数\n         */\n        if ((len = rdbLoadLen(rdb,NULL)) == REDIS_RDB_LENERR) return NULL;\n\n        /* Use a real list when there are too many entries \n         *\n         * 根据节点数，创建对象的编码\n         */\n        if (len > server.list_max_ziplist_entries) {\n            o = createListObject();\n        } else {\n            o = createZiplistObject();\n        }\n\n        /* Load every single element of the list \n         *\n         * 载入所有列表项\n         */\n        while(len--) {\n\n            // 载入字符串对象\n            if ((ele = rdbLoadEncodedStringObject(rdb)) == NULL) return NULL;\n\n            /* If we are using a ziplist and the value is too big, convert\n             * the object to a real list. \n             *\n             * 根据字符串对象，\n             * 检查是否需要将列表从 ZIPLIST 编码转换为 LINKEDLIST 编码\n             */\n            if (o->encoding == REDIS_ENCODING_ZIPLIST &&\n                sdsEncodedObject(ele) &&\n                sdslen(ele->ptr) > server.list_max_ziplist_value)\n                    listTypeConvert(o,REDIS_ENCODING_LINKEDLIST);\n\n            // ZIPLIST\n            if (o->encoding == REDIS_ENCODING_ZIPLIST) {\n                dec = getDecodedObject(ele);\n\n                // 将字符串值推入 ZIPLIST 末尾来重建列表\n                o->ptr = ziplistPush(o->ptr,dec->ptr,sdslen(dec->ptr),REDIS_TAIL);\n\n                decrRefCount(dec);\n                decrRefCount(ele);\n            } else {\n                // 将新列表项推入到链表的末尾\n                ele = tryObjectEncoding(ele);\n                listAddNodeTail(o->ptr,ele);\n            }\n        }\n\n    // 载入集合对象\n    } else if (rdbtype == REDIS_RDB_TYPE_SET) {\n\n        /* Read list/set value \n         *\n         * 载入列表元素的数量\n         */\n        if ((len = rdbLoadLen(rdb,NULL)) == REDIS_RDB_LENERR) return NULL;\n\n        /* Use a regular set when there are too many entries. \n         *\n         * 根据数量，选择 INTSET 编码还是 HT 编码*/\n        if (len > server.set_max_intset_entries) {\n            o = createSetObject();\n            /* It's faster to expand the dict to the right size asap in order\n             * to avoid rehashing */\n            if (len > DICT_HT_INITIAL_SIZE)\n                dictExpand(o->ptr,len);\n        } else {\n            o = createIntsetObject();\n        }\n\n        /* Load every single element of the list/set \n         *\n         * 载入所有集合元素*/\n        for (i = 0; i < len; i++) {\n            long long llval;\n\n            // 载入元素\n            if ((ele = rdbLoadEncodedStringObject(rdb)) == NULL) return NULL;\n            ele = tryObjectEncoding(ele);\n\n            // 将元素添加到 INTSET 集合，并在有需要的时候，转换编码为 HT\n            if (o->encoding == REDIS_ENCODING_INTSET) {\n                /* Fetch integer value from element */\n                if (isObjectRepresentableAsLongLong(ele,&llval) == REDIS_OK) {\n                    o->ptr = intsetAdd(o->ptr,llval,NULL);\n                } else {\n                    setTypeConvert(o,REDIS_ENCODING_HT);\n                    dictExpand(o->ptr,len);\n                }\n            }\n\n            /* This will also be called when the set was just converted\n             * to a regular hash table encoded set \n             *\n             * 将元素添加到 HT 编码的集合\n             */\n            if (o->encoding == REDIS_ENCODING_HT) {\n                dictAdd((dict*)o->ptr,ele,NULL);\n            } else {\n                decrRefCount(ele);\n            }\n        }\n\n    // 载入有序集合对象\n    } else if (rdbtype == REDIS_RDB_TYPE_ZSET) {\n        /* Read list/set value */\n        size_t zsetlen;\n        size_t maxelelen = 0;\n        zset *zs;\n\n        // 载入有序集合的元素数量\n        if ((zsetlen = rdbLoadLen(rdb,NULL)) == REDIS_RDB_LENERR) return NULL;\n\n        // 创建有序集合\n        o = createZsetObject();\n        zs = o->ptr;\n\n        /* Load every single element of the list/set */\n        while(zsetlen--) {\n            robj *ele;\n            double score;\n            zskiplistNode *znode;\n\n            // 载入元素成员\n            if ((ele = rdbLoadEncodedStringObject(rdb)) == NULL) return NULL;\n            ele = tryObjectEncoding(ele);\n\n            // 载入元素分值\n            if (rdbLoadDoubleValue(rdb,&score) == -1) return NULL;\n\n            /* Don't care about integer-encoded strings. */\n            // 记录成员的最大长度\n            if (sdsEncodedObject(ele) && sdslen(ele->ptr) > maxelelen)\n                maxelelen = sdslen(ele->ptr);\n\n            // 将元素插入到跳跃表中\n            znode = zslInsert(zs->zsl,score,ele);\n            // 将元素关联到字典中\n            dictAdd(zs->dict,ele,&znode->score);\n\n            incrRefCount(ele); /* added to skiplist */\n        }\n\n        /* Convert *after* loading, since sorted sets are not stored ordered. \n         *\n         * 如果有序集合符合条件的话，将它转换为 ZIPLIST 编码\n         * 节约空间\n         */\n        if (zsetLength(o) <= server.zset_max_ziplist_entries &&\n            maxelelen <= server.zset_max_ziplist_value)\n                zsetConvert(o,REDIS_ENCODING_ZIPLIST);\n\n    // 载入哈希表对象\n    } else if (rdbtype == REDIS_RDB_TYPE_HASH) {\n        size_t len;\n        int ret;\n\n        // 载入哈希表节点数量\n        len = rdbLoadLen(rdb, NULL);\n        if (len == REDIS_RDB_LENERR) return NULL;\n\n        // 创建哈希表\n        o = createHashObject();\n\n        /* Too many entries? Use a hash table.\n         * 根据节点数量，选择使用 ZIPLIST 编码还是 HT 编码\n         */\n        if (len > server.hash_max_ziplist_entries)\n            hashTypeConvert(o, REDIS_ENCODING_HT);\n\n        /* Load every field and value into the ziplist \n         *\n         * 载入所有域和值，并将它们推入到 ZIPLIST 中\n         */\n        while (o->encoding == REDIS_ENCODING_ZIPLIST && len > 0) {\n            robj *field, *value;\n\n            len--;\n\n            /* Load raw strings */\n            // 载入域（一个字符串）\n            field = rdbLoadStringObject(rdb);\n            if (field == NULL) return NULL;\n            // 载入值（一个字符串）\n            redisAssert(sdsEncodedObject(field));\n            value = rdbLoadStringObject(rdb);\n            if (value == NULL) return NULL;\n            redisAssert(sdsEncodedObject(value));\n\n            /* Add pair to ziplist \n             *\n             * 将域和值推入到 ZIPLIST 末尾\n             *\n             * 先推入域，再推入值。\n             */\n            o->ptr = ziplistPush(o->ptr, field->ptr, sdslen(field->ptr), ZIPLIST_TAIL);\n            o->ptr = ziplistPush(o->ptr, value->ptr, sdslen(value->ptr), ZIPLIST_TAIL);\n\n            /* Convert to hash table if size threshold is exceeded \n             *\n             * 如果元素过多，那么将编码转换为 HT \n             */\n            if (sdslen(field->ptr) > server.hash_max_ziplist_value ||\n                sdslen(value->ptr) > server.hash_max_ziplist_value)\n            {\n                decrRefCount(field);\n                decrRefCount(value);\n                hashTypeConvert(o, REDIS_ENCODING_HT);\n                break;\n            }\n            decrRefCount(field);\n            decrRefCount(value);\n        }\n\n        /* Load remaining fields and values into the hash table \n         *\n         * 载入域值对到哈希表\n         */\n        while (o->encoding == REDIS_ENCODING_HT && len > 0) {\n            robj *field, *value;\n\n            len--;\n\n            /* Load encoded strings */\n            // 域和值都载入为字符串对象\n            field = rdbLoadEncodedStringObject(rdb);\n            if (field == NULL) return NULL;\n            value = rdbLoadEncodedStringObject(rdb);\n            if (value == NULL) return NULL;\n\n            // 尝试编码\n            field = tryObjectEncoding(field);\n            value = tryObjectEncoding(value);\n\n            /* Add pair to hash table \n             *\n             * 添加到哈希表\n             */\n            ret = dictAdd((dict*)o->ptr, field, value);\n            redisAssert(ret == REDIS_OK);\n        }\n\n        /* All pairs should be read by now */\n        redisAssert(len == 0);\n\n    } else if (rdbtype == REDIS_RDB_TYPE_HASH_ZIPMAP  ||\n               rdbtype == REDIS_RDB_TYPE_LIST_ZIPLIST ||\n               rdbtype == REDIS_RDB_TYPE_SET_INTSET   ||\n               rdbtype == REDIS_RDB_TYPE_ZSET_ZIPLIST ||\n               rdbtype == REDIS_RDB_TYPE_HASH_ZIPLIST)\n    {\n        // 载入字符串对象\n        robj *aux = rdbLoadStringObject(rdb);\n\n        if (aux == NULL) return NULL;\n\n        o = createObject(REDIS_STRING,NULL); /* string is just placeholder */\n        o->ptr = zmalloc(sdslen(aux->ptr));\n        memcpy(o->ptr,aux->ptr,sdslen(aux->ptr));\n        decrRefCount(aux);\n\n        /* Fix the object encoding, and make sure to convert the encoded\n         * data type into the base type if accordingly to the current\n         * configuration there are too many elements in the encoded data\n         * type. Note that we only check the length and not max element\n         * size as this is an O(N) scan. Eventually everything will get\n         * converted. \n         *\n         * 根据读取的类型，将值恢复成原来的编码对象。\n         *\n         * 在创建编码对象的过程中，程序会检查对象的元素长度，\n         * 如果长度超过指定值的话，就会将内存编码对象转换成普通数据结构对象。\n         */\n        switch(rdbtype) {\n\n            // ZIPMAP 编码的哈希表\n            case REDIS_RDB_TYPE_HASH_ZIPMAP:\n                /* Convert to ziplist encoded hash. This must be deprecated\n                 * when loading dumps created by Redis 2.4 gets deprecated. */\n                {\n                    // 创建 ZIPLIST\n                    unsigned char *zl = ziplistNew();\n                    unsigned char *zi = zipmapRewind(o->ptr);\n                    unsigned char *fstr, *vstr;\n                    unsigned int flen, vlen;\n                    unsigned int maxlen = 0;\n\n                    // 从 2.6 开始， HASH 不再使用 ZIPMAP 来进行编码\n                    // 所以遇到 ZIPMAP 编码的值时，要将它转换为 ZIPLIST\n\n                    // 从字符串中取出 ZIPMAP 的域和值，然后推入到 ZIPLIST 中\n                    while ((zi = zipmapNext(zi, &fstr, &flen, &vstr, &vlen)) != NULL) {\n                        if (flen > maxlen) maxlen = flen;\n                        if (vlen > maxlen) maxlen = vlen;\n                        zl = ziplistPush(zl, fstr, flen, ZIPLIST_TAIL);\n                        zl = ziplistPush(zl, vstr, vlen, ZIPLIST_TAIL);\n                    }\n\n                    zfree(o->ptr);\n\n                    // 设置类型、编码和值指针\n                    o->ptr = zl;\n                    o->type = REDIS_HASH;\n                    o->encoding = REDIS_ENCODING_ZIPLIST;\n\n                    // 是否需要从 ZIPLIST 编码转换为 HT 编码\n                    if (hashTypeLength(o) > server.hash_max_ziplist_entries ||\n                        maxlen > server.hash_max_ziplist_value)\n                    {\n                        hashTypeConvert(o, REDIS_ENCODING_HT);\n                    }\n                }\n                break;\n\n            // ZIPLIST 编码的列表\n            case REDIS_RDB_TYPE_LIST_ZIPLIST:\n\n                o->type = REDIS_LIST;\n                o->encoding = REDIS_ENCODING_ZIPLIST;\n\n                // 检查是否需要转换编码\n                if (ziplistLen(o->ptr) > server.list_max_ziplist_entries)\n                    listTypeConvert(o,REDIS_ENCODING_LINKEDLIST);\n                break;\n\n            // INTSET 编码的集合\n            case REDIS_RDB_TYPE_SET_INTSET:\n\n                o->type = REDIS_SET;\n                o->encoding = REDIS_ENCODING_INTSET;\n\n                // 检查是否需要转换编码\n                if (intsetLen(o->ptr) > server.set_max_intset_entries)\n                    setTypeConvert(o,REDIS_ENCODING_HT);\n                break;\n\n            // ZIPLIST 编码的有序集合\n            case REDIS_RDB_TYPE_ZSET_ZIPLIST:\n\n                o->type = REDIS_ZSET;\n                o->encoding = REDIS_ENCODING_ZIPLIST;\n\n                // 检查是否需要转换编码\n                if (zsetLength(o) > server.zset_max_ziplist_entries)\n                    zsetConvert(o,REDIS_ENCODING_SKIPLIST);\n                break;\n\n            // ZIPLIST 编码的 HASH\n            case REDIS_RDB_TYPE_HASH_ZIPLIST:\n\n                o->type = REDIS_HASH;\n                o->encoding = REDIS_ENCODING_ZIPLIST;\n\n                // 检查是否需要转换编码\n                if (hashTypeLength(o) > server.hash_max_ziplist_entries)\n                    hashTypeConvert(o, REDIS_ENCODING_HT);\n                break;\n\n            default:\n                redisPanic(\"Unknown encoding\");\n                break;\n        }\n\n    } else {\n        redisPanic(\"Unknown object type\");\n    }\n\n    return o;\n}\n\n/* Mark that we are loading in the global state and setup the fields\n * needed to provide loading stats. \n *\n * 在全局状态中标记程序正在进行载入，\n * 并设置相应的载入状态。\n */\nvoid startLoading(FILE *fp) {\n    struct stat sb;\n\n    /* Load the DB */\n\n    // 正在载入\n    server.loading = 1;\n\n    // 开始进行载入的时间\n    server.loading_start_time = time(NULL);\n\n    // 文件的大小\n    if (fstat(fileno(fp), &sb) == -1) {\n        server.loading_total_bytes = 1; /* just to avoid division by zero */\n    } else {\n        server.loading_total_bytes = sb.st_size;\n    }\n}\n\n/* Refresh the loading progress info */\n// 刷新载入进度信息\nvoid loadingProgress(off_t pos) {\n    server.loading_loaded_bytes = pos;\n    if (server.stat_peak_memory < zmalloc_used_memory())\n        server.stat_peak_memory = zmalloc_used_memory();\n}\n\n/* Loading finished \n *\n * 关闭服务器载入状态\n */\nvoid stopLoading(void) {\n    server.loading = 0;\n}\n\n/* Track loading progress in order to serve client's from time to time\n   and if needed calculate rdb checksum  */\n// 记录载入进度信息，以便让客户端进行查询\n// 这也会在计算 RDB 校验和时用到。\nvoid rdbLoadProgressCallback(rio *r, const void *buf, size_t len) {\n    if (server.rdb_checksum)\n        rioGenericUpdateChecksum(r, buf, len);\n    if (server.loading_process_events_interval_bytes &&\n        (r->processed_bytes + len)/server.loading_process_events_interval_bytes > r->processed_bytes/server.loading_process_events_interval_bytes)\n    {\n        /* The DB can take some non trivial amount of time to load. Update\n         * our cached time since it is used to create and update the last\n         * interaction time with clients and for other important things. */\n        updateCachedTime();\n        if (server.masterhost && server.repl_state == REDIS_REPL_TRANSFER)\n            replicationSendNewlineToMaster();\n        loadingProgress(r->processed_bytes);\n        processEventsWhileBlocked();\n    }\n}\n\n/*\n * 将给定 rdb 中保存的数据载入到数据库中。\n */\nint rdbLoad(char *filename) {\n    uint32_t dbid;\n    int type, rdbver;\n    redisDb *db = server.db+0;\n    char buf[1024];\n    long long expiretime, now = mstime();\n    FILE *fp;\n    rio rdb;\n\n    // 打开 rdb 文件\n    if ((fp = fopen(filename,\"r\")) == NULL) return REDIS_ERR;\n\n    // 初始化写入流\n    rioInitWithFile(&rdb,fp);\n    rdb.update_cksum = rdbLoadProgressCallback;\n    rdb.max_processing_chunk = server.loading_process_events_interval_bytes;\n    if (rioRead(&rdb,buf,9) == 0) goto eoferr;\n    buf[9] = '\\0';\n\n    // 检查版本号\n    if (memcmp(buf,\"REDIS\",5) != 0) {\n        fclose(fp);\n        redisLog(REDIS_WARNING,\"Wrong signature trying to load DB from file\");\n        errno = EINVAL;\n        return REDIS_ERR;\n    }\n    rdbver = atoi(buf+5);\n    if (rdbver < 1 || rdbver > REDIS_RDB_VERSION) {\n        fclose(fp);\n        redisLog(REDIS_WARNING,\"Can't handle RDB format version %d\",rdbver);\n        errno = EINVAL;\n        return REDIS_ERR;\n    }\n\n    // 将服务器状态调整到开始载入状态\n    startLoading(fp);\n    while(1) {\n        robj *key, *val;\n        expiretime = -1;\n\n        /* Read type. \n         *\n         * 读入类型指示，决定该如何读入之后跟着的数据。\n         *\n         * 这个指示可以是 rdb.h 中定义的所有以\n         * REDIS_RDB_TYPE_* 为前缀的常量的其中一个\n         * 或者所有以 REDIS_RDB_OPCODE_* 为前缀的常量的其中一个\n         */\n        if ((type = rdbLoadType(&rdb)) == -1) goto eoferr;\n\n        // 读入过期时间值\n        if (type == REDIS_RDB_OPCODE_EXPIRETIME) {\n\n            // 以秒计算的过期时间\n\n            if ((expiretime = rdbLoadTime(&rdb)) == -1) goto eoferr;\n\n            /* We read the time so we need to read the object type again. \n             *\n             * 在过期时间之后会跟着一个键值对，我们要读入这个键值对的类型\n             */\n            if ((type = rdbLoadType(&rdb)) == -1) goto eoferr;\n\n            /* the EXPIRETIME opcode specifies time in seconds, so convert\n             * into milliseconds. \n             *\n             * 将格式转换为毫秒*/\n            expiretime *= 1000;\n        } else if (type == REDIS_RDB_OPCODE_EXPIRETIME_MS) {\n\n            // 以毫秒计算的过期时间\n\n            /* Milliseconds precision expire times introduced with RDB\n             * version 3. */\n            if ((expiretime = rdbLoadMillisecondTime(&rdb)) == -1) goto eoferr;\n\n            /* We read the time so we need to read the object type again.\n             *\n             * 在过期时间之后会跟着一个键值对，我们要读入这个键值对的类型\n             */\n            if ((type = rdbLoadType(&rdb)) == -1) goto eoferr;\n        }\n            \n        // 读入数据 EOF （不是 rdb 文件的 EOF）\n        if (type == REDIS_RDB_OPCODE_EOF)\n            break;\n\n        /* Handle SELECT DB opcode as a special case \n         *\n         * 读入切换数据库指示\n         */\n        if (type == REDIS_RDB_OPCODE_SELECTDB) {\n\n            // 读入数据库号码\n            if ((dbid = rdbLoadLen(&rdb,NULL)) == REDIS_RDB_LENERR)\n                goto eoferr;\n\n            // 检查数据库号码的正确性\n            if (dbid >= (unsigned)server.dbnum) {\n                redisLog(REDIS_WARNING,\"FATAL: Data file was created with a Redis server configured to handle more than %d databases. Exiting\\n\", server.dbnum);\n                exit(1);\n            }\n\n            // 在程序内容切换数据库\n            db = server.db+dbid;\n\n            // 跳过\n            continue;\n        }\n\n        /* Read key \n         *\n         * 读入键\n         */\n        if ((key = rdbLoadStringObject(&rdb)) == NULL) goto eoferr;\n\n        /* Read value \n         *\n         * 读入值\n         */\n        if ((val = rdbLoadObject(type,&rdb)) == NULL) goto eoferr;\n\n        /* Check if the key already expired. This function is used when loading\n         * an RDB file from disk, either at startup, or when an RDB was\n         * received from the master. In the latter case, the master is\n         * responsible for key expiry. If we would expire keys here, the\n         * snapshot taken by the master may not be reflected on the slave. \n         *\n         * 如果服务器为主节点的话，\n         * 那么在键已经过期的时候，不再将它们关联到数据库中去\n         */\n        if (server.masterhost == NULL && expiretime != -1 && expiretime < now) {\n            decrRefCount(key);\n            decrRefCount(val);\n            // 跳过\n            continue;\n        }\n\n        /* Add the new object in the hash table \n         *\n         * 将键值对关联到数据库中\n         */\n        dbAdd(db,key,val);\n\n        /* Set the expire time if needed \n         *\n         * 设置过期时间\n         */\n        if (expiretime != -1) setExpire(db,key,expiretime);\n\n        decrRefCount(key);\n    }\n\n    /* Verify the checksum if RDB version is >= 5 \n     *\n     * 如果 RDB 版本 >= 5 ，那么比对校验和\n     */\n    if (rdbver >= 5 && server.rdb_checksum) {\n        uint64_t cksum, expected = rdb.cksum;\n\n        // 读入文件的校验和\n        if (rioRead(&rdb,&cksum,8) == 0) goto eoferr;\n        memrev64ifbe(&cksum);\n\n        // 比对校验和\n        if (cksum == 0) {\n            redisLog(REDIS_WARNING,\"RDB file was saved with checksum disabled: no check performed.\");\n        } else if (cksum != expected) {\n            redisLog(REDIS_WARNING,\"Wrong RDB checksum. Aborting now.\");\n            exit(1);\n        }\n    }\n\n    // 关闭 RDB \n    fclose(fp);\n\n    // 服务器从载入状态中退出\n    stopLoading();\n\n    return REDIS_OK;\n\neoferr: /* unexpected end of file is handled here with a fatal exit */\n    redisLog(REDIS_WARNING,\"Short read or OOM loading DB. Unrecoverable error, aborting now.\");\n    exit(1);\n    return REDIS_ERR; /* Just to avoid warning */\n}\n\n/* A background saving child (BGSAVE) terminated its work. Handle this. \n *\n * 处理 BGSAVE 完成时发送的信号\n */\nvoid backgroundSaveDoneHandler(int exitcode, int bysignal) {\n\n    // BGSAVE 成功\n    if (!bysignal && exitcode == 0) {\n        redisLog(REDIS_NOTICE,\n            \"Background saving terminated with success\");\n        server.dirty = server.dirty - server.dirty_before_bgsave;\n        server.lastsave = time(NULL);\n        server.lastbgsave_status = REDIS_OK;\n\n    // BGSAVE 出错\n    } else if (!bysignal && exitcode != 0) {\n        redisLog(REDIS_WARNING, \"Background saving error\");\n        server.lastbgsave_status = REDIS_ERR;\n\n    // BGSAVE 被中断\n    } else {\n        redisLog(REDIS_WARNING,\n            \"Background saving terminated by signal %d\", bysignal);\n        // 移除临时文件\n        rdbRemoveTempFile(server.rdb_child_pid);\n        /* SIGUSR1 is whitelisted, so we have a way to kill a child without\n         * tirggering an error conditon. */\n        if (bysignal != SIGUSR1)\n            server.lastbgsave_status = REDIS_ERR;\n    }\n\n    // 更新服务器状态\n    server.rdb_child_pid = -1;\n    server.rdb_save_time_last = time(NULL)-server.rdb_save_time_start;\n    server.rdb_save_time_start = -1;\n\n    /* Possibly there are slaves waiting for a BGSAVE in order to be served\n     * (the first stage of SYNC is a bulk transfer of dump.rdb) */\n    // 处理正在等待 BGSAVE 完成的那些 slave\n    updateSlavesWaitingBgsave(exitcode == 0 ? REDIS_OK : REDIS_ERR);\n}\n\nvoid saveCommand(redisClient *c) {\n\n    // BGSAVE 已经在执行中，不能再执行 SAVE\n    // 否则将产生竞争条件\n    if (server.rdb_child_pid != -1) {\n        addReplyError(c,\"Background save already in progress\");\n        return;\n    }\n\n    // 执行 \n    if (rdbSave(server.rdb_filename) == REDIS_OK) {\n        addReply(c,shared.ok);\n    } else {\n        addReply(c,shared.err);\n    }\n}\n\nvoid bgsaveCommand(redisClient *c) {\n\n    // 不能重复执行 BGSAVE\n    if (server.rdb_child_pid != -1) {\n        addReplyError(c,\"Background save already in progress\");\n\n    // 不能在 BGREWRITEAOF 正在运行时执行\n    } else if (server.aof_child_pid != -1) {\n        addReplyError(c,\"Can't BGSAVE while AOF log rewriting is in progress\");\n\n    // 执行 BGSAVE\n    } else if (rdbSaveBackground(server.rdb_filename) == REDIS_OK) {\n        addReplyStatus(c,\"Background saving started\");\n\n    } else {\n        addReply(c,shared.err);\n    }\n}\n"
  },
  {
    "path": "src/rdb.h",
    "content": "/*\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#ifndef __REDIS_RDB_H\n#define __REDIS_RDB_H\n\n#include <stdio.h>\n#include \"rio.h\"\n\n/* TBD: include only necessary headers. */\n#include \"redis.h\"\n\n/* The current RDB version. When the format changes in a way that is no longer\n * backward compatible this number gets incremented.\n *\n * RDB 的版本，当新版本不向就版本兼容时，增一\n */\n#define REDIS_RDB_VERSION 6\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 * 通过读取第一字节的最高 2 位来判断长度\n *\n * 00|000000 => if the two MSB are 00 the len is the 6 bits of this byte\n *              长度编码在这一字节的其余 6 位中\n *\n * 01|000000 00000000 =>  01, the len is 14 byes, 6 bits + 8 bits of next byte\n *                        长度为 14 位，当前字节 6 位，加上下个字节 8 位\n *\n * 10|000000 [32 bit integer] => if it's 01, a full 32 bit len will follow\n *                               长度由后跟的 32 位保存\n *\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 REDIS_RDB_ENC_* defines.\n *           后跟一个特殊编码的对象。字节中的 6 位指定对象的类型。\n *           查看 REDIS_RDB_ENC_* 定义获得更多消息\n *\n * Lenghts up to 63 are stored using a single byte, most DB keys, and may\n * values, will fit inside. \n *\n * 一个字节（的其中 6 个字节）可以保存的最大长度是 63 （包括在内），\n * 对于大多数键和值来说，都已经足够了。\n */\n#define REDIS_RDB_6BITLEN 0\n#define REDIS_RDB_14BITLEN 1\n#define REDIS_RDB_32BITLEN 2\n#define REDIS_RDB_ENCVAL 3\n// 表示读取/写入错误\n#define REDIS_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 *\n * 当对象是一个字符串对象时，\n * 最高两个位之后的两个位（第 3 个位和第 4 个位）指定了对象的特殊编码\n */\n#define REDIS_RDB_ENC_INT8 0        /* 8 bit signed integer */\n#define REDIS_RDB_ENC_INT16 1       /* 16 bit signed integer */\n#define REDIS_RDB_ENC_INT32 2       /* 32 bit signed integer */\n#define REDIS_RDB_ENC_LZF 3         /* string compressed with FASTLZ */\n\n/* Dup object types to RDB object types. Only reason is readability (are we\n * dealing with RDB types or with in-memory object types?).\n *\n * 对象类型在 RDB 文件中的类型\n */\n#define REDIS_RDB_TYPE_STRING 0\n#define REDIS_RDB_TYPE_LIST   1\n#define REDIS_RDB_TYPE_SET    2\n#define REDIS_RDB_TYPE_ZSET   3\n#define REDIS_RDB_TYPE_HASH   4\n\n/* Object types for encoded objects.\n *\n * 对象的编码方式\n */\n#define REDIS_RDB_TYPE_HASH_ZIPMAP    9\n#define REDIS_RDB_TYPE_LIST_ZIPLIST  10\n#define REDIS_RDB_TYPE_SET_INTSET    11\n#define REDIS_RDB_TYPE_ZSET_ZIPLIST  12\n#define REDIS_RDB_TYPE_HASH_ZIPLIST  13\n\n/* Test if a type is an object type.\n *\n * 检查给定类型是否对象\n */\n#define rdbIsObjectType(t) ((t >= 0 && t <= 4) || (t >= 9 && t <= 13))\n\n/* Special RDB opcodes (saved/loaded with rdbSaveType/rdbLoadType).\n *\n * 数据库特殊操作标识符\n */\n// 以 MS 计算的过期时间\n#define REDIS_RDB_OPCODE_EXPIRETIME_MS 252\n// 以秒计算的过期时间\n#define REDIS_RDB_OPCODE_EXPIRETIME 253\n// 选择数据库\n#define REDIS_RDB_OPCODE_SELECTDB   254\n// 数据库的结尾（但不是 RDB 文件的结尾）\n#define REDIS_RDB_OPCODE_EOF        255\n\nint rdbSaveType(rio *rdb, unsigned char type);\nint rdbLoadType(rio *rdb);\nint rdbSaveTime(rio *rdb, time_t t);\ntime_t rdbLoadTime(rio *rdb);\nint rdbSaveLen(rio *rdb, uint32_t len);\nuint32_t rdbLoadLen(rio *rdb, int *isencoded);\nint rdbSaveObjectType(rio *rdb, robj *o);\nint rdbLoadObjectType(rio *rdb);\nint rdbLoad(char *filename);\nint rdbSaveBackground(char *filename);\nvoid rdbRemoveTempFile(pid_t childpid);\nint rdbSave(char *filename);\nint rdbSaveObject(rio *rdb, robj *o);\noff_t rdbSavedObjectLen(robj *o);\noff_t rdbSavedObjectPages(robj *o);\nrobj *rdbLoadObject(int type, rio *rdb);\nvoid backgroundSaveDoneHandler(int exitcode, int bysignal);\nint rdbSaveKeyValuePair(rio *rdb, robj *key, robj *val, long long expiretime, long long now);\nrobj *rdbLoadStringObject(rio *rdb);\n\n#endif\n"
  },
  {
    "path": "src/redis-benchmark.c",
    "content": "/* Redis benchmark utility.\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#include \"fmacros.h\"\n\n#include <stdio.h>\n#include <string.h>\n#include <stdlib.h>\n#include <unistd.h>\n#include <errno.h>\n#include <time.h>\n#include <sys/time.h>\n#include <signal.h>\n#include <assert.h>\n\n#include \"ae.h\"\n#include \"hiredis.h\"\n#include \"sds.h\"\n#include \"adlist.h\"\n#include \"zmalloc.h\"\n\n#define REDIS_NOTUSED(V) ((void) V)\n#define RANDPTR_INITIAL_SIZE 8\n\nstatic struct config {\n    aeEventLoop *el;\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 keepalive;\n    int pipeline;\n    long long start;\n    long long totlatency;\n    long long *latency;\n    const char *title;\n    list *clients;\n    int quiet;\n    int csv;\n    int loop;\n    int idlemode;\n    int dbnum;\n    sds dbnumstr;\n    char *tests;\n} config;\n\ntypedef struct _client {\n    redisContext *context;\n    sds obuf;\n    char **randptr;         /* Pointers to :rand: strings inside the command buf */\n    size_t randlen;         /* Number of pointers in client->randptr */\n    size_t randfree;        /* Number of unused pointers in client->randptr */\n    unsigned int 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 selectlen;  /* If non-zero, a SELECT of 'selectlen' bytes is currently\n                       used as a prefix of the pipline of commands. This gets\n                       discarded the first time it's sent. */\n} *client;\n\n/* Prototypes */\nstatic void writeHandler(aeEventLoop *el, int fd, void *privdata, int mask);\nstatic void createMissingClients(client c);\n\n/* Implementation */\nstatic long long ustime(void) {\n    struct timeval tv;\n    long long ust;\n\n    gettimeofday(&tv, NULL);\n    ust = ((long)tv.tv_sec)*1000000;\n    ust += tv.tv_usec;\n    return ust;\n}\n\nstatic long long mstime(void) {\n    struct timeval tv;\n    long long mst;\n\n    gettimeofday(&tv, NULL);\n    mst = ((long long)tv.tv_sec)*1000;\n    mst += tv.tv_usec/1000;\n    return mst;\n}\n\nstatic void freeClient(client c) {\n    listNode *ln;\n    aeDeleteFileEvent(config.el,c->context->fd,AE_WRITABLE);\n    aeDeleteFileEvent(config.el,c->context->fd,AE_READABLE);\n    redisFree(c->context);\n    sdsfree(c->obuf);\n    zfree(c->randptr);\n    zfree(c);\n    config.liveclients--;\n    ln = listSearchKey(config.clients,c);\n    assert(ln != NULL);\n    listDelNode(config.clients,ln);\n}\n\nstatic void freeAllClients(void) {\n    listNode *ln = config.clients->head, *next;\n\n    while(ln) {\n        next = ln->next;\n        freeClient(ln->value);\n        ln = next;\n    }\n}\n\nstatic void resetClient(client c) {\n    aeDeleteFileEvent(config.el,c->context->fd,AE_WRITABLE);\n    aeDeleteFileEvent(config.el,c->context->fd,AE_READABLE);\n    aeCreateFileEvent(config.el,c->context->fd,AE_WRITABLE,writeHandler,c);\n    c->written = 0;\n    c->pending = config.pipeline;\n}\n\nstatic void randomizeClientKey(client c) {\n    size_t i;\n\n    for (i = 0; i < c->randlen; i++) {\n        char *p = c->randptr[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 clientDone(client c) {\n    if (config.requests_finished == config.requests) {\n        freeClient(c);\n        aeStop(config.el);\n        return;\n    }\n    if (config.keepalive) {\n        resetClient(c);\n    } else {\n        config.liveclients--;\n        createMissingClients(c);\n        config.liveclients++;\n        freeClient(c);\n    }\n}\n\nstatic void readHandler(aeEventLoop *el, int fd, void *privdata, int mask) {\n    client c = privdata;\n    void *reply = NULL;\n    REDIS_NOTUSED(el);\n    REDIS_NOTUSED(fd);\n    REDIS_NOTUSED(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 = ustime()-(c->start);\n\n    if (redisBufferRead(c->context) != REDIS_OK) {\n        fprintf(stderr,\"Error: %s\\n\",c->context->errstr);\n        exit(1);\n    } else {\n        while(c->pending) {\n            if (redisGetReply(c->context,&reply) != REDIS_OK) {\n                fprintf(stderr,\"Error: %s\\n\",c->context->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                freeReplyObject(reply);\n\n                if (c->selectlen) {\n                    int j;\n\n                    /* This is the OK from SELECT. Just discard the SELECT\n                     * from the buffer. */\n                    c->pending--;\n                    sdsrange(c->obuf,c->selectlen,-1);\n                    /* We also need to fix the pointers to the strings\n                     * we need to randomize. */\n                    for (j = 0; j < c->randlen; j++)\n                        c->randptr[j] -= c->selectlen;\n                    c->selectlen = 0;\n                    continue;\n                }\n\n                if (config.requests_finished < config.requests)\n                    config.latency[config.requests_finished++] = c->latency;\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    client c = privdata;\n    REDIS_NOTUSED(el);\n    REDIS_NOTUSED(fd);\n    REDIS_NOTUSED(mask);\n\n    /* Initialize request when nothing was written. */\n    if (c->written == 0) {\n        /* Enforce upper bound to number of requests. */\n        if (config.requests_issued++ >= config.requests) {\n            freeClient(c);\n            return;\n        }\n\n        /* Really initialize: randomize keys and set start time. */\n        if (config.randomkeys) randomizeClientKey(c);\n        c->start = ustime();\n        c->latency = -1;\n    }\n\n    if (sdslen(c->obuf) > c->written) {\n        void *ptr = c->obuf+c->written;\n        int nwritten = write(c->context->fd,ptr,sdslen(c->obuf)-c->written);\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(config.el,c->context->fd,AE_WRITABLE);\n            aeCreateFileEvent(config.el,c->context->fd,AE_READABLE,readHandler,c);\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_int__ elements inside the command line, used\n *    for arguments randomization.\n *\n * Even when cloning another client, the SELECT command is automatically prefixed\n * if needed. */\nstatic client createClient(char *cmd, size_t len, client from) {\n    int j;\n    client c = zmalloc(sizeof(struct _client));\n\n    if (config.hostsocket == NULL) {\n        c->context = redisConnectNonBlock(config.hostip,config.hostport);\n    } else {\n        c->context = redisConnectUnixNonBlock(config.hostsocket);\n    }\n    if (c->context->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->context->errstr);\n        else\n            fprintf(stderr,\"%s: %s\\n\",config.hostsocket,c->context->errstr);\n        exit(1);\n    }\n    /* Suppress hiredis cleanup of unused buffers for max speed. */\n    c->context->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\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->selectlen = sdslen(c->obuf);\n    } else {\n        c->selectlen = 0;\n    }\n\n    /* Append the request itself. */\n    if (from) {\n        c->obuf = sdscatlen(c->obuf,\n            from->obuf+from->selectlen,\n            sdslen(from->obuf)-from->selectlen);\n    } else {\n        for (j = 0; j < config.pipeline; j++)\n            c->obuf = sdscatlen(c->obuf,cmd,len);\n    }\n    c->written = 0;\n    c->pending = config.pipeline;\n    c->randptr = NULL;\n    c->randlen = 0;\n    if (c->selectlen) c->pending++;\n\n    /* Find substrings in the output buffer that need to be randomized. */\n    if (config.randomkeys) {\n        if (from) {\n            c->randlen = from->randlen;\n            c->randfree = 0;\n            c->randptr = zmalloc(sizeof(char*)*c->randlen);\n            /* copy the offsets. */\n            for (j = 0; j < c->randlen; j++) {\n                c->randptr[j] = c->obuf + (from->randptr[j]-from->obuf);\n                /* Adjust for the different select prefix length. */\n                c->randptr[j] += c->selectlen - from->selectlen;\n            }\n        } else {\n            char *p = c->obuf;\n\n            c->randlen = 0;\n            c->randfree = RANDPTR_INITIAL_SIZE;\n            c->randptr = zmalloc(sizeof(char*)*c->randfree);\n            while ((p = strstr(p,\"__rand_int__\")) != NULL) {\n                if (c->randfree == 0) {\n                    c->randptr = zrealloc(c->randptr,sizeof(char*)*c->randlen*2);\n                    c->randfree += c->randlen;\n                }\n                c->randptr[c->randlen++] = p;\n                c->randfree--;\n                p += 12; /* 12 is strlen(\"__rand_int__). */\n            }\n        }\n    }\n    aeCreateFileEvent(config.el,c->context->fd,AE_WRITABLE,writeHandler,c);\n    listAddNodeTail(config.clients,c);\n    config.liveclients++;\n    return c;\n}\n\nstatic void createMissingClients(client c) {\n    int n = 0;\n    char *buf = c->obuf;\n    size_t buflen = sdslen(c->obuf);\n\n    /* If we are cloning from a client with a SELECT prefix, skip it since the\n     * client will be created with the prefixed SELECT if needed. */\n    if (c->selectlen) {\n        buf += c->selectlen;\n        buflen -= c->selectlen;\n    }\n\n    while(config.liveclients < config.numclients) {\n        createClient(NULL,0,c);\n\n        /* Listen backlog is quite limited on most systems */\n        if (++n > 64) {\n            usleep(50000);\n            n = 0;\n        }\n    }\n}\n\nstatic int compareLatency(const void *a, const void *b) {\n    return (*(long long*)a)-(*(long long*)b);\n}\n\nstatic void showLatencyReport(void) {\n    int i, curlat = 0;\n    float perc, reqpersec;\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        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    client c;\n\n    config.title = title;\n    config.requests_issued = 0;\n    config.requests_finished = 0;\n\n    c = createClient(cmd,len,NULL);\n    createMissingClients(c);\n\n    config.start = mstime();\n    aeMain(config.el);\n    config.totlatency = mstime()-config.start;\n\n    showLatencyReport();\n    freeAllClients();\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],\"-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],\"-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],\"-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],\"--dbnum\")) {\n            if (lastarg) goto invalid;\n            config.dbnum = atoi(argv[++i]);\n            config.dbnumstr = sdsfromlonglong(config.dbnum);\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: redis-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\" -c <clients>       Number of parallel connections (default 50)\\n\"\n\" -n <requests>      Total number of requests (default 10000)\\n\"\n\" -d <size>          Data size of SET/GET value in bytes (default 2)\\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, random values for SADD\\n\"\n\"  Using this option the benchmark will expand the string __rand_int__\\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\" -P <numreq>        Pipeline <numreq> requests. Default 1 (no pipeline).\\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\" -I                 Idle mode. Just open N idle connections and wait.\\n\\n\"\n\"Examples:\\n\\n\"\n\" Run the benchmark with the default configuration against 127.0.0.1:6379:\\n\"\n\"   $ redis-benchmark\\n\\n\"\n\" Use 20 parallel clients, for a total of 100k requests, against 192.168.1.1:\\n\"\n\"   $ redis-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\"   $ redis-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\"   $ redis-benchmark -t ping,set,get -n 100000 --csv\\n\\n\"\n\" Benchmark a specific command line:\\n\"\n\"   $ redis-benchmark -r 10000 -n 10000 eval 'return redis.call(\\\"ping\\\")' 0\\n\\n\"\n\" Fill a list with 10000 random elements:\\n\"\n\"   $ redis-benchmark -r 10000 -n 10000 lpush mylist __rand_int__\\n\\n\"\n\" On user specified command lines __rand_int__ is replaced with a random integer\\n\"\n\" with a range of values selected by the -r option.\\n\"\n    );\n    exit(exit_status);\n}\n\nint showThroughput(struct aeEventLoop *eventLoop, long long id, void *clientData) {\n    REDIS_NOTUSED(eventLoop);\n    REDIS_NOTUSED(id);\n    REDIS_NOTUSED(clientData);\n\n    if (config.csv) return 250;\n    float dt = (float)(mstime()-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 main(int argc, const char **argv) {\n    int i;\n    char *data, *cmd;\n    int len;\n\n    client c;\n\n    srandom(time(NULL));\n    signal(SIGHUP, SIG_IGN);\n    signal(SIGPIPE, SIG_IGN);\n\n    config.numclients = 50;\n    config.requests = 10000;\n    config.liveclients = 0;\n    config.el = aeCreateEventLoop(1024*10);\n    aeCreateTimeEvent(config.el,1,showThroughput,NULL,NULL);\n    config.keepalive = 1;\n    config.datasize = 3;\n    config.pipeline = 1;\n    config.randomkeys = 0;\n    config.randomkeys_keyspacelen = 0;\n    config.quiet = 0;\n    config.csv = 0;\n    config.loop = 0;\n    config.idlemode = 0;\n    config.latency = NULL;\n    config.clients = listCreate();\n    config.hostip = \"127.0.0.1\";\n    config.hostport = 6379;\n    config.hostsocket = NULL;\n    config.tests = NULL;\n    config.dbnum = 0;\n\n    i = parseOptions(argc,argv);\n    argc -= i;\n    argv += i;\n\n    config.latency = zmalloc(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    /* 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    do {\n        data = zmalloc(config.datasize+1);\n        memset(data,'x',config.datasize);\n        data[config.datasize] = '\\0';\n\n        if (test_is_selected(\"ping_inline\") || test_is_selected(\"ping\"))\n            benchmark(\"PING_INLINE\",\"PING\\r\\n\",6);\n\n        if (test_is_selected(\"ping_mbulk\") || test_is_selected(\"ping\")) {\n            len = redisFormatCommand(&cmd,\"PING\");\n            benchmark(\"PING_BULK\",cmd,len);\n            free(cmd);\n        }\n\n        if (test_is_selected(\"set\")) {\n            len = redisFormatCommand(&cmd,\"SET key:__rand_int__ %s\",data);\n            benchmark(\"SET\",cmd,len);\n            free(cmd);\n        }\n\n        if (test_is_selected(\"get\")) {\n            len = redisFormatCommand(&cmd,\"GET key:__rand_int__\");\n            benchmark(\"GET\",cmd,len);\n            free(cmd);\n        }\n\n        if (test_is_selected(\"incr\")) {\n            len = redisFormatCommand(&cmd,\"INCR counter:__rand_int__\");\n            benchmark(\"INCR\",cmd,len);\n            free(cmd);\n        }\n\n        if (test_is_selected(\"lpush\")) {\n            len = redisFormatCommand(&cmd,\"LPUSH mylist %s\",data);\n            benchmark(\"LPUSH\",cmd,len);\n            free(cmd);\n        }\n\n        if (test_is_selected(\"lpop\")) {\n            len = redisFormatCommand(&cmd,\"LPOP mylist\");\n            benchmark(\"LPOP\",cmd,len);\n            free(cmd);\n        }\n\n        if (test_is_selected(\"sadd\")) {\n            len = redisFormatCommand(&cmd,\n                \"SADD myset element:__rand_int__\");\n            benchmark(\"SADD\",cmd,len);\n            free(cmd);\n        }\n\n        if (test_is_selected(\"spop\")) {\n            len = redisFormatCommand(&cmd,\"SPOP myset\");\n            benchmark(\"SPOP\",cmd,len);\n            free(cmd);\n        }\n\n        if (test_is_selected(\"lrange\") ||\n            test_is_selected(\"lrange_100\") ||\n            test_is_selected(\"lrange_300\") ||\n            test_is_selected(\"lrange_500\") ||\n            test_is_selected(\"lrange_600\"))\n        {\n            len = redisFormatCommand(&cmd,\"LPUSH mylist %s\",data);\n            benchmark(\"LPUSH (needed to benchmark LRANGE)\",cmd,len);\n            free(cmd);\n        }\n\n        if (test_is_selected(\"lrange\") || test_is_selected(\"lrange_100\")) {\n            len = redisFormatCommand(&cmd,\"LRANGE mylist 0 99\");\n            benchmark(\"LRANGE_100 (first 100 elements)\",cmd,len);\n            free(cmd);\n        }\n\n        if (test_is_selected(\"lrange\") || test_is_selected(\"lrange_300\")) {\n            len = redisFormatCommand(&cmd,\"LRANGE mylist 0 299\");\n            benchmark(\"LRANGE_300 (first 300 elements)\",cmd,len);\n            free(cmd);\n        }\n\n        if (test_is_selected(\"lrange\") || test_is_selected(\"lrange_500\")) {\n            len = redisFormatCommand(&cmd,\"LRANGE mylist 0 449\");\n            benchmark(\"LRANGE_500 (first 450 elements)\",cmd,len);\n            free(cmd);\n        }\n\n        if (test_is_selected(\"lrange\") || test_is_selected(\"lrange_600\")) {\n            len = redisFormatCommand(&cmd,\"LRANGE mylist 0 599\");\n            benchmark(\"LRANGE_600 (first 600 elements)\",cmd,len);\n            free(cmd);\n        }\n\n        if (test_is_selected(\"mset\")) {\n            const char *argv[21];\n            argv[0] = \"MSET\";\n            for (i = 1; i < 21; i += 2) {\n                argv[i] = \"key:__rand_int__\";\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 (!config.csv) printf(\"\\n\");\n    } while(config.loop);\n\n    return 0;\n}\n"
  },
  {
    "path": "src/redis-check-aof.c",
    "content": "/*\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 \"fmacros.h\"\n#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n#include <unistd.h>\n#include <sys/stat.h>\n#include \"config.h\"\n\n#define ERROR(...) { \\\n    char __buf[1024]; \\\n    sprintf(__buf, __VA_ARGS__); \\\n    sprintf(error, \"0x%16llx: %s\", (long long)epos, __buf); \\\n}\n\n// 保存错误信息\nstatic char error[1024];\n\n// 文件读取的当前偏移量\nstatic off_t epos;\n\n/*\n * 确认 buf 是以 \\r\\n 结尾的新行。\n *\n * 确认成功返回 1 ，读入失败返回 0 ，并打印错误信息。\n */\nint consumeNewline(char *buf) {\n    if (strncmp(buf,\"\\r\\n\",2) != 0) {\n        ERROR(\"Expected \\\\r\\\\n, got: %02x%02x\",buf[0],buf[1]);\n        return 0;\n    }\n    return 1;\n}\n\n/*\n * 从 fp 中读入一个以 prefix 为前缀的 long 值，并将它保存到 *target 中。\n *\n * 读入成功返回 1 ，读入出错返回 0 ，并打印错误信息。\n */\nint readLong(FILE *fp, char prefix, long *target) {\n    char buf[128], *eptr;\n\n    epos = ftello(fp);\n\n    // 读入行\n    if (fgets(buf,sizeof(buf),fp) == NULL) {\n        return 0;\n    }\n\n    // 确保前缀相同\n    if (buf[0] != prefix) {\n        ERROR(\"Expected prefix '%c', got: '%c'\",buf[0],prefix);\n        return 0;\n    }\n\n    // 将字符串转换成 long 值\n    *target = strtol(buf+1,&eptr,10);\n\n    return consumeNewline(eptr);\n}\n\n/*\n * 从 fp 中读取指定的字节，并将值保存到 *target 中。\n *\n * 如果读取的量和 length 参数不相同，那么返回 0 ，并打印错误信息。\n * 读取成功则返回 1 。\n */\nint readBytes(FILE *fp, char *target, long length) {\n    long real;\n\n    epos = ftello(fp);\n\n    real = fread(target,1,length,fp);\n    if (real != length) {\n        ERROR(\"Expected to read %ld bytes, got %ld bytes\",length,real);\n        return 0;\n    }\n\n    return 1;\n}\n\n/*\n * 读取字符串\n *\n * 读取成功函数返回 1 ，并将值保存在 target 指针中。\n * 失败返回 0 。\n */\nint readString(FILE *fp, char** target) {\n\n    // 读取字符串的长度\n    long len;\n    *target = NULL;\n    if (!readLong(fp,'$',&len)) {\n        return 0;\n    }\n\n    /* Increase length to also consume \\r\\n */\n    len += 2;\n\n    // 为字符串分配空间\n    *target = (char*)malloc(len);\n\n    // 读取内容\n    if (!readBytes(fp,*target,len)) {\n        return 0;\n    }\n\n    // 确认 \\r\\n\n    if (!consumeNewline(*target+len-2)) {\n        return 0;\n    }\n\n    (*target)[len-2] = '\\0';\n\n    return 1;\n}\n\n/*\n * 读取参数数量\n *\n * 读取成功函数返回 1 ，并将参数数量保存到 target 中。\n * 读取失败返回 0 。\n */\nint readArgc(FILE *fp, long *target) {\n    return readLong(fp,'*',target);\n}\n\n/*\n * 返回一个偏移量，这个偏移量可能是：\n *\n * 1）文件的末尾\n * 2）文件首次出现读入错误的地方\n * 3）文件第一个没有 EXEC 匹配的 MULTI 的位置\n */\noff_t process(FILE *fp) {\n    long argc;\n    off_t pos = 0;\n    int i, multi = 0;\n    char *str;\n\n    while(1) {\n\n        // 定位到最后一个 MULTI 出现的偏移量\n        if (!multi) pos = ftello(fp);\n\n        // 读取参数的个数\n        if (!readArgc(fp, &argc)) break;\n\n        // 遍历各个参数\n        // 参数包括命令以及命令参数\n        // 比如 SET key value \n        // SET 就是第一个参数，而 key 和 value 就是第二和第三个参数\n        for (i = 0; i < argc; i++) {\n\n            // 读取参数\n            if (!readString(fp,&str)) break;\n\n            // 检查命令是否 MULTI 或者 EXEC\n            if (i == 0) {\n                if (strcasecmp(str, \"multi\") == 0) {\n                    // 记录一个 MULTI \n                    // 如果前面已经有一个 MULTI ，那么报错（MULTI 不应该嵌套）\n                    if (multi++) {\n                        ERROR(\"Unexpected MULTI\");\n                        break;\n                    }\n                } else if (strcasecmp(str, \"exec\") == 0) {\n                    // 清除一个 MULTI 记录\n                    // 如果前面没有 MULTI ，那么报错（MULTI 和 EXEC 应该一对对出现）\n                    if (--multi) {\n                        ERROR(\"Unexpected EXEC\");\n                        break;\n                    }\n                }\n            }\n\n            // 释放\n            free(str);\n        }\n\n        /* Stop if the loop did not finish \n         *\n         * 如果 for 循环没有正常结束，那么跳出 while\n         */\n        if (i < argc) {\n            if (str) free(str);\n            break;\n        }\n    }\n\n    // 文件读取完了，但是没有找到和 MULTI 对应的 EXEC\n    if (feof(fp) && multi && strlen(error) == 0) {\n        ERROR(\"Reached EOF before reading EXEC for MULTI\");\n    }\n\n    // 如果有错误出现，那么打印错误\n    if (strlen(error) > 0) {\n        printf(\"%s\\n\", error);\n    }\n\n    // 返回偏移量\n    return pos;\n}\n\nint main(int argc, char **argv) {\n    char *filename;\n    int fix = 0;\n\n    // 选项，如果不带 --fix 就只检查，不进行修复\n    if (argc < 2) {\n        printf(\"Usage: %s [--fix] <file.aof>\\n\", argv[0]);\n        exit(1);\n    } else if (argc == 2) {\n        filename = argv[1];\n    } else if (argc == 3) {\n        if (strcmp(argv[1],\"--fix\") != 0) {\n            printf(\"Invalid argument: %s\\n\", argv[1]);\n            exit(1);\n        }\n        filename = argv[2];\n        fix = 1;\n    } else {\n        printf(\"Invalid arguments\\n\");\n        exit(1);\n    }\n\n    // 打开指定文件\n    FILE *fp = fopen(filename,\"r+\");\n    if (fp == NULL) {\n        printf(\"Cannot open file: %s\\n\", filename);\n        exit(1);\n    }\n\n    // 读取文件信息\n    struct redis_stat sb;\n    if (redis_fstat(fileno(fp),&sb) == -1) {\n        printf(\"Cannot stat file: %s\\n\", filename);\n        exit(1);\n    }\n\n    // 取出文件的大小\n    off_t size = sb.st_size;\n    if (size == 0) {\n        printf(\"Empty file: %s\\n\", filename);\n        exit(1);\n    }\n\n    // 如果文件出错，那么这个偏移量指向：\n    // 1） 第一个不符合格式的位置\n    // 2） 第一个没有 EXEC 对应的 MULTI 的位置\n    // 如果文件没有出错，那么这个偏移量指向：\n    // 3） 文件末尾\n    off_t pos = process(fp);\n    // 计算偏移量距离文件末尾有多远\n    off_t diff = size-pos;\n    printf(\"AOF analyzed: size=%lld, ok_up_to=%lld, diff=%lld\\n\",\n        (long long) size, (long long) pos, (long long) diff);\n\n    // 大于 0 表示未到达文件末尾，出错\n    if (diff > 0) {\n\n        // fix 模式：尝试修复文件\n        if (fix) {\n\n            // 尝试从出错的位置开始，一直删除到文件的末尾\n            char buf[2];\n            printf(\"This will shrink the AOF from %lld bytes, with %lld bytes, to %lld bytes\\n\",(long long)size,(long long)diff,(long long)pos);\n            printf(\"Continue? [y/N]: \");\n            if (fgets(buf,sizeof(buf),stdin) == NULL ||\n                strncasecmp(buf,\"y\",1) != 0) {\n                    printf(\"Aborting...\\n\");\n                    exit(1);\n            }\n\n            // 删除不正确的内容\n            if (ftruncate(fileno(fp), pos) == -1) {\n                printf(\"Failed to truncate AOF\\n\");\n                exit(1);\n            } else {\n                printf(\"Successfully truncated AOF\\n\");\n            }\n\n        // 非 fix 模式：只报告文件不合法\n        } else {\n            printf(\"AOF is not valid\\n\");\n            exit(1);\n        }\n\n    // 等于 0 表示文件已经顺利读完，无错\n    } else {\n        printf(\"AOF is valid\\n\");\n    }\n\n    // 关闭文件\n    fclose(fp);\n\n    return 0;\n}\n"
  },
  {
    "path": "src/redis-check-dump.c",
    "content": "/*\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\n#include <stdlib.h>\n#include <stdio.h>\n#include <unistd.h>\n#include <fcntl.h>\n#include <sys/stat.h>\n#include <sys/mman.h>\n#include <string.h>\n#include <arpa/inet.h>\n#include <stdint.h>\n#include <limits.h>\n#include \"lzf.h\"\n#include \"crc64.h\"\n\n/* Object 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#define REDIS_HASH_ZIPMAP 9\n#define REDIS_LIST_ZIPLIST 10\n#define REDIS_SET_INTSET 11\n#define REDIS_ZSET_ZIPLIST 12\n#define REDIS_HASH_ZIPLIST 13\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 REDIS_ENCODING_RAW 0    /* Raw representation */\n#define REDIS_ENCODING_INT 1    /* Encoded as integer */\n#define REDIS_ENCODING_ZIPMAP 2 /* Encoded as zipmap */\n#define REDIS_ENCODING_HT 3     /* Encoded as a hash table */\n\n/* Object types only used for dumping to disk */\n#define REDIS_EXPIRETIME_MS 252\n#define REDIS_EXPIRETIME 253\n#define REDIS_SELECTDB 254\n#define REDIS_EOF 255\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 01, 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 REDIS_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 REDIS_RDB_6BITLEN 0\n#define REDIS_RDB_14BITLEN 1\n#define REDIS_RDB_32BITLEN 2\n#define REDIS_RDB_ENCVAL 3\n#define REDIS_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 REDIS_RDB_ENC_INT8 0        /* 8 bit signed integer */\n#define REDIS_RDB_ENC_INT16 1       /* 16 bit signed integer */\n#define REDIS_RDB_ENC_INT32 2       /* 32 bit signed integer */\n#define REDIS_RDB_ENC_LZF 3         /* string compressed with FASTLZ */\n\n#define ERROR(...) { \\\n    printf(__VA_ARGS__); \\\n    exit(1); \\\n}\n\n/* data type to hold offset in file and size */\ntypedef struct {\n    void *data;\n    size_t size;\n    size_t offset;\n} pos;\n\nstatic unsigned char level = 0;\nstatic pos positions[16];\n\n#define CURR_OFFSET (positions[level].offset)\n\n/* Hold a stack of errors */\ntypedef struct {\n    char error[16][1024];\n    size_t offset[16];\n    size_t level;\n} errors_t;\nstatic errors_t errors;\n\n#define SHIFT_ERROR(provided_offset, ...) { \\\n    sprintf(errors.error[errors.level], __VA_ARGS__); \\\n    errors.offset[errors.level] = provided_offset; \\\n    errors.level++; \\\n}\n\n/* Data type to hold opcode with optional key name an success status */\ntypedef struct {\n    char* key;\n    int type;\n    char success;\n} entry;\n\n/* Global vars that are actually used as constants. The following double\n * values are used for double on-disk serialization, and are initialized\n * at runtime to avoid strange compiler optimizations. */\nstatic double R_Zero, R_PosInf, R_NegInf, R_Nan;\n\n/* store string types for output */\nstatic char types[256][16];\n\n/* Return true if 't' is a valid object type. */\nint checkType(unsigned char t) {\n    /* In case a new object type is added, update the following \n     * condition as necessary. */\n    return\n        (t >= REDIS_HASH_ZIPMAP && t <= REDIS_HASH_ZIPLIST) ||\n        t <= REDIS_HASH ||\n        t >= REDIS_EXPIRETIME_MS;\n}\n\n/* when number of bytes to read is negative, do a peek */\nint readBytes(void *target, long num) {\n    char peek = (num < 0) ? 1 : 0;\n    num = (num < 0) ? -num : num;\n\n    pos p = positions[level];\n    if (p.offset + num > p.size) {\n        return 0;\n    } else {\n        memcpy(target, (void*)((size_t)p.data + p.offset), num);\n        if (!peek) positions[level].offset += num;\n    }\n    return 1;\n}\n\nint processHeader() {\n    char buf[10] = \"_________\";\n    int dump_version;\n\n    if (!readBytes(buf, 9)) {\n        ERROR(\"Cannot read header\\n\");\n    }\n\n    /* expect the first 5 bytes to equal REDIS */\n    if (memcmp(buf,\"REDIS\",5) != 0) {\n        ERROR(\"Wrong signature in header\\n\");\n    }\n\n    dump_version = (int)strtol(buf + 5, NULL, 10);\n    if (dump_version < 1 || dump_version > 6) {\n        ERROR(\"Unknown RDB format version: %d\\n\", dump_version);\n    }\n    return dump_version;\n}\n\nint loadType(entry *e) {\n    uint32_t offset = CURR_OFFSET;\n\n    /* this byte needs to qualify as type */\n    unsigned char t;\n    if (readBytes(&t, 1)) {\n        if (checkType(t)) {\n            e->type = t;\n            return 1;\n        } else {\n            SHIFT_ERROR(offset, \"Unknown type (0x%02x)\", t);\n        }\n    } else {\n        SHIFT_ERROR(offset, \"Could not read type\");\n    }\n\n    /* failure */\n    return 0;\n}\n\nint peekType() {\n    unsigned char t;\n    if (readBytes(&t, -1) && (checkType(t)))\n        return t;\n    return -1;\n}\n\n/* discard time, just consume the bytes */\nint processTime(int type) {\n    uint32_t offset = CURR_OFFSET;\n    unsigned char t[8];\n    int timelen = (type == REDIS_EXPIRETIME_MS) ? 8 : 4;\n\n    if (readBytes(t,timelen)) {\n        return 1;\n    } else {\n        SHIFT_ERROR(offset, \"Could not read time\");\n    }\n\n    /* failure */\n    return 0;\n}\n\nuint32_t loadLength(int *isencoded) {\n    unsigned char buf[2];\n    uint32_t len;\n    int type;\n\n    if (isencoded) *isencoded = 0;\n    if (!readBytes(buf, 1)) return REDIS_RDB_LENERR;\n    type = (buf[0] & 0xC0) >> 6;\n    if (type == REDIS_RDB_6BITLEN) {\n        /* Read a 6 bit len */\n        return buf[0] & 0x3F;\n    } else if (type == REDIS_RDB_ENCVAL) {\n        /* Read a 6 bit len encoding type */\n        if (isencoded) *isencoded = 1;\n        return buf[0] & 0x3F;\n    } else if (type == REDIS_RDB_14BITLEN) {\n        /* Read a 14 bit len */\n        if (!readBytes(buf+1,1)) return REDIS_RDB_LENERR;\n        return ((buf[0] & 0x3F) << 8) | buf[1];\n    } else {\n        /* Read a 32 bit len */\n        if (!readBytes(&len, 4)) return REDIS_RDB_LENERR;\n        return (unsigned int)ntohl(len);\n    }\n}\n\nchar *loadIntegerObject(int enctype) {\n    uint32_t offset = CURR_OFFSET;\n    unsigned char enc[4];\n    long long val;\n\n    if (enctype == REDIS_RDB_ENC_INT8) {\n        uint8_t v;\n        if (!readBytes(enc, 1)) return NULL;\n        v = enc[0];\n        val = (int8_t)v;\n    } else if (enctype == REDIS_RDB_ENC_INT16) {\n        uint16_t v;\n        if (!readBytes(enc, 2)) return NULL;\n        v = enc[0]|(enc[1]<<8);\n        val = (int16_t)v;\n    } else if (enctype == REDIS_RDB_ENC_INT32) {\n        uint32_t v;\n        if (!readBytes(enc, 4)) return NULL;\n        v = enc[0]|(enc[1]<<8)|(enc[2]<<16)|(enc[3]<<24);\n        val = (int32_t)v;\n    } else {\n        SHIFT_ERROR(offset, \"Unknown integer encoding (0x%02x)\", enctype);\n        return NULL;\n    }\n\n    /* convert val into string */\n    char *buf;\n    buf = malloc(sizeof(char) * 128);\n    sprintf(buf, \"%lld\", val);\n    return buf;\n}\n\nchar* loadLzfStringObject() {\n    unsigned int slen, clen;\n    char *c, *s;\n\n    if ((clen = loadLength(NULL)) == REDIS_RDB_LENERR) return NULL;\n    if ((slen = loadLength(NULL)) == REDIS_RDB_LENERR) return NULL;\n\n    c = malloc(clen);\n    if (!readBytes(c, clen)) {\n        free(c);\n        return NULL;\n    }\n\n    s = malloc(slen+1);\n    if (lzf_decompress(c,clen,s,slen) == 0) {\n        free(c); free(s);\n        return NULL;\n    }\n\n    free(c);\n    return s;\n}\n\n/* returns NULL when not processable, char* when valid */\nchar* loadStringObject() {\n    uint32_t offset = CURR_OFFSET;\n    int isencoded;\n    uint32_t len;\n\n    len = loadLength(&isencoded);\n    if (isencoded) {\n        switch(len) {\n        case REDIS_RDB_ENC_INT8:\n        case REDIS_RDB_ENC_INT16:\n        case REDIS_RDB_ENC_INT32:\n            return loadIntegerObject(len);\n        case REDIS_RDB_ENC_LZF:\n            return loadLzfStringObject();\n        default:\n            /* unknown encoding */\n            SHIFT_ERROR(offset, \"Unknown string encoding (0x%02x)\", len);\n            return NULL;\n        }\n    }\n\n    if (len == REDIS_RDB_LENERR) return NULL;\n\n    char *buf = malloc(sizeof(char) * (len+1));\n    buf[len] = '\\0';\n    if (!readBytes(buf, len)) {\n        free(buf);\n        return NULL;\n    }\n    return buf;\n}\n\nint processStringObject(char** store) {\n    unsigned long offset = CURR_OFFSET;\n    char *key = loadStringObject();\n    if (key == NULL) {\n        SHIFT_ERROR(offset, \"Error reading string object\");\n        free(key);\n        return 0;\n    }\n\n    if (store != NULL) {\n        *store = key;\n    } else {\n        free(key);\n    }\n    return 1;\n}\n\ndouble* loadDoubleValue() {\n    char buf[256];\n    unsigned char len;\n    double* val;\n\n    if (!readBytes(&len,1)) return NULL;\n\n    val = malloc(sizeof(double));\n    switch(len) {\n    case 255: *val = R_NegInf;  return val;\n    case 254: *val = R_PosInf;  return val;\n    case 253: *val = R_Nan;     return val;\n    default:\n        if (!readBytes(buf, len)) {\n            free(val);\n            return NULL;\n        }\n        buf[len] = '\\0';\n        sscanf(buf, \"%lg\", val);\n        return val;\n    }\n}\n\nint processDoubleValue(double** store) {\n    unsigned long offset = CURR_OFFSET;\n    double *val = loadDoubleValue();\n    if (val == NULL) {\n        SHIFT_ERROR(offset, \"Error reading double value\");\n        free(val);\n        return 0;\n    }\n\n    if (store != NULL) {\n        *store = val;\n    } else {\n        free(val);\n    }\n    return 1;\n}\n\nint loadPair(entry *e) {\n    uint32_t offset = CURR_OFFSET;\n    uint32_t i;\n\n    /* read key first */\n    char *key;\n    if (processStringObject(&key)) {\n        e->key = key;\n    } else {\n        SHIFT_ERROR(offset, \"Error reading entry key\");\n        return 0;\n    }\n\n    uint32_t length = 0;\n    if (e->type == REDIS_LIST ||\n        e->type == REDIS_SET  ||\n        e->type == REDIS_ZSET ||\n        e->type == REDIS_HASH) {\n        if ((length = loadLength(NULL)) == REDIS_RDB_LENERR) {\n            SHIFT_ERROR(offset, \"Error reading %s length\", types[e->type]);\n            return 0;\n        }\n    }\n\n    switch(e->type) {\n    case REDIS_STRING:\n    case REDIS_HASH_ZIPMAP:\n    case REDIS_LIST_ZIPLIST:\n    case REDIS_SET_INTSET:\n    case REDIS_ZSET_ZIPLIST:\n    case REDIS_HASH_ZIPLIST:\n        if (!processStringObject(NULL)) {\n            SHIFT_ERROR(offset, \"Error reading entry value\");\n            return 0;\n        }\n    break;\n    case REDIS_LIST:\n    case REDIS_SET:\n        for (i = 0; i < length; i++) {\n            offset = CURR_OFFSET;\n            if (!processStringObject(NULL)) {\n                SHIFT_ERROR(offset, \"Error reading element at index %d (length: %d)\", i, length);\n                return 0;\n            }\n        }\n    break;\n    case REDIS_ZSET:\n        for (i = 0; i < length; i++) {\n            offset = CURR_OFFSET;\n            if (!processStringObject(NULL)) {\n                SHIFT_ERROR(offset, \"Error reading element key at index %d (length: %d)\", i, length);\n                return 0;\n            }\n            offset = CURR_OFFSET;\n            if (!processDoubleValue(NULL)) {\n                SHIFT_ERROR(offset, \"Error reading element value at index %d (length: %d)\", i, length);\n                return 0;\n            }\n        }\n    break;\n    case REDIS_HASH:\n        for (i = 0; i < length; i++) {\n            offset = CURR_OFFSET;\n            if (!processStringObject(NULL)) {\n                SHIFT_ERROR(offset, \"Error reading element key at index %d (length: %d)\", i, length);\n                return 0;\n            }\n            offset = CURR_OFFSET;\n            if (!processStringObject(NULL)) {\n                SHIFT_ERROR(offset, \"Error reading element value at index %d (length: %d)\", i, length);\n                return 0;\n            }\n        }\n    break;\n    default:\n        SHIFT_ERROR(offset, \"Type not implemented\");\n        return 0;\n    }\n    /* because we're done, we assume success */\n    e->success = 1;\n    return 1;\n}\n\nentry loadEntry() {\n    entry e = { NULL, -1, 0 };\n    uint32_t length, offset[4];\n\n    /* reset error container */\n    errors.level = 0;\n\n    offset[0] = CURR_OFFSET;\n    if (!loadType(&e)) {\n        return e;\n    }\n\n    offset[1] = CURR_OFFSET;\n    if (e.type == REDIS_SELECTDB) {\n        if ((length = loadLength(NULL)) == REDIS_RDB_LENERR) {\n            SHIFT_ERROR(offset[1], \"Error reading database number\");\n            return e;\n        }\n        if (length > 63) {\n            SHIFT_ERROR(offset[1], \"Database number out of range (%d)\", length);\n            return e;\n        }\n    } else if (e.type == REDIS_EOF) {\n        if (positions[level].offset < positions[level].size) {\n            SHIFT_ERROR(offset[0], \"Unexpected EOF\");\n        } else {\n            e.success = 1;\n        }\n        return e;\n    } else {\n        /* optionally consume expire */\n        if (e.type == REDIS_EXPIRETIME || \n            e.type == REDIS_EXPIRETIME_MS) {\n            if (!processTime(e.type)) return e;\n            if (!loadType(&e)) return e;\n        }\n\n        offset[1] = CURR_OFFSET;\n        if (!loadPair(&e)) {\n            SHIFT_ERROR(offset[1], \"Error for type %s\", types[e.type]);\n            return e;\n        }\n    }\n\n    /* all entries are followed by a valid type:\n     * e.g. a new entry, SELECTDB, EXPIRE, EOF */\n    offset[2] = CURR_OFFSET;\n    if (peekType() == -1) {\n        SHIFT_ERROR(offset[2], \"Followed by invalid type\");\n        SHIFT_ERROR(offset[0], \"Error for type %s\", types[e.type]);\n        e.success = 0;\n    } else {\n        e.success = 1;\n    }\n\n    return e;\n}\n\nvoid printCentered(int indent, int width, char* body) {\n    char head[256], tail[256];\n    memset(head, '\\0', 256);\n    memset(tail, '\\0', 256);\n\n    memset(head, '=', indent);\n    memset(tail, '=', width - 2 - indent - strlen(body));\n    printf(\"%s %s %s\\n\", head, body, tail);\n}\n\nvoid printValid(uint64_t ops, uint64_t bytes) {\n    char body[80];\n    sprintf(body, \"Processed %llu valid opcodes (in %llu bytes)\",\n        (unsigned long long) ops, (unsigned long long) bytes);\n    printCentered(4, 80, body);\n}\n\nvoid printSkipped(uint64_t bytes, uint64_t offset) {\n    char body[80];\n    sprintf(body, \"Skipped %llu bytes (resuming at 0x%08llx)\",\n        (unsigned long long) bytes, (unsigned long long) offset);\n    printCentered(4, 80, body);\n}\n\nvoid printErrorStack(entry *e) {\n    unsigned int i;\n    char body[64];\n\n    if (e->type == -1) {\n        sprintf(body, \"Error trace\");\n    } else if (e->type >= 253) {\n        sprintf(body, \"Error trace (%s)\", types[e->type]);\n    } else if (!e->key) {\n        sprintf(body, \"Error trace (%s: (unknown))\", types[e->type]);\n    } else {\n        char tmp[41];\n        strncpy(tmp, e->key, 40);\n\n        /* display truncation at the last 3 chars */\n        if (strlen(e->key) > 40) {\n            memset(&tmp[37], '.', 3);\n        }\n\n        /* display unprintable characters as ? */\n        for (i = 0; i < strlen(tmp); i++) {\n            if (tmp[i] <= 32) tmp[i] = '?';\n        }\n        sprintf(body, \"Error trace (%s: %s)\", types[e->type], tmp);\n    }\n\n    printCentered(4, 80, body);\n\n    /* display error stack */\n    for (i = 0; i < errors.level; i++) {\n        printf(\"0x%08lx - %s\\n\",\n            (unsigned long) errors.offset[i], errors.error[i]);\n    }\n}\n\nvoid process() {\n    uint64_t num_errors = 0, num_valid_ops = 0, num_valid_bytes = 0;\n    entry entry;\n    int dump_version = processHeader();\n\n    /* Exclude the final checksum for RDB >= 5. Will be checked at the end. */\n    if (dump_version >= 5) {\n        if (positions[0].size < 8) {\n            printf(\"RDB version >= 5 but no room for checksum.\\n\");\n            exit(1);\n        }\n        positions[0].size -= 8;;\n    }\n\n    level = 1;\n    while(positions[0].offset < positions[0].size) {\n        positions[1] = positions[0];\n\n        entry = loadEntry();\n        if (!entry.success) {\n            printValid(num_valid_ops, num_valid_bytes);\n            printErrorStack(&entry);\n            num_errors++;\n            num_valid_ops = 0;\n            num_valid_bytes = 0;\n\n            /* search for next valid entry */\n            uint64_t offset = positions[0].offset + 1;\n            int i = 0;\n\n            while (!entry.success && offset < positions[0].size) {\n                positions[1].offset = offset;\n\n                /* find 3 consecutive valid entries */\n                for (i = 0; i < 3; i++) {\n                    entry = loadEntry();\n                    if (!entry.success) break;\n                }\n                /* check if we found 3 consecutive valid entries */\n                if (i < 3) {\n                    offset++;\n                }\n            }\n\n            /* print how many bytes we have skipped to find a new valid opcode */\n            if (offset < positions[0].size) {\n                printSkipped(offset - positions[0].offset, offset);\n            }\n\n            positions[0].offset = offset;\n        } else {\n            num_valid_ops++;\n            num_valid_bytes += positions[1].offset - positions[0].offset;\n\n            /* advance position */\n            positions[0] = positions[1];\n        }\n        free(entry.key);\n    }\n\n    /* because there is another potential error,\n     * print how many valid ops we have processed */\n    printValid(num_valid_ops, num_valid_bytes);\n\n    /* expect an eof */\n    if (entry.type != REDIS_EOF) {\n        /* last byte should be EOF, add error */\n        errors.level = 0;\n        SHIFT_ERROR(positions[0].offset, \"Expected EOF, got %s\", types[entry.type]);\n\n        /* this is an EOF error so reset type */\n        entry.type = -1;\n        printErrorStack(&entry);\n\n        num_errors++;\n    }\n\n    /* Verify checksum */\n    if (dump_version >= 5) {\n        uint64_t crc = crc64(0,positions[0].data,positions[0].size);\n        uint64_t crc2;\n        unsigned char *p = (unsigned char*)positions[0].data+positions[0].size;\n        crc2 = ((uint64_t)p[0] << 0) |\n               ((uint64_t)p[1] << 8) |\n               ((uint64_t)p[2] << 16) |\n               ((uint64_t)p[3] << 24) |\n               ((uint64_t)p[4] << 32) |\n               ((uint64_t)p[5] << 40) |\n               ((uint64_t)p[6] << 48) |\n               ((uint64_t)p[7] << 56);\n        if (crc != crc2) {\n            SHIFT_ERROR(positions[0].offset, \"RDB CRC64 does not match.\");\n        } else {\n            printf(\"CRC64 checksum is OK\\n\");\n        }\n    }\n\n    /* print summary on errors */\n    if (num_errors) {\n        printf(\"\\n\");\n        printf(\"Total unprocessable opcodes: %llu\\n\",\n            (unsigned long long) num_errors);\n    }\n}\n\nint main(int argc, char **argv) {\n    /* expect the first argument to be the dump file */\n    if (argc <= 1) {\n        printf(\"Usage: %s <dump.rdb>\\n\", argv[0]);\n        exit(0);\n    }\n\n    int fd;\n    off_t size;\n    struct stat stat;\n    void *data;\n\n    fd = open(argv[1], O_RDONLY);\n    if (fd < 1) {\n        ERROR(\"Cannot open file: %s\\n\", argv[1]);\n    }\n    if (fstat(fd, &stat) == -1) {\n        ERROR(\"Cannot stat: %s\\n\", argv[1]);\n    } else {\n        size = stat.st_size;\n    }\n\n    if (sizeof(size_t) == sizeof(int32_t) && size >= INT_MAX) {\n        ERROR(\"Cannot check dump files >2GB on a 32-bit platform\\n\");\n    }\n\n    data = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);\n    if (data == MAP_FAILED) {\n        ERROR(\"Cannot mmap: %s\\n\", argv[1]);\n    }\n\n    /* Initialize static vars */\n    positions[0].data = data;\n    positions[0].size = size;\n    positions[0].offset = 0;\n    errors.level = 0;\n\n    /* Object types */\n    sprintf(types[REDIS_STRING], \"STRING\");\n    sprintf(types[REDIS_LIST], \"LIST\");\n    sprintf(types[REDIS_SET], \"SET\");\n    sprintf(types[REDIS_ZSET], \"ZSET\");\n    sprintf(types[REDIS_HASH], \"HASH\");\n\n    /* Object types only used for dumping to disk */\n    sprintf(types[REDIS_EXPIRETIME], \"EXPIRETIME\");\n    sprintf(types[REDIS_SELECTDB], \"SELECTDB\");\n    sprintf(types[REDIS_EOF], \"EOF\");\n\n    /* Double constants initialization */\n    R_Zero = 0.0;\n    R_PosInf = 1.0/R_Zero;\n    R_NegInf = -1.0/R_Zero;\n    R_Nan = R_Zero/R_Zero;\n\n    process();\n\n    munmap(data, size);\n    close(fd);\n    return 0;\n}\n"
  },
  {
    "path": "src/redis-cli.c",
    "content": "/* Redis CLI (command line interface)\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#include \"fmacros.h\"\n#include \"version.h\"\n\n#include <stdio.h>\n#include <string.h>\n#include <stdlib.h>\n#include <unistd.h>\n#include <time.h>\n#include <ctype.h>\n#include <errno.h>\n#include <sys/stat.h>\n#include <sys/time.h>\n#include <assert.h>\n#include <fcntl.h>\n#include <limits.h>\n\n#include \"hiredis.h\"\n#include \"sds.h\"\n#include \"zmalloc.h\"\n#include \"linenoise.h\"\n#include \"help.h\"\n#include \"anet.h\"\n#include \"ae.h\"\n\n#define REDIS_NOTUSED(V) ((void) V)\n\n#define OUTPUT_STANDARD 0\n#define OUTPUT_RAW 1\n#define OUTPUT_CSV 2\n#define REDIS_CLI_KEEPALIVE_INTERVAL 15 /* seconds */\n#define REDIS_CLI_DEFAULT_PIPE_TIMEOUT 30 /* seconds */\n\nstatic redisContext *context;\nstatic struct config {\n    char *hostip;\n    int hostport;\n    char *hostsocket;\n    long repeat;\n    long interval;\n    int dbnum;\n    int interactive;\n    int shutdown;\n    int monitor_mode;\n    int pubsub_mode;\n    int latency_mode;\n    int latency_history;\n    int cluster_mode;\n    int cluster_reissue_command;\n    int slave_mode;\n    int pipe_mode;\n    int pipe_timeout;\n    int getrdb_mode;\n    int stat_mode;\n    int scan_mode;\n    int intrinsic_latency_mode;\n    int intrinsic_latency_duration;\n    char *pattern;\n    char *rdb_filename;\n    int bigkeys;\n    int stdinarg; /* get last arg from stdin. (-x option) */\n    char *auth;\n    int output; /* output mode, see OUTPUT_* defines */\n    sds mb_delim;\n    char prompt[128];\n    char *eval;\n} config;\n\nstatic void usage();\nstatic void slaveMode(void);\nchar *redisGitSHA1(void);\nchar *redisGitDirty(void);\n\n/*------------------------------------------------------------------------------\n * Utility functions\n *--------------------------------------------------------------------------- */\n\nstatic long long ustime(void) {\n    struct timeval tv;\n    long long ust;\n\n    gettimeofday(&tv, NULL);\n    ust = ((long long)tv.tv_sec)*1000000;\n    ust += tv.tv_usec;\n    return ust;\n}\n\nstatic long long mstime(void) {\n    return ustime()/1000;\n}\n\nstatic void cliRefreshPrompt(void) {\n    int len;\n\n    if (config.hostsocket != NULL)\n        len = snprintf(config.prompt,sizeof(config.prompt),\"redis %s\",\n                       config.hostsocket);\n    else\n        len = snprintf(config.prompt,sizeof(config.prompt),\n                       strchr(config.hostip,':') ? \"[%s]:%d\" : \"%s:%d\",\n                       config.hostip, config.hostport);\n    /* Add [dbnum] if needed */\n    if (config.dbnum != 0)\n        len += snprintf(config.prompt+len,sizeof(config.prompt)-len,\"[%d]\",\n            config.dbnum);\n    snprintf(config.prompt+len,sizeof(config.prompt)-len,\"> \");\n}\n\n/*------------------------------------------------------------------------------\n * Help functions\n *--------------------------------------------------------------------------- */\n\n#define CLI_HELP_COMMAND 1\n#define CLI_HELP_GROUP 2\n\ntypedef struct {\n    int type;\n    int argc;\n    sds *argv;\n    sds full;\n\n    /* Only used for help on commands */\n    struct commandHelp *org;\n} helpEntry;\n\nstatic helpEntry *helpEntries;\nstatic int helpEntriesLen;\n\nstatic sds cliVersion() {\n    sds version;\n    version = sdscatprintf(sdsempty(), \"%s\", REDIS_VERSION);\n\n    /* Add git commit and working tree status when available */\n    if (strtoll(redisGitSHA1(),NULL,16)) {\n        version = sdscatprintf(version, \" (git:%s\", redisGitSHA1());\n        if (strtoll(redisGitDirty(),NULL,10))\n            version = sdscatprintf(version, \"-dirty\");\n        version = sdscat(version, \")\");\n    }\n    return version;\n}\n\nstatic void cliInitHelp() {\n    int commandslen = sizeof(commandHelp)/sizeof(struct commandHelp);\n    int groupslen = sizeof(commandGroups)/sizeof(char*);\n    int i, len, pos = 0;\n    helpEntry tmp;\n\n    helpEntriesLen = len = commandslen+groupslen;\n    helpEntries = malloc(sizeof(helpEntry)*len);\n\n    for (i = 0; i < groupslen; i++) {\n        tmp.argc = 1;\n        tmp.argv = malloc(sizeof(sds));\n        tmp.argv[0] = sdscatprintf(sdsempty(),\"@%s\",commandGroups[i]);\n        tmp.full = tmp.argv[0];\n        tmp.type = CLI_HELP_GROUP;\n        tmp.org = NULL;\n        helpEntries[pos++] = tmp;\n    }\n\n    for (i = 0; i < commandslen; i++) {\n        tmp.argv = sdssplitargs(commandHelp[i].name,&tmp.argc);\n        tmp.full = sdsnew(commandHelp[i].name);\n        tmp.type = CLI_HELP_COMMAND;\n        tmp.org = &commandHelp[i];\n        helpEntries[pos++] = tmp;\n    }\n}\n\n/* Output command help to stdout. */\nstatic void cliOutputCommandHelp(struct commandHelp *help, int group) {\n    printf(\"\\r\\n  \\x1b[1m%s\\x1b[0m \\x1b[90m%s\\x1b[0m\\r\\n\", help->name, help->params);\n    printf(\"  \\x1b[33msummary:\\x1b[0m %s\\r\\n\", help->summary);\n    printf(\"  \\x1b[33msince:\\x1b[0m %s\\r\\n\", help->since);\n    if (group) {\n        printf(\"  \\x1b[33mgroup:\\x1b[0m %s\\r\\n\", commandGroups[help->group]);\n    }\n}\n\n/* Print generic help. */\nstatic void cliOutputGenericHelp() {\n    sds version = cliVersion();\n    printf(\n        \"redis-cli %s\\r\\n\"\n        \"Type: \\\"help @<group>\\\" to get a list of commands in <group>\\r\\n\"\n        \"      \\\"help <command>\\\" for help on <command>\\r\\n\"\n        \"      \\\"help <tab>\\\" to get a list of possible help topics\\r\\n\"\n        \"      \\\"quit\\\" to exit\\r\\n\",\n        version\n    );\n    sdsfree(version);\n}\n\n/* Output all command help, filtering by group or command name. */\nstatic void cliOutputHelp(int argc, char **argv) {\n    int i, j, len;\n    int group = -1;\n    helpEntry *entry;\n    struct commandHelp *help;\n\n    if (argc == 0) {\n        cliOutputGenericHelp();\n        return;\n    } else if (argc > 0 && argv[0][0] == '@') {\n        len = sizeof(commandGroups)/sizeof(char*);\n        for (i = 0; i < len; i++) {\n            if (strcasecmp(argv[0]+1,commandGroups[i]) == 0) {\n                group = i;\n                break;\n            }\n        }\n    }\n\n    assert(argc > 0);\n    for (i = 0; i < helpEntriesLen; i++) {\n        entry = &helpEntries[i];\n        if (entry->type != CLI_HELP_COMMAND) continue;\n\n        help = entry->org;\n        if (group == -1) {\n            /* Compare all arguments */\n            if (argc == entry->argc) {\n                for (j = 0; j < argc; j++) {\n                    if (strcasecmp(argv[j],entry->argv[j]) != 0) break;\n                }\n                if (j == argc) {\n                    cliOutputCommandHelp(help,1);\n                }\n            }\n        } else {\n            if (group == help->group) {\n                cliOutputCommandHelp(help,0);\n            }\n        }\n    }\n    printf(\"\\r\\n\");\n}\n\nstatic void completionCallback(const char *buf, linenoiseCompletions *lc) {\n    size_t startpos = 0;\n    int mask;\n    int i;\n    size_t matchlen;\n    sds tmp;\n\n    if (strncasecmp(buf,\"help \",5) == 0) {\n        startpos = 5;\n        while (isspace(buf[startpos])) startpos++;\n        mask = CLI_HELP_COMMAND | CLI_HELP_GROUP;\n    } else {\n        mask = CLI_HELP_COMMAND;\n    }\n\n    for (i = 0; i < helpEntriesLen; i++) {\n        if (!(helpEntries[i].type & mask)) continue;\n\n        matchlen = strlen(buf+startpos);\n        if (strncasecmp(buf+startpos,helpEntries[i].full,matchlen) == 0) {\n            tmp = sdsnewlen(buf,startpos);\n            tmp = sdscat(tmp,helpEntries[i].full);\n            linenoiseAddCompletion(lc,tmp);\n            sdsfree(tmp);\n        }\n    }\n}\n\n/*------------------------------------------------------------------------------\n * Networking / parsing\n *--------------------------------------------------------------------------- */\n\n/* Send AUTH command to the server */\nstatic int cliAuth() {\n    redisReply *reply;\n    if (config.auth == NULL) return REDIS_OK;\n\n    reply = redisCommand(context,\"AUTH %s\",config.auth);\n    if (reply != NULL) {\n        freeReplyObject(reply);\n        return REDIS_OK;\n    }\n    return REDIS_ERR;\n}\n\n/* Send SELECT dbnum to the server */\nstatic int cliSelect() {\n    redisReply *reply;\n    if (config.dbnum == 0) return REDIS_OK;\n\n    reply = redisCommand(context,\"SELECT %d\",config.dbnum);\n    if (reply != NULL) {\n        freeReplyObject(reply);\n        return REDIS_OK;\n    }\n    return REDIS_ERR;\n}\n\n/* Connect to the server. If force is not zero the connection is performed\n * even if there is already a connected socket. */\nstatic int cliConnect(int force) {\n    if (context == NULL || force) {\n        if (context != NULL)\n            redisFree(context);\n\n        if (config.hostsocket == NULL) {\n            context = redisConnect(config.hostip,config.hostport);\n        } else {\n            context = redisConnectUnix(config.hostsocket);\n        }\n\n        if (context->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,context->errstr);\n            else\n                fprintf(stderr,\"%s: %s\\n\",config.hostsocket,context->errstr);\n            redisFree(context);\n            context = NULL;\n            return REDIS_ERR;\n        }\n\n        /* Set aggressive KEEP_ALIVE socket option in the Redis context socket\n         * in order to prevent timeouts caused by the execution of long\n         * commands. At the same time this improves the detection of real\n         * errors. */\n        anetKeepAlive(NULL, context->fd, REDIS_CLI_KEEPALIVE_INTERVAL);\n\n        /* Do AUTH and select the right DB. */\n        if (cliAuth() != REDIS_OK)\n            return REDIS_ERR;\n        if (cliSelect() != REDIS_OK)\n            return REDIS_ERR;\n    }\n    return REDIS_OK;\n}\n\nstatic void cliPrintContextError() {\n    if (context == NULL) return;\n    fprintf(stderr,\"Error: %s\\n\",context->errstr);\n}\n\nstatic sds cliFormatReplyTTY(redisReply *r, char *prefix) {\n    sds out = sdsempty();\n    switch (r->type) {\n    case REDIS_REPLY_ERROR:\n        out = sdscatprintf(out,\"(error) %s\\n\", r->str);\n    break;\n    case REDIS_REPLY_STATUS:\n        out = sdscat(out,r->str);\n        out = sdscat(out,\"\\n\");\n    break;\n    case REDIS_REPLY_INTEGER:\n        out = sdscatprintf(out,\"(integer) %lld\\n\",r->integer);\n    break;\n    case REDIS_REPLY_STRING:\n        /* If you are producing output for the standard output we want\n        * a more interesting output with quoted characters and so forth */\n        out = sdscatrepr(out,r->str,r->len);\n        out = sdscat(out,\"\\n\");\n    break;\n    case REDIS_REPLY_NIL:\n        out = sdscat(out,\"(nil)\\n\");\n    break;\n    case REDIS_REPLY_ARRAY:\n        if (r->elements == 0) {\n            out = sdscat(out,\"(empty list or set)\\n\");\n        } else {\n            unsigned int i, idxlen = 0;\n            char _prefixlen[16];\n            char _prefixfmt[16];\n            sds _prefix;\n            sds tmp;\n\n            /* Calculate chars needed to represent the largest index */\n            i = r->elements;\n            do {\n                idxlen++;\n                i /= 10;\n            } while(i);\n\n            /* Prefix for nested multi bulks should grow with idxlen+2 spaces */\n            memset(_prefixlen,' ',idxlen+2);\n            _prefixlen[idxlen+2] = '\\0';\n            _prefix = sdscat(sdsnew(prefix),_prefixlen);\n\n            /* Setup prefix format for every entry */\n            snprintf(_prefixfmt,sizeof(_prefixfmt),\"%%s%%%dd) \",idxlen);\n\n            for (i = 0; i < r->elements; i++) {\n                /* Don't use the prefix for the first element, as the parent\n                 * caller already prepended the index number. */\n                out = sdscatprintf(out,_prefixfmt,i == 0 ? \"\" : prefix,i+1);\n\n                /* Format the multi bulk entry */\n                tmp = cliFormatReplyTTY(r->element[i],_prefix);\n                out = sdscatlen(out,tmp,sdslen(tmp));\n                sdsfree(tmp);\n            }\n            sdsfree(_prefix);\n        }\n    break;\n    default:\n        fprintf(stderr,\"Unknown reply type: %d\\n\", r->type);\n        exit(1);\n    }\n    return out;\n}\n\nstatic sds cliFormatReplyRaw(redisReply *r) {\n    sds out = sdsempty(), tmp;\n    size_t i;\n\n    switch (r->type) {\n    case REDIS_REPLY_NIL:\n        /* Nothing... */\n        break;\n    case REDIS_REPLY_ERROR:\n        out = sdscatlen(out,r->str,r->len);\n        out = sdscatlen(out,\"\\n\",1);\n        break;\n    case REDIS_REPLY_STATUS:\n    case REDIS_REPLY_STRING:\n        out = sdscatlen(out,r->str,r->len);\n        break;\n    case REDIS_REPLY_INTEGER:\n        out = sdscatprintf(out,\"%lld\",r->integer);\n        break;\n    case REDIS_REPLY_ARRAY:\n        for (i = 0; i < r->elements; i++) {\n            if (i > 0) out = sdscat(out,config.mb_delim);\n            tmp = cliFormatReplyRaw(r->element[i]);\n            out = sdscatlen(out,tmp,sdslen(tmp));\n            sdsfree(tmp);\n        }\n        break;\n    default:\n        fprintf(stderr,\"Unknown reply type: %d\\n\", r->type);\n        exit(1);\n    }\n    return out;\n}\n\nstatic sds cliFormatReplyCSV(redisReply *r) {\n    unsigned int i;\n\n    sds out = sdsempty();\n    switch (r->type) {\n    case REDIS_REPLY_ERROR:\n        out = sdscat(out,\"ERROR,\");\n        out = sdscatrepr(out,r->str,strlen(r->str));\n    break;\n    case REDIS_REPLY_STATUS:\n        out = sdscatrepr(out,r->str,r->len);\n    break;\n    case REDIS_REPLY_INTEGER:\n        out = sdscatprintf(out,\"%lld\",r->integer);\n    break;\n    case REDIS_REPLY_STRING:\n        out = sdscatrepr(out,r->str,r->len);\n    break;\n    case REDIS_REPLY_NIL:\n        out = sdscat(out,\"NIL\\n\");\n    break;\n    case REDIS_REPLY_ARRAY:\n        for (i = 0; i < r->elements; i++) {\n            sds tmp = cliFormatReplyCSV(r->element[i]);\n            out = sdscatlen(out,tmp,sdslen(tmp));\n            if (i != r->elements-1) out = sdscat(out,\",\");\n            sdsfree(tmp);\n        }\n    break;\n    default:\n        fprintf(stderr,\"Unknown reply type: %d\\n\", r->type);\n        exit(1);\n    }\n    return out;\n}\n\nstatic int cliReadReply(int output_raw_strings) {\n    void *_reply;\n    redisReply *reply;\n    sds out = NULL;\n    int output = 1;\n\n    if (redisGetReply(context,&_reply) != REDIS_OK) {\n        if (config.shutdown)\n            return REDIS_OK;\n        if (config.interactive) {\n            /* Filter cases where we should reconnect */\n            if (context->err == REDIS_ERR_IO && errno == ECONNRESET)\n                return REDIS_ERR;\n            if (context->err == REDIS_ERR_EOF)\n                return REDIS_ERR;\n        }\n        cliPrintContextError();\n        exit(1);\n        return REDIS_ERR; /* avoid compiler warning */\n    }\n\n    reply = (redisReply*)_reply;\n\n    /* Check if we need to connect to a different node and reissue the\n     * request. */\n    if (config.cluster_mode && reply->type == REDIS_REPLY_ERROR &&\n        (!strncmp(reply->str,\"MOVED\",5) || !strcmp(reply->str,\"ASK\")))\n    {\n        char *p = reply->str, *s;\n        int slot;\n\n        output = 0;\n        /* Comments show the position of the pointer as:\n         *\n         * [S] for pointer 's'\n         * [P] for pointer 'p'\n         */\n        s = strchr(p,' ');      /* MOVED[S]3999 127.0.0.1:6381 */\n        p = strchr(s+1,' ');    /* MOVED[S]3999[P]127.0.0.1:6381 */\n        *p = '\\0';\n        slot = atoi(s+1);\n        s = strchr(p+1,':');    /* MOVED 3999[P]127.0.0.1[S]6381 */\n        *s = '\\0';\n        sdsfree(config.hostip);\n        config.hostip = sdsnew(p+1);\n        config.hostport = atoi(s+1);\n        if (config.interactive)\n            printf(\"-> Redirected to slot [%d] located at %s:%d\\n\",\n                slot, config.hostip, config.hostport);\n        config.cluster_reissue_command = 1;\n        cliRefreshPrompt();\n    }\n\n    if (output) {\n        if (output_raw_strings) {\n            out = cliFormatReplyRaw(reply);\n        } else {\n            if (config.output == OUTPUT_RAW) {\n                out = cliFormatReplyRaw(reply);\n                out = sdscat(out,\"\\n\");\n            } else if (config.output == OUTPUT_STANDARD) {\n                out = cliFormatReplyTTY(reply,\"\");\n            } else if (config.output == OUTPUT_CSV) {\n                out = cliFormatReplyCSV(reply);\n                out = sdscat(out,\"\\n\");\n            }\n        }\n        fwrite(out,sdslen(out),1,stdout);\n        sdsfree(out);\n    }\n    freeReplyObject(reply);\n    return REDIS_OK;\n}\n\nstatic int cliSendCommand(int argc, char **argv, int repeat) {\n    char *command = argv[0];\n    size_t *argvlen;\n    int j, output_raw;\n\n    if (!strcasecmp(command,\"help\") || !strcasecmp(command,\"?\")) {\n        cliOutputHelp(--argc, ++argv);\n        return REDIS_OK;\n    }\n\n    if (context == NULL) return REDIS_ERR;\n\n    output_raw = 0;\n    if (!strcasecmp(command,\"info\") ||\n        (argc == 2 && !strcasecmp(command,\"cluster\") &&\n                      (!strcasecmp(argv[1],\"nodes\") ||\n                       !strcasecmp(argv[1],\"info\"))) ||\n        (argc == 2 && !strcasecmp(command,\"client\") &&\n                       !strcasecmp(argv[1],\"list\")))\n\n    {\n        output_raw = 1;\n    }\n\n    if (!strcasecmp(command,\"shutdown\")) config.shutdown = 1;\n    if (!strcasecmp(command,\"monitor\")) config.monitor_mode = 1;\n    if (!strcasecmp(command,\"subscribe\") ||\n        !strcasecmp(command,\"psubscribe\")) config.pubsub_mode = 1;\n    if (!strcasecmp(command,\"sync\") ||\n        !strcasecmp(command,\"psync\")) config.slave_mode = 1;\n\n    /* Setup argument length */\n    argvlen = malloc(argc*sizeof(size_t));\n    for (j = 0; j < argc; j++)\n        argvlen[j] = sdslen(argv[j]);\n\n    while(repeat--) {\n        redisAppendCommandArgv(context,argc,(const char**)argv,argvlen);\n        while (config.monitor_mode) {\n            if (cliReadReply(output_raw) != REDIS_OK) exit(1);\n            fflush(stdout);\n        }\n\n        if (config.pubsub_mode) {\n            if (config.output != OUTPUT_RAW)\n                printf(\"Reading messages... (press Ctrl-C to quit)\\n\");\n            while (1) {\n                if (cliReadReply(output_raw) != REDIS_OK) exit(1);\n            }\n        }\n\n        if (config.slave_mode) {\n            printf(\"Entering slave output mode...  (press Ctrl-C to quit)\\n\");\n            slaveMode();\n            config.slave_mode = 0;\n            return REDIS_ERR;  /* Error = slaveMode lost connection to master */\n        }\n\n        if (cliReadReply(output_raw) != REDIS_OK) {\n            free(argvlen);\n            return REDIS_ERR;\n        } else {\n            /* Store database number when SELECT was successfully executed. */\n            if (!strcasecmp(command,\"select\") && argc == 2) {\n                config.dbnum = atoi(argv[1]);\n                cliRefreshPrompt();\n            }\n        }\n        if (config.interval) usleep(config.interval);\n        fflush(stdout); /* Make it grep friendly */\n    }\n\n    free(argvlen);\n    return REDIS_OK;\n}\n\n/* Send the INFO command, reconnecting the link if needed. */\nstatic redisReply *reconnectingInfo(void) {\n    redisContext *c = context;\n    redisReply *reply = NULL;\n    int tries = 0;\n\n    assert(!c->err);\n    while(reply == NULL) {\n        while (c->err & (REDIS_ERR_IO | REDIS_ERR_EOF)) {\n            printf(\"Reconnecting (%d)...\\r\", ++tries);\n            fflush(stdout);\n\n            redisFree(c);\n            c = redisConnect(config.hostip,config.hostport);\n            usleep(1000000);\n        }\n\n        reply = redisCommand(c,\"INFO\");\n        if (c->err && !(c->err & (REDIS_ERR_IO | REDIS_ERR_EOF))) {\n            fprintf(stderr, \"Error: %s\\n\", c->errstr);\n            exit(1);\n        } else if (tries > 0) {\n            printf(\"\\n\");\n        }\n    }\n\n    context = c;\n    return reply;\n}\n\n/*------------------------------------------------------------------------------\n * User interface\n *--------------------------------------------------------------------------- */\n\nstatic int parseOptions(int argc, char **argv) {\n    int i;\n\n    for (i = 1; i < argc; i++) {\n        int lastarg = i==argc-1;\n\n        if (!strcmp(argv[i],\"-h\") && !lastarg) {\n            sdsfree(config.hostip);\n            config.hostip = sdsnew(argv[++i]);\n        } else if (!strcmp(argv[i],\"-h\") && lastarg) {\n            usage();\n        } else if (!strcmp(argv[i],\"--help\")) {\n            usage();\n        } else if (!strcmp(argv[i],\"-x\")) {\n            config.stdinarg = 1;\n        } else if (!strcmp(argv[i],\"-p\") && !lastarg) {\n            config.hostport = atoi(argv[++i]);\n        } else if (!strcmp(argv[i],\"-s\") && !lastarg) {\n            config.hostsocket = argv[++i];\n        } else if (!strcmp(argv[i],\"-r\") && !lastarg) {\n            config.repeat = strtoll(argv[++i],NULL,10);\n        } else if (!strcmp(argv[i],\"-i\") && !lastarg) {\n            double seconds = atof(argv[++i]);\n            config.interval = seconds*1000000;\n        } else if (!strcmp(argv[i],\"-n\") && !lastarg) {\n            config.dbnum = atoi(argv[++i]);\n        } else if (!strcmp(argv[i],\"-a\") && !lastarg) {\n            config.auth = argv[++i];\n        } else if (!strcmp(argv[i],\"--raw\")) {\n            config.output = OUTPUT_RAW;\n        } else if (!strcmp(argv[i],\"--csv\")) {\n            config.output = OUTPUT_CSV;\n        } else if (!strcmp(argv[i],\"--latency\")) {\n            config.latency_mode = 1;\n        } else if (!strcmp(argv[i],\"--latency-history\")) {\n            config.latency_mode = 1;\n            config.latency_history = 1;\n        } else if (!strcmp(argv[i],\"--slave\")) {\n            config.slave_mode = 1;\n        } else if (!strcmp(argv[i],\"--stat\")) {\n            config.stat_mode = 1;\n        } else if (!strcmp(argv[i],\"--scan\")) {\n            config.scan_mode = 1;\n        } else if (!strcmp(argv[i],\"--pattern\") && !lastarg) {\n            config.pattern = argv[++i];\n        } else if (!strcmp(argv[i],\"--intrinsic-latency\") && !lastarg) {\n            config.intrinsic_latency_mode = 1;\n            config.intrinsic_latency_duration = atoi(argv[++i]);\n        } else if (!strcmp(argv[i],\"--rdb\") && !lastarg) {\n            config.getrdb_mode = 1;\n            config.rdb_filename = argv[++i];\n        } else if (!strcmp(argv[i],\"--pipe\")) {\n            config.pipe_mode = 1;\n        } else if (!strcmp(argv[i],\"--pipe-timeout\") && !lastarg) {\n            config.pipe_timeout = atoi(argv[++i]);\n        } else if (!strcmp(argv[i],\"--bigkeys\")) {\n            config.bigkeys = 1;\n        } else if (!strcmp(argv[i],\"--eval\") && !lastarg) {\n            config.eval = argv[++i];\n        } else if (!strcmp(argv[i],\"-c\")) {\n            config.cluster_mode = 1;\n        } else if (!strcmp(argv[i],\"-d\") && !lastarg) {\n            sdsfree(config.mb_delim);\n            config.mb_delim = sdsnew(argv[++i]);\n        } else if (!strcmp(argv[i],\"-v\") || !strcmp(argv[i], \"--version\")) {\n            sds version = cliVersion();\n            printf(\"redis-cli %s\\n\", version);\n            sdsfree(version);\n            exit(0);\n        } else {\n            if (argv[i][0] == '-') {\n                fprintf(stderr,\n                    \"Unrecognized option or bad number of args for: '%s'\\n\",\n                    argv[i]);\n                exit(1);\n            } else {\n                /* Likely the command name, stop here. */\n                break;\n            }\n        }\n    }\n    return i;\n}\n\nstatic sds readArgFromStdin(void) {\n    char buf[1024];\n    sds arg = sdsempty();\n\n    while(1) {\n        int nread = read(fileno(stdin),buf,1024);\n\n        if (nread == 0) break;\n        else if (nread == -1) {\n            perror(\"Reading from standard input\");\n            exit(1);\n        }\n        arg = sdscatlen(arg,buf,nread);\n    }\n    return arg;\n}\n\nstatic void usage() {\n    sds version = cliVersion();\n    fprintf(stderr,\n\"redis-cli %s\\n\"\n\"\\n\"\n\"Usage: redis-cli [OPTIONS] [cmd [arg [arg ...]]]\\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 hostname and port).\\n\"\n\"  -a <password>      Password to use when connecting to the server.\\n\"\n\"  -r <repeat>        Execute specified command N times.\\n\"\n\"  -i <interval>      When -r is used, waits <interval> seconds per command.\\n\"\n\"                     It is possible to specify sub-second times like -i 0.1.\\n\"\n\"  -n <db>            Database number.\\n\"\n\"  -x                 Read last argument from STDIN.\\n\"\n\"  -d <delimiter>     Multi-bulk delimiter in for raw formatting (default: \\\\n).\\n\"\n\"  -c                 Enable cluster mode (follow -ASK and -MOVED redirections).\\n\"\n\"  --raw              Use raw formatting for replies (default when STDOUT is\\n\"\n\"                     not a tty).\\n\"\n\"  --csv              Output in CSV format.\\n\"\n\"  --latency          Enter a special mode continuously sampling latency.\\n\"\n\"  --latency-history  Like --latency but tracking latency changes over time.\\n\"\n\"                     Default time interval is 15 sec. Change it using -i.\\n\"\n\"  --slave            Simulate a slave showing commands received from the master.\\n\"\n\"  --rdb <filename>   Transfer an RDB dump from remote server to local file.\\n\"\n\"  --pipe             Transfer raw Redis protocol from stdin to server.\\n\"\n\"  --pipe-timeout <n> In --pipe mode, abort with error if after sending all data.\\n\"\n\"                     no reply is received within <n> seconds.\\n\"\n\"                     Default timeout: %d. Use 0 to wait forever.\\n\"\n\"  --bigkeys          Sample Redis keys looking for big keys.\\n\"\n\"  --scan             List all keys using the SCAN command.\\n\"\n\"  --pattern <pat>    Useful with --scan to specify a SCAN pattern.\\n\"\n\"  --intrinsic-latency <sec> Run a test to measure intrinsic system latency.\\n\"\n\"                     The test will run for the specified amount of seconds.\\n\"\n\"  --eval <file>      Send an EVAL command using the Lua script at <file>.\\n\"\n\"  --help             Output this help and exit.\\n\"\n\"  --version          Output version and exit.\\n\"\n\"\\n\"\n\"Examples:\\n\"\n\"  cat /etc/passwd | redis-cli -x set mypasswd\\n\"\n\"  redis-cli get mypasswd\\n\"\n\"  redis-cli -r 100 lpush mylist x\\n\"\n\"  redis-cli -r 100 -i 1 info | grep used_memory_human:\\n\"\n\"  redis-cli --eval myscript.lua key1 key2 , arg1 arg2 arg3\\n\"\n\"  redis-cli --scan --pattern '*:12345*'\\n\"\n\"\\n\"\n\"  (Note: when using --eval the comma separates KEYS[] from ARGV[] items)\\n\"\n\"\\n\"\n\"When no command is given, redis-cli starts in interactive mode.\\n\"\n\"Type \\\"help\\\" in interactive mode for information on available commands.\\n\"\n\"\\n\",\n        version, REDIS_CLI_DEFAULT_PIPE_TIMEOUT);\n    sdsfree(version);\n    exit(1);\n}\n\n/* Turn the plain C strings into Sds strings */\nstatic char **convertToSds(int count, char** args) {\n  int j;\n  char **sds = zmalloc(sizeof(char*)*count);\n\n  for(j = 0; j < count; j++)\n    sds[j] = sdsnew(args[j]);\n\n  return sds;\n}\n\n#define LINE_BUFLEN 4096\nstatic void repl() {\n    sds historyfile = NULL;\n    int history = 0;\n    char *line;\n    int argc;\n    sds *argv;\n\n    config.interactive = 1;\n    linenoiseSetMultiLine(1);\n    linenoiseSetCompletionCallback(completionCallback);\n\n    /* Only use history when stdin is a tty. */\n    if (isatty(fileno(stdin))) {\n        history = 1;\n\n        if (getenv(\"HOME\") != NULL) {\n            historyfile = sdscatprintf(sdsempty(),\"%s/.rediscli_history\",getenv(\"HOME\"));\n            linenoiseHistoryLoad(historyfile);\n        }\n    }\n\n    cliRefreshPrompt();\n    while((line = linenoise(context ? config.prompt : \"not connected> \")) != NULL) {\n        if (line[0] != '\\0') {\n            argv = sdssplitargs(line,&argc);\n            if (history) linenoiseHistoryAdd(line);\n            if (historyfile) linenoiseHistorySave(historyfile);\n\n            if (argv == NULL) {\n                printf(\"Invalid argument(s)\\n\");\n                free(line);\n                continue;\n            } else if (argc > 0) {\n                if (strcasecmp(argv[0],\"quit\") == 0 ||\n                    strcasecmp(argv[0],\"exit\") == 0)\n                {\n                    exit(0);\n                } else if (argc == 3 && !strcasecmp(argv[0],\"connect\")) {\n                    sdsfree(config.hostip);\n                    config.hostip = sdsnew(argv[1]);\n                    config.hostport = atoi(argv[2]);\n                    cliRefreshPrompt();\n                    cliConnect(1);\n                } else if (argc == 1 && !strcasecmp(argv[0],\"clear\")) {\n                    linenoiseClearScreen();\n                } else {\n                    long long start_time = mstime(), elapsed;\n                    int repeat, skipargs = 0;\n\n                    repeat = atoi(argv[0]);\n                    if (argc > 1 && repeat) {\n                        skipargs = 1;\n                    } else {\n                        repeat = 1;\n                    }\n\n                    while (1) {\n                        config.cluster_reissue_command = 0;\n                        if (cliSendCommand(argc-skipargs,argv+skipargs,repeat)\n                            != REDIS_OK)\n                        {\n                            cliConnect(1);\n\n                            /* If we still cannot send the command print error.\n                             * We'll try to reconnect the next time. */\n                            if (cliSendCommand(argc-skipargs,argv+skipargs,repeat)\n                                != REDIS_OK)\n                                cliPrintContextError();\n                        }\n                        /* Issue the command again if we got redirected in cluster mode */\n                        if (config.cluster_mode && config.cluster_reissue_command) {\n                            cliConnect(1);\n                        } else {\n                            break;\n                        }\n                    }\n                    elapsed = mstime()-start_time;\n                    if (elapsed >= 500) {\n                        printf(\"(%.2fs)\\n\",(double)elapsed/1000);\n                    }\n                }\n            }\n            /* Free the argument vector */\n            sdsfreesplitres(argv,argc);\n        }\n        /* linenoise() returns malloc-ed lines like readline() */\n        free(line);\n    }\n    exit(0);\n}\n\nstatic int noninteractive(int argc, char **argv) {\n    int retval = 0;\n    if (config.stdinarg) {\n        argv = zrealloc(argv, (argc+1)*sizeof(char*));\n        argv[argc] = readArgFromStdin();\n        retval = cliSendCommand(argc+1, argv, config.repeat);\n    } else {\n        /* stdin is probably a tty, can be tested with S_ISCHR(s.st_mode) */\n        retval = cliSendCommand(argc, argv, config.repeat);\n    }\n    return retval;\n}\n\n/*------------------------------------------------------------------------------\n * Eval mode\n *--------------------------------------------------------------------------- */\n\nstatic int evalMode(int argc, char **argv) {\n    sds script = sdsempty();\n    FILE *fp;\n    char buf[1024];\n    size_t nread;\n    char **argv2;\n    int j, got_comma = 0, keys = 0;\n\n    /* Load the script from the file, as an sds string. */\n    fp = fopen(config.eval,\"r\");\n    if (!fp) {\n        fprintf(stderr,\n            \"Can't open file '%s': %s\\n\", config.eval, strerror(errno));\n        exit(1);\n    }\n    while((nread = fread(buf,1,sizeof(buf),fp)) != 0) {\n        script = sdscatlen(script,buf,nread);\n    }\n    fclose(fp);\n\n    /* Create our argument vector */\n    argv2 = zmalloc(sizeof(sds)*(argc+3));\n    argv2[0] = sdsnew(\"EVAL\");\n    argv2[1] = script;\n    for (j = 0; j < argc; j++) {\n        if (!got_comma && argv[j][0] == ',' && argv[j][1] == 0) {\n            got_comma = 1;\n            continue;\n        }\n        argv2[j+3-got_comma] = sdsnew(argv[j]);\n        if (!got_comma) keys++;\n    }\n    argv2[2] = sdscatprintf(sdsempty(),\"%d\",keys);\n\n    /* Call it */\n    return cliSendCommand(argc+3-got_comma, argv2, config.repeat);\n}\n\n/*------------------------------------------------------------------------------\n * Latency and latency history modes\n *--------------------------------------------------------------------------- */\n\n#define LATENCY_SAMPLE_RATE 10 /* milliseconds. */\n#define LATENCY_HISTORY_DEFAULT_INTERVAL 15000 /* milliseconds. */\nstatic void latencyMode(void) {\n    redisReply *reply;\n    long long start, latency, min = 0, max = 0, tot = 0, count = 0;\n    long long history_interval =\n        config.interval ? config.interval/1000 :\n                          LATENCY_HISTORY_DEFAULT_INTERVAL;\n    double avg;\n    long long history_start = mstime();\n\n    if (!context) exit(1);\n    while(1) {\n        start = mstime();\n        reply = redisCommand(context,\"PING\");\n        if (reply == NULL) {\n            fprintf(stderr,\"\\nI/O error\\n\");\n            exit(1);\n        }\n        latency = mstime()-start;\n        freeReplyObject(reply);\n        count++;\n        if (count == 1) {\n            min = max = tot = latency;\n            avg = (double) latency;\n        } else {\n            if (latency < min) min = latency;\n            if (latency > max) max = latency;\n            tot += latency;\n            avg = (double) tot/count;\n        }\n        printf(\"\\x1b[0G\\x1b[2Kmin: %lld, max: %lld, avg: %.2f (%lld samples)\",\n            min, max, avg, count);\n        fflush(stdout);\n        if (config.latency_history && mstime()-history_start > history_interval)\n        {\n            printf(\" -- %.2f seconds range\\n\", (float)(mstime()-history_start)/1000);\n            history_start = mstime();\n            min = max = tot = count = 0;\n        }\n        usleep(LATENCY_SAMPLE_RATE * 1000);\n    }\n}\n\n/*------------------------------------------------------------------------------\n * Slave mode\n *--------------------------------------------------------------------------- */\n\n/* Sends SYNC and reads the number of bytes in the payload. Used both by\n * slaveMode() and getRDB(). */\nunsigned long long sendSync(int fd) {\n    /* To start we need to send the SYNC command and return the payload.\n     * The hiredis client lib does not understand this part of the protocol\n     * and we don't want to mess with its buffers, so everything is performed\n     * using direct low-level I/O. */\n    char buf[4096], *p;\n    ssize_t nread;\n\n    /* Send the SYNC command. */\n    if (write(fd,\"SYNC\\r\\n\",6) != 6) {\n        fprintf(stderr,\"Error writing to master\\n\");\n        exit(1);\n    }\n\n    /* Read $<payload>\\r\\n, making sure to read just up to \"\\n\" */\n    p = buf;\n    while(1) {\n        nread = read(fd,p,1);\n        if (nread <= 0) {\n            fprintf(stderr,\"Error reading bulk length while SYNCing\\n\");\n            exit(1);\n        }\n        if (*p == '\\n' && p != buf) break;\n        if (*p != '\\n') p++;\n    }\n    *p = '\\0';\n    if (buf[0] == '-') {\n        printf(\"SYNC with master failed: %s\\n\", buf);\n        exit(1);\n    }\n    return strtoull(buf+1,NULL,10);\n}\n\nstatic void slaveMode(void) {\n    int fd = context->fd;\n    unsigned long long payload = sendSync(fd);\n    char buf[1024];\n    int original_output = config.output;\n\n    fprintf(stderr,\"SYNC with master, discarding %llu \"\n                   \"bytes of bulk transfer...\\n\", payload);\n\n    /* Discard the payload. */\n    while(payload) {\n        ssize_t nread;\n\n        nread = read(fd,buf,(payload > sizeof(buf)) ? sizeof(buf) : payload);\n        if (nread <= 0) {\n            fprintf(stderr,\"Error reading RDB payload while SYNCing\\n\");\n            exit(1);\n        }\n        payload -= nread;\n    }\n    fprintf(stderr,\"SYNC done. Logging commands from master.\\n\");\n\n    /* Now we can use hiredis to read the incoming protocol. */\n    config.output = OUTPUT_CSV;\n    while (cliReadReply(0) == REDIS_OK);\n    config.output = original_output;\n}\n\n/*------------------------------------------------------------------------------\n * RDB transfer mode\n *--------------------------------------------------------------------------- */\n\n/* This function implements --rdb, so it uses the replication protocol in order\n * to fetch the RDB file from a remote server. */\nstatic void getRDB(void) {\n    int s = context->fd;\n    int fd;\n    unsigned long long payload = sendSync(s);\n    char buf[4096];\n\n    fprintf(stderr,\"SYNC sent to master, writing %llu bytes to '%s'\\n\",\n        payload, config.rdb_filename);\n\n    /* Write to file. */\n    if (!strcmp(config.rdb_filename,\"-\")) {\n        fd = STDOUT_FILENO;\n    } else {\n        fd = open(config.rdb_filename, O_CREAT|O_WRONLY, 0644);\n        if (fd == -1) {\n            fprintf(stderr, \"Error opening '%s': %s\\n\", config.rdb_filename,\n                strerror(errno));\n            exit(1);\n        }\n    }\n\n    while(payload) {\n        ssize_t nread, nwritten;\n        \n        nread = read(s,buf,(payload > sizeof(buf)) ? sizeof(buf) : payload);\n        if (nread <= 0) {\n            fprintf(stderr,\"I/O Error reading RDB payload from socket\\n\");\n            exit(1);\n        }\n        nwritten = write(fd, buf, nread);\n        if (nwritten != nread) {\n            fprintf(stderr,\"Error writing data to file: %s\\n\",\n                strerror(errno));\n            exit(1);\n        }\n        payload -= nread;\n    }\n    close(s); /* Close the file descriptor ASAP as fsync() may take time. */\n    fsync(fd);\n    fprintf(stderr,\"Transfer finished with success.\\n\");\n    exit(0);\n}\n\n/*------------------------------------------------------------------------------\n * Bulk import (pipe) mode\n *--------------------------------------------------------------------------- */\n\nstatic void pipeMode(void) {\n    int fd = context->fd;\n    long long errors = 0, replies = 0, obuf_len = 0, obuf_pos = 0;\n    char ibuf[1024*16], obuf[1024*16]; /* Input and output buffers */\n    char aneterr[ANET_ERR_LEN];\n    redisReader *reader = redisReaderCreate();\n    redisReply *reply;\n    int eof = 0; /* True once we consumed all the standard input. */\n    int done = 0;\n    char magic[20]; /* Special reply we recognize. */\n    time_t last_read_time = time(NULL);\n\n    srand(time(NULL));\n\n    /* Use non blocking I/O. */\n    if (anetNonBlock(aneterr,fd) == ANET_ERR) {\n        fprintf(stderr, \"Can't set the socket in non blocking mode: %s\\n\",\n            aneterr);\n        exit(1);\n    }\n\n    /* Transfer raw protocol and read replies from the server at the same\n     * time. */\n    while(!done) {\n        int mask = AE_READABLE;\n\n        if (!eof || obuf_len != 0) mask |= AE_WRITABLE;\n        mask = aeWait(fd,mask,1000);\n\n        /* Handle the readable state: we can read replies from the server. */\n        if (mask & AE_READABLE) {\n            ssize_t nread;\n\n            /* Read from socket and feed the hiredis reader. */\n            do {\n                nread = read(fd,ibuf,sizeof(ibuf));\n                if (nread == -1 && errno != EAGAIN && errno != EINTR) {\n                    fprintf(stderr, \"Error reading from the server: %s\\n\",\n                        strerror(errno));\n                    exit(1);\n                }\n                if (nread > 0) {\n                    redisReaderFeed(reader,ibuf,nread);\n                    last_read_time = time(NULL);\n                }\n            } while(nread > 0);\n\n            /* Consume replies. */\n            do {\n                if (redisReaderGetReply(reader,(void**)&reply) == REDIS_ERR) {\n                    fprintf(stderr, \"Error reading replies from server\\n\");\n                    exit(1);\n                }\n                if (reply) {\n                    if (reply->type == REDIS_REPLY_ERROR) {\n                        fprintf(stderr,\"%s\\n\", reply->str);\n                        errors++;\n                    } else if (eof && reply->type == REDIS_REPLY_STRING &&\n                                      reply->len == 20) {\n                        /* Check if this is the reply to our final ECHO \n                         * command. If so everything was received\n                         * from the server. */\n                        if (memcmp(reply->str,magic,20) == 0) {\n                            printf(\"Last reply received from server.\\n\");\n                            done = 1;\n                            replies--;\n                        }\n                    }\n                    replies++;\n                    freeReplyObject(reply);\n                }\n            } while(reply);\n        }\n\n        /* Handle the writable state: we can send protocol to the server. */\n        if (mask & AE_WRITABLE) {\n            while(1) {\n                /* Transfer current buffer to server. */\n                if (obuf_len != 0) {\n                    ssize_t nwritten = write(fd,obuf+obuf_pos,obuf_len);\n                    \n                    if (nwritten == -1) {\n                        if (errno != EAGAIN && errno != EINTR) {\n                            fprintf(stderr, \"Error writing to the server: %s\\n\",\n                                strerror(errno));\n                            exit(1);\n                        } else {\n                            nwritten = 0;\n                        }\n                    }\n                    obuf_len -= nwritten;\n                    obuf_pos += nwritten;\n                    if (obuf_len != 0) break; /* Can't accept more data. */\n                }\n                /* If buffer is empty, load from stdin. */\n                if (obuf_len == 0 && !eof) {\n                    ssize_t nread = read(STDIN_FILENO,obuf,sizeof(obuf));\n\n                    if (nread == 0) {\n                        /* The ECHO sequence starts with a \"\\r\\n\" so that if there\n                         * is garbage in the protocol we read from stdin, the ECHO\n                         * will likely still be properly formatted.\n                         * CRLF is ignored by Redis, so it has no effects. */\n                        char echo[] =\n                        \"\\r\\n*2\\r\\n$4\\r\\nECHO\\r\\n$20\\r\\n01234567890123456789\\r\\n\";\n                        int j;\n\n                        eof = 1;\n                        /* Everything transferred, so we queue a special\n                         * ECHO command that we can match in the replies\n                         * to make sure everything was read from the server. */\n                        for (j = 0; j < 20; j++)\n                            magic[j] = rand() & 0xff;\n                        memcpy(echo+21,magic,20);\n                        memcpy(obuf,echo,sizeof(echo)-1);\n                        obuf_len = sizeof(echo)-1;\n                        obuf_pos = 0;\n                        printf(\"All data transferred. Waiting for the last reply...\\n\");\n                    } else if (nread == -1) {\n                        fprintf(stderr, \"Error reading from stdin: %s\\n\",\n                            strerror(errno));\n                        exit(1);\n                    } else {\n                        obuf_len = nread;\n                        obuf_pos = 0;\n                    }\n                }\n                if (obuf_len == 0 && eof) break;\n            }\n        }\n\n        /* Handle timeout, that is, we reached EOF, and we are not getting\n         * replies from the server for a few seconds, nor the final ECHO is\n         * received. */\n        if (eof && config.pipe_timeout > 0 &&\n            time(NULL)-last_read_time > config.pipe_timeout)\n        {\n            fprintf(stderr,\"No replies for %d seconds: exiting.\\n\",\n                config.pipe_timeout);\n            errors++;\n            break;\n        }\n    }\n    redisReaderFree(reader);\n    printf(\"errors: %lld, replies: %lld\\n\", errors, replies);\n    if (errors)\n        exit(1);\n    else\n        exit(0);\n}\n\n/*------------------------------------------------------------------------------\n * Find big keys\n *--------------------------------------------------------------------------- */\n\n#define TYPE_STRING 0\n#define TYPE_LIST   1\n#define TYPE_SET    2\n#define TYPE_HASH   3\n#define TYPE_ZSET   4\n#define TYPE_NONE   5\n\nstatic redisReply *sendScan(unsigned long long *it) {\n    redisReply *reply = redisCommand(context, \"SCAN %llu\", *it);\n\n    /* Handle any error conditions */\n    if(reply == NULL) {\n        fprintf(stderr, \"\\nI/O error\\n\");\n        exit(1);\n    } else if(reply->type == REDIS_REPLY_ERROR) {\n        fprintf(stderr, \"SCAN error: %s\\n\", reply->str);\n        exit(1);\n    } else if(reply->type != REDIS_REPLY_ARRAY) {\n        fprintf(stderr, \"Non ARRAY response from SCAN!\\n\");\n        exit(1);\n    } else if(reply->elements != 2) {\n        fprintf(stderr, \"Invalid element count from SCAN!\\n\");\n        exit(1);\n    }\n\n    /* Validate our types are correct */\n    assert(reply->element[0]->type == REDIS_REPLY_STRING);\n    assert(reply->element[1]->type == REDIS_REPLY_ARRAY);\n    \n    /* Update iterator */\n    *it = atoi(reply->element[0]->str);\n\n    return reply;\n}\n\nstatic int getDbSize(void) {\n    redisReply *reply;\n    int size;\n\n    reply = redisCommand(context, \"DBSIZE\");\n    \n    if(reply == NULL || reply->type != REDIS_REPLY_INTEGER) {\n        fprintf(stderr, \"Couldn't determine DBSIZE!\\n\");\n        exit(1);\n    }\n\n    /* Grab the number of keys and free our reply */\n    size = reply->integer;\n    freeReplyObject(reply);\n\n    return size;\n}\n\nstatic int toIntType(char *key, char *type) {\n    if(!strcmp(type, \"string\")) {\n        return TYPE_STRING;\n    } else if(!strcmp(type, \"list\")) {\n        return TYPE_LIST;\n    } else if(!strcmp(type, \"set\")) {\n        return TYPE_SET;\n    } else if(!strcmp(type, \"hash\")) {\n        return TYPE_HASH;\n    } else if(!strcmp(type, \"zset\")) {\n        return TYPE_ZSET;\n    } else if(!strcmp(type, \"none\")) {\n        return TYPE_NONE;\n    } else {\n        fprintf(stderr, \"Unknown type '%s' for key '%s'\\n\", type, key);\n        exit(1);\n    }\n}\n\nstatic void getKeyTypes(redisReply *keys, int *types) {\n    redisReply *reply;\n    int i;\n\n    /* Pipeline TYPE commands */\n    for(i=0;i<keys->elements;i++) {\n        redisAppendCommand(context, \"TYPE %s\", keys->element[i]->str);\n    }\n\n    /* Retrieve types */\n    for(i=0;i<keys->elements;i++) {\n        if(redisGetReply(context, (void**)&reply)!=REDIS_OK) {\n            fprintf(stderr, \"Error getting type for key '%s' (%d: %s)\\n\",\n                keys->element[i]->str, context->err, context->errstr);\n            exit(1);\n        } else if(reply->type != REDIS_REPLY_STATUS) {\n            fprintf(stderr, \"Invalid reply type (%d) for TYPE on key '%s'!\\n\",\n                reply->type, keys->element[i]->str);\n            exit(1);\n        }\n\n        types[i] = toIntType(keys->element[i]->str, reply->str); \n        freeReplyObject(reply);\n    }\n}\n\nstatic void getKeySizes(redisReply *keys, int *types, \n                        unsigned long long *sizes) \n{\n    redisReply *reply;\n    char *sizecmds[] = {\"STRLEN\",\"LLEN\",\"SCARD\",\"HLEN\",\"ZCARD\"};\n    int i;\n\n    /* Pipeline size commands */\n    for(i=0;i<keys->elements;i++) {\n        /* Skip keys that were deleted */\n        if(types[i]==TYPE_NONE) \n            continue;\n\n        redisAppendCommand(context, \"%s %s\", sizecmds[types[i]], \n            keys->element[i]->str);\n    }\n\n    /* Retreive sizes */\n    for(i=0;i<keys->elements;i++) {\n        /* Skip keys that dissapeared between SCAN and TYPE */\n        if(types[i] == TYPE_NONE) {\n            sizes[i] = 0;\n            continue;\n        }\n\n        /* Retreive size */\n        if(redisGetReply(context, (void**)&reply)!=REDIS_OK) {\n            fprintf(stderr, \"Error getting size for key '%s' (%d: %s)\\n\",\n                keys->element[i]->str, context->err, context->errstr);\n            exit(1);\n        } else if(reply->type != REDIS_REPLY_INTEGER) {\n            /* Theoretically the key could have been removed and\n             * added as a different type between TYPE and SIZE */\n            fprintf(stderr, \n                \"Warning:  %s on '%s' failed (may have changed type)\\n\",\n                 sizecmds[types[i]], keys->element[i]->str);\n            sizes[i] = 0;\n        } else {\n            sizes[i] = reply->integer;\n        }\n            \n        freeReplyObject(reply);\n    }\n}\n\nstatic void findBigKeys(void) {\n    unsigned long long biggest[5] = {0}, counts[5] = {0}, totalsize[5] = {0};\n    unsigned long long sampled = 0, total_keys, totlen=0, *sizes=NULL, it=0;\n    sds maxkeys[5] = {0};\n    char *typename[] = {\"string\",\"list\",\"set\",\"hash\",\"zset\"};\n    char *typeunit[] = {\"bytes\",\"items\",\"members\",\"fields\",\"members\"};\n    redisReply *reply, *keys;\n    int type, *types=NULL, arrsize=0, i;\n    double pct;\n\n    /* Total keys pre scanning */\n    total_keys = getDbSize();\n\n    /* Status message */\n    printf(\"\\n# Scanning the entire keyspace to find biggest keys as well as\\n\");\n    printf(\"# average sizes per key type.  You can use -i 0.1 to sleep 0.1 sec\\n\");\n    printf(\"# per 100 SCAN commands (not usually needed).\\n\\n\");\n\n    /* New up sds strings to keep track of overall biggest per type */\n    for(i=0;i<TYPE_NONE; i++) {\n        maxkeys[i] = sdsempty();\n        if(!maxkeys[i]) {\n            fprintf(stderr, \"Failed to allocate memory for largest key names!\");\n            exit(1);\n        }\n    }\n\n    /* SCAN loop */\n    do {\n        /* Calculate approximate percentage completion */\n        pct = 100 * (double)sampled/total_keys;\n\n        /* Grab some keys and point to the keys array */\n        reply = sendScan(&it);\n        keys  = reply->element[1];\n\n        /* Reallocate our type and size array if we need to */\n        if(keys->elements > arrsize) {\n            types = zrealloc(types, sizeof(int)*keys->elements);\n            sizes = zrealloc(sizes, sizeof(unsigned long long)*keys->elements);\n\n            if(!types || !sizes) {\n                fprintf(stderr, \"Failed to allocate storage for keys!\\n\");\n                exit(1);\n            }\n\n            arrsize = keys->elements;\n        }\n\n        /* Retreive types and then sizes */\n        getKeyTypes(keys, types);\n        getKeySizes(keys, types, sizes);\n        \n        /* Now update our stats */\n        for(i=0;i<keys->elements;i++) {\n            if((type = types[i]) == TYPE_NONE)\n                continue;\n            \n            totalsize[type] += sizes[i];\n            counts[type]++;\n            totlen += keys->element[i]->len;\n            sampled++;\n\n            if(biggest[type]<sizes[i]) {\n                printf(\n                   \"[%05.2f%%] Biggest %-6s found so far '%s' with %llu %s\\n\",\n                   pct, typename[type], keys->element[i]->str, sizes[i],\n                   typeunit[type]);\n\n                /* Keep track of biggest key name for this type */\n                maxkeys[type] = sdscpy(maxkeys[type], keys->element[i]->str);\n                if(!maxkeys[type]) {\n                    fprintf(stderr, \"Failed to allocate memory for key!\\n\");\n                    exit(1);\n                }\n\n                /* Keep track of the biggest size for this type */\n                biggest[type] = sizes[i];                \n            }\n\n            /* Update overall progress */\n            if(sampled % 1000000 == 0) {\n                printf(\"[%05.2f%%] Sampled %llu keys so far\\n\", pct, sampled);\n            }\n        }\n\n        /* Sleep if we've been directed to do so */\n        if(sampled && (sampled %100) == 0 && config.interval) {\n            usleep(config.interval);\n        }\n        \n        freeReplyObject(reply);\n    } while(it != 0);\n\n    if(types) zfree(types);\n    if(sizes) zfree(sizes);\n\n    /* We're done */\n    printf(\"\\n-------- summary -------\\n\\n\");\n\n    printf(\"Sampled %llu keys in the keyspace!\\n\", sampled);\n    printf(\"Total key length in bytes is %llu (avg len %.2f)\\n\\n\",\n       totlen, totlen ? (double)totlen/sampled : 0);\n\n    /* Output the biggest keys we found, for types we did find */\n    for(i=0;i<TYPE_NONE;i++) {\n        if(sdslen(maxkeys[i])>0) {\n            printf(\"Biggest %6s found '%s' has %llu %s\\n\", typename[i], maxkeys[i],\n               biggest[i], typeunit[i]);\n        }\n    }\n\n    printf(\"\\n\");\n\n    for(i=0;i<TYPE_NONE;i++) {\n        printf(\"%llu %ss with %llu %s (%05.2f%% of keys, avg size %.2f)\\n\",\n           counts[i], typename[i], totalsize[i], typeunit[i],\n           sampled ? 100 * (double)counts[i]/sampled : 0,\n           counts[i] ? (double)totalsize[i]/counts[i] : 0);\n    }\n\n    /* Free sds strings containing max keys */\n    for(i=0;i<TYPE_NONE;i++) {\n        sdsfree(maxkeys[i]);\n    }\n\n    /* Success! */\n    exit(0);\n}\n\n/*------------------------------------------------------------------------------\n * Stats mode\n *--------------------------------------------------------------------------- */\n\n/* Return the specified INFO field from the INFO command output \"info\".\n * A new buffer is allocated for the result, that needs to be free'd.\n * If the field is not found NULL is returned. */\nstatic char *getInfoField(char *info, char *field) {\n    char *p = strstr(info,field);\n    char *n1, *n2;\n    char *result;\n\n    if (!p) return NULL;\n    p += strlen(field)+1;\n    n1 = strchr(p,'\\r');\n    n2 = strchr(p,',');\n    if (n2 && n2 < n1) n1 = n2;\n    result = malloc(sizeof(char)*(n1-p)+1);\n    memcpy(result,p,(n1-p));\n    result[n1-p] = '\\0';\n    return result;\n}\n\n/* Like the above function but automatically convert the result into\n * a long. On error (missing field) LONG_MIN is returned. */\nstatic long getLongInfoField(char *info, char *field) {\n    char *value = getInfoField(info,field);\n    long l;\n\n    if (!value) return LONG_MIN;\n    l = strtol(value,NULL,10);\n    free(value);\n    return l;\n}\n\n/* Convert number of bytes into a human readable string of the form:\n * 100B, 2G, 100M, 4K, and so forth. */\nvoid bytesToHuman(char *s, long long n) {\n    double d;\n\n    if (n < 0) {\n        *s = '-';\n        s++;\n        n = -n;\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    }\n}\n\nstatic void statMode() {\n    redisReply *reply;\n    long aux, requests = 0;\n    int i = 0;\n\n    while(1) {\n        char buf[64];\n        int j;\n\n        reply = reconnectingInfo();\n        if (reply->type == REDIS_REPLY_ERROR) {\n            printf(\"ERROR: %s\\n\", reply->str);\n            exit(1);\n        }\n\n        if ((i++ % 20) == 0) {\n            printf(\n\"------- data ------ --------------------- load -------------------- - child -\\n\"\n\"keys       mem      clients blocked requests            connections          \\n\");\n        }\n\n        /* Keys */\n        aux = 0;\n        for (j = 0; j < 20; j++) {\n            long k;\n\n            sprintf(buf,\"db%d:keys\",j);\n            k = getLongInfoField(reply->str,buf);\n            if (k == LONG_MIN) continue;\n            aux += k;\n        }\n        sprintf(buf,\"%ld\",aux);\n        printf(\"%-11s\",buf);\n\n        /* Used memory */\n        aux = getLongInfoField(reply->str,\"used_memory\");\n        bytesToHuman(buf,aux);\n        printf(\"%-8s\",buf);\n\n        /* Clients */\n        aux = getLongInfoField(reply->str,\"connected_clients\");\n        sprintf(buf,\"%ld\",aux);\n        printf(\" %-8s\",buf);\n\n        /* Blocked (BLPOPPING) Clients */\n        aux = getLongInfoField(reply->str,\"blocked_clients\");\n        sprintf(buf,\"%ld\",aux);\n        printf(\"%-8s\",buf);\n\n        /* Requets */\n        aux = getLongInfoField(reply->str,\"total_commands_processed\");\n        sprintf(buf,\"%ld (+%ld)\",aux,requests == 0 ? 0 : aux-requests);\n        printf(\"%-19s\",buf);\n        requests = aux;\n\n        /* Connections */\n        aux = getLongInfoField(reply->str,\"total_connections_received\");\n        sprintf(buf,\"%ld\",aux);\n        printf(\" %-12s\",buf);\n\n        /* Children */\n        aux = getLongInfoField(reply->str,\"bgsave_in_progress\");\n        aux |= getLongInfoField(reply->str,\"aof_rewrite_in_progress\") << 1;\n        switch(aux) {\n        case 0: break;\n        case 1:\n            printf(\"SAVE\");\n            break;\n        case 2:\n            printf(\"AOF\");\n            break;\n        case 3:\n            printf(\"SAVE+AOF\");\n            break;\n        }\n\n        printf(\"\\n\");\n        freeReplyObject(reply);\n        usleep(config.interval);\n    }\n}\n\n/*------------------------------------------------------------------------------\n * Scan mode\n *--------------------------------------------------------------------------- */\n\nstatic void scanMode() {\n    redisReply *reply;\n    unsigned long long cur = 0;\n\n    do {\n        if (config.pattern)\n            reply = redisCommand(context,\"SCAN %llu MATCH %s\",\n                cur,config.pattern);\n        else\n            reply = redisCommand(context,\"SCAN %llu\",cur);\n        if (reply == NULL) {\n            printf(\"I/O error\\n\");\n            exit(1);\n        } else if (reply->type == REDIS_REPLY_ERROR) {\n            printf(\"ERROR: %s\\n\", reply->str);\n            exit(1);\n        } else {\n            int j;\n\n            cur = strtoull(reply->element[0]->str,NULL,10);\n            for (j = 0; j < reply->element[1]->elements; j++)\n                printf(\"%s\\n\", reply->element[1]->element[j]->str);\n        }\n        freeReplyObject(reply);\n    } while(cur != 0);\n\n    exit(0);\n}\n\n/*------------------------------------------------------------------------------\n * Intrisic latency mode.\n *\n * Measure max latency of a running process that does not result from\n * syscalls. Basically this software should provide an hint about how much\n * time the kernel leaves the process without a chance to run.\n *--------------------------------------------------------------------------- */\n\n/* This is just some computation the compiler can't optimize out.\n * Should run in less than 100-200 microseconds even using very\n * slow hardware. Runs in less than 10 microseconds in modern HW. */\nunsigned long compute_something_fast(void) {\n    unsigned char s[256], i, j, t;\n    int count = 1000, k;\n    unsigned long output = 0;\n\n    for (k = 0; k < 256; k++) s[k] = k;\n\n    i = 0;\n    j = 0;\n    while(count--) {\n        i++;\n        j = j + s[i];\n        t = s[i];\n        s[i] = s[j];\n        s[j] = t;\n        output += s[(s[i]+s[j])&255];\n    }\n    return output;\n}\n\nstatic void intrinsicLatencyMode(void) {\n    long long test_end, run_time, max_latency = 0, runs = 0;\n\n    run_time = config.intrinsic_latency_duration*1000000;\n    test_end = ustime() + run_time;\n\n    while(1) {\n        long long start, end, latency;\n\n        start = ustime();\n        compute_something_fast();\n        end = ustime();\n        latency = end-start;\n        runs++;\n        if (latency <= 0) continue;\n\n        /* Reporting */\n        if (latency > max_latency) {\n            max_latency = latency;\n            printf(\"Max latency so far: %lld microseconds.\\n\", max_latency);\n        }\n\n        if (end > test_end) {\n            printf(\"\\n%lld total runs (avg %lld microseconds per run).\\n\",\n                runs, run_time/runs);\n            printf(\"Worst run took %.02fx times the avarege.\\n\",\n                (double) max_latency / (run_time/runs));\n            exit(0);\n        }\n    }\n}\n\n/*------------------------------------------------------------------------------\n * Program main()\n *--------------------------------------------------------------------------- */\n\nint main(int argc, char **argv) {\n    int firstarg;\n\n    config.hostip = sdsnew(\"127.0.0.1\");\n    config.hostport = 6379;\n    config.hostsocket = NULL;\n    config.repeat = 1;\n    config.interval = 0;\n    config.dbnum = 0;\n    config.interactive = 0;\n    config.shutdown = 0;\n    config.monitor_mode = 0;\n    config.pubsub_mode = 0;\n    config.latency_mode = 0;\n    config.latency_history = 0;\n    config.cluster_mode = 0;\n    config.slave_mode = 0;\n    config.getrdb_mode = 0;\n    config.stat_mode = 0;\n    config.scan_mode = 0;\n    config.intrinsic_latency_mode = 0;\n    config.pattern = NULL;\n    config.rdb_filename = NULL;\n    config.pipe_mode = 0;\n    config.pipe_timeout = REDIS_CLI_DEFAULT_PIPE_TIMEOUT;\n    config.bigkeys = 0;\n    config.stdinarg = 0;\n    config.auth = NULL;\n    config.eval = NULL;\n    if (!isatty(fileno(stdout)) && (getenv(\"FAKETTY\") == NULL))\n        config.output = OUTPUT_RAW;\n    else\n        config.output = OUTPUT_STANDARD;\n    config.mb_delim = sdsnew(\"\\n\");\n    cliInitHelp();\n\n    firstarg = parseOptions(argc,argv);\n    argc -= firstarg;\n    argv += firstarg;\n\n    /* Latency mode */\n    if (config.latency_mode) {\n        if (cliConnect(0) == REDIS_ERR) exit(1);\n        latencyMode();\n    }\n\n    /* Slave mode */\n    if (config.slave_mode) {\n        if (cliConnect(0) == REDIS_ERR) exit(1);\n        slaveMode();\n    }\n\n    /* Get RDB mode. */\n    if (config.getrdb_mode) {\n        if (cliConnect(0) == REDIS_ERR) exit(1);\n        getRDB();\n    }\n\n    /* Pipe mode */\n    if (config.pipe_mode) {\n        if (cliConnect(0) == REDIS_ERR) exit(1);\n        pipeMode();\n    }\n\n    /* Find big keys */\n    if (config.bigkeys) {\n        if (cliConnect(0) == REDIS_ERR) exit(1);\n        findBigKeys();\n    }\n\n    /* Stat mode */\n    if (config.stat_mode) {\n        if (cliConnect(0) == REDIS_ERR) exit(1);\n        if (config.interval == 0) config.interval = 1000000;\n        statMode();\n    }\n\n    /* Scan mode */\n    if (config.scan_mode) {\n        if (cliConnect(0) == REDIS_ERR) exit(1);\n        scanMode();\n    }\n\n    /* Intrinsic latency mode */\n    if (config.intrinsic_latency_mode) intrinsicLatencyMode();\n\n    /* Start interactive mode when no command is provided */\n    if (argc == 0 && !config.eval) {\n        /* Note that in repl mode we don't abort on connection error.\n         * A new attempt will be performed for every command send. */\n        cliConnect(0);\n        repl();\n    }\n\n    /* Otherwise, we have some arguments to execute */\n    if (cliConnect(0) != REDIS_OK) exit(1);\n    if (config.eval) {\n        return evalMode(argc,argv);\n    } else {\n        return noninteractive(argc,convertToSds(argc,argv));\n    }\n}\n"
  },
  {
    "path": "src/redis-trib.rb",
    "content": "#!/usr/bin/env ruby\n\n# TODO (temporary here, we'll move this into the Github issues once\n#       redis-trib initial implementation is completed).\n#\n# - Make sure that if the rehashing fails in the middle redis-trib will try\n#   to recover.\n# - When redis-trib performs a cluster check, if it detects a slot move in\n#   progress it should prompt the user to continue the move from where it\n#   stopped.\n# - Gracefully handle Ctrl+C in move_slot to prompt the user if really stop\n#   while rehashing, and performing the best cleanup possible if the user\n#   forces the quit.\n# - When doing \"fix\" set a global Fix to true, and prompt the user to\n#   fix the problem if automatically fixable every time there is something\n#   to fix. For instance:\n#   1) If there is a node that pretend to receive a slot, or to migrate a\n#      slot, but has no entries in that slot, fix it.\n#   2) If there is a node having keys in slots that are not owned by it\n#      fix this condition moving the entries in the same node.\n#   3) Perform more possibly slow tests about the state of the cluster.\n#   4) When aborted slot migration is detected, fix it.\n\nrequire 'rubygems'\nrequire 'redis'\n\nClusterHashSlots = 16384\n\ndef xputs(s)\n    case s[0..2]\n    when \">>>\"\n        color=\"29;1\"\n    when \"[ER\"\n        color=\"31;1\"\n    when \"[OK\"\n        color=\"32\"\n    when \"[FA\",\"***\"\n        color=\"33\"\n    else\n        color=nil\n    end\n\n    color = nil if ENV['TERM'] != \"xterm\"\n    print \"\\033[#{color}m\" if color\n    print s\n    print \"\\033[0m\" if color\n    print \"\\n\"\nend\n\nclass ClusterNode\n    def initialize(addr)\n        s = addr.split(\":\")\n        if s.length != 2\n            puts \"Invalid node name #{addr}\"\n            exit 1\n        end\n        @r = nil\n        @info = {}\n        @info[:host] = s[0]\n        @info[:port] = s[1]\n        @info[:slots] = {}\n        @info[:migrating] = {}\n        @info[:importing] = {}\n        @info[:replicate] = false\n        @dirty = false # True if we need to flush slots info into node.\n        @friends = []\n    end\n\n    def friends\n        @friends\n    end\n\n    def slots \n        @info[:slots]\n    end\n\n    def has_flag?(flag)\n        @info[:flags].index(flag)\n    end\n\n    def to_s\n        \"#{@info[:host]}:#{@info[:port]}\"\n    end\n\n    def connect(o={})\n        return if @r\n        print \"Connecting to node #{self}: \"\n        STDOUT.flush\n        begin\n            @r = Redis.new(:host => @info[:host], :port => @info[:port], :timeout => 60)\n            @r.ping\n        rescue\n            xputs \"[ERR] Sorry, can't connect to node #{self}\"\n            exit 1 if o[:abort]\n            @r = nil\n        end\n        xputs \"OK\"\n    end\n\n    def assert_cluster\n        info = @r.info\n        if !info[\"cluster_enabled\"] || info[\"cluster_enabled\"].to_i == 0\n            xputs \"[ERR] Node #{self} is not configured as a cluster node.\"\n            exit 1\n        end\n    end\n\n    def assert_empty\n        if !(@r.cluster(\"info\").split(\"\\r\\n\").index(\"cluster_known_nodes:1\")) ||\n            (@r.info['db0'])\n            xputs \"[ERR] Node #{self} is not empty. Either the node already knows other nodes (check with CLUSTER NODES) or contains some key in database 0.\"\n            exit 1\n        end\n    end\n\n    def load_info(o={})\n        self.connect\n        nodes = @r.cluster(\"nodes\").split(\"\\n\")\n        nodes.each{|n|\n            # name addr flags role ping_sent ping_recv link_status slots\n            split = n.split\n            name,addr,flags,master_id,ping_sent,ping_recv,config_epoch,link_status = split[0..6]\n            slots = split[8..-1]\n            info = {\n                :name => name,\n                :addr => addr,\n                :flags => flags.split(\",\"),\n                :replicate => master_id,\n                :ping_sent => ping_sent.to_i,\n                :ping_recv => ping_recv.to_i,\n                :link_status => link_status\n            }\n            info[:replicate] = false if master_id == \"-\"\n\n            if info[:flags].index(\"myself\")\n                @info = @info.merge(info)\n                @info[:slots] = {}\n                slots.each{|s|\n                    if s[0..0] == '['\n                        if s.index(\"->-\") # Migrating\n                            slot,dst = s[1..-1].split(\"->-\")\n                            @info[:migrating][slot] = dst\n                        elsif s.index(\"-<-\") # Importing\n                            slot,src = s[1..-1].split(\"-<-\")\n                            @info[:importing][slot] = src\n                        end\n                    elsif s.index(\"-\")\n                        start,stop = s.split(\"-\")\n                        self.add_slots((start.to_i)..(stop.to_i))\n                    else\n                        self.add_slots((s.to_i)..(s.to_i))\n                    end\n                } if slots\n                @dirty = false\n                @r.cluster(\"info\").split(\"\\n\").each{|e|    \n                    k,v=e.split(\":\")\n                    k = k.to_sym\n                    v.chop!\n                    if k != :cluster_state\n                        @info[k] = v.to_i\n                    else\n                        @info[k] = v\n                    end\n                }\n            elsif o[:getfriends]\n                @friends << info\n            end\n        }\n    end\n\n    def add_slots(slots)\n        slots.each{|s|\n            @info[:slots][s] = :new\n        }\n        @dirty = true\n    end\n\n    def set_as_replica(node_id)\n        @info[:replicate] = node_id\n        @dirty = true\n    end\n\n    def flush_node_config\n        return if !@dirty\n        if @info[:replicate]\n            begin\n                @r.cluster(\"replicate\",@info[:replicate])\n            rescue\n                # If the cluster did not already joined it is possible that\n                # the slave does not know the master node yet. So on errors\n                # we return ASAP leaving the dirty flag set, to flush the\n                # config later.\n                return\n            end\n        else\n            new = []\n            @info[:slots].each{|s,val|\n                if val == :new\n                    new << s\n                    @info[:slots][s] = true\n                end\n            }\n            @r.cluster(\"addslots\",*new)\n        end\n        @dirty = false\n    end\n\n    def info_string\n        # We want to display the hash slots assigned to this node\n        # as ranges, like in: \"1-5,8-9,20-25,30\"\n        #\n        # Note: this could be easily written without side effects,\n        # we use 'slots' just to split the computation into steps.\n        \n        # First step: we want an increasing array of integers\n        # for instance: [1,2,3,4,5,8,9,20,21,22,23,24,25,30]\n        slots = @info[:slots].keys.sort\n\n        # As we want to aggregate adjacent slots we convert all the\n        # slot integers into ranges (with just one element)\n        # So we have something like [1..1,2..2, ... and so forth.\n        slots.map!{|x| x..x}\n\n        # Finally we group ranges with adjacent elements.\n        slots = slots.reduce([]) {|a,b|\n            if !a.empty? && b.first == (a[-1].last)+1\n                a[0..-2] + [(a[-1].first)..(b.last)]\n            else\n                a + [b]\n            end\n        }\n\n        # Now our task is easy, we just convert ranges with just one\n        # element into a number, and a real range into a start-end format.\n        # Finally we join the array using the comma as separator.\n        slots = slots.map{|x|\n            x.count == 1 ? x.first.to_s : \"#{x.first}-#{x.last}\"\n        }.join(\",\")\n\n        role = self.has_flag?(\"master\") ? \"M\" : \"S\"\n\n        if self.info[:replicate] and @dirty\n            is = \"S: #{self.info[:name]} #{self.to_s}\"\n        else\n            is = \"#{role}: #{self.info[:name]} #{self.to_s}\\n\"+\n            \"   slots:#{slots} (#{self.slots.length} slots) \"+\n            \"#{(self.info[:flags]-[\"myself\"]).join(\",\")}\"\n        end\n        if self.info[:replicate]\n            is += \"\\n   replicates #{info[:replicate]}\"\n        elsif self.has_flag?(\"master\") && self.info[:replicas]\n            is += \"\\n   #{info[:replicas].length} additional replica(s)\"\n        end\n        is\n    end\n\n    # Return a single string representing nodes and associated slots.\n    # TODO: remove slaves from config when slaves will be handled\n    # by Redis Cluster.\n    def get_config_signature\n        config = []\n        @r.cluster(\"nodes\").each_line{|l|\n            s = l.split\n            slots = s[8..-1].select {|x| x[0..0] != \"[\"}\n            next if slots.length == 0\n            config << s[0]+\":\"+(slots.sort.join(\",\"))\n        }\n        config.sort.join(\"|\")\n    end\n\n    def info\n        @info\n    end\n    \n    def is_dirty?\n        @dirty\n    end\n\n    def r\n        @r\n    end\nend\n\nclass RedisTrib\n    def initialize\n        @nodes = []\n        @fix = false\n        @errors = []\n    end\n\n    def check_arity(req_args, num_args)\n        if ((req_args > 0 and num_args != req_args) ||\n           (req_args < 0 and num_args < req_args.abs))\n           xputs \"[ERR] Wrong number of arguments for specified sub command\"\n           exit 1\n        end\n    end\n\n    def add_node(node)\n        @nodes << node\n    end\n\n    def cluster_error(msg)\n        @errors << msg\n        xputs msg\n    end\n\n    def get_node_by_name(name)\n        @nodes.each{|n|\n            return n if n.info[:name] == name.downcase\n        }\n        return nil\n    end\n\n    # This function returns the master that has the least number of replicas\n    # in the cluster. If there are multiple masters with the same smaller\n    # number of replicas, one at random is returned.\n    def get_master_with_least_replicas\n        masters = @nodes.select{|n| n.has_flag? \"master\"}\n        sorted = masters.sort{|a,b|\n            a.info[:replicas].length <=> b.info[:replicas].length\n        }\n        sorted[0]\n    end\n\n    def check_cluster\n        xputs \">>> Performing Cluster Check (using node #{@nodes[0]})\"\n        show_nodes\n        check_config_consistency\n        check_open_slots\n        check_slots_coverage\n    end\n\n    # Merge slots of every known node. If the resulting slots are equal\n    # to ClusterHashSlots, then all slots are served.\n    def covered_slots\n        slots = {}\n        @nodes.each{|n|\n            slots = slots.merge(n.slots)\n        }\n        slots\n    end\n\n    def check_slots_coverage\n        xputs \">>> Check slots coverage...\"\n        slots = covered_slots\n        if slots.length == ClusterHashSlots\n            xputs \"[OK] All #{ClusterHashSlots} slots covered.\"\n        else\n            cluster_error \\\n                \"[ERR] Not all #{ClusterHashSlots} slots are covered by nodes.\"\n            fix_slots_coverage if @fix\n        end\n    end\n\n    def check_open_slots\n        xputs \">>> Check for open slots...\"\n        open_slots = []\n        @nodes.each{|n|\n            if n.info[:migrating].size > 0\n                cluster_error \\\n                    \"[WARNING] Node #{n} has slots in migrating state (#{n.info[:migrating].keys.join(\",\")}).\"\n                open_slots += n.info[:migrating].keys\n            elsif n.info[:importing].size > 0\n                cluster_error \\\n                    \"[WARNING] Node #{n} has slots in importing state (#{n.info[:importing].keys.join(\",\")}).\"\n                open_slots += n.info[:importing].keys\n            end\n        }\n        open_slots.uniq!\n        if open_slots.length > 0\n            xputs \"[WARNING] The following slots are open: #{open_slots.join(\",\")}\"\n        end\n        if @fix\n            open_slots.each{|slot| fix_open_slot slot}\n        end\n    end\n\n    def nodes_with_keys_in_slot(slot)\n        nodes = []\n        @nodes.each{|n|\n            nodes << n if n.r.cluster(\"getkeysinslot\",slot,1).length > 0\n        }\n        nodes\n    end\n\n    def fix_slots_coverage\n        not_covered = (0...ClusterHashSlots).to_a - covered_slots.keys\n        xputs \">>> Fixing slots coverage...\"\n        xputs \"List of not covered slots: \" + not_covered.join(\",\")\n\n        # For every slot, take action depending on the actual condition:\n        # 1) No node has keys for this slot.\n        # 2) A single node has keys for this slot.\n        # 3) Multiple nodes have keys for this slot.\n        slots = {}\n        not_covered.each{|slot|\n            nodes = nodes_with_keys_in_slot(slot)\n            slots[slot] = nodes\n            xputs \"Slot #{slot} has keys in #{nodes.length} nodes: #{nodes.join}\"\n        }\n\n        none = slots.select {|k,v| v.length == 0}\n        single = slots.select {|k,v| v.length == 1}\n        multi = slots.select {|k,v| v.length > 1}\n\n        # Handle case \"1\": keys in no node.\n        if none.length > 0\n            xputs \"The folowing uncovered slots have no keys across the cluster:\"\n            xputs none.keys.join(\",\")\n            yes_or_die \"Fix these slots by covering with a random node?\"\n            none.each{|slot,nodes|\n                node = @nodes.sample\n                xputs \">>> Covering slot #{slot} with #{node}\"\n                node.r.cluster(\"addslots\",slot)\n            }\n        end\n\n        # Handle case \"2\": keys only in one node.\n        if single.length > 0\n            xputs \"The folowing uncovered slots have keys in just one node:\"\n            puts single.keys.join(\",\")\n            yes_or_die \"Fix these slots by covering with those nodes?\"\n            single.each{|slot,nodes|\n                xputs \">>> Covering slot #{slot} with #{nodes[0]}\"\n                nodes[0].r.cluster(\"addslots\",slot)\n            }\n        end\n\n        # Handle case \"3\": keys in multiple nodes.\n        if multi.length > 0\n            xputs \"The folowing uncovered slots have keys in multiple nodes:\"\n            xputs multi.keys.join(\",\")\n            yes_or_die \"Fix these slots by moving keys into a single node?\"\n            multi.each{|slot,nodes|\n                xputs \">>> Covering slot #{slot} moving keys to #{nodes[0]}\"\n                # TODO\n                # 1) Set all nodes as \"MIGRATING\" for this slot, so that we\n                # can access keys in the hash slot using ASKING.\n                # 2) Move everything to node[0]\n                # 3) Clear MIGRATING from nodes, and ADDSLOTS the slot to\n                # node[0].\n                raise \"TODO: Work in progress\"\n            }\n        end\n    end\n\n    # Slot 'slot' was found to be in importing or migrating state in one or\n    # more nodes. This function fixes this condition by migrating keys where\n    # it seems more sensible.\n    def fix_open_slot(slot)\n        migrating = []\n        importing = []\n        @nodes.each{|n|\n            next if n.has_flag? \"slave\"\n            if n.info[:migrating][slot]\n                migrating << n\n            elsif n.info[:importing][slot]\n                importing << n\n            elsif n.r.cluster(\"countkeysinslot\",slot) > 0\n                xputs \"*** Found keys about slot #{slot} in node #{n}!\"\n            end\n        }\n        puts \">>> Fixing open slot #{slot}\"\n        puts \"Set as migrating in: #{migrating.join(\",\")}\"\n        puts \"Set as importing in: #{importing.join(\",\")}\"\n\n        # Case 1: The slot is in migrating state in one slot, and in\n        #         importing state in 1 slot. That's trivial to address.\n        if migrating.length == 1 && importing.length == 1\n            move_slot(migrating[0],importing[0],slot,:verbose=>true)\n        elsif migrating.length == 1 && importing.length == 0\n            xputs \">>> Setting #{slot} as STABLE\"\n            migrating[0].r.cluster(\"setslot\",slot,\"stable\")\n        elsif migrating.length == 0 && importing.length == 1\n            xputs \">>> Setting #{slot} as STABLE\"\n            importing[0].r.cluster(\"setslot\",slot,\"stable\")\n        else\n            xputs \"[ERR] Sorry, Redis-trib can't fix this slot yet (work in progress)\"\n        end\n    end\n\n    # Check if all the nodes agree about the cluster configuration\n    def check_config_consistency\n        if !is_config_consistent?\n            cluster_error \"[ERR] Nodes don't agree about configuration!\"\n        else\n            xputs \"[OK] All nodes agree about slots configuration.\"\n        end\n    end\n\n    def is_config_consistent?\n        signatures=[]\n        @nodes.each{|n|\n            signatures << n.get_config_signature\n        }\n        return signatures.uniq.length == 1\n    end\n\n    def wait_cluster_join\n        print \"Waiting for the cluster to join\"\n        while !is_config_consistent?\n            print \".\"\n            STDOUT.flush\n            sleep 1\n        end\n        print \"\\n\"\n    end\n\n    def alloc_slots\n        nodes_count = @nodes.length\n        masters_count = @nodes.length / (@replicas+1)\n        masters = []\n        slaves = []\n\n        # The first step is to split instances by IP. This is useful as\n        # we'll try to allocate master nodes in different physical machines\n        # (as much as possible) and to allocate slaves of a given master in\n        # different physical machines as well.\n        #\n        # This code assumes just that if the IP is different, than it is more\n        # likely that the instance is running in a different physical host\n        # or at least a different virtual machine.\n        ips = {}\n        @nodes.each{|n|\n            ips[n.info[:host]] = [] if !ips[n.info[:host]]\n            ips[n.info[:host]] << n\n        }\n\n        # Select master instances\n        puts \"Using #{masters_count} masters:\"\n        while masters.length < masters_count\n            ips.each{|ip,nodes_list|\n                next if nodes_list.length == 0\n                masters << nodes_list.shift\n                puts masters[-1]\n                nodes_count -= 1\n                break if masters.length == masters_count\n            }\n        end\n\n        # Alloc slots on masters\n        slots_per_node = ClusterHashSlots.to_f / masters_count\n        first = 0\n        cursor = 0.0\n        masters.each_with_index{|n,masternum|\n            last = (cursor+slots_per_node-1).round\n            if last > ClusterHashSlots || masternum == masters.length-1\n                last = ClusterHashSlots-1\n            end\n            last = first if last < first # Min step is 1.\n            n.add_slots first..last\n            first = last+1\n            cursor += slots_per_node\n        }\n\n        # Select N replicas for every master.\n        # We try to split the replicas among all the IPs with spare nodes\n        # trying to avoid the host where the master is running, if possible.\n        #\n        # Note we loop two times.  The first loop assigns the requested\n        # number of replicas to each master.  The second loop assigns any\n        # remaining instances as extra replicas to masters.  Some masters\n        # may end up with more than their requested number of replicas, but\n        # all nodes will be used.\n        assignment_verbose = false\n\n        [:requested,:unused].each{|assign|\n            masters.each{|m|\n                assigned_replicas = 0\n                while assigned_replicas < @replicas\n                    break if nodes_count == 0\n                    if assignment_verbose\n                        if assign == :requested\n                            puts \"Requesting total of #{@replicas} replicas \" \\\n                                 \"(#{assigned_replicas} replicas assigned \" \\\n                                 \"so far with #{nodes_count} total remaining).\"\n                        elsif assign == :unused\n                            puts \"Assigning extra instance to replication \" \\\n                                 \"role too (#{nodes_count} remaining).\"\n                        end\n                    end\n                    ips.each{|ip,nodes_list|\n                        next if nodes_list.length == 0\n                        # Skip instances with the same IP as the master if we\n                        # have some more IPs available.\n                        next if ip == m.info[:host] && nodes_count > nodes_list.length\n                        slave = nodes_list.shift\n                        slave.set_as_replica(m.info[:name])\n                        nodes_count -= 1\n                        assigned_replicas += 1\n                        puts \"Adding replica #{slave} to #{m}\"\n                        break\n                    }\n                end\n            }\n        }\n    end\n\n    def flush_nodes_config\n        @nodes.each{|n|\n            n.flush_node_config\n        }\n    end\n\n    def show_nodes\n        @nodes.each{|n|\n            xputs n.info_string\n        }\n    end\n\n    # Redis Cluster config epoch collision resolution code is able to eventually\n    # set a different epoch to each node after a new cluster is created, but\n    # it is slow compared to assign a progressive config epoch to each node\n    # before joining the cluster. However we do just a best-effort try here\n    # since if we fail is not a problem.\n    def assign_config_epoch\n        config_epoch = 1\n        @nodes.each{|n|\n            begin\n                n.r.cluster(\"set-config-epoch\",config_epoch)\n            rescue\n            end\n            config_epoch += 1\n        }\n    end\n\n    def join_cluster\n        # We use a brute force approach to make sure the node will meet\n        # each other, that is, sending CLUSTER MEET messages to all the nodes\n        # about the very same node.\n        # Thanks to gossip this information should propagate across all the\n        # cluster in a matter of seconds.\n        first = false\n        @nodes.each{|n|\n            if !first then first = n.info; next; end # Skip the first node\n            n.r.cluster(\"meet\",first[:host],first[:port])\n        }\n    end\n\n    def yes_or_die(msg)\n        print \"#{msg} (type 'yes' to accept): \"\n        STDOUT.flush\n        if !(STDIN.gets.chomp.downcase == \"yes\")\n            xputs \"*** Aborting...\"\n            exit 1\n        end\n    end\n\n    def load_cluster_info_from_node(nodeaddr)\n        node = ClusterNode.new(nodeaddr)\n        node.connect(:abort => true)\n        node.assert_cluster\n        node.load_info(:getfriends => true)\n        add_node(node)\n        node.friends.each{|f|\n            next if f[:flags].index(\"noaddr\") ||\n                    f[:flags].index(\"disconnected\") ||\n                    f[:flags].index(\"fail\")\n            fnode = ClusterNode.new(f[:addr])\n            fnode.connect()\n            fnode.load_info()\n            add_node(fnode)\n        }\n        populate_nodes_replicas_info\n    end\n\n    # This function is called by load_cluster_info_from_node in order to\n    # add additional information to every node as a list of replicas.\n    def populate_nodes_replicas_info\n        # Start adding the new field to every node.\n        @nodes.each{|n|\n            n.info[:replicas] = []\n        }\n\n        # Populate the replicas field using the replicate field of slave\n        # nodes.\n        @nodes.each{|n|\n            if n.info[:replicate]\n                master = get_node_by_name(n.info[:replicate])\n                if !master\n                    xputs \"*** WARNING: #{n} claims to be slave of unknown node ID #{n.info[:replicate]}.\"\n                else\n                    master.info[:replicas] << n\n                end\n            end\n        }\n    end\n\n    # Given a list of source nodes return a \"resharding plan\"\n    # with what slots to move in order to move \"numslots\" slots to another\n    # instance.\n    def compute_reshard_table(sources,numslots)\n        moved = []\n        # Sort from bigger to smaller instance, for two reasons:\n        # 1) If we take less slots than instances it is better to start\n        #    getting from the biggest instances.\n        # 2) We take one slot more from the first instance in the case of not\n        #    perfect divisibility. Like we have 3 nodes and need to get 10\n        #    slots, we take 4 from the first, and 3 from the rest. So the\n        #    biggest is always the first.\n        sources = sources.sort{|a,b| b.slots.length <=> a.slots.length}\n        source_tot_slots = sources.inject(0) {|sum,source|\n            sum+source.slots.length\n        }\n        sources.each_with_index{|s,i|\n            # Every node will provide a number of slots proportional to the\n            # slots it has assigned.\n            n = (numslots.to_f/source_tot_slots*s.slots.length)\n            if i == 0\n                n = n.ceil\n            else\n                n = n.floor\n            end\n            s.slots.keys.sort[(0...n)].each{|slot|\n                if moved.length < numslots\n                    moved << {:source => s, :slot => slot}\n                end\n            }\n        }\n        return moved\n    end\n\n    def show_reshard_table(table)\n        table.each{|e|\n            puts \"    Moving slot #{e[:slot]} from #{e[:source].info[:name]}\"\n        }\n    end\n\n    def move_slot(source,target,slot,o={})\n        # We start marking the slot as importing in the destination node,\n        # and the slot as migrating in the target host. Note that the order of\n        # the operations is important, as otherwise a client may be redirected\n        # to the target node that does not yet know it is importing this slot.\n        print \"Moving slot #{slot} from #{source} to #{target}: \"; STDOUT.flush\n        target.r.cluster(\"setslot\",slot,\"importing\",source.info[:name])\n        source.r.cluster(\"setslot\",slot,\"migrating\",target.info[:name])\n        # Migrate all the keys from source to target using the MIGRATE command\n        while true\n            keys = source.r.cluster(\"getkeysinslot\",slot,10)\n            break if keys.length == 0\n            keys.each{|key|\n                source.r.client.call([\"migrate\",target.info[:host],target.info[:port],key,0,15000])\n                print \".\" if o[:verbose]\n                STDOUT.flush\n            }\n        end\n        puts\n        # Set the new node as the owner of the slot in all the known nodes.\n        @nodes.each{|n|\n            n.r.cluster(\"setslot\",slot,\"node\",target.info[:name])\n        }\n    end\n\n    # redis-trib subcommands implementations\n\n    def check_cluster_cmd(argv,opt)\n        load_cluster_info_from_node(argv[0])\n        check_cluster\n    end\n\n    def fix_cluster_cmd(argv,opt)\n        @fix = true\n        load_cluster_info_from_node(argv[0])\n        check_cluster\n    end\n\n    def reshard_cluster_cmd(argv,opt)\n        load_cluster_info_from_node(argv[0])\n        check_cluster\n        if @errors.length != 0\n            puts \"*** Please fix your cluster problems before resharding\"\n            exit 1\n        end\n        numslots = 0\n        while numslots <= 0 or numslots > ClusterHashSlots\n            print \"How many slots do you want to move (from 1 to #{ClusterHashSlots})? \"\n            numslots = STDIN.gets.to_i\n        end\n        target = nil\n        while not target\n            print \"What is the receiving node ID? \"\n            target = get_node_by_name(STDIN.gets.chop)\n            if !target || target.has_flag?(\"slave\")\n                xputs \"*** The specified node is not known or not a master, please retry.\"\n                target = nil\n            end\n        end\n        sources = []\n        xputs \"Please enter all the source node IDs.\"\n        xputs \"  Type 'all' to use all the nodes as source nodes for the hash slots.\"\n        xputs \"  Type 'done' once you entered all the source nodes IDs.\"\n        while true\n            print \"Source node ##{sources.length+1}:\"\n            line = STDIN.gets.chop\n            src = get_node_by_name(line)\n            if line == \"done\"\n                if sources.length == 0\n                    puts \"No source nodes given, operation aborted\"\n                    exit 1\n                else\n                    break\n                end\n            elsif line == \"all\"\n                @nodes.each{|n|\n                    next if n.info[:name] == target.info[:name]\n                    next if n.has_flag?(\"slave\")\n                    sources << n\n                }\n                break\n            elsif !src || src.has_flag?(\"slave\")\n                xputs \"*** The specified node is not known or is not a master, please retry.\"\n            elsif src.info[:name] == target.info[:name]\n                xputs \"*** It is not possible to use the target node as source node.\"\n            else\n                sources << src\n            end\n        end\n        puts \"\\nReady to move #{numslots} slots.\"\n        puts \"  Source nodes:\"\n        sources.each{|s| puts \"    \"+s.info_string}\n        puts \"  Destination node:\"\n        puts \"    #{target.info_string}\"\n        reshard_table = compute_reshard_table(sources,numslots)\n        puts \"  Resharding plan:\"\n        show_reshard_table(reshard_table)\n        print \"Do you want to proceed with the proposed reshard plan (yes/no)? \"\n        yesno = STDIN.gets.chop\n        exit(1) if (yesno != \"yes\")\n        reshard_table.each{|e|\n            move_slot(e[:source],target,e[:slot],:verbose=>true)\n        }\n    end\n\n    # This is an helper function for create_cluster_cmd that verifies if\n    # the number of nodes and the specified replicas have a valid configuration\n    # where there are at least three master nodes and enough replicas per node.\n    def check_create_parameters\n        masters = @nodes.length/(@replicas+1)\n        if masters < 3\n            puts \"*** ERROR: Invalid configuration for cluster creation.\"\n            puts \"*** Redis Cluster requires at least 3 master nodes.\"\n            puts \"*** This is not possible with #{@nodes.length} nodes and #{@replicas} replicas per node.\"\n            puts \"*** At least #{3*(@replicas+1)} nodes are required.\"\n            exit 1\n        end\n    end\n\n    def create_cluster_cmd(argv,opt)\n        opt = {'replicas' => 0}.merge(opt)\n        @replicas = opt['replicas'].to_i\n\n        xputs \">>> Creating cluster\"\n        argv[0..-1].each{|n|\n            node = ClusterNode.new(n)\n            node.connect(:abort => true)\n            node.assert_cluster\n            node.load_info\n            node.assert_empty\n            add_node(node)\n        }\n        check_create_parameters\n        xputs \">>> Performing hash slots allocation on #{@nodes.length} nodes...\"\n        alloc_slots\n        show_nodes\n        yes_or_die \"Can I set the above configuration?\"\n        flush_nodes_config\n        xputs \">>> Nodes configuration updated\"\n        xputs \">>> Assign a different config epoch to each node\"\n        assign_config_epoch\n        xputs \">>> Sending CLUSTER MEET messages to join the cluster\"\n        join_cluster\n        # Give one second for the join to start, in order to avoid that\n        # wait_cluster_join will find all the nodes agree about the config as\n        # they are still empty with unassigned slots.\n        sleep 1\n        wait_cluster_join\n        flush_nodes_config # Useful for the replicas\n        check_cluster\n    end\n\n    def addnode_cluster_cmd(argv,opt)\n        xputs \">>> Adding node #{argv[0]} to cluster #{argv[1]}\"\n\n        # Check the existing cluster\n        load_cluster_info_from_node(argv[1])\n        check_cluster\n\n        # If --master-id was specified, try to resolve it now so that we\n        # abort before starting with the node configuration.\n        if opt['slave']\n            if opt['master-id']\n                master = get_node_by_name(opt['master-id'])\n                if !master\n                    xputs \"[ERR] No such master ID #{opt['master-id']}\"\n                end\n            else\n                master = get_master_with_least_replicas\n                xputs \"Automatically selected master #{master}\"\n            end\n        end\n\n        # Add the new node\n        new = ClusterNode.new(argv[0])\n        new.connect(:abort => true)\n        new.assert_cluster\n        new.load_info\n        new.assert_empty\n        first = @nodes.first.info\n        add_node(new)\n\n        # Send CLUSTER MEET command to the new node\n        xputs \">>> Send CLUSTER MEET to node #{new} to make it join the cluster.\"\n        new.r.cluster(\"meet\",first[:host],first[:port])\n\n        # Additional configuration is needed if the node is added as\n        # a slave.\n        if opt['slave']\n            wait_cluster_join\n            xputs \">>> Configure node as replica of #{master}.\"\n            new.r.cluster(\"replicate\",master.info[:name])\n        end\n        xputs \"[OK] New node added correctly.\"\n    end\n\n    def delnode_cluster_cmd(argv,opt)\n        id = argv[1].downcase\n        xputs \">>> Removing node #{id} from cluster #{argv[0]}\"\n\n        # Load cluster information\n        load_cluster_info_from_node(argv[0])\n\n        # Check if the node exists and is not empty\n        node = get_node_by_name(id)\n\n        if !node\n            xputs \"[ERR] No such node ID #{id}\"\n            exit 1\n        end\n\n        if node.slots.length != 0\n            xputs \"[ERR] Node #{node} is not empty! Reshard data away and try again.\"\n            exit 1\n        end\n\n        # Send CLUSTER FORGET to all the nodes but the node to remove\n        xputs \">>> Sending CLUSTER FORGET messages to the cluster...\"\n        @nodes.each{|n|\n            next if n == node\n            if n.info[:replicate] && n.info[:replicate].downcase == id\n                # Reconfigure the slave to replicate with some other node\n                master = get_master_with_least_replicas\n                xputs \">>> #{n} as replica of #{master}\"\n                n.r.cluster(\"replicate\",master.info[:name])\n            end\n            n.r.cluster(\"forget\",argv[1])\n        }\n\n        # Finally shutdown the node\n        xputs \">>> SHUTDOWN the node.\"\n        node.r.shutdown\n    end\n\n    def set_timeout_cluster_cmd(argv,opt)\n        timeout = argv[1].to_i\n        if timeout < 100\n            puts \"Setting a node timeout of less than 100 milliseconds is a bad idea.\"\n            exit 1\n        end\n\n        # Load cluster information\n        load_cluster_info_from_node(argv[0])\n        ok_count = 0\n        err_count = 0\n\n        # Send CLUSTER FORGET to all the nodes but the node to remove\n        xputs \">>> Reconfiguring node timeout in every cluster node...\"\n        @nodes.each{|n|\n            begin\n                n.r.config(\"set\",\"cluster-node-timeout\",timeout)\n                n.r.config(\"rewrite\")\n                ok_count += 1\n                xputs \"*** New timeout set for #{n}\"\n            rescue => e\n                puts \"ERR setting node-timeot for #{n}: #{e}\"\n                err_count += 1\n            end\n        }\n        xputs \">>> New node timeout set. #{ok_count} OK, #{err_count} ERR.\"\n    end\n\n    def call_cluster_cmd(argv,opt)\n        cmd = argv[1..-1]\n        cmd[0] = cmd[0].upcase\n\n        # Load cluster information\n        load_cluster_info_from_node(argv[0])\n        xputs \">>> Calling #{cmd.join(\" \")}\"\n        @nodes.each{|n|\n            begin\n                res = n.r.send(*cmd)\n                puts \"#{n}: #{res}\"\n            rescue => e\n                puts \"#{n}: #{e}\"\n            end\n        }\n    end\n\n    def import_cluster_cmd(argv,opt)\n        source_addr = opt['from']\n        xputs \">>> Importing data from #{source_addr} to cluster #{argv[1]}\"\n\n        # Check the existing cluster.\n        load_cluster_info_from_node(argv[0])\n        check_cluster\n\n        # Connect to the source node.\n        xputs \">>> Connecting to the source Redis instance\"\n        src_host,src_port = source_addr.split(\":\")\n        source = Redis.new(:host =>src_host, :port =>src_port)\n        if source.info['cluster_enabled'].to_i == 1\n            xputs \"[ERR] The source node should not be a cluster node.\"\n        end\n        xputs \"*** Importing #{source.dbsize} keys from DB 0\"\n\n        # Build a slot -> node map\n        slots = {}\n        @nodes.each{|n|\n            n.slots.each{|s,_|\n                slots[s] = n\n            }\n        }\n\n        # Use SCAN to iterate over the keys, migrating to the\n        # right node as needed.\n        cursor = nil\n        while cursor != 0\n            cursor,keys = source.scan(cursor,:count,1000)\n            cursor = cursor.to_i\n            keys.each{|k|\n                # Migrate keys using the MIGRATE command.\n                slot = key_to_slot(k)\n                target = slots[slot]\n                print \"Migrating #{k} to #{target}: \"\n                STDOUT.flush\n                begin\n                    source.client.call([\"migrate\",target.info[:host],target.info[:port],k,0,15000])\n                rescue => e\n                    puts e\n                else\n                    puts \"OK\"\n                end\n            }\n        end\n    end\n\n    def help_cluster_cmd(argv,opt)\n        show_help\n        exit 0\n    end\n\n    # Parse the options for the specific command \"cmd\".\n    # Returns an hash populate with option => value pairs, and the index of\n    # the first non-option argument in ARGV.\n    def parse_options(cmd)\n        idx = 1 ; # Current index into ARGV\n        options={}\n        while idx < ARGV.length && ARGV[idx][0..1] == '--'\n            if ARGV[idx][0..1] == \"--\"\n                option = ARGV[idx][2..-1]\n                idx += 1\n                if ALLOWED_OPTIONS[cmd] == nil || ALLOWED_OPTIONS[cmd][option] == nil\n                    puts \"Unknown option '#{option}' for command '#{cmd}'\"\n                    exit 1\n                end\n                if ALLOWED_OPTIONS[cmd][option]\n                    value = ARGV[idx]\n                    idx += 1\n                else\n                    value = true\n                end\n                options[option] = value\n            else\n                # Remaining arguments are not options.\n                break\n            end\n        end\n\n        # Enforce mandatory options\n        if ALLOWED_OPTIONS[cmd]\n            ALLOWED_OPTIONS[cmd].each {|option,val|\n                if !options[option] && val == :required\n                    puts \"Option '--#{option}' is required \"+ \\\n                         \"for subcommand '#{cmd}'\"\n                    exit 1\n                end\n            }\n        end\n        return options,idx\n    end\nend\n\n#################################################################################\n# Libraries\n# \n# We try to don't depend on external libs since this is a critical part\n# of Redis Cluster.\n#################################################################################\n\n# This is the CRC16 algorithm used by Redis Cluster to hash keys.\n# Implementation according to CCITT standards.\n#\n# This is actually the XMODEM CRC 16 algorithm, using the\n# following parameters:\n#\n# Name                       : \"XMODEM\", also known as \"ZMODEM\", \"CRC-16/ACORN\"\n# Width                      : 16 bit\n# Poly                       : 1021 (That is actually x^16 + x^12 + x^5 + 1)\n# Initialization             : 0000\n# Reflect Input byte         : False\n# Reflect Output CRC         : False\n# Xor constant to output CRC : 0000\n# Output for \"123456789\"     : 31C3\n\nmodule RedisClusterCRC16\n    def RedisClusterCRC16.crc16(bytes)\n        crc = 0\n        bytes.each_byte{|b|\n            crc = ((crc<<8) & 0xffff) ^ XMODEMCRC16Lookup[((crc>>8)^b) & 0xff]\n        }\n        crc\n    end\n\nprivate\n    XMODEMCRC16Lookup = [\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    ]\nend\n\n# Turn a key name into the corrisponding Redis Cluster slot.\ndef key_to_slot(key)\n    # Only hash what is inside {...} if there is such a pattern in the key.\n    # Note that the specification requires the content that is between\n    # the first { and the first } after the first {. If we found {} without\n    # nothing in the middle, the whole key is hashed as usually.\n    s = key.index \"{\"\n    if s\n        e = key.index \"}\",s+1\n        if e && e != s+1\n            key = key[s+1..e-1]\n        end\n    end\n    RedisClusterCRC16.crc16(key) % 16384\nend\n\n#################################################################################\n# Definition of commands\n#################################################################################\n\nCOMMANDS={\n    \"create\"  => [\"create_cluster_cmd\", -2, \"host1:port1 ... hostN:portN\"],\n    \"check\"   => [\"check_cluster_cmd\", 2, \"host:port\"],\n    \"fix\"     => [\"fix_cluster_cmd\", 2, \"host:port\"],\n    \"reshard\" => [\"reshard_cluster_cmd\", 2, \"host:port\"],\n    \"add-node\" => [\"addnode_cluster_cmd\", 3, \"new_host:new_port existing_host:existing_port\"],\n    \"del-node\" => [\"delnode_cluster_cmd\", 3, \"host:port node_id\"],\n    \"set-timeout\" => [\"set_timeout_cluster_cmd\", 3, \"host:port milliseconds\"],\n    \"call\" =>    [\"call_cluster_cmd\", -3, \"host:port command arg arg .. arg\"],\n    \"import\" =>  [\"import_cluster_cmd\", 2, \"host:port\"],\n    \"help\"    => [\"help_cluster_cmd\", 1, \"(show this help)\"]\n}\n\nALLOWED_OPTIONS={\n    \"create\" => {\"replicas\" => true},\n    \"add-node\" => {\"slave\" => false, \"master-id\" => true},\n    \"import\" => {\"from\" => :required}\n}\n\ndef show_help\n    puts \"Usage: redis-trib <command> <options> <arguments ...>\\n\\n\"\n    COMMANDS.each{|k,v|\n        o = \"\"\n        puts \"  #{k.ljust(15)} #{v[2]}\"\n        if ALLOWED_OPTIONS[k]\n            ALLOWED_OPTIONS[k].each{|optname,has_arg|\n                puts \"                  --#{optname}\" + (has_arg ? \" <arg>\" : \"\")\n            }\n        end\n    }\n    puts \"\\nFor check, fix, reshard, del-node, set-timeout you can specify the host and port of any working node in the cluster.\\n\"\nend\n\n# Sanity check\nif ARGV.length == 0\n    show_help\n    exit 1\nend\n\nrt = RedisTrib.new\ncmd_spec = COMMANDS[ARGV[0].downcase]\nif !cmd_spec\n    puts \"Unknown redis-trib subcommand '#{ARGV[0]}'\"\n    exit 1\nend\n\n# Parse options\ncmd_options,first_non_option = rt.parse_options(ARGV[0].downcase)\nrt.check_arity(cmd_spec[1],ARGV.length-(first_non_option-1))\n\n# Dispatch\nrt.send(cmd_spec[0],ARGV[first_non_option..-1],cmd_options)\n"
  },
  {
    "path": "src/redis.c",
    "content": "/*\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 \"redis.h\"\n#include \"cluster.h\"\n#include \"slowlog.h\"\n#include \"bio.h\"\n\n#include <time.h>\n#include <signal.h>\n#include <sys/wait.h>\n#include <errno.h>\n#include <assert.h>\n#include <ctype.h>\n#include <stdarg.h>\n#include <arpa/inet.h>\n#include <sys/stat.h>\n#include <fcntl.h>\n#include <sys/time.h>\n#include <sys/resource.h>\n#include <sys/uio.h>\n#include <limits.h>\n#include <float.h>\n#include <math.h>\n#include <sys/resource.h>\n#include <sys/utsname.h>\n#include <locale.h>\n\n/* Our shared \"common\" objects */\n\nstruct sharedObjectsStruct shared;\n\n/* Global vars that are actually used as constants. The following double\n * values are used for double on-disk serialization, and are initialized\n * at runtime to avoid strange compiler optimizations. */\n\ndouble R_Zero, R_PosInf, R_NegInf, R_Nan;\n\n/*================================= Globals ================================= */\n\n/* Global vars */\nstruct redisServer server; /* server global state */\nstruct redisCommand *commandTable;\n\n/* Our command table.\n *\n * 命令表\n *\n * Every entry is composed of the following fields:\n *\n * 表中的每个项都由以下域组成：\n *\n * name: a string representing the command name.\n *       命令的名字\n *\n * function: pointer to the C function implementing the command.\n *           一个指向命令的实现函数的指针\n *\n * arity: number of arguments, it is possible to use -N to say >= N\n *        参数的数量。可以用 -N 表示 >= N \n *\n * sflags: command flags as string. See below for a table of flags.\n *         字符串形式的 FLAG ，用来计算以下的真实 FLAG \n *\n * flags: flags as bitmask. Computed by Redis using the 'sflags' field.\n *        位掩码形式的 FLAG ，根据 sflags 的字符串计算得出\n *\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 *                一个可选的函数，用于从命令中取出 key 参数，仅在以下三个参数都不足以表示 key 参数时使用\n *\n * first_key_index: first argument that is a key\n *                  第一个 key 参数的位置\n *\n * last_key_index: last argument that is a key\n *                 最后一个 key 参数的位置\n *\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 *           从 first 参数和 last 参数之间，所有 key 的步数（step）\n *           比如说， MSET 命令的格式为 MSET key value [key value ...]\n *           它的 step 就为 2\n *\n * microseconds: microseconds of total execution time for this command.\n *               执行这个命令耗费的总微秒数\n *\n * calls: total number of calls of this command.\n *        命令被执行的总次数\n *\n * The flags, microseconds and calls fields are computed by Redis and should\n * always be set to zero.\n *\n * microseconds 和 call 由 Redis 计算，总是初始化为 0 。\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 * 命令的 FLAG 首先由 SFLAG 域设置，之后 populateCommandTable() 函数从 sflags 属性中计算出真正的 FLAG 到 flags 属性中。\n *\n * This is the meaning of the flags:\n *\n * 以下是各个 FLAG 的意义：\n *\n * w: write command (may modify the key space).\n *    写入命令，可能会修改 key space\n *\n * r: read command  (will never modify the key space).\n *    读命令，不修改 key space\n * m: may increase memory usage once called. Don't allow if out of memory.\n *    可能会占用大量内存的命令，调用时对内存占用进行检查\n *\n * a: admin command, like SAVE or SHUTDOWN.\n *    管理用途的命令，比如 SAVE 和 SHUTDOWN\n *\n * p: Pub/Sub related command.\n *    发布/订阅相关的命令\n *\n * f: force replication of this command, regardless of server.dirty.\n *    无视 server.dirty ，强制复制这个命令。\n *\n * s: command not allowed in scripts.\n *    不允许在脚本中使用的命令\n *\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 *    随机命令。\n *    命令是非确定性的：对于同样的命令，同样的参数，同样的键，结果可能不同。\n *    比如 SPOP 和 RANDOMKEY 就是这样的例子。\n *\n * S: Sort command output array if called from script, so that the output\n *    is deterministic.\n *    如果命令在 Lua 脚本中执行，那么对输出进行排序，从而得出确定性的输出。\n *\n * l: Allow command while loading the database.\n *    允许在载入数据库时使用的命令。\n *\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 *    允许在附属节点带有过期数据时执行的命令。\n *    这类命令很少有，只有几个。\n *\n * M: Do not automatically propagate the command on MONITOR.\n *    不要在 MONITOR 模式下自动广播的命令。\n *\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 *    为这个命令执行一个显式的 ASKING ，\n *    使得在集群模式下，一个被标示为 importing 的槽可以接收这命令。\n */\nstruct redisCommand redisCommandTable[] = {\n    {\"get\",getCommand,2,\"r\",0,NULL,1,1,1,0,0},\n    {\"set\",setCommand,-3,\"wm\",0,NULL,1,1,1,0,0},\n    {\"setnx\",setnxCommand,3,\"wm\",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    {\"append\",appendCommand,3,\"wm\",0,NULL,1,1,1,0,0},\n    {\"strlen\",strlenCommand,2,\"r\",0,NULL,1,1,1,0,0},\n    {\"del\",delCommand,-2,\"w\",0,NULL,1,-1,1,0,0},\n    {\"exists\",existsCommand,2,\"r\",0,NULL,1,1,1,0,0},\n    {\"setbit\",setbitCommand,4,\"wm\",0,NULL,1,1,1,0,0},\n    {\"getbit\",getbitCommand,3,\"r\",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    {\"substr\",getrangeCommand,4,\"r\",0,NULL,1,1,1,0,0},\n    {\"incr\",incrCommand,2,\"wm\",0,NULL,1,1,1,0,0},\n    {\"decr\",decrCommand,2,\"wm\",0,NULL,1,1,1,0,0},\n    {\"mget\",mgetCommand,-2,\"r\",0,NULL,1,-1,1,0,0},\n    {\"rpush\",rpushCommand,-3,\"wm\",0,NULL,1,1,1,0,0},\n    {\"lpush\",lpushCommand,-3,\"wm\",0,NULL,1,1,1,0,0},\n    {\"rpushx\",rpushxCommand,3,\"wm\",0,NULL,1,1,1,0,0},\n    {\"lpushx\",lpushxCommand,3,\"wm\",0,NULL,1,1,1,0,0},\n    {\"linsert\",linsertCommand,5,\"wm\",0,NULL,1,1,1,0,0},\n    {\"rpop\",rpopCommand,2,\"w\",0,NULL,1,1,1,0,0},\n    {\"lpop\",lpopCommand,2,\"w\",0,NULL,1,1,1,0,0},\n    {\"brpop\",brpopCommand,-3,\"ws\",0,NULL,1,1,1,0,0},\n    {\"brpoplpush\",brpoplpushCommand,4,\"wms\",0,NULL,1,2,1,0,0},\n    {\"blpop\",blpopCommand,-3,\"ws\",0,NULL,1,-2,1,0,0},\n    {\"llen\",llenCommand,2,\"r\",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    {\"lrange\",lrangeCommand,4,\"r\",0,NULL,1,1,1,0,0},\n    {\"ltrim\",ltrimCommand,4,\"w\",0,NULL,1,1,1,0,0},\n    {\"lrem\",lremCommand,4,\"w\",0,NULL,1,1,1,0,0},\n    {\"rpoplpush\",rpoplpushCommand,3,\"wm\",0,NULL,1,2,1,0,0},\n    {\"sadd\",saddCommand,-3,\"wm\",0,NULL,1,1,1,0,0},\n    {\"srem\",sremCommand,-3,\"w\",0,NULL,1,1,1,0,0},\n    {\"smove\",smoveCommand,4,\"w\",0,NULL,1,2,1,0,0},\n    {\"sismember\",sismemberCommand,3,\"r\",0,NULL,1,1,1,0,0},\n    {\"scard\",scardCommand,2,\"r\",0,NULL,1,1,1,0,0},\n    {\"spop\",spopCommand,2,\"wRs\",0,NULL,1,1,1,0,0},\n    {\"srandmember\",srandmemberCommand,-2,\"rR\",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    {\"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    {\"smembers\",sinterCommand,2,\"rS\",0,NULL,1,1,1,0,0},\n    {\"sscan\",sscanCommand,-3,\"rR\",0,NULL,1,1,1,0,0},\n    {\"zadd\",zaddCommand,-4,\"wm\",0,NULL,1,1,1,0,0},\n    {\"zincrby\",zincrbyCommand,4,\"wm\",0,NULL,1,1,1,0,0},\n    {\"zrem\",zremCommand,-3,\"w\",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    {\"zunionstore\",zunionstoreCommand,-4,\"wm\",0,zunionInterGetKeys,0,0,0,0,0},\n    {\"zinterstore\",zinterstoreCommand,-4,\"wm\",0,zunionInterGetKeys,0,0,0,0,0},\n    {\"zrange\",zrangeCommand,-4,\"r\",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    {\"zrangebylex\",zrangebylexCommand,-4,\"r\",0,NULL,1,1,1,0,0},\n    {\"zrevrangebylex\",zrevrangebylexCommand,-4,\"r\",0,NULL,1,1,1,0,0},\n    {\"zcount\",zcountCommand,4,\"r\",0,NULL,1,1,1,0,0},\n    {\"zlexcount\",zlexcountCommand,4,\"r\",0,NULL,1,1,1,0,0},\n    {\"zrevrange\",zrevrangeCommand,-4,\"r\",0,NULL,1,1,1,0,0},\n    {\"zcard\",zcardCommand,2,\"r\",0,NULL,1,1,1,0,0},\n    {\"zscore\",zscoreCommand,3,\"r\",0,NULL,1,1,1,0,0},\n    {\"zrank\",zrankCommand,3,\"r\",0,NULL,1,1,1,0,0},\n    {\"zrevrank\",zrevrankCommand,3,\"r\",0,NULL,1,1,1,0,0},\n    {\"zscan\",zscanCommand,-3,\"rR\",0,NULL,1,1,1,0,0},\n    {\"hset\",hsetCommand,4,\"wm\",0,NULL,1,1,1,0,0},\n    {\"hsetnx\",hsetnxCommand,4,\"wm\",0,NULL,1,1,1,0,0},\n    {\"hget\",hgetCommand,3,\"r\",0,NULL,1,1,1,0,0},\n    {\"hmset\",hmsetCommand,-4,\"wm\",0,NULL,1,1,1,0,0},\n    {\"hmget\",hmgetCommand,-3,\"r\",0,NULL,1,1,1,0,0},\n    {\"hincrby\",hincrbyCommand,4,\"wm\",0,NULL,1,1,1,0,0},\n    {\"hincrbyfloat\",hincrbyfloatCommand,4,\"wm\",0,NULL,1,1,1,0,0},\n    {\"hdel\",hdelCommand,-3,\"w\",0,NULL,1,1,1,0,0},\n    {\"hlen\",hlenCommand,2,\"r\",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    {\"hexists\",hexistsCommand,3,\"r\",0,NULL,1,1,1,0,0},\n    {\"hscan\",hscanCommand,-3,\"rR\",0,NULL,1,1,1,0,0},\n    {\"incrby\",incrbyCommand,3,\"wm\",0,NULL,1,1,1,0,0},\n    {\"decrby\",decrbyCommand,3,\"wm\",0,NULL,1,1,1,0,0},\n    {\"incrbyfloat\",incrbyfloatCommand,3,\"wm\",0,NULL,1,1,1,0,0},\n    {\"getset\",getsetCommand,3,\"wm\",0,NULL,1,1,1,0,0},\n    {\"mset\",msetCommand,-3,\"wm\",0,NULL,1,-1,2,0,0},\n    {\"msetnx\",msetnxCommand,-3,\"wm\",0,NULL,1,-1,2,0,0},\n    {\"randomkey\",randomkeyCommand,1,\"rR\",0,NULL,0,0,0,0,0},\n    {\"select\",selectCommand,2,\"rl\",0,NULL,0,0,0,0,0},\n    {\"move\",moveCommand,3,\"w\",0,NULL,1,1,1,0,0},\n    {\"rename\",renameCommand,3,\"w\",0,NULL,1,2,1,0,0},\n    {\"renamenx\",renamenxCommand,3,\"w\",0,NULL,1,2,1,0,0},\n    {\"expire\",expireCommand,3,\"w\",0,NULL,1,1,1,0,0},\n    {\"expireat\",expireatCommand,3,\"w\",0,NULL,1,1,1,0,0},\n    {\"pexpire\",pexpireCommand,3,\"w\",0,NULL,1,1,1,0,0},\n    {\"pexpireat\",pexpireatCommand,3,\"w\",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    {\"dbsize\",dbsizeCommand,1,\"r\",0,NULL,0,0,0,0,0},\n    {\"auth\",authCommand,2,\"rslt\",0,NULL,0,0,0,0,0},\n    {\"ping\",pingCommand,1,\"rt\",0,NULL,0,0,0,0,0},\n    {\"echo\",echoCommand,2,\"r\",0,NULL,0,0,0,0,0},\n    {\"save\",saveCommand,1,\"ars\",0,NULL,0,0,0,0,0},\n    {\"bgsave\",bgsaveCommand,1,\"ar\",0,NULL,0,0,0,0,0},\n    {\"bgrewriteaof\",bgrewriteaofCommand,1,\"ar\",0,NULL,0,0,0,0,0},\n    {\"shutdown\",shutdownCommand,-1,\"arlt\",0,NULL,0,0,0,0,0},\n    {\"lastsave\",lastsaveCommand,1,\"rR\",0,NULL,0,0,0,0,0},\n    {\"type\",typeCommand,2,\"r\",0,NULL,1,1,1,0,0},\n    {\"multi\",multiCommand,1,\"rs\",0,NULL,0,0,0,0,0},\n    {\"exec\",execCommand,1,\"sM\",0,NULL,0,0,0,0,0},\n    {\"discard\",discardCommand,1,\"rs\",0,NULL,0,0,0,0,0},\n    {\"sync\",syncCommand,1,\"ars\",0,NULL,0,0,0,0,0},\n    {\"psync\",syncCommand,3,\"ars\",0,NULL,0,0,0,0,0},\n    {\"replconf\",replconfCommand,-1,\"arslt\",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    {\"sort\",sortCommand,-2,\"wm\",0,sortGetKeys,1,1,1,0,0},\n    {\"info\",infoCommand,-1,\"rlt\",0,NULL,0,0,0,0,0},\n    {\"monitor\",monitorCommand,1,\"ars\",0,NULL,0,0,0,0,0},\n    {\"ttl\",ttlCommand,2,\"r\",0,NULL,1,1,1,0,0},\n    {\"pttl\",pttlCommand,2,\"r\",0,NULL,1,1,1,0,0},\n    {\"persist\",persistCommand,2,\"w\",0,NULL,1,1,1,0,0},\n    {\"slaveof\",slaveofCommand,3,\"ast\",0,NULL,0,0,0,0,0},\n    {\"debug\",debugCommand,-2,\"as\",0,NULL,0,0,0,0,0},\n    {\"config\",configCommand,-2,\"art\",0,NULL,0,0,0,0,0},\n    {\"subscribe\",subscribeCommand,-2,\"rpslt\",0,NULL,0,0,0,0,0},\n    {\"unsubscribe\",unsubscribeCommand,-1,\"rpslt\",0,NULL,0,0,0,0,0},\n    {\"psubscribe\",psubscribeCommand,-2,\"rpslt\",0,NULL,0,0,0,0,0},\n    {\"punsubscribe\",punsubscribeCommand,-1,\"rpslt\",0,NULL,0,0,0,0,0},\n    {\"publish\",publishCommand,3,\"pltr\",0,NULL,0,0,0,0,0},\n    {\"pubsub\",pubsubCommand,-2,\"pltrR\",0,NULL,0,0,0,0,0},\n    {\"watch\",watchCommand,-2,\"rs\",0,NULL,1,-1,1,0,0},\n    {\"unwatch\",unwatchCommand,1,\"rs\",0,NULL,0,0,0,0,0},\n    {\"cluster\",clusterCommand,-2,\"ar\",0,NULL,0,0,0,0,0},\n    {\"restore\",restoreCommand,-4,\"awm\",0,NULL,1,1,1,0,0},\n    {\"restore-asking\",restoreCommand,-4,\"awmk\",0,NULL,1,1,1,0,0},\n    {\"migrate\",migrateCommand,-6,\"aw\",0,NULL,0,0,0,0,0},\n    {\"asking\",askingCommand,1,\"r\",0,NULL,0,0,0,0,0},\n    {\"readonly\",readonlyCommand,1,\"r\",0,NULL,0,0,0,0,0},\n    {\"readwrite\",readwriteCommand,1,\"r\",0,NULL,0,0,0,0,0},\n    {\"dump\",dumpCommand,2,\"ar\",0,NULL,1,1,1,0,0},\n    {\"object\",objectCommand,-2,\"r\",0,NULL,2,2,2,0,0},\n    {\"client\",clientCommand,-2,\"ar\",0,NULL,0,0,0,0,0},\n    {\"eval\",evalCommand,-3,\"s\",0,evalGetKeys,0,0,0,0,0},\n    {\"evalsha\",evalShaCommand,-3,\"s\",0,evalGetKeys,0,0,0,0,0},\n    {\"slowlog\",slowlogCommand,-2,\"r\",0,NULL,0,0,0,0,0},\n    {\"script\",scriptCommand,-2,\"ras\",0,NULL,0,0,0,0,0},\n    {\"time\",timeCommand,1,\"rR\",0,NULL,0,0,0,0,0},\n    {\"bitop\",bitopCommand,-4,\"wm\",0,NULL,2,-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    {\"wait\",waitCommand,3,\"rs\",0,NULL,0,0,0,0,0},\n    {\"pfselftest\",pfselftestCommand,1,\"r\",0,NULL,0,0,0,0,0},\n    {\"pfadd\",pfaddCommand,-2,\"wm\",0,NULL,1,1,1,0,0},\n    {\"pfcount\",pfcountCommand,-2,\"w\",0,NULL,1,1,1,0,0},\n    {\"pfmerge\",pfmergeCommand,-2,\"wm\",0,NULL,1,-1,1,0,0},\n    {\"pfdebug\",pfdebugCommand,-3,\"w\",0,NULL,0,0,0,0,0}\n};\n\nstruct evictionPoolEntry *evictionPoolAlloc(void);\n\n/*============================ Utility functions ============================ */\n\n/* Low level logging. To use only for very big messages, otherwise\n * redisLog() is to prefer. */\nvoid redisLogRaw(int level, const char *msg) {\n    const int syslogLevelMap[] = { LOG_DEBUG, LOG_INFO, LOG_NOTICE, LOG_WARNING };\n    const char *c = \".-*#\";\n    FILE *fp;\n    char buf[64];\n    int rawmode = (level & REDIS_LOG_RAW);\n    int log_to_stdout = server.logfile[0] == '\\0';\n\n    level &= 0xff; /* clear flags */\n    if (level < server.verbosity) return;\n\n    fp = log_to_stdout ? stdout : fopen(server.logfile,\"a\");\n    if (!fp) return;\n\n    if (rawmode) {\n        fprintf(fp,\"%s\",msg);\n    } else {\n        int off;\n        struct timeval tv;\n\n        gettimeofday(&tv,NULL);\n        off = strftime(buf,sizeof(buf),\"%d %b %H:%M:%S.\",localtime(&tv.tv_sec));\n        snprintf(buf+off,sizeof(buf)-off,\"%03d\",(int)tv.tv_usec/1000);\n        fprintf(fp,\"[%d] %s %c %s\\n\",(int)getpid(),buf,c[level],msg);\n    }\n    fflush(fp);\n\n    if (!log_to_stdout) fclose(fp);\n    if (server.syslog_enabled) syslog(syslogLevelMap[level], \"%s\", msg);\n}\n\n/* Like redisLogRaw() but with printf-alike support. This is the function that\n * is used across the code. The raw version is only used in order to dump\n * the INFO output on crash. */\nvoid redisLog(int level, const char *fmt, ...) {\n    va_list ap;\n    char msg[REDIS_MAX_LOGMSG_LEN];\n\n    if ((level&0xff) < server.verbosity) return;\n\n    va_start(ap, fmt);\n    vsnprintf(msg, sizeof(msg), fmt, ap);\n    va_end(ap);\n\n    redisLogRaw(level,msg);\n}\n\n/* Log a fixed message without printf-alike capabilities, in a way that is\n * safe to call from a signal handler.\n *\n * We actually use this only for signals that are not fatal from the point\n * of view of Redis. Signals that are going to kill the server anyway and\n * where we need printf-alike features are served by redisLog(). */\nvoid redisLogFromHandler(int level, const char *msg) {\n    int fd;\n    int log_to_stdout = server.logfile[0] == '\\0';\n    char buf[64];\n\n    if ((level&0xff) < server.verbosity || (log_to_stdout && server.daemonize))\n        return;\n    fd = log_to_stdout ? STDOUT_FILENO : \n                         open(server.logfile, O_APPEND|O_CREAT|O_WRONLY, 0644);\n    if (fd == -1) return;\n    ll2string(buf,sizeof(buf),getpid());\n    if (write(fd,\"[\",1) == -1) goto err;\n    if (write(fd,buf,strlen(buf)) == -1) goto err;\n    if (write(fd,\" | signal handler] (\",20) == -1) goto err;\n    ll2string(buf,sizeof(buf),time(NULL));\n    if (write(fd,buf,strlen(buf)) == -1) goto err;\n    if (write(fd,\") \",2) == -1) goto err;\n    if (write(fd,msg,strlen(msg)) == -1) goto err;\n    if (write(fd,\"\\n\",1) == -1) goto err;\nerr:\n    if (!log_to_stdout) close(fd);\n}\n\n/* Return the UNIX time in microseconds */\n// 返回微秒格式的 UNIX 时间\n// 1 秒 = 1 000 000 微秒\nlong long ustime(void) {\n    struct timeval tv;\n    long long ust;\n\n    gettimeofday(&tv, NULL);\n    ust = ((long long)tv.tv_sec)*1000000;\n    ust += tv.tv_usec;\n    return ust;\n}\n\n/* Return the UNIX time in milliseconds */\n// 返回毫秒格式的 UNIX 时间\n// 1 秒 = 1 000 毫秒\nlong long mstime(void) {\n    return ustime()/1000;\n}\n\n/* After an RDB dump or AOF rewrite we exit from children using _exit() instead of\n * exit(), because the latter may interact with the same file objects used by\n * the parent process. However if we are testing the coverage normal exit() is\n * used in order to obtain the right coverage information. */\nvoid exitFromChild(int retcode) {\n#ifdef COVERAGE_TEST\n    exit(retcode);\n#else\n    _exit(retcode);\n#endif\n}\n\n/*====================== Hash table type implementation  ==================== */\n\n/* This is a hash table type that uses the SDS dynamic strings library as\n * keys and radis objects as values (objects can hold SDS strings,\n * lists, sets). */\n\nvoid dictVanillaFree(void *privdata, void *val)\n{\n    DICT_NOTUSED(privdata);\n    zfree(val);\n}\n\nvoid dictListDestructor(void *privdata, void *val)\n{\n    DICT_NOTUSED(privdata);\n    listRelease((list*)val);\n}\n\nint dictSdsKeyCompare(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 dictSdsKeyCaseCompare(void *privdata, const void *key1,\n        const void *key2)\n{\n    DICT_NOTUSED(privdata);\n\n    return strcasecmp(key1, key2) == 0;\n}\n\nvoid dictRedisObjectDestructor(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    decrRefCount(val);\n}\n\nvoid dictSdsDestructor(void *privdata, void *val)\n{\n    DICT_NOTUSED(privdata);\n\n    sdsfree(val);\n}\n\nint dictObjKeyCompare(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\nunsigned int dictObjHash(const void *key) {\n    const robj *o = key;\n    return dictGenHashFunction(o->ptr, sdslen((sds)o->ptr));\n}\n\nunsigned int dictSdsHash(const void *key) {\n    return dictGenHashFunction((unsigned char*)key, sdslen((char*)key));\n}\n\nunsigned int dictSdsCaseHash(const void *key) {\n    return dictGenCaseHashFunction((unsigned char*)key, sdslen((char*)key));\n}\n\nint dictEncObjKeyCompare(void *privdata, const void *key1,\n        const void *key2)\n{\n    robj *o1 = (robj*) key1, *o2 = (robj*) key2;\n    int cmp;\n\n    if (o1->encoding == REDIS_ENCODING_INT &&\n        o2->encoding == REDIS_ENCODING_INT)\n            return o1->ptr == o2->ptr;\n\n    o1 = getDecodedObject(o1);\n    o2 = getDecodedObject(o2);\n    cmp = dictSdsKeyCompare(privdata,o1->ptr,o2->ptr);\n    decrRefCount(o1);\n    decrRefCount(o2);\n    return cmp;\n}\n\nunsigned int dictEncObjHash(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 == REDIS_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\n            o = getDecodedObject(o);\n            hash = dictGenHashFunction(o->ptr, sdslen((sds)o->ptr));\n            decrRefCount(o);\n            return hash;\n        }\n    }\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    dictRedisObjectDestructor, /* 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    dictRedisObjectDestructor, /* key destructor */\n    NULL                       /* val destructor */\n};\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    dictRedisObjectDestructor   /* val destructor */\n};\n\n/* server.lua_scripts sha (as sds string) -> scripts (as robj) cache. */\ndictType shaScriptObjectDictType = {\n    dictSdsCaseHash,            /* hash function */\n    NULL,                       /* key dup */\n    NULL,                       /* val dup */\n    dictSdsKeyCaseCompare,      /* key compare */\n    dictSdsDestructor,          /* key destructor */\n    dictRedisObjectDestructor   /* 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/* 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/* 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    dictRedisObjectDestructor,  /* key destructor */\n    dictRedisObjectDestructor   /* 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    dictRedisObjectDestructor,  /* key destructor */\n    dictListDestructor          /* val destructor */\n};\n\n/* Cluster nodes hash table, mapping nodes addresses 1.2.3.4:6379 to\n * clusterNode structures. */\ndictType clusterNodesDictType = {\n    dictSdsHash,                /* hash function */\n    NULL,                       /* key dup */\n    NULL,                       /* val dup */\n    dictSdsKeyCompare,          /* key compare */\n    dictSdsDestructor,          /* key destructor */\n    NULL                        /* val destructor */\n};\n\n/* Cluster re-addition blacklist. This maps node IDs to the time\n * we can re-add this node. The goal is to avoid readding a removed\n * node for some time. */\ndictType clusterNodesBlackListDictType = {\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/* Migrate cache dict type. */\ndictType migrateCacheDictType = {\n    dictSdsHash,                /* hash function */\n    NULL,                       /* key dup */\n    NULL,                       /* val dup */\n    dictSdsKeyCompare,          /* key compare */\n    dictSdsDestructor,          /* key destructor */\n    NULL                        /* val destructor */\n};\n\n/* Replication cached script dict (server.repl_scriptcache_dict).\n * Keys are sds SHA1 strings, while values are not used at all in the current\n * implementation. */\ndictType replScriptCacheDictType = {\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\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 < REDIS_HT_MINFILL));\n}\n\n/* If the percentage of used slots in the HT reaches REDIS_HT_MINFILL\n * we resize the hash table to save memory */\n// 如果字典的使用率比 REDIS_HT_MINFILL 常量要低\n// 那么通过缩小字典的体积来节约内存\nvoid tryResizeHashTables(int dbid) {\n    if (htNeedsResize(server.db[dbid].dict))\n        dictResize(server.db[dbid].dict);\n    if (htNeedsResize(server.db[dbid].expires))\n        dictResize(server.db[dbid].expires);\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 * 虽然服务器在对数据库执行读取/写入命令时会对数据库进行渐进式 rehash ，\n * 但如果服务器长期没有执行命令的话，数据库字典的 rehash 就可能一直没办法完成，\n * 为了防止出现这种情况，我们需要对数据库执行主动 rehash 。\n *\n * The function returns 1 if some rehashing was performed, otherwise 0\n * is returned. \n *\n * 函数在执行了主动 rehash 时返回 1 ，否则返回 0 。\n */\nint incrementallyRehash(int dbid) {\n\n    /* Keys dictionary */\n    if (dictIsRehashing(server.db[dbid].dict)) {\n        dictRehashMilliseconds(server.db[dbid].dict,1);\n        return 1; /* already used our millisecond for this loop... */\n    }\n\n    /* Expires */\n    if (dictIsRehashing(server.db[dbid].expires)) {\n        dictRehashMilliseconds(server.db[dbid].expires,1);\n        return 1; /* already used our millisecond for this loop... */\n    }\n\n    return 0;\n}\n\n/* This function is called once a background process of some kind terminates,\n * as we want to avoid resizing the hash tables when there is a child in order\n * to play well with copy-on-write (otherwise when a resize happens lots of\n * memory pages are copied). The goal of this function is to update the ability\n * for dict.c to resize the hash tables accordingly to the fact we have o not\n * running childs. */\nvoid updateDictResizePolicy(void) {\n    if (server.rdb_child_pid == -1 && server.aof_child_pid == -1)\n        dictEnableResize();\n    else\n        dictDisableResize();\n}\n\n/* ======================= Cron: called every 100 ms ======================== */\n\n/* Helper function for the activeExpireCycle() function.\n * This function will try to expire the key that is stored in the hash table\n * entry 'de' of the 'expires' hash table of a Redis database.\n *\n * activeExpireCycle() 函数使用的检查键是否过期的辅佐函数。\n *\n * If the key is found to be expired, it is removed from the database and\n * 1 is returned. Otherwise no operation is performed and 0 is returned.\n *\n * 如果 de 中的键已经过期，那么移除它，并返回 1 ，否则不做动作，并返回 0 。\n *\n * When a key is expired, server.stat_expiredkeys is incremented.\n *\n * The parameter 'now' is the current time in milliseconds as is passed\n * to the function to avoid too many gettimeofday() syscalls.\n *\n * 参数 now 是毫秒格式的当前时间\n */\nint activeExpireCycleTryExpire(redisDb *db, dictEntry *de, long long now) {\n    // 获取键的过期时间\n    long long t = dictGetSignedIntegerVal(de);\n    if (now > t) {\n\n        // 键已过期\n\n        sds key = dictGetKey(de);\n        robj *keyobj = createStringObject(key,sdslen(key));\n\n        // 传播过期命令\n        propagateExpire(db,keyobj);\n        // 从数据库中删除该键\n        dbDelete(db,keyobj);\n        // 发送事件\n        notifyKeyspaceEvent(REDIS_NOTIFY_EXPIRED,\n            \"expired\",keyobj,db->id);\n        decrRefCount(keyobj);\n        // 更新计数器\n        server.stat_expiredkeys++;\n        return 1;\n    } else {\n\n        // 键未过期\n        return 0;\n    }\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 * 函数尝试删除数据库中已经过期的键。\n * 当带有过期时间的键比较少时，函数运行得比较保守，\n * 如果带有过期时间的键比较多，那么函数会以更积极的方式来删除过期键，\n * 从而可能地释放被过期键占用的内存。\n *\n * No more than REDIS_DBCRON_DBS_PER_CALL databases are tested at every\n * iteration.\n *\n * 每次循环中被测试的数据库数目不会超过 REDIS_DBCRON_DBS_PER_CALL 。\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 * 如果 timelimit_exit 为真，那么说明还有更多删除工作要做，\n * 那么在 beforeSleep() 函数调用时，程序会再次执行这个函数。\n *\n * Expire cycle type:\n *\n * 过期循环的类型：\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 * 如果循环的类型为 ACTIVE_EXPIRE_CYCLE_FAST ，\n * 那么函数会以“快速过期”模式执行，\n * 执行的时间不会长过 EXPIRE_FAST_CYCLE_DURATION 毫秒，\n * 并且在 EXPIRE_FAST_CYCLE_DURATION 毫秒之内不会再重新执行。\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 *\n * 如果循环的类型为 ACTIVE_EXPIRE_CYCLE_SLOW ，\n * 那么函数会以“正常过期”模式执行，\n * 函数的执行时限为 REDIS_HS 常量的一个百分比，\n * 这个百分比由 REDIS_EXPIRELOOKUPS_TIME_PERC 定义。\n */\n\nvoid activeExpireCycle(int type) {\n    /* This function has some global state in order to continue the work\n     * incrementally across calls. */\n    // 静态变量，用来累积函数连续执行时的数据\n    static unsigned int current_db = 0; /* Last DB tested. */\n    static int timelimit_exit = 0;      /* Time limit hit in previous call? */\n    static long long last_fast_cycle = 0; /* When last fast cycle ran. */\n\n    unsigned int j, iteration = 0;\n    // 默认每次处理的数据库数量\n    unsigned int dbs_per_call = REDIS_DBCRON_DBS_PER_CALL;\n    // 函数开始的时间\n    long long start = ustime(), timelimit;\n\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        // 如果上次函数没有触发 timelimit_exit ，那么不执行处理\n        if (!timelimit_exit) return;\n        // 如果距离上次执行未够一定时间，那么不执行处理\n        if (start < last_fast_cycle + ACTIVE_EXPIRE_CYCLE_FAST_DURATION*2) return;\n        // 运行到这里，说明执行快速处理，记录当前时间\n        last_fast_cycle = start;\n    }\n\n    /* We usually should test REDIS_DBCRON_DBS_PER_CALL per iteration, with\n     * two exceptions:\n     *\n     * 一般情况下，函数只处理 REDIS_DBCRON_DBS_PER_CALL 个数据库，\n     * 除非：\n     *\n     * 1) Don't test more DBs than we have.\n     *    当前数据库的数量小于 REDIS_DBCRON_DBS_PER_CALL\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     *     如果上次处理遇到了时间上限，那么这次需要对所有数据库进行扫描，\n     *     这可以避免过多的过期键占用空间\n     */\n    if (dbs_per_call > server.dbnum || 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    // 函数处理的微秒时间上限\n    // ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC 默认为 25 ，也即是 25 % 的 CPU 时间\n    timelimit = 1000000*ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC/server.hz/100;\n    timelimit_exit = 0;\n    if (timelimit <= 0) timelimit = 1;\n\n    // 如果是运行在快速模式之下\n    // 那么最多只能运行 FAST_DURATION 微秒 \n    // 默认值为 1000 （微秒）\n    if (type == ACTIVE_EXPIRE_CYCLE_FAST)\n        timelimit = ACTIVE_EXPIRE_CYCLE_FAST_DURATION; /* in microseconds. */\n\n    // 遍历数据库\n    for (j = 0; j < dbs_per_call; j++) {\n        int expired;\n        // 指向要处理的数据库\n        redisDb *db = server.db+(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        // 为 DB 计数器加一，如果进入 do 循环之后因为超时而跳出\n        // 那么下次会直接从下个 DB 开始处理\n        current_db++;\n\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\n            /* If there is nothing to expire try next DB ASAP. */\n            // 获取数据库中带过期时间的键的数量\n            // 如果该数量为 0 ，直接跳过这个数据库\n            if ((num = dictSize(db->expires)) == 0) {\n                db->avg_ttl = 0;\n                break;\n            }\n            // 获取数据库中键值对的数量\n            slots = dictSlots(db->expires);\n            // 当前时间\n            now = mstime();\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            // 这个数据库的使用率低于 1% ，扫描起来太费力了（大部分都会 MISS）\n            // 跳过，等待字典收缩程序运行\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             *\n             * 样本计数器\n             */\n            // 已处理过期键计数器\n            expired = 0;\n            // 键的总 TTL 计数器\n            ttl_sum = 0;\n            // 总共处理的键计数器\n            ttl_samples = 0;\n\n            // 每次最多只能检查 LOOKUPS_PER_LOOP 个键\n            if (num > ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP)\n                num = ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP;\n\n            // 开始遍历数据库\n            while (num--) {\n                dictEntry *de;\n                long long ttl;\n\n                // 从 expires 中随机取出一个带过期时间的键\n                if ((de = dictGetRandomKey(db->expires)) == NULL) break;\n                // 计算 TTL\n                ttl = dictGetSignedIntegerVal(de)-now;\n                // 如果键已经过期，那么删除它，并将 expired 计数器增一\n                if (activeExpireCycleTryExpire(db,de,now)) expired++;\n                if (ttl < 0) ttl = 0;\n                // 累积键的 TTL\n                ttl_sum += ttl;\n                // 累积处理键的个数\n                ttl_samples++;\n            }\n\n            /* Update the average TTL stats for this database. */\n            // 为这个数据库更新平均 TTL 统计数据\n            if (ttl_samples) {\n                // 计算当前平均值\n                long long avg_ttl = ttl_sum/ttl_samples;\n                \n                // 如果这是第一次设置数据库平均 TTL ，那么进行初始化\n                if (db->avg_ttl == 0) db->avg_ttl = avg_ttl;\n                /* Smooth the value averaging with the previous one. */\n                // 取数据库的上次平均 TTL 和今次平均 TTL 的平均值\n                db->avg_ttl = (db->avg_ttl+avg_ttl)/2;\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            // 我们不能用太长时间处理过期键，\n            // 所以这个函数执行一定时间之后就要返回\n\n            // 更新遍历次数\n            iteration++;\n\n            // 每遍历 16 次执行一次\n            if ((iteration & 0xf) == 0 && /* check once every 16 iterations. */\n                (ustime()-start) > timelimit)\n            {\n                // 如果遍历次数正好是 16 的倍数\n                // 并且遍历的时间超过了 timelimit\n                // 那么断开 timelimit_exit\n                timelimit_exit = 1;\n            }\n\n            // 已经超时了，返回\n            if (timelimit_exit) 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            // 如果已删除的过期键占当前总数据库带过期时间的键数量的 25 %\n            // 那么不再遍历\n        } while (expired > ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP/4);\n    }\n}\n\nunsigned int getLRUClock(void) {\n    return (mstime()/REDIS_LRU_CLOCK_RESOLUTION) & REDIS_LRU_CLOCK_MAX;\n}\n\n/* Add a sample to the operations per second array of samples. */\n// 将服务器的命令执行次数记录到抽样数组中\nvoid trackOperationsPerSecond(void) {\n\n    // 计算两次抽样之间的时间长度，毫秒格式\n    long long t = mstime() - server.ops_sec_last_sample_time;\n\n    // 计算两次抽样之间，执行了多少个命令\n    long long ops = server.stat_numcommands - server.ops_sec_last_sample_ops;\n\n    long long ops_sec;\n\n    // 计算距离上一次抽样之后，每秒执行命令的数量\n    ops_sec = t > 0 ? (ops*1000/t) : 0;\n\n    // 将计算出的执行命令数量保存到抽样数组\n    server.ops_sec_samples[server.ops_sec_idx] = ops_sec;\n    // 更新抽样数组的索引\n    server.ops_sec_idx = (server.ops_sec_idx+1) % REDIS_OPS_SEC_SAMPLES;\n    // 更新最后一次抽样的时间\n    server.ops_sec_last_sample_time = mstime();\n    // 更新最后一次抽样时的执行命令数量\n    server.ops_sec_last_sample_ops = server.stat_numcommands;\n}\n\n/* Return the mean of all the samples. */\n// 根据所有取样信息，计算服务器最近一秒执行命令数的平均值\nlong long getOperationsPerSecond(void) {\n    int j;\n    long long sum = 0;\n\n    // 计算所有取样值的总和\n    for (j = 0; j < REDIS_OPS_SEC_SAMPLES; j++)\n        sum += server.ops_sec_samples[j];\n\n    // 计算取样的平均值\n    return sum / REDIS_OPS_SEC_SAMPLES;\n}\n\n/* Check for timeouts. Returns non-zero if the client was terminated */\n// 检查客户端是否已经超时，如果超时就关闭客户端，并返回 1 ；\n// 否则返回 0 。\nint clientsCronHandleTimeout(redisClient *c) {\n\n    // 获取当前时间\n    time_t now = server.unixtime;\n\n    // 服务器设置了 maxidletime 时间\n    if (server.maxidletime &&\n        // 不检查作为从服务器的客户端\n        !(c->flags & REDIS_SLAVE) &&    /* no timeout for slaves */\n        // 不检查作为主服务器的客户端\n        !(c->flags & REDIS_MASTER) &&   /* no timeout for masters */\n        // 不检查被阻塞的客户端\n        !(c->flags & REDIS_BLOCKED) &&  /* no timeout for BLPOP */\n        // 不检查订阅了频道的客户端\n        dictSize(c->pubsub_channels) == 0 && /* no timeout for pubsub */\n        // 不检查订阅了模式的客户端\n        listLength(c->pubsub_patterns) == 0 &&\n        // 客户端最后一次与服务器通讯的时间已经超过了 maxidletime 时间\n        (now - c->lastinteraction > server.maxidletime))\n    {\n        redisLog(REDIS_VERBOSE,\"Closing idle client\");\n        // 关闭超时客户端\n        freeClient(c);\n        return 1;\n    } else if (c->flags & REDIS_BLOCKED) {\n\n        /* Blocked OPS timeout is handled with milliseconds resolution.\n         * However note that the actual resolution is limited by\n         * server.hz. */\n        // 获取最新的系统时间\n        mstime_t now_ms = mstime();\n\n        // 检查被 BLPOP 等命令阻塞的客户端的阻塞时间是否已经到达\n        // 如果是的话，取消客户端的阻塞\n        if (c->bpop.timeout != 0 && c->bpop.timeout < now_ms) {\n            // 向客户端返回空回复\n            replyToBlockedClientTimedOut(c);\n            // 取消客户端的阻塞状态\n            unblockClient(c);\n        }\n    }\n\n    // 客户度没有被关闭\n    return 0;\n}\n\n/* The client query buffer is an sds.c string that can end with a lot of\n * free space not used, this function reclaims space if needed.\n *\n * 根据情况，缩小查询缓冲区的大小。\n *\n * The function always returns 0 as it never terminates the client. \n *\n * 函数总是返回 0 ，因为它不会中止客户端。\n */\nint clientsCronResizeQueryBuffer(redisClient *c) {\n    size_t querybuf_size = sdsAllocSize(c->querybuf);\n    time_t idletime = server.unixtime - c->lastinteraction;\n\n    /* There are two conditions to resize the query buffer:\n     *\n     * 符合以下两个条件的话，执行大小调整：\n     *\n     * 1) Query buffer is > BIG_ARG and too big for latest peak.\n     *    查询缓冲区的大小大于 BIG_ARG 以及 querybuf_peak\n     *\n     * 2) Client is inactive and the buffer is bigger than 1k. \n     *    客户端不活跃，并且缓冲区大于 1k 。\n     */\n    if (((querybuf_size > REDIS_MBULK_BIG_ARG) &&\n         (querybuf_size/(c->querybuf_peak+1)) > 2) ||\n         (querybuf_size > 1024 && idletime > 2))\n    {\n        /* Only resize the query buffer if it is actually wasting space. */\n        if (sdsavail(c->querybuf) > 1024) {\n            c->querybuf = sdsRemoveFreeSpace(c->querybuf);\n        }\n    }\n\n    /* Reset the peak again to capture the peak memory usage in the next\n     * cycle. */\n    // 重置峰值\n    c->querybuf_peak = 0;\n\n    return 0;\n}\n\nvoid clientsCron(void) {\n    /* Make sure to process at least 1/(server.hz*10) of clients per call.\n     *\n     * 这个函数每次执行都会处理至少 1/server.hz*10 个客户端。\n     *\n     * Since this function is called server.hz times per second we are sure that\n     * in the worst case we process all the clients in 10 seconds.\n     *\n     * 因为这个函数每秒钟会调用 server.hz 次，\n     * 所以在最坏情况下，服务器需要使用 10 秒钟来遍历所有客户端。\n     *\n     * In normal conditions (a reasonable number of clients) we process\n     * all the clients in a shorter time. \n     *\n     * 在一般情况下，遍历所有客户端所需的时间会比实际中短很多。\n     */\n\n    // 客户端数量\n    int numclients = listLength(server.clients);\n\n    // 要处理的客户端数量\n    int iterations = numclients/(server.hz*10);\n\n    // 至少要处理 50 个客户端\n    if (iterations < 50)\n        iterations = (numclients < 50) ? numclients : 50;\n\n    while(listLength(server.clients) && iterations--) {\n        redisClient *c;\n        listNode *head;\n\n        /* Rotate the list, take the current head, process.\n         * This way if the client must be removed from the list it's the\n         * first element and we don't incur into O(N) computation. */\n        // 翻转列表，然后取出表头元素，这样一来上一个被处理的客户端会被放到表头\n        // 另外，如果程序要删除当前客户端，那么只要删除表头元素就可以了\n        listRotate(server.clients);\n        head = listFirst(server.clients);\n        c = listNodeValue(head);\n        /* The following functions do different service checks on the client.\n         * The protocol is that they return non-zero if the client was\n         * terminated. */\n        // 检查客户端，并在客户端超时时关闭它\n        if (clientsCronHandleTimeout(c)) continue;\n        // 根据情况，缩小客户端查询缓冲区的大小\n        if (clientsCronResizeQueryBuffer(c)) continue;\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. */\n// 对数据库执行删除过期键，调整大小，以及主动和渐进式 rehash\nvoid databasesCron(void) {\n\n    // 函数先从数据库中删除过期键，然后再对数据库的大小进行修改\n\n    /* Expire keys by random sampling. Not required for slaves\n     * as master will synthesize DELs for us. */\n    // 如果服务器不是从服务器，那么执行主动过期键清除\n    if (server.active_expire_enabled && server.masterhost == NULL)\n        // 清除模式为 CYCLE_SLOW ，这个模式会尽量多清除过期键\n        activeExpireCycle(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    // 在没有 BGSAVE 或者 BGREWRITEAOF 执行时，对哈希表进行 rehash\n    if (server.rdb_child_pid == -1 && server.aof_child_pid == -1) {\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. */\n        static unsigned int resize_db = 0;\n        static unsigned int rehash_db = 0;\n        unsigned int dbs_per_call = REDIS_DBCRON_DBS_PER_CALL;\n        unsigned int j;\n\n        /* Don't test more DBs than we have. */\n        // 设定要测试的数据库数量\n        if (dbs_per_call > server.dbnum) dbs_per_call = server.dbnum;\n\n        /* Resize */\n        // 调整字典的大小\n        for (j = 0; j < dbs_per_call; j++) {\n            tryResizeHashTables(resize_db % server.dbnum);\n            resize_db++;\n        }\n\n        /* Rehash */\n        // 对字典进行渐进式 rehash\n        if (server.activerehashing) {\n            for (j = 0; j < dbs_per_call; j++) {\n                int work_done = incrementallyRehash(rehash_db % server.dbnum);\n                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/* We take a cached value of the unix time in the global state because with\n * virtual memory and aging there is to store the current time in objects at\n * every object access, and accuracy is not needed. To access a global var is\n * a lot faster than calling time(NULL) */\nvoid updateCachedTime(void) {\n    server.unixtime = time(NULL);\n    server.mstime = mstime();\n}\n\n/* This is our timer interrupt, called server.hz times per second.\n *\n * 这是 Redis 的时间中断器，每秒调用 server.hz 次。\n *\n * Here is where we do a number of things that need to be done asynchronously.\n * For instance:\n *\n * 以下是需要异步执行的操作：\n *\n * - Active expired keys collection (it is also performed in a lazy way on\n *   lookup).\n *   主动清除过期键。\n *\n * - Software watchdog.\n *   更新软件 watchdog 的信息。\n *\n * - Update some statistic.\n *   更新统计信息。\n *\n * - Incremental rehashing of the DBs hash tables.\n *   对数据库进行渐增式 Rehash\n *\n * - Triggering BGSAVE / AOF rewrite, and handling of terminated children.\n *   触发 BGSAVE 或者 AOF 重写，并处理之后由 BGSAVE 和 AOF 重写引发的子进程停止。\n *\n * - Clients timeout of different kinds.\n *   处理客户端超时。\n *\n * - Replication reconnection.\n *   复制重连\n *\n * - Many more...\n *   等等。。。\n *\n * Everything directly called here will be called server.hz times per second,\n * so in order to throttle execution of things we want to do less frequently\n * a macro is used: run_with_period(milliseconds) { .... }\n *\n * 因为 serverCron 函数中的所有代码都会每秒调用 server.hz 次，\n * 为了对部分代码的调用次数进行限制，\n * 使用了一个宏 run_with_period(milliseconds) { ... } ，\n * 这个宏可以将被包含代码的执行次数降低为每 milliseconds 执行一次。\n */\n\nint serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {\n    int j;\n    REDIS_NOTUSED(eventLoop);\n    REDIS_NOTUSED(id);\n    REDIS_NOTUSED(clientData);\n\n    /* Software watchdog: deliver the SIGALRM that will reach the signal\n     * handler if we don't return here fast enough. */\n    if (server.watchdog_period) watchdogScheduleSignal(server.watchdog_period);\n\n    /* Update the time cache. */\n    updateCachedTime();\n\n    // 记录服务器执行命令的次数\n    run_with_period(100) trackOperationsPerSecond();\n\n    /* We have just REDIS_LRU_BITS bits per object for LRU information.\n     * So we use an (eventually wrapping) LRU clock.\n     *\n     * Note that even if the counter wraps it's not a big problem,\n     * everything will still work but some object will appear younger\n     * to Redis. However for this to happen a given object should never be\n     * touched for all the time needed to the counter to wrap, which is\n     * not likely.\n     *\n     * 即使服务器的时间最终比 1.5 年长也无所谓，\n     * 对象系统仍会正常运作，不过一些对象可能会比服务器本身的时钟更年轻。\n     * 不过这要这个对象在 1.5 年内都没有被访问过，才会出现这种现象。\n     *\n     * Note that you can change the resolution altering the\n     * REDIS_LRU_CLOCK_RESOLUTION define.\n     *\n     * LRU 时间的精度可以通过修改 REDIS_LRU_CLOCK_RESOLUTION 常量来改变。\n     */\n    server.lruclock = getLRUClock();\n\n    /* Record the max memory used since the server was started. */\n    // 记录服务器的内存峰值\n    if (zmalloc_used_memory() > server.stat_peak_memory)\n        server.stat_peak_memory = zmalloc_used_memory();\n\n    /* Sample the RSS here since this is a relatively slow call. */\n    server.resident_set_size = zmalloc_get_rss();\n\n    /* We received a SIGTERM, shutting down here in a safe way, as it is\n     * not ok doing so inside the signal handler. */\n    // 服务器进程收到 SIGTERM 信号，关闭服务器\n    if (server.shutdown_asap) {\n\n        // 尝试关闭服务器\n        if (prepareForShutdown(0) == REDIS_OK) exit(0);\n\n        // 如果关闭失败，那么打印 LOG ，并移除关闭标识\n        redisLog(REDIS_WARNING,\"SIGTERM received but errors trying to shut down the server, check the logs for more information\");\n        server.shutdown_asap = 0;\n    }\n\n    /* Show some info about non-empty databases */\n    // 打印数据库的键值对信息\n    run_with_period(5000) {\n        for (j = 0; j < server.dbnum; j++) {\n            long long size, used, vkeys;\n\n            // 可用键值对的数量\n            size = dictSlots(server.db[j].dict);\n            // 已用键值对的数量\n            used = dictSize(server.db[j].dict);\n            // 带有过期时间的键值对数量\n            vkeys = dictSize(server.db[j].expires);\n\n            // 用 LOG 打印数量\n            if (used || vkeys) {\n                redisLog(REDIS_VERBOSE,\"DB %d: %lld keys (%lld volatile) in %lld slots HT.\",j,used,vkeys,size);\n                /* dictPrintStats(server.dict); */\n            }\n        }\n    }\n\n    /* Show information about connected clients */\n    // 如果服务器没有运行在 SENTINEL 模式下，那么打印客户端的连接信息\n    if (!server.sentinel_mode) {\n        run_with_period(5000) {\n            redisLog(REDIS_VERBOSE,\n                \"%lu clients connected (%lu slaves), %zu bytes in use\",\n                listLength(server.clients)-listLength(server.slaves),\n                listLength(server.slaves),\n                zmalloc_used_memory());\n        }\n    }\n\n    /* We need to do a few operations on clients asynchronously. */\n    // 检查客户端，关闭超时客户端，并释放客户端多余的缓冲区\n    clientsCron();\n\n    /* Handle background operations on Redis databases. */\n    // 对数据库执行各种操作\n    databasesCron();\n\n    /* Start a scheduled AOF rewrite if this was requested by the user while\n     * a BGSAVE was in progress. */\n    // 如果 BGSAVE 和 BGREWRITEAOF 都没有在执行\n    // 并且有一个 BGREWRITEAOF 在等待，那么执行 BGREWRITEAOF\n    if (server.rdb_child_pid == -1 && server.aof_child_pid == -1 &&\n        server.aof_rewrite_scheduled)\n    {\n        rewriteAppendOnlyFileBackground();\n    }\n\n    /* Check if a background saving or AOF rewrite in progress terminated. */\n    // 检查 BGSAVE 或者 BGREWRITEAOF 是否已经执行完毕\n    if (server.rdb_child_pid != -1 || server.aof_child_pid != -1) {\n        int statloc;\n        pid_t pid;\n\n        // 接收子进程发来的信号，非阻塞\n        if ((pid = wait3(&statloc,WNOHANG,NULL)) != 0) {\n            int exitcode = WEXITSTATUS(statloc);\n            int bysignal = 0;\n            \n            if (WIFSIGNALED(statloc)) bysignal = WTERMSIG(statloc);\n\n            // BGSAVE 执行完毕\n            if (pid == server.rdb_child_pid) {\n                backgroundSaveDoneHandler(exitcode,bysignal);\n\n            // BGREWRITEAOF 执行完毕\n            } else if (pid == server.aof_child_pid) {\n                backgroundRewriteDoneHandler(exitcode,bysignal);\n\n            } else {\n                redisLog(REDIS_WARNING,\n                    \"Warning, detected child with unmatched pid: %ld\",\n                    (long)pid);\n            }\n            updateDictResizePolicy();\n        }\n    } else {\n\n        /* If there is not a background saving/rewrite in progress check if\n         * we have to save/rewrite now */\n        // 既然没有 BGSAVE 或者 BGREWRITEAOF 在执行，那么检查是否需要执行它们\n\n        // 遍历所有保存条件，看是否需要执行 BGSAVE 命令\n         for (j = 0; j < server.saveparamslen; j++) {\n            struct saveparam *sp = server.saveparams+j;\n\n            /* Save if we reached the given amount of changes,\n             * the given amount of seconds, and if the latest bgsave was\n             * successful or if, in case of an error, at least\n             * REDIS_BGSAVE_RETRY_DELAY seconds already elapsed. */\n            // 检查是否有某个保存条件已经满足了\n            if (server.dirty >= sp->changes &&\n                server.unixtime-server.lastsave > sp->seconds &&\n                (server.unixtime-server.lastbgsave_try >\n                 REDIS_BGSAVE_RETRY_DELAY ||\n                 server.lastbgsave_status == REDIS_OK))\n            {\n                redisLog(REDIS_NOTICE,\"%d changes in %d seconds. Saving...\",\n                    sp->changes, (int)sp->seconds);\n                // 执行 BGSAVE\n                rdbSaveBackground(server.rdb_filename);\n                break;\n            }\n         }\n\n         /* Trigger an AOF rewrite if needed */\n        // 出发 BGREWRITEAOF\n         if (server.rdb_child_pid == -1 &&\n             server.aof_child_pid == -1 &&\n             server.aof_rewrite_perc &&\n             // AOF 文件的当前大小大于执行 BGREWRITEAOF 所需的最小大小\n             server.aof_current_size > server.aof_rewrite_min_size)\n         {\n            // 上一次完成 AOF 写入之后，AOF 文件的大小\n            long long base = server.aof_rewrite_base_size ?\n                            server.aof_rewrite_base_size : 1;\n\n            // AOF 文件当前的体积相对于 base 的体积的百分比\n            long long growth = (server.aof_current_size*100/base) - 100;\n\n            // 如果增长体积的百分比超过了 growth ，那么执行 BGREWRITEAOF\n            if (growth >= server.aof_rewrite_perc) {\n                redisLog(REDIS_NOTICE,\"Starting automatic rewriting of AOF on %lld%% growth\",growth);\n                // 执行 BGREWRITEAOF\n                rewriteAppendOnlyFileBackground();\n            }\n         }\n    }\n\n    // 根据 AOF 政策，\n    // 考虑是否需要将 AOF 缓冲区中的内容写入到 AOF 文件中\n    /* AOF postponed flush: Try at every cron cycle if the slow fsync\n     * completed. */\n    if (server.aof_flush_postponed_start) flushAppendOnlyFile(0);\n\n    /* AOF write errors: in this case we have a buffer to flush as well and\n     * clear the AOF error in case of success to make the DB writable again,\n     * however to try every second is enough in case of 'hz' is set to\n     * an higher frequency. */\n    run_with_period(1000) {\n        if (server.aof_last_write_status == REDIS_ERR)\n            flushAppendOnlyFile(0);\n    }\n\n    /* Close clients that need to be closed asynchronous */\n    // 关闭那些需要异步关闭的客户端\n    freeClientsInAsyncFreeQueue();\n\n    /* Clear the paused clients flag if needed. */\n    clientsArePaused(); /* Don't check return value, just use the side effect. */\n\n    /* Replication cron function -- used to reconnect to master and\n     * to detect transfer failures. */\n    // 复制函数\n    // 重连接主服务器、向主服务器发送 ACK 、判断数据发送失败情况、断开本服务器超时的从服务器，等等\n    run_with_period(1000) replicationCron();\n\n    /* Run the Redis Cluster cron. */\n    // 如果服务器运行在集群模式下，那么执行集群操作\n    run_with_period(100) {\n        if (server.cluster_enabled) clusterCron();\n    }\n\n    /* Run the Sentinel timer if we are in sentinel mode. */\n    // 如果服务器运行在 sentinel 模式下，那么执行 SENTINEL 的主函数\n    run_with_period(100) {\n        if (server.sentinel_mode) sentinelTimer();\n    }\n\n    /* Cleanup expired MIGRATE cached sockets. */\n    // 集群。。。TODO\n    run_with_period(1000) {\n        migrateCloseTimedoutSockets();\n    }\n\n    // 增加 loop 计数器\n    server.cronloops++;\n\n    return 1000/server.hz;\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. */\n// 每次处理事件之前执行\nvoid beforeSleep(struct aeEventLoop *eventLoop) {\n    REDIS_NOTUSED(eventLoop);\n\n    /* Run a fast expire cycle (the called function will return\n     * ASAP if a fast cycle is not needed). */\n    // 执行一次快速的主动过期检查\n    if (server.active_expire_enabled && server.masterhost == NULL)\n        activeExpireCycle(ACTIVE_EXPIRE_CYCLE_FAST);\n\n    /* Send all the slaves an ACK request if at least one client blocked\n     * during the previous event loop iteration. */\n    if (server.get_ack_from_slaves) {\n        robj *argv[3];\n\n        argv[0] = createStringObject(\"REPLCONF\",8);\n        argv[1] = createStringObject(\"GETACK\",6);\n        argv[2] = createStringObject(\"*\",1); /* Not used argument. */\n        replicationFeedSlaves(server.slaves, server.slaveseldb, argv, 3);\n        decrRefCount(argv[0]);\n        decrRefCount(argv[1]);\n        decrRefCount(argv[2]);\n        server.get_ack_from_slaves = 0;\n    }\n\n    /* Unblock all the clients blocked for synchronous replication\n     * in WAIT. */\n    if (listLength(server.clients_waiting_acks))\n        processClientsWaitingReplicas();\n\n    /* Try to process pending commands for clients that were just unblocked. */\n    if (listLength(server.unblocked_clients))\n        processUnblockedClients();\n\n    /* Write the AOF buffer on disk */\n    // 将 AOF 缓冲区的内容写入到 AOF 文件\n    flushAppendOnlyFile(0);\n\n    /* Call the Redis Cluster before sleep function. */\n    // 在进入下个事件循环前，执行一些集群收尾工作\n    if (server.cluster_enabled) clusterBeforeSleep();\n}\n\n/* =========================== Server initialization ======================== */\n\nvoid createSharedObjects(void) {\n    int j;\n\n    // 常用回复\n    shared.crlf = createObject(REDIS_STRING,sdsnew(\"\\r\\n\"));\n    shared.ok = createObject(REDIS_STRING,sdsnew(\"+OK\\r\\n\"));\n    shared.err = createObject(REDIS_STRING,sdsnew(\"-ERR\\r\\n\"));\n    shared.emptybulk = createObject(REDIS_STRING,sdsnew(\"$0\\r\\n\\r\\n\"));\n    shared.czero = createObject(REDIS_STRING,sdsnew(\":0\\r\\n\"));\n    shared.cone = createObject(REDIS_STRING,sdsnew(\":1\\r\\n\"));\n    shared.cnegone = createObject(REDIS_STRING,sdsnew(\":-1\\r\\n\"));\n    shared.nullbulk = createObject(REDIS_STRING,sdsnew(\"$-1\\r\\n\"));\n    shared.nullmultibulk = createObject(REDIS_STRING,sdsnew(\"*-1\\r\\n\"));\n    shared.emptymultibulk = createObject(REDIS_STRING,sdsnew(\"*0\\r\\n\"));\n    shared.pong = createObject(REDIS_STRING,sdsnew(\"+PONG\\r\\n\"));\n    shared.queued = createObject(REDIS_STRING,sdsnew(\"+QUEUED\\r\\n\"));\n    shared.emptyscan = createObject(REDIS_STRING,sdsnew(\"*2\\r\\n$1\\r\\n0\\r\\n*0\\r\\n\"));\n    // 常用错误回复\n    shared.wrongtypeerr = createObject(REDIS_STRING,sdsnew(\n        \"-WRONGTYPE Operation against a key holding the wrong kind of value\\r\\n\"));\n    shared.nokeyerr = createObject(REDIS_STRING,sdsnew(\n        \"-ERR no such key\\r\\n\"));\n    shared.syntaxerr = createObject(REDIS_STRING,sdsnew(\n        \"-ERR syntax error\\r\\n\"));\n    shared.sameobjecterr = createObject(REDIS_STRING,sdsnew(\n        \"-ERR source and destination objects are the same\\r\\n\"));\n    shared.outofrangeerr = createObject(REDIS_STRING,sdsnew(\n        \"-ERR index out of range\\r\\n\"));\n    shared.noscripterr = createObject(REDIS_STRING,sdsnew(\n        \"-NOSCRIPT No matching script. Please use EVAL.\\r\\n\"));\n    shared.loadingerr = createObject(REDIS_STRING,sdsnew(\n        \"-LOADING Redis is loading the dataset in memory\\r\\n\"));\n    shared.slowscripterr = createObject(REDIS_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(REDIS_STRING,sdsnew(\n        \"-MASTERDOWN Link with MASTER is down and slave-serve-stale-data is set to 'no'.\\r\\n\"));\n    shared.bgsaveerr = createObject(REDIS_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(REDIS_STRING,sdsnew(\n        \"-READONLY You can't write against a read only slave.\\r\\n\"));\n    shared.noautherr = createObject(REDIS_STRING,sdsnew(\n        \"-NOAUTH Authentication required.\\r\\n\"));\n    shared.oomerr = createObject(REDIS_STRING,sdsnew(\n        \"-OOM command not allowed when used memory > 'maxmemory'.\\r\\n\"));\n    shared.execaborterr = createObject(REDIS_STRING,sdsnew(\n        \"-EXECABORT Transaction discarded because of previous errors.\\r\\n\"));\n    shared.noreplicaserr = createObject(REDIS_STRING,sdsnew(\n        \"-NOREPLICAS Not enough good slaves to write.\\r\\n\"));\n    shared.busykeyerr = createObject(REDIS_STRING,sdsnew(\n        \"-BUSYKEY Target key name already exists.\\r\\n\"));\n\n    // 常用字符\n    shared.space = createObject(REDIS_STRING,sdsnew(\" \"));\n    shared.colon = createObject(REDIS_STRING,sdsnew(\":\"));\n    shared.plus = createObject(REDIS_STRING,sdsnew(\"+\"));\n\n    // 常用 SELECT 命令\n    for (j = 0; j < REDIS_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(REDIS_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\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\n    // 常用命令\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\n    // 常用整数\n    for (j = 0; j < REDIS_SHARED_INTEGERS; j++) {\n        shared.integers[j] = createObject(REDIS_STRING,(void*)(long)j);\n        shared.integers[j]->encoding = REDIS_ENCODING_INT;\n    }\n\n    // 常用长度 bulk 或者 multi bulk 回复\n    for (j = 0; j < REDIS_SHARED_BULKHDR_LEN; j++) {\n        shared.mbulkhdr[j] = createObject(REDIS_STRING,\n            sdscatprintf(sdsempty(),\"*%d\\r\\n\",j));\n        shared.bulkhdr[j] = createObject(REDIS_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\nvoid initServerConfig() {\n    int j;\n\n    // 服务器状态\n\n    // 设置服务器的运行 ID\n    getRandomHexChars(server.runid,REDIS_RUN_ID_SIZE);\n    // 设置默认配置文件路径\n    server.configfile = NULL;\n    // 设置默认服务器频率\n    server.hz = REDIS_DEFAULT_HZ;\n    // 为运行 ID 加上结尾字符\n    server.runid[REDIS_RUN_ID_SIZE] = '\\0';\n    // 设置服务器的运行架构\n    server.arch_bits = (sizeof(long) == 8) ? 64 : 32;\n    // 设置默认服务器端口号\n    server.port = REDIS_SERVERPORT;\n    server.tcp_backlog = REDIS_TCP_BACKLOG;\n    server.bindaddr_count = 0;\n    server.unixsocket = NULL;\n    server.unixsocketperm = REDIS_DEFAULT_UNIX_SOCKET_PERM;\n    server.ipfd_count = 0;\n    server.sofd = -1;\n    server.dbnum = REDIS_DEFAULT_DBNUM;\n    server.verbosity = REDIS_DEFAULT_VERBOSITY;\n    server.maxidletime = REDIS_MAXIDLETIME;\n    server.tcpkeepalive = REDIS_DEFAULT_TCP_KEEPALIVE;\n    server.active_expire_enabled = 1;\n    server.client_max_querybuf_len = REDIS_MAX_QUERYBUF_LEN;\n    server.saveparams = NULL;\n    server.loading = 0;\n    server.logfile = zstrdup(REDIS_DEFAULT_LOGFILE);\n    server.syslog_enabled = REDIS_DEFAULT_SYSLOG_ENABLED;\n    server.syslog_ident = zstrdup(REDIS_DEFAULT_SYSLOG_IDENT);\n    server.syslog_facility = LOG_LOCAL0;\n    server.daemonize = REDIS_DEFAULT_DAEMONIZE;\n    server.aof_state = REDIS_AOF_OFF;\n    server.aof_fsync = REDIS_DEFAULT_AOF_FSYNC;\n    server.aof_no_fsync_on_rewrite = REDIS_DEFAULT_AOF_NO_FSYNC_ON_REWRITE;\n    server.aof_rewrite_perc = REDIS_AOF_REWRITE_PERC;\n    server.aof_rewrite_min_size = REDIS_AOF_REWRITE_MIN_SIZE;\n    server.aof_rewrite_base_size = 0;\n    server.aof_rewrite_scheduled = 0;\n    server.aof_last_fsync = time(NULL);\n    server.aof_rewrite_time_last = -1;\n    server.aof_rewrite_time_start = -1;\n    server.aof_lastbgrewrite_status = REDIS_OK;\n    server.aof_delayed_fsync = 0;\n    server.aof_fd = -1;\n    server.aof_selected_db = -1; /* Make sure the first time will not match */\n    server.aof_flush_postponed_start = 0;\n    server.aof_rewrite_incremental_fsync = REDIS_DEFAULT_AOF_REWRITE_INCREMENTAL_FSYNC;\n    server.pidfile = zstrdup(REDIS_DEFAULT_PID_FILE);\n    server.rdb_filename = zstrdup(REDIS_DEFAULT_RDB_FILENAME);\n    server.aof_filename = zstrdup(REDIS_DEFAULT_AOF_FILENAME);\n    server.requirepass = NULL;\n    server.rdb_compression = REDIS_DEFAULT_RDB_COMPRESSION;\n    server.rdb_checksum = REDIS_DEFAULT_RDB_CHECKSUM;\n    server.stop_writes_on_bgsave_err = REDIS_DEFAULT_STOP_WRITES_ON_BGSAVE_ERROR;\n    server.activerehashing = REDIS_DEFAULT_ACTIVE_REHASHING;\n    server.notify_keyspace_events = 0;\n    server.maxclients = REDIS_MAX_CLIENTS;\n    server.bpop_blocked_clients = 0;\n    server.maxmemory = REDIS_DEFAULT_MAXMEMORY;\n    server.maxmemory_policy = REDIS_DEFAULT_MAXMEMORY_POLICY;\n    server.maxmemory_samples = REDIS_DEFAULT_MAXMEMORY_SAMPLES;\n    server.hash_max_ziplist_entries = REDIS_HASH_MAX_ZIPLIST_ENTRIES;\n    server.hash_max_ziplist_value = REDIS_HASH_MAX_ZIPLIST_VALUE;\n    server.list_max_ziplist_entries = REDIS_LIST_MAX_ZIPLIST_ENTRIES;\n    server.list_max_ziplist_value = REDIS_LIST_MAX_ZIPLIST_VALUE;\n    server.set_max_intset_entries = REDIS_SET_MAX_INTSET_ENTRIES;\n    server.zset_max_ziplist_entries = REDIS_ZSET_MAX_ZIPLIST_ENTRIES;\n    server.zset_max_ziplist_value = REDIS_ZSET_MAX_ZIPLIST_VALUE;\n    server.hll_sparse_max_bytes = REDIS_DEFAULT_HLL_SPARSE_MAX_BYTES;\n    server.shutdown_asap = 0;\n    server.repl_ping_slave_period = REDIS_REPL_PING_SLAVE_PERIOD;\n    server.repl_timeout = REDIS_REPL_TIMEOUT;\n    server.repl_min_slaves_to_write = REDIS_DEFAULT_MIN_SLAVES_TO_WRITE;\n    server.repl_min_slaves_max_lag = REDIS_DEFAULT_MIN_SLAVES_MAX_LAG;\n    server.cluster_enabled = 0;\n    server.cluster_node_timeout = REDIS_CLUSTER_DEFAULT_NODE_TIMEOUT;\n    server.cluster_migration_barrier = REDIS_CLUSTER_DEFAULT_MIGRATION_BARRIER;\n    server.cluster_configfile = zstrdup(REDIS_DEFAULT_CLUSTER_CONFIG_FILE);\n    server.lua_caller = NULL;\n    server.lua_time_limit = REDIS_LUA_TIME_LIMIT;\n    server.lua_client = NULL;\n    server.lua_timedout = 0;\n    server.migrate_cached_sockets = dictCreate(&migrateCacheDictType,NULL);\n    server.loading_process_events_interval_bytes = (1024*1024*2);\n\n    // 初始化 LRU 时间\n    server.lruclock = getLRUClock();\n\n    // 初始化并设置保存条件\n    resetServerSaveParams();\n\n    appendServerSaveParams(60*60,1);  /* save after 1 hour and 1 change */\n    appendServerSaveParams(300,100);  /* save after 5 minutes and 100 changes */\n    appendServerSaveParams(60,10000); /* save after 1 minute and 10000 changes */\n\n    /* Replication related */\n    // 初始化和复制相关的状态\n    server.masterauth = NULL;\n    server.masterhost = NULL;\n    server.masterport = 6379;\n    server.master = NULL;\n    server.cached_master = NULL;\n    server.repl_master_initial_offset = -1;\n    server.repl_state = REDIS_REPL_NONE;\n    server.repl_syncio_timeout = REDIS_REPL_SYNCIO_TIMEOUT;\n    server.repl_serve_stale_data = REDIS_DEFAULT_SLAVE_SERVE_STALE_DATA;\n    server.repl_slave_ro = REDIS_DEFAULT_SLAVE_READ_ONLY;\n    server.repl_down_since = 0; /* Never connected, repl is down since EVER. */\n    server.repl_disable_tcp_nodelay = REDIS_DEFAULT_REPL_DISABLE_TCP_NODELAY;\n    server.slave_priority = REDIS_DEFAULT_SLAVE_PRIORITY;\n    server.master_repl_offset = 0;\n\n    /* Replication partial resync backlog */\n    // 初始化 PSYNC 命令所使用的 backlog\n    server.repl_backlog = NULL;\n    server.repl_backlog_size = REDIS_DEFAULT_REPL_BACKLOG_SIZE;\n    server.repl_backlog_histlen = 0;\n    server.repl_backlog_idx = 0;\n    server.repl_backlog_off = 0;\n    server.repl_backlog_time_limit = REDIS_DEFAULT_REPL_BACKLOG_TIME_LIMIT;\n    server.repl_no_slaves_since = time(NULL);\n\n    /* Client output buffer limits */\n    // 设置客户端的输出缓冲区限制\n    for (j = 0; j < REDIS_CLIENT_LIMIT_NUM_CLASSES; j++)\n        server.client_obuf_limits[j] = clientBufferLimitsDefaults[j];\n\n    /* Double constants initialization */\n    // 初始化浮点常量\n    R_Zero = 0.0;\n    R_PosInf = 1.0/R_Zero;\n    R_NegInf = -1.0/R_Zero;\n    R_Nan = R_Zero/R_Zero;\n\n    /* Command table -- we initiialize it here as it is part of the\n     * initial configuration, since command names may be changed via\n     * redis.conf using the rename-command directive. */\n    // 初始化命令表\n    // 在这里初始化是因为接下来读取 .conf 文件时可能会用到这些命令\n    server.commands = dictCreate(&commandTableDictType,NULL);\n    server.orig_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    \n    /* Slow log */\n    // 初始化慢查询日志\n    server.slowlog_log_slower_than = REDIS_SLOWLOG_LOG_SLOWER_THAN;\n    server.slowlog_max_len = REDIS_SLOWLOG_MAX_LEN;\n\n    /* Debugging */\n    // 初始化调试项\n    server.assert_failed = \"<no assertion failed>\";\n    server.assert_file = \"<no file>\";\n    server.assert_line = 0;\n    server.bug_report_start = 0;\n    server.watchdog_period = 0;\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 (REDIS_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. */\nvoid adjustOpenFilesLimit(void) {\n    rlim_t maxfiles = server.maxclients+REDIS_MIN_RESERVED_FDS;\n    struct rlimit limit;\n\n    if (getrlimit(RLIMIT_NOFILE,&limit) == -1) {\n        redisLog(REDIS_WARNING,\"Unable to obtain the current NOFILE limit (%s), assuming 1024 and setting the max clients configuration accordingly.\",\n            strerror(errno));\n        server.maxclients = 1024-REDIS_MIN_RESERVED_FDS;\n    } else {\n        rlim_t 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 f;\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            f = maxfiles;\n            while(f > oldlimit) {\n                int decr_step = 16;\n\n                limit.rlim_cur = f;\n                limit.rlim_max = f;\n                if (setrlimit(RLIMIT_NOFILE,&limit) != -1) break;\n                setrlimit_error = errno;\n\n                /* We failed to set file limit to 'f'. Try with a\n                 * smaller limit decrementing by a few FDs per iteration. */\n                if (f < decr_step) break;\n                f -= 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 (f < oldlimit) f = oldlimit;\n\n            if (f != maxfiles) {\n                int old_maxclients = server.maxclients;\n                server.maxclients = f-REDIS_MIN_RESERVED_FDS;\n                if (server.maxclients < 1) {\n                    redisLog(REDIS_WARNING,\"Your current 'ulimit -n' \"\n                        \"of %llu is not enough for Redis 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                    exit(1);\n                }\n                redisLog(REDIS_WARNING,\"You requested maxclients of %d \"\n                    \"requiring at least %llu max file descriptors.\",\n                    old_maxclients,\n                    (unsigned long long) maxfiles);\n                redisLog(REDIS_WARNING,\"Redis can't set maximum open files \"\n                    \"to %llu because of OS error: %s.\",\n                    (unsigned long long) maxfiles, strerror(setrlimit_error));\n                redisLog(REDIS_WARNING,\"Current maximum open files is %llu. \"\n                    \"maxclients has been reduced to %d to compensate for \"\n                    \"low ulimit. \"\n                    \"If you need higher maxclients increase 'ulimit -n'.\",\n                    (unsigned long long) oldlimit, server.maxclients);\n            } else {\n                redisLog(REDIS_NOTICE,\"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        }\n    }\n}\n\n/* Initialize a set of file descriptors to listen to the specified 'port'\n * binding the addresses specified in the Redis server configuration.\n *\n * The listening file descriptors are stored in the integer array 'fds'\n * and their number is set in '*count'.\n *\n * The addresses to bind are specified in the global server.bindaddr array\n * and their number is server.bindaddr_count. If the server configuration\n * contains no specific addresses to bind, this function will try to\n * bind * (all addresses) for both the IPv4 and IPv6 protocols.\n *\n * On success the function returns REDIS_OK.\n *\n * On error the function returns REDIS_ERR. For the function to be on\n * error, at least one of the server.bindaddr addresses was\n * impossible to bind, or no bind addresses were specified in the server\n * configuration but the function is not able to bind * for at least\n * one of the IPv4 or IPv6 protocols. */\nint listenToPort(int port, int *fds, int *count) {\n    int j;\n\n    /* Force binding of 0.0.0.0 if no bind address is specified, always\n     * entering the loop if j == 0. */\n    if (server.bindaddr_count == 0) server.bindaddr[0] = NULL;\n    for (j = 0; j < server.bindaddr_count || j == 0; j++) {\n        if (server.bindaddr[j] == NULL) {\n            /* Bind * for both IPv6 and IPv4, we enter here only if\n             * server.bindaddr_count == 0. */\n            fds[*count] = anetTcp6Server(server.neterr,port,NULL,\n                server.tcp_backlog);\n            if (fds[*count] != ANET_ERR) {\n                anetNonBlock(NULL,fds[*count]);\n                (*count)++;\n            }\n            fds[*count] = anetTcpServer(server.neterr,port,NULL,\n                server.tcp_backlog);\n            if (fds[*count] != ANET_ERR) {\n                anetNonBlock(NULL,fds[*count]);\n                (*count)++;\n            }\n            /* Exit the loop if we were able to bind * on IPv4 or IPv6,\n             * otherwise fds[*count] will be ANET_ERR and we'll print an\n             * error and return to the caller with an error. */\n            if (*count) break;\n        } else if (strchr(server.bindaddr[j],':')) {\n            /* Bind IPv6 address. */\n            fds[*count] = anetTcp6Server(server.neterr,port,server.bindaddr[j],\n                server.tcp_backlog);\n        } else {\n            /* Bind IPv4 address. */\n            fds[*count] = anetTcpServer(server.neterr,port,server.bindaddr[j],\n                server.tcp_backlog);\n        }\n        if (fds[*count] == ANET_ERR) {\n            redisLog(REDIS_WARNING,\n                \"Creating Server TCP listening socket %s:%d: %s\",\n                server.bindaddr[j] ? server.bindaddr[j] : \"*\",\n                port, server.neterr);\n            return REDIS_ERR;\n        }\n        anetNonBlock(NULL,fds[*count]);\n        (*count)++;\n    }\n    return REDIS_OK;\n}\n\n/* Resets the stats that we expose via INFO or other means that we want\n * to reset via CONFIG RESETSTAT. The function is also used in order to\n * initialize these fields in initServer() at server startup. */\nvoid resetServerStats(void) {\n    server.stat_numcommands = 0;\n    server.stat_numconnections = 0;\n    server.stat_expiredkeys = 0;\n    server.stat_evictedkeys = 0;\n    server.stat_keyspace_misses = 0;\n    server.stat_keyspace_hits = 0;\n    server.stat_fork_time = 0;\n    server.stat_rejected_conn = 0;\n    server.stat_sync_full = 0;\n    server.stat_sync_partial_ok = 0;\n    server.stat_sync_partial_err = 0;\n    memset(server.ops_sec_samples,0,sizeof(server.ops_sec_samples));\n    server.ops_sec_idx = 0;\n    server.ops_sec_last_sample_time = mstime();\n    server.ops_sec_last_sample_ops = 0;\n}\n\nvoid initServer() {\n    int j;\n\n    // 设置信号处理函数\n    signal(SIGHUP, SIG_IGN);\n    signal(SIGPIPE, SIG_IGN);\n    setupSignalHandlers();\n\n    // 设置 syslog\n    if (server.syslog_enabled) {\n        openlog(server.syslog_ident, LOG_PID | LOG_NDELAY | LOG_NOWAIT,\n            server.syslog_facility);\n    }\n\n    // 初始化并创建数据结构\n    server.current_client = NULL;\n    server.clients = listCreate();\n    server.clients_to_close = listCreate();\n    server.slaves = listCreate();\n    server.monitors = listCreate();\n    server.slaveseldb = -1; /* Force to emit the first SELECT command. */\n    server.unblocked_clients = listCreate();\n    server.ready_keys = listCreate();\n    server.clients_waiting_acks = listCreate();\n    server.get_ack_from_slaves = 0;\n    server.clients_paused = 0;\n\n    // 创建共享对象\n    createSharedObjects();\n    adjustOpenFilesLimit();\n    server.el = aeCreateEventLoop(server.maxclients+REDIS_EVENTLOOP_FDSET_INCR);\n    server.db = zmalloc(sizeof(redisDb)*server.dbnum);\n\n    /* Open the TCP listening socket for the user commands. */\n    // 打开 TCP 监听端口，用于等待客户端的命令请求\n    if (server.port != 0 &&\n        listenToPort(server.port,server.ipfd,&server.ipfd_count) == REDIS_ERR)\n        exit(1);\n\n    /* Open the listening Unix domain socket. */\n    // 打开 UNIX 本地端口\n    if (server.unixsocket != NULL) {\n        unlink(server.unixsocket); /* don't care if this fails */\n        server.sofd = anetUnixServer(server.neterr,server.unixsocket,\n            server.unixsocketperm, server.tcp_backlog);\n        if (server.sofd == ANET_ERR) {\n            redisLog(REDIS_WARNING, \"Opening socket: %s\", server.neterr);\n            exit(1);\n        }\n        anetNonBlock(NULL,server.sofd);\n    }\n\n    /* Abort if there are no listening sockets at all. */\n    if (server.ipfd_count == 0 && server.sofd < 0) {\n        redisLog(REDIS_WARNING, \"Configured to not listen anywhere, exiting.\");\n        exit(1);\n    }\n\n    /* Create the Redis databases, and initialize other internal state. */\n    // 创建并初始化数据库结构\n    for (j = 0; j < server.dbnum; j++) {\n        server.db[j].dict = dictCreate(&dbDictType,NULL);\n        server.db[j].expires = dictCreate(&keyptrDictType,NULL);\n        server.db[j].blocking_keys = dictCreate(&keylistDictType,NULL);\n        server.db[j].ready_keys = dictCreate(&setDictType,NULL);\n        server.db[j].watched_keys = dictCreate(&keylistDictType,NULL);\n        server.db[j].eviction_pool = evictionPoolAlloc();\n        server.db[j].id = j;\n        server.db[j].avg_ttl = 0;\n    }\n\n    // 创建 PUBSUB 相关结构\n    server.pubsub_channels = dictCreate(&keylistDictType,NULL);\n    server.pubsub_patterns = listCreate();\n    listSetFreeMethod(server.pubsub_patterns,freePubsubPattern);\n    listSetMatchMethod(server.pubsub_patterns,listMatchPubsubPattern);\n\n    server.cronloops = 0;\n    server.rdb_child_pid = -1;\n    server.aof_child_pid = -1;\n    aofRewriteBufferReset();\n    server.aof_buf = sdsempty();\n    server.lastsave = time(NULL); /* At startup we consider the DB saved. */\n    server.lastbgsave_try = 0;    /* At startup we never tried to BGSAVE. */\n    server.rdb_save_time_last = -1;\n    server.rdb_save_time_start = -1;\n    server.dirty = 0;\n    resetServerStats();\n    /* A few stats we don't want to reset: server startup time, and peak mem. */\n    server.stat_starttime = time(NULL);\n    server.stat_peak_memory = 0;\n    server.resident_set_size = 0;\n    server.lastbgsave_status = REDIS_OK;\n    server.aof_last_write_status = REDIS_OK;\n    server.aof_last_write_errno = 0;\n    server.repl_good_slaves_count = 0;\n    updateCachedTime();\n\n    /* Create the serverCron() time event, that's our main way to process\n     * background operations. */\n    // 为 serverCron() 创建时间事件\n    if(aeCreateTimeEvent(server.el, 1, serverCron, NULL, NULL) == AE_ERR) {\n        redisPanic(\"Can't create the serverCron time event.\");\n        exit(1);\n    }\n\n    /* Create an event handler for accepting new connections in TCP and Unix\n     * domain sockets. */\n    // 为 TCP 连接关联连接应答（accept）处理器\n    // 用于接受并应答客户端的 connect() 调用\n    for (j = 0; j < server.ipfd_count; j++) {\n        if (aeCreateFileEvent(server.el, server.ipfd[j], AE_READABLE,\n            acceptTcpHandler,NULL) == AE_ERR)\n            {\n                redisPanic(\n                    \"Unrecoverable error creating server.ipfd file event.\");\n            }\n    }\n\n    // 为本地套接字关联应答处理器\n    if (server.sofd > 0 && aeCreateFileEvent(server.el,server.sofd,AE_READABLE,\n        acceptUnixHandler,NULL) == AE_ERR) redisPanic(\"Unrecoverable error creating server.sofd file event.\");\n\n    /* Open the AOF file if needed. */\n    // 如果 AOF 持久化功能已经打开，那么打开或创建一个 AOF 文件\n    if (server.aof_state == REDIS_AOF_ON) {\n        server.aof_fd = open(server.aof_filename,\n                               O_WRONLY|O_APPEND|O_CREAT,0644);\n        if (server.aof_fd == -1) {\n            redisLog(REDIS_WARNING, \"Can't open the append-only file: %s\",\n                strerror(errno));\n            exit(1);\n        }\n    }\n\n    /* 32 bit instances are limited to 4GB of address space, so if there is\n     * no explicit limit in the user provided configuration we set a limit\n     * at 3 GB using maxmemory with 'noeviction' policy'. This avoids\n     * useless crashes of the Redis instance for out of memory. */\n    // 对于 32 位实例来说，默认将最大可用内存限制在 3 GB\n    if (server.arch_bits == 32 && server.maxmemory == 0) {\n        redisLog(REDIS_WARNING,\"Warning: 32 bit instance detected but no memory limit set. Setting 3 GB maxmemory limit with 'noeviction' policy now.\");\n        server.maxmemory = 3072LL*(1024*1024); /* 3 GB */\n        server.maxmemory_policy = REDIS_MAXMEMORY_NO_EVICTION;\n    }\n\n    // 如果服务器以 cluster 模式打开，那么初始化 cluster\n    if (server.cluster_enabled) clusterInit();\n\n    // 初始化复制功能有关的脚本缓存\n    replicationScriptCacheInit();\n\n    // 初始化脚本系统\n    scriptingInit();\n\n    // 初始化慢查询功能\n    slowlogInit();\n\n    // 初始化 BIO 系统\n    bioInit();\n}\n\n/* Populates the Redis Command Table starting from the hard coded list\n * we have on top of redis.c file. \n *\n * 根据 redis.c 文件顶部的命令列表，创建命令表\n */\nvoid populateCommandTable(void) {\n    int j;\n\n    // 命令的数量\n    int numcommands = sizeof(redisCommandTable)/sizeof(struct redisCommand);\n\n    for (j = 0; j < numcommands; j++) {\n        \n        // 指定命令\n        struct redisCommand *c = redisCommandTable+j;\n\n        // 取出字符串 FLAG\n        char *f = c->sflags;\n\n        int retval1, retval2;\n\n        // 根据字符串 FLAG 生成实际 FLAG\n        while(*f != '\\0') {\n            switch(*f) {\n            case 'w': c->flags |= REDIS_CMD_WRITE; break;\n            case 'r': c->flags |= REDIS_CMD_READONLY; break;\n            case 'm': c->flags |= REDIS_CMD_DENYOOM; break;\n            case 'a': c->flags |= REDIS_CMD_ADMIN; break;\n            case 'p': c->flags |= REDIS_CMD_PUBSUB; break;\n            case 's': c->flags |= REDIS_CMD_NOSCRIPT; break;\n            case 'R': c->flags |= REDIS_CMD_RANDOM; break;\n            case 'S': c->flags |= REDIS_CMD_SORT_FOR_SCRIPT; break;\n            case 'l': c->flags |= REDIS_CMD_LOADING; break;\n            case 't': c->flags |= REDIS_CMD_STALE; break;\n            case 'M': c->flags |= REDIS_CMD_SKIP_MONITOR; break;\n            case 'k': c->flags |= REDIS_CMD_ASKING; break;\n            default: redisPanic(\"Unsupported command flag\"); break;\n            }\n            f++;\n        }\n\n        // 将命令关联到命令表\n        retval1 = dictAdd(server.commands, sdsnew(c->name), c);\n\n        /* Populate an additional dictionary that will be unaffected\n         * by rename-command statements in redis.conf. \n         *\n         * 将命令也关联到原始命令表\n         *\n         * 原始命令表不会受 redis.conf 中命令改名的影响\n         */\n        retval2 = dictAdd(server.orig_commands, sdsnew(c->name), c);\n\n        redisAssert(retval1 == DICT_OK && retval2 == DICT_OK);\n    }\n}\n\n/*\n * 重置命令表中的统计信息\n */\nvoid resetCommandTableStats(void) {\n    int numcommands = sizeof(redisCommandTable)/sizeof(struct redisCommand);\n    int j;\n\n    for (j = 0; j < numcommands; j++) {\n        struct redisCommand *c = redisCommandTable+j;\n\n        // 清零时间\n        c->microseconds = 0;\n\n        // 清零调用次数\n        c->calls = 0;\n    }\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 = zrealloc(oa->ops,sizeof(redisOp)*(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        zfree(op->argv);\n    }\n    zfree(oa->ops);\n}\n\n/* ====================== Commands lookup and execution ===================== */\n\n/*\n * 根据给定命令名字（SDS），查找命令\n */\nstruct redisCommand *lookupCommand(sds name) {\n    return dictFetchValue(server.commands, name);\n}\n\n/*\n * 根据给定命令名字（C 字符串），查找命令\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/* 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 * 从当前命令表 server.commands 中查找给定名字，\n * 如果没找到的话，就尝试从 server.orig_commands 中查找未被改名的原始名字\n * 原始表中的命令名不受 redis.conf 中命令改名的影响\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. \n *\n * 这个函数可以在命令被更名之后，仍然在重写命令时得出正确的名字。\n */\nstruct redisCommand *lookupCommandOrOriginal(sds name) {\n\n    // 查找当前表\n    struct redisCommand *cmd = dictFetchValue(server.commands, name);\n\n    // 如果有需要的话，查找原始表\n    if (!cmd) cmd = dictFetchValue(server.orig_commands,name);\n\n    return cmd;\n}\n\n/* Propagate the specified command (in the context of the specified database id)\n * to AOF and Slaves.\n *\n * 将指定命令（以及执行该命令的上下文，比如数据库 id 等信息）传播到 AOF 和 slave 。\n *\n * flags are an xor between:\n * FLAG 可以是以下标识的 xor ：\n *\n * + REDIS_PROPAGATE_NONE (no propagation of command at all)\n *   不传播\n *\n * + REDIS_PROPAGATE_AOF (propagate into the AOF file if is enabled)\n *   传播到 AOF\n *\n * + REDIS_PROPAGATE_REPL (propagate into the replication link)\n *   传播到 slave\n */\nvoid propagate(struct redisCommand *cmd, int dbid, robj **argv, int argc,\n               int flags)\n{\n    // 传播到 AOF\n    if (server.aof_state != REDIS_AOF_OFF && flags & REDIS_PROPAGATE_AOF)\n        feedAppendOnlyFile(cmd,dbid,argv,argc);\n\n    // 传播到 slave\n    if (flags & REDIS_PROPAGATE_REPL)\n        replicationFeedSlaves(server.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. */\nvoid alsoPropagate(struct redisCommand *cmd, int dbid, robj **argv, int argc,\n                   int target)\n{\n    redisOpArrayAppend(&server.also_propagate,cmd,dbid,argv,argc,target);\n}\n\n/* It is possible to call the function forceCommandPropagation() inside a\n * Redis command implementaiton in order to to force the propagation of a\n * specific command execution into AOF / Replication. */\nvoid forceCommandPropagation(redisClient *c, int flags) {\n    if (flags & REDIS_PROPAGATE_REPL) c->flags |= REDIS_FORCE_REPL;\n    if (flags & REDIS_PROPAGATE_AOF) c->flags |= REDIS_FORCE_AOF;\n}\n\n/* Call() is the core of Redis execution of a command */\n// 调用命令的实现函数，执行命令\nvoid call(redisClient *c, int flags) {\n    // start 记录命令开始执行的时间\n    long long dirty, start, duration;\n    // 记录命令开始执行前的 FLAG\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    // 如果可以的话，将命令发送到 MONITOR\n    if (listLength(server.monitors) &&\n        !server.loading &&\n        !(c->cmd->flags & REDIS_CMD_SKIP_MONITOR))\n    {\n        replicationFeedMonitors(c,server.monitors,c->db->id,c->argv,c->argc);\n    }\n\n    /* Call the command. */\n    c->flags &= ~(REDIS_FORCE_AOF|REDIS_FORCE_REPL);\n    redisOpArrayInit(&server.also_propagate);\n    // 保留旧 dirty 计数器值\n    dirty = server.dirty;\n    // 计算命令开始执行的时间\n    start = ustime();\n    // 执行实现函数\n    c->cmd->proc(c);\n    // 计算命令执行耗费的时间\n    duration = ustime()-start;\n    // 计算命令执行之后的 dirty 值\n    dirty = server.dirty-dirty;\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    // 不将从 Lua 中发出的命令放入 SLOWLOG ，也不进行统计\n    if (server.loading && c->flags & REDIS_LUA_CLIENT)\n        flags &= ~(REDIS_CALL_SLOWLOG | REDIS_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    // 如果调用者是 Lua ，那么根据命令 FLAG 和客户端 FLAG\n    // 打开传播（propagate)标志\n    if (c->flags & REDIS_LUA_CLIENT && server.lua_caller) {\n        if (c->flags & REDIS_FORCE_REPL)\n            server.lua_caller->flags |= REDIS_FORCE_REPL;\n        if (c->flags & REDIS_FORCE_AOF)\n            server.lua_caller->flags |= REDIS_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    // 如果有需要，将命令放到 SLOWLOG 里面\n    if (flags & REDIS_CALL_SLOWLOG && c->cmd->proc != execCommand)\n        slowlogPushEntryIfNeeded(c->argv,c->argc,duration);\n    // 更新命令的统计信息\n    if (flags & REDIS_CALL_STATS) {\n        c->cmd->microseconds += duration;\n        c->cmd->calls++;\n    }\n\n    /* Propagate the command into the AOF and replication link */\n    // 将命令复制到 AOF 和 slave 节点\n    if (flags & REDIS_CALL_PROPAGATE) {\n        int flags = REDIS_PROPAGATE_NONE;\n\n        // 强制 REPL 传播\n        if (c->flags & REDIS_FORCE_REPL) flags |= REDIS_PROPAGATE_REPL;\n\n        // 强制 AOF 传播\n        if (c->flags & REDIS_FORCE_AOF) flags |= REDIS_PROPAGATE_AOF;\n\n        // 如果数据库有被修改，那么启用 REPL 和 AOF 传播\n        if (dirty)\n            flags |= (REDIS_PROPAGATE_REPL | REDIS_PROPAGATE_AOF);\n\n        if (flags != REDIS_PROPAGATE_NONE)\n            propagate(c->cmd,c->db->id,c->argv,c->argc,flags);\n    }\n\n    /* Restore the old FORCE_AOF/REPL flags, since call can be executed\n     * recursively. */\n    // 将客户端的 FLAG 恢复到命令执行之前\n    // 因为 call 可能会递归执行\n    c->flags &= ~(REDIS_FORCE_AOF|REDIS_FORCE_REPL);\n    c->flags |= client_old_flags & (REDIS_FORCE_AOF|REDIS_FORCE_REPL);\n\n    /* Handle the alsoPropagate() API to handle commands that want to propagate\n     * multiple separated commands. */\n    // 传播额外的命令\n    if (server.also_propagate.numops) {\n        int j;\n        redisOp *rop;\n\n        for (j = 0; j < server.also_propagate.numops; j++) {\n            rop = &server.also_propagate.ops[j];\n            propagate(rop->cmd, rop->dbid, rop->argv, rop->argc, rop->target);\n        }\n        redisOpArrayFree(&server.also_propagate);\n    }\n    server.stat_numcommands++;\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 * 这个函数执行时，我们已经读入了一个完整的命令到客户端，\n * 这个函数负责执行这个命令，\n * 或者服务器准备从客户端中进行一次读取。\n *\n * If 1 is returned the client is still alive and valid and\n * other operations can be performed by the caller. Otherwise\n * if 0 is returned the client was destroyed (i.e. after QUIT). \n *\n * 如果这个函数返回 1 ，那么表示客户端在执行命令之后仍然存在，\n * 调用者可以继续执行其他操作。\n * 否则，如果这个函数返回 0 ，那么表示客户端已经被销毁。\n */\nint processCommand(redisClient *c) {\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    // 特别处理 quit 命令\n    if (!strcasecmp(c->argv[0]->ptr,\"quit\")) {\n        addReply(c,shared.ok);\n        c->flags |= REDIS_CLOSE_AFTER_REPLY;\n        return REDIS_ERR;\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    // 查找命令，并进行命令合法性检查，以及命令参数个数检查\n    c->cmd = c->lastcmd = lookupCommand(c->argv[0]->ptr);\n    if (!c->cmd) {\n        // 没找到指定的命令\n        flagTransaction(c);\n        addReplyErrorFormat(c,\"unknown command '%s'\",\n            (char*)c->argv[0]->ptr);\n        return REDIS_OK;\n    } else if ((c->cmd->arity > 0 && c->cmd->arity != c->argc) ||\n               (c->argc < -c->cmd->arity)) {\n        // 参数个数错误\n        flagTransaction(c);\n        addReplyErrorFormat(c,\"wrong number of arguments for '%s' command\",\n            c->cmd->name);\n        return REDIS_OK;\n    }\n\n    /* Check if the user is authenticated */\n    // 检查认证信息\n    if (server.requirepass && !c->authenticated && c->cmd->proc != authCommand)\n    {\n        flagTransaction(c);\n        addReply(c,shared.noautherr);\n        return REDIS_OK;\n    }\n\n    /* If cluster is enabled perform the cluster redirection here.\n     *\n     * 如果开启了集群模式，那么在这里进行转向操作。\n     *\n     * However we don't perform the redirection if:\n     *\n     * 不过，如果有以下情况出现，那么节点不进行转向：\n     *\n     * 1) The sender of this command is our master.\n     *    命令的发送者是本节点的主节点\n     *\n     * 2) The command has no key arguments. \n     *    命令没有 key 参数\n     */\n    if (server.cluster_enabled &&\n        !(c->flags & REDIS_MASTER) &&\n        !(c->cmd->getkeys_proc == NULL && c->cmd->firstkey == 0))\n    {\n        int hashslot;\n\n        // 集群已下线\n        if (server.cluster->state != REDIS_CLUSTER_OK) {\n            flagTransaction(c);\n            addReplySds(c,sdsnew(\"-CLUSTERDOWN The cluster is down. Use CLUSTER INFO for more information\\r\\n\"));\n            return REDIS_OK;\n\n        // 集群运作正常\n        } else {\n            int error_code;\n            clusterNode *n = getNodeByQuery(c,c->cmd,c->argv,c->argc,&hashslot,&error_code);\n            // 不能执行多键处理命令\n            if (n == NULL) {\n                flagTransaction(c);\n                if (error_code == REDIS_CLUSTER_REDIR_CROSS_SLOT) {\n                    addReplySds(c,sdsnew(\"-CROSSSLOT Keys in request don't hash to the same slot\\r\\n\"));\n                } else if (error_code == REDIS_CLUSTER_REDIR_UNSTABLE) {\n                    /* The request spawns mutliple keys in the same slot,\n                     * but the slot is not \"stable\" currently as there is\n                     * a migration or import in progress. */\n                    addReplySds(c,sdsnew(\"-TRYAGAIN Multiple keys request during rehashing of slot\\r\\n\"));\n                } else {\n                    redisPanic(\"getNodeByQuery() unknown error.\");\n                }\n                return REDIS_OK;\n\n            // 命令针对的槽和键不是本节点处理的，进行转向\n            } else if (n != server.cluster->myself) {\n                flagTransaction(c);\n                // -<ASK or MOVED> <slot> <ip>:<port>\n                // 例如 -ASK 10086 127.0.0.1:12345\n                addReplySds(c,sdscatprintf(sdsempty(),\n                    \"-%s %d %s:%d\\r\\n\",\n                    (error_code == REDIS_CLUSTER_REDIR_ASK) ? \"ASK\" : \"MOVED\",\n                    hashslot,n->ip,n->port));\n\n                return REDIS_OK;\n            }\n\n            // 如果执行到这里，说明键 key 所在的槽由本节点处理\n            // 或者客户端执行的是无参数命令\n        }\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    // 如果设置了最大内存，那么检查内存是否超过限制，并做相应的操作\n    if (server.maxmemory) {\n        // 如果内存已超过限制，那么尝试通过删除过期键来释放内存\n        int retval = freeMemoryIfNeeded();\n        // 如果即将要执行的命令可能占用大量内存（REDIS_CMD_DENYOOM）\n        // 并且前面的内存释放失败的话\n        // 那么向客户端返回内存错误\n        if ((c->cmd->flags & REDIS_CMD_DENYOOM) && retval == REDIS_ERR) {\n            flagTransaction(c);\n            addReply(c, shared.oomerr);\n            return REDIS_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    // 如果这是一个主服务器，并且这个服务器之前执行 BGSAVE 时发生了错误\n    // 那么不执行写命令\n    if (((server.stop_writes_on_bgsave_err &&\n          server.saveparamslen > 0 &&\n          server.lastbgsave_status == REDIS_ERR) ||\n          server.aof_last_write_status == REDIS_ERR) &&\n        server.masterhost == NULL &&\n        (c->cmd->flags & REDIS_CMD_WRITE ||\n         c->cmd->proc == pingCommand))\n    {\n        flagTransaction(c);\n        if (server.aof_last_write_status == REDIS_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 REDIS_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    // 如果服务器没有足够多的状态良好服务器\n    // 并且 min-slaves-to-write 选项已打开\n    if (server.repl_min_slaves_to_write &&\n        server.repl_min_slaves_max_lag &&\n        c->cmd->flags & REDIS_CMD_WRITE &&\n        server.repl_good_slaves_count < server.repl_min_slaves_to_write)\n    {\n        flagTransaction(c);\n        addReply(c, shared.noreplicaserr);\n        return REDIS_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    // 如果这个服务器是一个只读 slave 的话，那么拒绝执行写命令\n    if (server.masterhost && server.repl_slave_ro &&\n        !(c->flags & REDIS_MASTER) &&\n        c->cmd->flags & REDIS_CMD_WRITE)\n    {\n        addReply(c, shared.roslaveerr);\n        return REDIS_OK;\n    }\n\n    /* Only allow SUBSCRIBE and UNSUBSCRIBE in the context of Pub/Sub */\n    // 在订阅于发布模式的上下文中，只能执行订阅和退订相关的命令\n    if ((dictSize(c->pubsub_channels) > 0 || listLength(c->pubsub_patterns) > 0)\n        &&\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 / QUIT allowed in this context\");\n        return REDIS_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 (server.masterhost && server.repl_state != REDIS_REPL_CONNECTED &&\n        server.repl_serve_stale_data == 0 &&\n        !(c->cmd->flags & REDIS_CMD_STALE))\n    {\n        flagTransaction(c);\n        addReply(c, shared.masterdownerr);\n        return REDIS_OK;\n    }\n\n    /* Loading DB? Return an error if the command has not the\n     * REDIS_CMD_LOADING flag. */\n    // 如果服务器正在载入数据到数据库，那么只执行带有 REDIS_CMD_LOADING\n    // 标识的命令，否则将出错\n    if (server.loading && !(c->cmd->flags & REDIS_CMD_LOADING)) {\n        addReply(c, shared.loadingerr);\n        return REDIS_OK;\n    }\n\n    /* Lua script too slow? Only allow a limited number of commands. */\n    // Lua 脚本超时，只允许执行限定的操作，比如 SHUTDOWN 和 SCRIPT KILL\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 REDIS_OK;\n    }\n\n    /* Exec the command */\n    if (c->flags & REDIS_MULTI &&\n        c->cmd->proc != execCommand && c->cmd->proc != discardCommand &&\n        c->cmd->proc != multiCommand && c->cmd->proc != watchCommand)\n    {\n        // 在事务上下文中\n        // 除 EXEC 、 DISCARD 、 MULTI 和 WATCH 命令之外\n        // 其他所有命令都会被入队到事务队列中\n        queueMultiCommand(c);\n        addReply(c,shared.queued);\n    } else {\n        // 执行命令\n        call(c,REDIS_CALL_FULL);\n\n        c->woff = server.master_repl_offset;\n        // 处理那些解除了阻塞的键\n        if (listLength(server.ready_keys))\n            handleClientsBlockedOnLists();\n    }\n\n    return REDIS_OK;\n}\n\n/*================================== Shutdown =============================== */\n\n/* Close listening sockets. Also unlink the unix domain socket if\n * unlink_unix_socket is non-zero. */\n// 关闭监听套接字\nvoid closeListeningSockets(int unlink_unix_socket) {\n    int j;\n\n    for (j = 0; j < server.ipfd_count; j++) close(server.ipfd[j]);\n\n    if (server.sofd != -1) close(server.sofd);\n\n    if (server.cluster_enabled)\n        for (j = 0; j < server.cfd_count; j++) close(server.cfd[j]);\n\n    if (unlink_unix_socket && server.unixsocket) {\n        redisLog(REDIS_NOTICE,\"Removing the unix socket file.\");\n        unlink(server.unixsocket); /* don't care if this fails */\n    }\n}\n\nint prepareForShutdown(int flags) {\n    int save = flags & REDIS_SHUTDOWN_SAVE;\n    int nosave = flags & REDIS_SHUTDOWN_NOSAVE;\n\n    redisLog(REDIS_WARNING,\"User requested shutdown...\");\n\n    /* Kill the saving child if there is a background saving in progress.\n       We want to avoid race conditions, for instance our saving child may\n       overwrite the synchronous saving did by SHUTDOWN. */\n    // 如果有 BGSAVE 正在执行，那么杀死子进程，避免竞争条件\n    if (server.rdb_child_pid != -1) {\n        redisLog(REDIS_WARNING,\"There is a child saving an .rdb. Killing it!\");\n        kill(server.rdb_child_pid,SIGUSR1);\n        rdbRemoveTempFile(server.rdb_child_pid);\n    }\n\n    // 同理，杀死正在执行 BGREWRITEAOF 的子进程\n    if (server.aof_state != REDIS_AOF_OFF) {\n        /* Kill the AOF saving child as the AOF we already have may be longer\n         * but contains the full dataset anyway. */\n        if (server.aof_child_pid != -1) {\n            redisLog(REDIS_WARNING,\n                \"There is a child rewriting the AOF. Killing it!\");\n            kill(server.aof_child_pid,SIGUSR1);\n        }\n        /* Append only file: fsync() the AOF and exit */\n        redisLog(REDIS_NOTICE,\"Calling fsync() on the AOF file.\");\n        // 将缓冲区的内容写入到硬盘里面\n        aof_fsync(server.aof_fd);\n    }\n\n    // 如果客户端执行的是 SHUTDOWN save ，或者 SAVE 功能被打开\n    // 那么执行 SAVE 操作\n    if ((server.saveparamslen > 0 && !nosave) || save) {\n        redisLog(REDIS_NOTICE,\"Saving the final RDB snapshot before exiting.\");\n        /* Snapshotting. Perform a SYNC SAVE and exit */\n        if (rdbSave(server.rdb_filename) != REDIS_OK) {\n            /* Ooops.. error saving! The best we can do is to continue\n             * operating. Note that if there was a background saving process,\n             * in the next cron() Redis will be notified that the background\n             * saving aborted, handling special stuff like slaves pending for\n             * synchronization... */\n            redisLog(REDIS_WARNING,\"Error trying to save the DB, can't exit.\");\n            return REDIS_ERR;\n        }\n    }\n\n    // 移除 pidfile 文件\n    if (server.daemonize) {\n        redisLog(REDIS_NOTICE,\"Removing the pid file.\");\n        unlink(server.pidfile);\n    }\n\n    /* Close the listening sockets. Apparently this allows faster restarts. */\n    // 关闭监听套接字，这样在重启的时候会快一点\n    closeListeningSockets(1);\n    redisLog(REDIS_WARNING,\"%s is now ready to exit, bye bye...\",\n        server.sentinel_mode ? \"Sentinel\" : \"Redis\");\n    return REDIS_OK;\n}\n\n/*================================== Commands =============================== */\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[REDIS_AUTHPASS_MAX_LEN], bufb[REDIS_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    int alen = strlen(a);\n    int blen = strlen(b);\n    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(redisClient *c) {\n    if (!server.requirepass) {\n        addReplyError(c,\"Client sent AUTH, but no password is set\");\n    } else if (!time_independent_strcmp(c->argv[1]->ptr, server.requirepass)) {\n      c->authenticated = 1;\n      addReply(c,shared.ok);\n    } else {\n      c->authenticated = 0;\n      addReplyError(c,\"invalid password\");\n    }\n}\n\nvoid pingCommand(redisClient *c) {\n    addReply(c,shared.pong);\n}\n\nvoid echoCommand(redisClient *c) {\n    addReplyBulk(c,c->argv[1]);\n}\n\nvoid timeCommand(redisClient *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/* 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    }\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 genRedisInfoString(char *section) {\n    sds info = sdsempty();\n    time_t uptime = server.unixtime-server.stat_starttime;\n    int j, numcommands;\n    struct rusage self_ru, c_ru;\n    unsigned long lol, bib;\n    int allsections = 0, defsections = 0;\n    int sections = 0;\n\n    if (section) {\n        allsections = strcasecmp(section,\"all\") == 0;\n        defsections = strcasecmp(section,\"default\") == 0;\n    }\n\n    getrusage(RUSAGE_SELF, &self_ru);\n    getrusage(RUSAGE_CHILDREN, &c_ru);\n    getClientsMaxBuffers(&lol,&bib);\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        if (server.cluster_enabled) mode = \"cluster\";\n        else if (server.sentinel_mode) mode = \"sentinel\";\n        else 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            \"redis_version:%s\\r\\n\"\n            \"redis_git_sha1:%s\\r\\n\"\n            \"redis_git_dirty:%d\\r\\n\"\n            \"redis_build_id:%llx\\r\\n\"\n            \"redis_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            \"lru_clock:%ld\\r\\n\"\n            \"config_file:%s\\r\\n\",\n            REDIS_VERSION,\n            redisGitSHA1(),\n            strtol(redisGitDirty(),NULL,10) > 0,\n            (unsigned long long) redisBuildId(),\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            (unsigned long) server.lruclock,\n            server.configfile ? server.configfile : \"\");\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:%lu\\r\\n\"\n            \"client_longest_output_list:%lu\\r\\n\"\n            \"client_biggest_input_buf:%lu\\r\\n\"\n            \"blocked_clients:%d\\r\\n\",\n            listLength(server.clients)-listLength(server.slaves),\n            lol, bib,\n            server.bpop_blocked_clients);\n    }\n\n    /* Memory */\n    if (allsections || defsections || !strcasecmp(section,\"memory\")) {\n        char hmem[64];\n        char peak_hmem[64];\n        size_t zmalloc_used = zmalloc_used_memory();\n\n        /* Peak memory is updated from time to time by serverCron() 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        if (zmalloc_used > server.stat_peak_memory)\n            server.stat_peak_memory = zmalloc_used;\n\n        bytesToHuman(hmem,zmalloc_used);\n        bytesToHuman(peak_hmem,server.stat_peak_memory);\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_peak:%zu\\r\\n\"\n            \"used_memory_peak_human:%s\\r\\n\"\n            \"used_memory_lua:%lld\\r\\n\"\n            \"mem_fragmentation_ratio:%.2f\\r\\n\"\n            \"mem_allocator:%s\\r\\n\",\n            zmalloc_used,\n            hmem,\n            server.resident_set_size,\n            server.stat_peak_memory,\n            peak_hmem,\n            ((long long)lua_gc(server.lua,LUA_GCCOUNT,0))*1024LL,\n            zmalloc_get_fragmentation_ratio(server.resident_set_size),\n            ZMALLOC_LIB\n            );\n    }\n\n    /* Persistence */\n    if (allsections || defsections || !strcasecmp(section,\"persistence\")) {\n        if (sections++) info = sdscat(info,\"\\r\\n\");\n        info = sdscatprintf(info,\n            \"# Persistence\\r\\n\"\n            \"loading:%d\\r\\n\"\n            \"rdb_changes_since_last_save:%lld\\r\\n\"\n            \"rdb_bgsave_in_progress:%d\\r\\n\"\n            \"rdb_last_save_time:%jd\\r\\n\"\n            \"rdb_last_bgsave_status:%s\\r\\n\"\n            \"rdb_last_bgsave_time_sec:%jd\\r\\n\"\n            \"rdb_current_bgsave_time_sec:%jd\\r\\n\"\n            \"aof_enabled:%d\\r\\n\"\n            \"aof_rewrite_in_progress:%d\\r\\n\"\n            \"aof_rewrite_scheduled:%d\\r\\n\"\n            \"aof_last_rewrite_time_sec:%jd\\r\\n\"\n            \"aof_current_rewrite_time_sec:%jd\\r\\n\"\n            \"aof_last_bgrewrite_status:%s\\r\\n\"\n            \"aof_last_write_status:%s\\r\\n\",\n            server.loading,\n            server.dirty,\n            server.rdb_child_pid != -1,\n            (intmax_t)server.lastsave,\n            (server.lastbgsave_status == REDIS_OK) ? \"ok\" : \"err\",\n            (intmax_t)server.rdb_save_time_last,\n            (intmax_t)((server.rdb_child_pid == -1) ?\n                -1 : time(NULL)-server.rdb_save_time_start),\n            server.aof_state != REDIS_AOF_OFF,\n            server.aof_child_pid != -1,\n            server.aof_rewrite_scheduled,\n            (intmax_t)server.aof_rewrite_time_last,\n            (intmax_t)((server.aof_child_pid == -1) ?\n                -1 : time(NULL)-server.aof_rewrite_time_start),\n            (server.aof_lastbgrewrite_status == REDIS_OK) ? \"ok\" : \"err\",\n            (server.aof_last_write_status == REDIS_OK) ? \"ok\" : \"err\");\n\n        if (server.aof_state != REDIS_AOF_OFF) {\n            info = sdscatprintf(info,\n                \"aof_current_size:%lld\\r\\n\"\n                \"aof_base_size:%lld\\r\\n\"\n                \"aof_pending_rewrite:%d\\r\\n\"\n                \"aof_buffer_length:%zu\\r\\n\"\n                \"aof_rewrite_buffer_length:%lu\\r\\n\"\n                \"aof_pending_bio_fsync:%llu\\r\\n\"\n                \"aof_delayed_fsync:%lu\\r\\n\",\n                (long long) server.aof_current_size,\n                (long long) server.aof_rewrite_base_size,\n                server.aof_rewrite_scheduled,\n                sdslen(server.aof_buf),\n                aofRewriteBufferSize(),\n                bioPendingJobsOfType(REDIS_BIO_AOF_FSYNC),\n                server.aof_delayed_fsync);\n        }\n\n        if (server.loading) {\n            double perc;\n            time_t eta, elapsed;\n            off_t remaining_bytes = server.loading_total_bytes-\n                                    server.loading_loaded_bytes;\n\n            perc = ((double)server.loading_loaded_bytes /\n                   server.loading_total_bytes) * 100;\n\n            elapsed = server.unixtime-server.loading_start_time;\n            if (elapsed == 0) {\n                eta = 1; /* A fake 1 second figure if we don't have\n                            enough info */\n            } else {\n                eta = (elapsed*remaining_bytes)/server.loading_loaded_bytes;\n            }\n\n            info = sdscatprintf(info,\n                \"loading_start_time:%jd\\r\\n\"\n                \"loading_total_bytes:%llu\\r\\n\"\n                \"loading_loaded_bytes:%llu\\r\\n\"\n                \"loading_loaded_perc:%.2f\\r\\n\"\n                \"loading_eta_seconds:%jd\\r\\n\",\n                (intmax_t) server.loading_start_time,\n                (unsigned long long) server.loading_total_bytes,\n                (unsigned long long) server.loading_loaded_bytes,\n                perc,\n                (intmax_t)eta\n            );\n        }\n    }\n\n    /* Stats */\n    if (allsections || defsections || !strcasecmp(section,\"stats\")) {\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            \"rejected_connections:%lld\\r\\n\"\n            \"sync_full:%lld\\r\\n\"\n            \"sync_partial_ok:%lld\\r\\n\"\n            \"sync_partial_err:%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            \"pubsub_channels:%ld\\r\\n\"\n            \"pubsub_patterns:%lu\\r\\n\"\n            \"latest_fork_usec:%lld\\r\\n\"\n            \"migrate_cached_sockets:%ld\\r\\n\",\n            server.stat_numconnections,\n            server.stat_numcommands,\n            getOperationsPerSecond(),\n            server.stat_rejected_conn,\n            server.stat_sync_full,\n            server.stat_sync_partial_ok,\n            server.stat_sync_partial_err,\n            server.stat_expiredkeys,\n            server.stat_evictedkeys,\n            server.stat_keyspace_hits,\n            server.stat_keyspace_misses,\n            dictSize(server.pubsub_channels),\n            listLength(server.pubsub_patterns),\n            server.stat_fork_time,\n            dictSize(server.migrate_cached_sockets));\n    }\n\n    /* Replication */\n    if (allsections || defsections || !strcasecmp(section,\"replication\")) {\n        if (sections++) info = sdscat(info,\"\\r\\n\");\n        info = sdscatprintf(info,\n            \"# Replication\\r\\n\"\n            \"role:%s\\r\\n\",\n            server.masterhost == NULL ? \"master\" : \"slave\");\n        if (server.masterhost) {\n            long long slave_repl_offset = 1;\n\n            if (server.master)\n                slave_repl_offset = server.master->reploff;\n            else if (server.cached_master)\n                slave_repl_offset = server.cached_master->reploff;\n\n            info = sdscatprintf(info,\n                \"master_host:%s\\r\\n\"\n                \"master_port:%d\\r\\n\"\n                \"master_link_status:%s\\r\\n\"\n                \"master_last_io_seconds_ago:%d\\r\\n\"\n                \"master_sync_in_progress:%d\\r\\n\"\n                \"slave_repl_offset:%lld\\r\\n\"\n                ,server.masterhost,\n                server.masterport,\n                (server.repl_state == REDIS_REPL_CONNECTED) ?\n                    \"up\" : \"down\",\n                server.master ?\n                ((int)(server.unixtime-server.master->lastinteraction)) : -1,\n                server.repl_state == REDIS_REPL_TRANSFER,\n                slave_repl_offset\n            );\n\n            if (server.repl_state == REDIS_REPL_TRANSFER) {\n                info = sdscatprintf(info,\n                    \"master_sync_left_bytes:%lld\\r\\n\"\n                    \"master_sync_last_io_seconds_ago:%d\\r\\n\"\n                    , (long long)\n                        (server.repl_transfer_size - server.repl_transfer_read),\n                    (int)(server.unixtime-server.repl_transfer_lastio)\n                );\n            }\n\n            if (server.repl_state != REDIS_REPL_CONNECTED) {\n                info = sdscatprintf(info,\n                    \"master_link_down_since_seconds:%jd\\r\\n\",\n                    (intmax_t)server.unixtime-server.repl_down_since);\n            }\n            info = sdscatprintf(info,\n                \"slave_priority:%d\\r\\n\"\n                \"slave_read_only:%d\\r\\n\",\n                server.slave_priority,\n                server.repl_slave_ro);\n        }\n\n        info = sdscatprintf(info,\n            \"connected_slaves:%lu\\r\\n\",\n            listLength(server.slaves));\n\n        /* If min-slaves-to-write is active, write the number of slaves\n         * currently considered 'good'. */\n        if (server.repl_min_slaves_to_write &&\n            server.repl_min_slaves_max_lag) {\n            info = sdscatprintf(info,\n                \"min_slaves_good_slaves:%d\\r\\n\",\n                server.repl_good_slaves_count);\n        }\n\n        if (listLength(server.slaves)) {\n            int slaveid = 0;\n            listNode *ln;\n            listIter li;\n\n            listRewind(server.slaves,&li);\n            while((ln = listNext(&li))) {\n                redisClient *slave = listNodeValue(ln);\n                char *state = NULL;\n                char ip[REDIS_IP_STR_LEN];\n                int port;\n                long lag = 0;\n\n                if (anetPeerToString(slave->fd,ip,sizeof(ip),&port) == -1) continue;\n                switch(slave->replstate) {\n                case REDIS_REPL_WAIT_BGSAVE_START:\n                case REDIS_REPL_WAIT_BGSAVE_END:\n                    state = \"wait_bgsave\";\n                    break;\n                case REDIS_REPL_SEND_BULK:\n                    state = \"send_bulk\";\n                    break;\n                case REDIS_REPL_ONLINE:\n                    state = \"online\";\n                    break;\n                }\n                if (state == NULL) continue;\n                if (slave->replstate == REDIS_REPL_ONLINE)\n                    lag = time(NULL) - slave->repl_ack_time;\n\n                info = sdscatprintf(info,\n                    \"slave%d:ip=%s,port=%d,state=%s,\"\n                    \"offset=%lld,lag=%ld\\r\\n\",\n                    slaveid,ip,slave->slave_listening_port,state,\n                    slave->repl_ack_off, lag);\n                slaveid++;\n            }\n        }\n        info = sdscatprintf(info,\n            \"master_repl_offset:%lld\\r\\n\"\n            \"repl_backlog_active:%d\\r\\n\"\n            \"repl_backlog_size:%lld\\r\\n\"\n            \"repl_backlog_first_byte_offset:%lld\\r\\n\"\n            \"repl_backlog_histlen:%lld\\r\\n\",\n            server.master_repl_offset,\n            server.repl_backlog != NULL,\n            server.repl_backlog_size,\n            server.repl_backlog_off,\n            server.repl_backlog_histlen);\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        \"used_cpu_sys_children:%.2f\\r\\n\"\n        \"used_cpu_user_children:%.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        (float)c_ru.ru_stime.tv_sec+(float)c_ru.ru_stime.tv_usec/1000000,\n        (float)c_ru.ru_utime.tv_sec+(float)c_ru.ru_utime.tv_usec/1000000);\n    }\n\n    /* cmdtime */\n    if (allsections || !strcasecmp(section,\"commandstats\")) {\n        if (sections++) info = sdscat(info,\"\\r\\n\");\n        info = sdscatprintf(info, \"# Commandstats\\r\\n\");\n        numcommands = sizeof(redisCommandTable)/sizeof(struct redisCommand);\n        for (j = 0; j < numcommands; j++) {\n            struct redisCommand *c = redisCommandTable+j;\n\n            if (!c->calls) continue;\n            info = sdscatprintf(info,\n                \"cmdstat_%s:calls=%lld,usec=%lld,usec_per_call=%.2f\\r\\n\",\n                c->name, c->calls, c->microseconds,\n                (c->calls == 0) ? 0 : ((float)c->microseconds/c->calls));\n        }\n    }\n\n    /* Cluster */\n    if (allsections || defsections || !strcasecmp(section,\"cluster\")) {\n        if (sections++) info = sdscat(info,\"\\r\\n\");\n        info = sdscatprintf(info,\n        \"# Cluster\\r\\n\"\n        \"cluster_enabled:%d\\r\\n\",\n        server.cluster_enabled);\n    }\n\n    /* Key space */\n    if (allsections || defsections || !strcasecmp(section,\"keyspace\")) {\n        if (sections++) info = sdscat(info,\"\\r\\n\");\n        info = sdscatprintf(info, \"# Keyspace\\r\\n\");\n        for (j = 0; j < server.dbnum; j++) {\n            long long keys, vkeys;\n\n            keys = dictSize(server.db[j].dict);\n            vkeys = dictSize(server.db[j].expires);\n            if (keys || vkeys) {\n                info = sdscatprintf(info,\n                    \"db%d:keys=%lld,expires=%lld,avg_ttl=%lld\\r\\n\",\n                    j, keys, vkeys, server.db[j].avg_ttl);\n            }\n        }\n    }\n    return info;\n}\n\nvoid infoCommand(redisClient *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    sds info = genRedisInfoString(section);\n    addReplySds(c,sdscatprintf(sdsempty(),\"$%lu\\r\\n\",\n        (unsigned long)sdslen(info)));\n    addReplySds(c,info);\n    addReply(c,shared.crlf);\n}\n\nvoid monitorCommand(redisClient *c) {\n    /* ignore MONITOR if already slave or in monitor mode */\n\n    // 这个客户端是从服务器，或者已经是监视器\n    if (c->flags & REDIS_SLAVE) return;\n\n    // 打开 SLAVE 标志和 MONITOR 标志\n    c->flags |= (REDIS_SLAVE|REDIS_MONITOR);\n\n    // 添加客户端到 monitors 链表\n    listAddNodeTail(server.monitors,c);\n\n    // 返回 OK\n    addReply(c,shared.ok);\n}\n\n/* ============================ Maxmemory directive  ======================== */\n\n/* freeMemoryIfNeeded() gets called when 'maxmemory' is set on the config\n * file to limit the max memory used by the server, before processing a\n * command.\n *\n * 此函数在 maxmemory 选项被打开，并且内存超出限制时调用。\n *\n * The goal of the function is to free enough memory to keep Redis under the\n * configured memory limit.\n *\n * 此函数的目的是释放 Redis 的占用内存至 maxmemory 选项设置的最大值之下。\n *\n * The function starts calculating how many bytes should be freed to keep\n * Redis under the limit, and enters a loop selecting the best keys to\n * evict accordingly to the configured policy.\n *\n * 函数先计算出需要释放多少字节才能低于 maxmemory 选项设置的最大值，\n * 然后根据指定的淘汰算法，选出最适合被淘汰的键进行释放。\n *\n * If all the bytes needed to return back under the limit were freed the\n * function returns REDIS_OK, otherwise REDIS_ERR is returned, and the caller\n * should block the execution of commands that will result in more memory\n * used by the server.\n *\n * 如果成功释放了所需数量的内存，那么函数返回 REDIS_OK ，否则函数将返回 REDIS_ERR ，\n * 并阻止执行新的命令。\n *\n * ------------------------------------------------------------------------\n *\n * LRU approximation algorithm\n *\n * Redis uses an approximation of the LRU algorithm that runs in constant\n * memory. Every time there is a key to expire, we sample N keys (with\n * N very small, usually in around 5) to populate a pool of best keys to\n * evict of M keys (the pool size is defined by REDIS_EVICTION_POOL_SIZE).\n *\n * The N keys sampled are added in the pool of good keys to expire (the one\n * with an old access time) if they are better than one of the current keys\n * in the pool.\n *\n * After the pool is populated, the best key we have in the pool is expired.\n * However note that we don't remove keys from the pool when they are deleted\n * so the pool may contain keys that no longer exist.\n *\n * When we try to evict a key, and all the entries in the pool don't exist\n * we populate it again. This time we'll be sure that the pool has at least\n * one key that can be evicted, if there is at least one key that can be\n * evicted in the whole database. */\n\n/* Create a new eviction pool. */\nstruct evictionPoolEntry *evictionPoolAlloc(void) {\n    struct evictionPoolEntry *ep;\n    int j;\n\n    ep = zmalloc(sizeof(*ep)*REDIS_EVICTION_POOL_SIZE);\n    for (j = 0; j < REDIS_EVICTION_POOL_SIZE; j++) {\n        ep[j].idle = 0;\n        ep[j].key = NULL;\n    }\n    return ep;\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, struct evictionPoolEntry *pool) {\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 (server.maxmemory_samples <= EVICTION_SAMPLES_ARRAY_SIZE) {\n        samples = _samples;\n    } else {\n        samples = zmalloc(sizeof(samples[0])*server.maxmemory_samples);\n    }\n\n#if 1 /* Use bulk get by default. */\n    count = dictGetRandomKeys(sampledict,samples,server.maxmemory_samples);\n#else\n    count = server.maxmemory_samples;\n    for (j = 0; j < count; j++) samples[j] = dictGetRandomKey(sampledict);\n#endif\n\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 < REDIS_EVICTION_POOL_SIZE &&\n               pool[k].key &&\n               pool[k].idle < idle) k++;\n        if (k == 0 && pool[REDIS_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 < REDIS_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[REDIS_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])*(REDIS_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) zfree(samples);\n}\n\nint freeMemoryIfNeeded(void) {\n    size_t mem_used, mem_tofree, mem_freed;\n    int slaves = listLength(server.slaves);\n\n    /* Remove the size of slaves output buffers and AOF buffer from the\n     * count of used memory. */\n    // 计算出 Redis 目前占用的内存总数，但有两个方面的内存不会计算在内：\n    // 1）从服务器的输出缓冲区的内存\n    // 2）AOF 缓冲区的内存\n    mem_used = zmalloc_used_memory();\n    if (slaves) {\n        listIter li;\n        listNode *ln;\n\n        listRewind(server.slaves,&li);\n        while((ln = listNext(&li))) {\n            redisClient *slave = listNodeValue(ln);\n            unsigned long obuf_bytes = getClientOutputBufferMemoryUsage(slave);\n            if (obuf_bytes > mem_used)\n                mem_used = 0;\n            else\n                mem_used -= obuf_bytes;\n        }\n    }\n    if (server.aof_state != REDIS_AOF_OFF) {\n        mem_used -= sdslen(server.aof_buf);\n        mem_used -= aofRewriteBufferSize();\n    }\n\n    /* Check if we are over the memory limit. */\n    // 如果目前使用的内存大小比设置的 maxmemory 要小，那么无须执行进一步操作\n    if (mem_used <= server.maxmemory) return REDIS_OK;\n\n    // 如果占用内存比 maxmemory 要大，但是 maxmemory 策略为不淘汰，那么直接返回\n    if (server.maxmemory_policy == REDIS_MAXMEMORY_NO_EVICTION)\n        return REDIS_ERR; /* We need to free memory, but policy forbids. */\n\n    /* Compute how much memory we need to free. */\n    // 计算需要释放多少字节的内存\n    mem_tofree = mem_used - server.maxmemory;\n\n    // 初始化已释放内存的字节数为 0\n    mem_freed = 0;\n\n    // 根据 maxmemory 策略，\n    // 遍历字典，释放内存并记录被释放内存的字节数\n    while (mem_freed < mem_tofree) {\n        int j, k, keys_freed = 0;\n\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 = server.db+j;\n            dict *dict;\n\n            if (server.maxmemory_policy == REDIS_MAXMEMORY_ALLKEYS_LRU ||\n                server.maxmemory_policy == REDIS_MAXMEMORY_ALLKEYS_RANDOM)\n            {\n                // 如果策略是 allkeys-lru 或者 allkeys-random \n                // 那么淘汰的目标为所有数据库键\n                dict = server.db[j].dict;\n            } else {\n                // 如果策略是 volatile-lru 、 volatile-random 或者 volatile-ttl \n                // 那么淘汰的目标为带过期时间的数据库键\n                dict = server.db[j].expires;\n            }\n\n            // 跳过空字典\n            if (dictSize(dict) == 0) continue;\n\n            /* volatile-random and allkeys-random policy */\n            // 如果使用的是随机策略，那么从目标字典中随机选出键\n            if (server.maxmemory_policy == REDIS_MAXMEMORY_ALLKEYS_RANDOM ||\n                server.maxmemory_policy == REDIS_MAXMEMORY_VOLATILE_RANDOM)\n            {\n                de = dictGetRandomKey(dict);\n                bestkey = dictGetKey(de);\n            }\n\n            /* volatile-lru and allkeys-lru policy */\n            // 如果使用的是 LRU 策略，\n            // 那么从一集 sample 键中选出 IDLE 时间最长的那个键\n            else if (server.maxmemory_policy == REDIS_MAXMEMORY_ALLKEYS_LRU ||\n                server.maxmemory_policy == REDIS_MAXMEMORY_VOLATILE_LRU)\n            {\n                struct evictionPoolEntry *pool = db->eviction_pool;\n\n                while(bestkey == NULL) {\n                    evictionPoolPopulate(dict, db->dict, db->eviction_pool);\n                    /* Go backward from best to worst element to evict. */\n                    for (k = REDIS_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])*(REDIS_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[REDIS_EVICTION_POOL_SIZE-1].key = NULL;\n                        pool[REDIS_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            // 策略为 volatile-ttl ，从一集 sample 键中选出过期时间距离当前时间最接近的键\n            else if (server.maxmemory_policy == REDIS_MAXMEMORY_VOLATILE_TTL) {\n                for (k = 0; k < server.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            // 删除被选中的键\n            if (bestkey) {\n                long long delta;\n\n                robj *keyobj = createStringObject(bestkey,sdslen(bestkey));\n                propagateExpire(db,keyobj);\n                /* We compute the amount of memory freed by dbDelete() alone.\n                 * It is possible that actually the memory needed to propagate\n                 * the DEL in AOF and replication link is greater than the one\n                 * we are freeing removing the key, but we can't account for\n                 * that otherwise we would never exit the loop.\n                 *\n                 * AOF and Output buffer memory will be freed eventually so\n                 * we only care about memory used by the key space. */\n                // 计算删除键所释放的内存数量\n                delta = (long long) zmalloc_used_memory();\n                dbDelete(db,keyobj);\n                delta -= (long long) zmalloc_used_memory();\n                mem_freed += delta;\n                \n                // 对淘汰键的计数器增一\n                server.stat_evictedkeys++;\n\n                notifyKeyspaceEvent(REDIS_NOTIFY_EVICTED, \"evicted\",\n                    keyobj, db->id);\n                decrRefCount(keyobj);\n                keys_freed++;\n\n                /* When the memory to free starts to be big enough, we may\n                 * start spending so much time here that is impossible to\n                 * deliver data to the slaves fast enough, so we force the\n                 * transmission here inside the loop. */\n                if (slaves) flushSlavesOutputBuffers();\n            }\n        }\n\n        if (!keys_freed) return REDIS_ERR; /* nothing to free... */\n    }\n\n    return REDIS_OK;\n}\n\n/* =================================== Main! ================================ */\n\n#ifdef __linux__\nint linuxOvercommitMemoryValue(void) {\n    FILE *fp = fopen(\"/proc/sys/vm/overcommit_memory\",\"r\");\n    char buf[64];\n\n    if (!fp) return -1;\n    if (fgets(buf,64,fp) == NULL) {\n        fclose(fp);\n        return -1;\n    }\n    fclose(fp);\n\n    return atoi(buf);\n}\n\nvoid linuxOvercommitMemoryWarning(void) {\n    if (linuxOvercommitMemoryValue() == 0) {\n        redisLog(REDIS_WARNING,\"WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.\");\n    }\n}\n#endif /* __linux__ */\n\nvoid createPidFile(void) {\n    /* Try to write the pid file in a best-effort way. */\n    FILE *fp = fopen(server.pidfile,\"w\");\n    if (fp) {\n        fprintf(fp,\"%d\\n\",(int)getpid());\n        fclose(fp);\n    }\n}\n\nvoid daemonize(void) {\n    int fd;\n\n    if (fork() != 0) exit(0); /* parent exits */\n    setsid(); /* create a new session */\n\n    /* Every output goes to /dev/null. If Redis is daemonized but\n     * the 'logfile' is set to 'stdout' in the configuration file\n     * it will not log at all. */\n    if ((fd = open(\"/dev/null\", O_RDWR, 0)) != -1) {\n        dup2(fd, STDIN_FILENO);\n        dup2(fd, STDOUT_FILENO);\n        dup2(fd, STDERR_FILENO);\n        if (fd > STDERR_FILENO) close(fd);\n    }\n}\n\nvoid version() {\n    printf(\"Redis server v=%s sha=%s:%d malloc=%s bits=%d build=%llx\\n\",\n        REDIS_VERSION,\n        redisGitSHA1(),\n        atoi(redisGitDirty()) > 0,\n        ZMALLOC_LIB,\n        sizeof(long) == 4 ? 32 : 64,\n        (unsigned long long) redisBuildId());\n    exit(0);\n}\n\nvoid usage() {\n    fprintf(stderr,\"Usage: ./redis-server [/path/to/redis.conf] [options]\\n\");\n    fprintf(stderr,\"       ./redis-server - (read config from stdin)\\n\");\n    fprintf(stderr,\"       ./redis-server -v or --version\\n\");\n    fprintf(stderr,\"       ./redis-server -h or --help\\n\");\n    fprintf(stderr,\"       ./redis-server --test-memory <megabytes>\\n\\n\");\n    fprintf(stderr,\"Examples:\\n\");\n    fprintf(stderr,\"       ./redis-server (run the server with default conf)\\n\");\n    fprintf(stderr,\"       ./redis-server /etc/redis/6379.conf\\n\");\n    fprintf(stderr,\"       ./redis-server --port 7777\\n\");\n    fprintf(stderr,\"       ./redis-server --port 7777 --slaveof 127.0.0.1 8888\\n\");\n    fprintf(stderr,\"       ./redis-server /etc/myredis.conf --loglevel verbose\\n\\n\");\n    fprintf(stderr,\"Sentinel mode:\\n\");\n    fprintf(stderr,\"       ./redis-server /etc/sentinel.conf --sentinel\\n\");\n    exit(1);\n}\n\nvoid redisAsciiArt(void) {\n#include \"asciilogo.h\"\n    char *buf = zmalloc(1024*16);\n    char *mode = \"stand alone\";\n\n    if (server.cluster_enabled) mode = \"cluster\";\n    else if (server.sentinel_mode) mode = \"sentinel\";\n\n    snprintf(buf,1024*16,ascii_logo,\n        REDIS_VERSION,\n        redisGitSHA1(),\n        strtol(redisGitDirty(),NULL,10) > 0,\n        (sizeof(long) == 8) ? \"64\" : \"32\",\n        mode, server.port,\n        (long) getpid()\n    );\n    redisLogRaw(REDIS_NOTICE|REDIS_LOG_RAW,buf);\n    zfree(buf);\n}\n\n// SIGTERM 信号的处理器\nstatic void sigtermHandler(int sig) {\n    REDIS_NOTUSED(sig);\n\n    redisLogFromHandler(REDIS_WARNING,\"Received SIGTERM, scheduling shutdown...\");\n    \n    // 打开关闭标识\n    server.shutdown_asap = 1;\n}\n\nvoid setupSignalHandlers(void) {\n    struct sigaction act;\n\n    /* When the SA_SIGINFO flag is set in sa_flags then sa_sigaction is used.\n     * Otherwise, sa_handler is used. */\n    sigemptyset(&act.sa_mask);\n    act.sa_flags = 0;\n    act.sa_handler = sigtermHandler;\n    sigaction(SIGTERM, &act, NULL);\n\n#ifdef HAVE_BACKTRACE\n    sigemptyset(&act.sa_mask);\n    act.sa_flags = SA_NODEFER | SA_RESETHAND | SA_SIGINFO;\n    act.sa_sigaction = sigsegvHandler;\n    sigaction(SIGSEGV, &act, NULL);\n    sigaction(SIGBUS, &act, NULL);\n    sigaction(SIGFPE, &act, NULL);\n    sigaction(SIGILL, &act, NULL);\n#endif\n    return;\n}\n\nvoid memtest(size_t megabytes, int passes);\n\n/* Returns 1 if there is --sentinel among the arguments or if\n * argv[0] is exactly \"redis-sentinel\". */\nint checkForSentinelMode(int argc, char **argv) {\n    int j;\n\n    if (strstr(argv[0],\"redis-sentinel\") != NULL) return 1;\n    for (j = 1; j < argc; j++)\n        if (!strcmp(argv[j],\"--sentinel\")) return 1;\n    return 0;\n}\n\n/* Function called at startup to load RDB or AOF file in memory. */\nvoid loadDataFromDisk(void) {\n    // 记录开始时间\n    long long start = ustime();\n\n    // AOF 持久化已打开？\n    if (server.aof_state == REDIS_AOF_ON) {\n        // 尝试载入 AOF 文件\n        if (loadAppendOnlyFile(server.aof_filename) == REDIS_OK)\n            // 打印载入信息，并计算载入耗时长度\n            redisLog(REDIS_NOTICE,\"DB loaded from append only file: %.3f seconds\",(float)(ustime()-start)/1000000);\n    // AOF 持久化未打开\n    } else {\n        // 尝试载入 RDB 文件\n        if (rdbLoad(server.rdb_filename) == REDIS_OK) {\n            // 打印载入信息，并计算载入耗时长度\n            redisLog(REDIS_NOTICE,\"DB loaded from disk: %.3f seconds\",\n                (float)(ustime()-start)/1000000);\n        } else if (errno != ENOENT) {\n            redisLog(REDIS_WARNING,\"Fatal error loading the DB: %s. Exiting.\",strerror(errno));\n            exit(1);\n        }\n    }\n}\n\nvoid redisOutOfMemoryHandler(size_t allocation_size) {\n    redisLog(REDIS_WARNING,\"Out Of Memory allocating %zu bytes!\",\n        allocation_size);\n    redisPanic(\"Redis aborting for OUT OF MEMORY\");\n}\n\nvoid redisSetProcTitle(char *title) {\n#ifdef USE_SETPROCTITLE\n    char *server_mode = \"\";\n    if (server.cluster_enabled) server_mode = \" [cluster]\";\n    else if (server.sentinel_mode) server_mode = \" [sentinel]\";\n\n    setproctitle(\"%s %s:%d%s\",\n        title,\n        server.bindaddr_count ? server.bindaddr[0] : \"*\",\n        server.port,\n        server_mode);\n#else\n    REDIS_NOTUSED(title);\n#endif\n}\n\nint main(int argc, char **argv) {\n    struct timeval tv;\n\n    /* We need to initialize our libraries, and the server configuration. */\n    // 初始化库\n#ifdef INIT_SETPROCTITLE_REPLACEMENT\n    spt_init(argc, argv);\n#endif\n    setlocale(LC_COLLATE,\"\");\n    zmalloc_enable_thread_safeness();\n    zmalloc_set_oom_handler(redisOutOfMemoryHandler);\n    srand(time(NULL)^getpid());\n    gettimeofday(&tv,NULL);\n    dictSetHashFunctionSeed(tv.tv_sec^tv.tv_usec^getpid());\n\n    // 检查服务器是否以 Sentinel 模式启动\n    server.sentinel_mode = checkForSentinelMode(argc,argv);\n\n    // 初始化服务器\n    initServerConfig();\n\n    /* We need to init sentinel right now as parsing the configuration file\n     * in sentinel mode will have the effect of populating the sentinel\n     * data structures with master nodes to monitor. */\n    // 如果服务器以 Sentinel 模式启动，那么进行 Sentinel 功能相关的初始化\n    // 并为要监视的主服务器创建一些相应的数据结构\n    if (server.sentinel_mode) {\n        initSentinelConfig();\n        initSentinel();\n    }\n\n    // 检查用户是否指定了配置文件，或者配置选项\n    if (argc >= 2) {\n        int j = 1; /* First option to parse in argv[] */\n        sds options = sdsempty();\n        char *configfile = NULL;\n\n        /* Handle special options --help and --version */\n        // 处理特殊选项 -h 、-v 和 --test-memory\n        if (strcmp(argv[1], \"-v\") == 0 ||\n            strcmp(argv[1], \"--version\") == 0) version();\n        if (strcmp(argv[1], \"--help\") == 0 ||\n            strcmp(argv[1], \"-h\") == 0) usage();\n        if (strcmp(argv[1], \"--test-memory\") == 0) {\n            if (argc == 3) {\n                memtest(atoi(argv[2]),50);\n                exit(0);\n            } else {\n                fprintf(stderr,\"Please specify the amount of memory to test in megabytes.\\n\");\n                fprintf(stderr,\"Example: ./redis-server --test-memory 4096\\n\\n\");\n                exit(1);\n            }\n        }\n\n        /* First argument is the config file name? */\n        // 如果第一个参数（argv[1]）不是以 \"--\" 开头\n        // 那么它应该是一个配置文件\n        if (argv[j][0] != '-' || argv[j][1] != '-')\n            configfile = argv[j++];\n\n        /* All the other options are parsed and conceptually appended to the\n         * configuration file. For instance --port 6380 will generate the\n         * string \"port 6380\\n\" to be parsed after the actual file name\n         * is parsed, if any. */\n        // 对用户给定的其余选项进行分析，并将分析所得的字符串追加稍后载入的配置文件的内容之后\n        // 比如 --port 6380 会被分析为 \"port 6380\\n\"\n        while(j != argc) {\n            if (argv[j][0] == '-' && argv[j][1] == '-') {\n                /* Option name */\n                if (sdslen(options)) options = sdscat(options,\"\\n\");\n                options = sdscat(options,argv[j]+2);\n                options = sdscat(options,\" \");\n            } else {\n                /* Option argument */\n                options = sdscatrepr(options,argv[j],strlen(argv[j]));\n                options = sdscat(options,\" \");\n            }\n            j++;\n        }\n        if (configfile) server.configfile = getAbsolutePath(configfile);\n        // 重置保存条件\n        resetServerSaveParams();\n\n        // 载入配置文件， options 是前面分析出的给定选项\n        loadServerConfig(configfile,options);\n        sdsfree(options);\n\n        // 获取配置文件的绝对路径\n        if (configfile) server.configfile = getAbsolutePath(configfile);\n    } else {\n        redisLog(REDIS_WARNING, \"Warning: no config file specified, using the default config. In order to specify a config file use %s /path/to/%s.conf\", argv[0], server.sentinel_mode ? \"sentinel\" : \"redis\");\n    }\n\n    // 将服务器设置为守护进程\n    if (server.daemonize) daemonize();\n\n    // 创建并初始化服务器数据结构\n    initServer();\n\n    // 如果服务器是守护进程，那么创建 PID 文件\n    if (server.daemonize) createPidFile();\n\n    // 为服务器进程设置名字\n    redisSetProcTitle(argv[0]);\n\n    // 打印 ASCII LOGO\n    redisAsciiArt();\n\n    // 如果服务器不是运行在 SENTINEL 模式，那么执行以下代码\n    if (!server.sentinel_mode) {\n        /* Things not needed when running in Sentinel mode. */\n        // 打印问候语\n        redisLog(REDIS_WARNING,\"Server started, Redis version \" REDIS_VERSION);\n    #ifdef __linux__\n        // 打印内存警告\n        linuxOvercommitMemoryWarning();\n    #endif\n        // 从 AOF 文件或者 RDB 文件中载入数据\n        loadDataFromDisk();\n        // 启动集群？\n        if (server.cluster_enabled) {\n            if (verifyClusterConfigWithData() == REDIS_ERR) {\n                redisLog(REDIS_WARNING,\n                    \"You can't have keys in a DB different than DB 0 when in \"\n                    \"Cluster mode. Exiting.\");\n                exit(1);\n            }\n        }\n        // 打印 TCP 端口\n        if (server.ipfd_count > 0)\n            redisLog(REDIS_NOTICE,\"The server is now ready to accept connections on port %d\", server.port);\n        // 打印本地套接字端口\n        if (server.sofd > 0)\n            redisLog(REDIS_NOTICE,\"The server is now ready to accept connections at %s\", server.unixsocket);\n    } else {\n        sentinelIsRunning();\n    }\n\n    /* Warning the user about suspicious maxmemory setting. */\n    // 检查不正常的 maxmemory 配置\n    if (server.maxmemory > 0 && server.maxmemory < 1024*1024) {\n        redisLog(REDIS_WARNING,\"WARNING: You specified a maxmemory value that is less than 1MB (current value is %llu bytes). Are you sure this is what you really want?\", server.maxmemory);\n    }\n\n    // 运行事件处理器，一直到服务器关闭为止\n    aeSetBeforeSleepProc(server.el,beforeSleep);\n    aeMain(server.el);\n\n    // 服务器关闭，停止事件循环\n    aeDeleteEventLoop(server.el);\n\n    return 0;\n}\n\n/* The End */\n"
  },
  {
    "path": "src/redis.h",
    "content": "/*\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#ifndef __REDIS_H\n#define __REDIS_H\n\n#include \"fmacros.h\"\n#include \"config.h\"\n\n#if defined(__sun)\n#include \"solarisfixes.h\"\n#endif\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <time.h>\n#include <limits.h>\n#include <unistd.h>\n#include <errno.h>\n#include <inttypes.h>\n#include <pthread.h>\n#include <syslog.h>\n#include <netinet/in.h>\n#include <lua.h>\n#include <signal.h>\n\n#include \"ae.h\"      /* Event driven programming library */\n#include \"sds.h\"     /* Dynamic safe strings */\n#include \"dict.h\"    /* Hash tables */\n#include \"adlist.h\"  /* Linked lists */\n#include \"zmalloc.h\" /* total memory usage aware version of malloc/free */\n#include \"anet.h\"    /* Networking the easy way */\n#include \"ziplist.h\" /* Compact list data structure */\n#include \"intset.h\"  /* Compact integer set structure */\n#include \"version.h\" /* Version macro */\n#include \"util.h\"    /* Misc functions useful in many places */\n\n/* Error codes */\n#define REDIS_OK                0\n#define REDIS_ERR               -1\n\n/* Static server configuration */\n/* 默认的服务器配置值 */\n#define REDIS_DEFAULT_HZ        10      /* Time interrupt calls/sec. */\n#define REDIS_MIN_HZ            1\n#define REDIS_MAX_HZ            500 \n#define REDIS_SERVERPORT        6379    /* TCP port */\n#define REDIS_TCP_BACKLOG       511     /* TCP listen backlog */\n#define REDIS_MAXIDLETIME       0       /* default client timeout: infinite */\n#define REDIS_DEFAULT_DBNUM     16\n#define REDIS_CONFIGLINE_MAX    1024\n#define REDIS_DBCRON_DBS_PER_CALL 16\n#define REDIS_MAX_WRITE_PER_EVENT (1024*64)\n#define REDIS_SHARED_SELECT_CMDS 10\n#define REDIS_SHARED_INTEGERS 10000\n#define REDIS_SHARED_BULKHDR_LEN 32\n#define REDIS_MAX_LOGMSG_LEN    1024 /* Default maximum length of syslog messages */\n#define REDIS_AOF_REWRITE_PERC  100\n#define REDIS_AOF_REWRITE_MIN_SIZE (64*1024*1024)\n#define REDIS_AOF_REWRITE_ITEMS_PER_CMD 64\n#define REDIS_SLOWLOG_LOG_SLOWER_THAN 10000\n#define REDIS_SLOWLOG_MAX_LEN 128\n#define REDIS_MAX_CLIENTS 10000\n#define REDIS_AUTHPASS_MAX_LEN 512\n#define REDIS_DEFAULT_SLAVE_PRIORITY 100\n#define REDIS_REPL_TIMEOUT 60\n#define REDIS_REPL_PING_SLAVE_PERIOD 10\n#define REDIS_RUN_ID_SIZE 40\n#define REDIS_OPS_SEC_SAMPLES 16\n#define REDIS_DEFAULT_REPL_BACKLOG_SIZE (1024*1024)    /* 1mb */\n#define REDIS_DEFAULT_REPL_BACKLOG_TIME_LIMIT (60*60)  /* 1 hour */\n#define REDIS_REPL_BACKLOG_MIN_SIZE (1024*16)          /* 16k */\n#define REDIS_BGSAVE_RETRY_DELAY 5 /* Wait a few secs before trying again. */\n#define REDIS_DEFAULT_PID_FILE \"/var/run/redis.pid\"\n#define REDIS_DEFAULT_SYSLOG_IDENT \"redis\"\n#define REDIS_DEFAULT_CLUSTER_CONFIG_FILE \"nodes.conf\"\n#define REDIS_DEFAULT_DAEMONIZE 0\n#define REDIS_DEFAULT_UNIX_SOCKET_PERM 0\n#define REDIS_DEFAULT_TCP_KEEPALIVE 0\n#define REDIS_DEFAULT_LOGFILE \"\"\n#define REDIS_DEFAULT_SYSLOG_ENABLED 0\n#define REDIS_DEFAULT_STOP_WRITES_ON_BGSAVE_ERROR 1\n#define REDIS_DEFAULT_RDB_COMPRESSION 1\n#define REDIS_DEFAULT_RDB_CHECKSUM 1\n#define REDIS_DEFAULT_RDB_FILENAME \"dump.rdb\"\n#define REDIS_DEFAULT_SLAVE_SERVE_STALE_DATA 1\n#define REDIS_DEFAULT_SLAVE_READ_ONLY 1\n#define REDIS_DEFAULT_REPL_DISABLE_TCP_NODELAY 0\n#define REDIS_DEFAULT_MAXMEMORY 0\n#define REDIS_DEFAULT_MAXMEMORY_SAMPLES 5\n#define REDIS_DEFAULT_AOF_FILENAME \"appendonly.aof\"\n#define REDIS_DEFAULT_AOF_NO_FSYNC_ON_REWRITE 0\n#define REDIS_DEFAULT_ACTIVE_REHASHING 1\n#define REDIS_DEFAULT_AOF_REWRITE_INCREMENTAL_FSYNC 1\n#define REDIS_DEFAULT_MIN_SLAVES_TO_WRITE 0\n#define REDIS_DEFAULT_MIN_SLAVES_MAX_LAG 10\n#define REDIS_IP_STR_LEN INET6_ADDRSTRLEN\n#define REDIS_PEER_ID_LEN (REDIS_IP_STR_LEN+32) /* Must be enough for ip:port */\n#define REDIS_BINDADDR_MAX 16\n#define REDIS_MIN_RESERVED_FDS 32\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/* Protocol and I/O related defines */\n#define REDIS_MAX_QUERYBUF_LEN  (1024*1024*1024) /* 1GB max query buffer. */\n#define REDIS_IOBUF_LEN         (1024*16)  /* Generic I/O buffer size */\n#define REDIS_REPLY_CHUNK_BYTES (16*1024) /* 16k output buffer */\n#define REDIS_INLINE_MAX_SIZE   (1024*64) /* Max size of inline reads */\n#define REDIS_MBULK_BIG_ARG     (1024*32)\n#define REDIS_LONGSTR_SIZE      21          /* Bytes needed for long -> str */\n// 指示 AOF 程序每累积这个量的写入数据\n// 就执行一次显式的 fsync\n#define REDIS_AOF_AUTOSYNC_BYTES (1024*1024*32) /* fdatasync every 32MB */\n/* When configuring the Redis eventloop, we setup it so that the total number\n * of file descriptors we can handle are server.maxclients + RESERVED_FDS + FDSET_INCR\n * that is our safety margin. */\n#define REDIS_EVENTLOOP_FDSET_INCR (REDIS_MIN_RESERVED_FDS+96)\n\n/* Hash table parameters */\n#define REDIS_HT_MINFILL        10      /* Minimal hash table fill 10% */\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// 命令标志\n#define REDIS_CMD_WRITE 1                   /* \"w\" flag */\n#define REDIS_CMD_READONLY 2                /* \"r\" flag */\n#define REDIS_CMD_DENYOOM 4                 /* \"m\" flag */\n#define REDIS_CMD_NOT_USED_1 8              /* no longer used flag */\n#define REDIS_CMD_ADMIN 16                  /* \"a\" flag */\n#define REDIS_CMD_PUBSUB 32                 /* \"p\" flag */\n#define REDIS_CMD_NOSCRIPT  64              /* \"s\" flag */\n#define REDIS_CMD_RANDOM 128                /* \"R\" flag */\n#define REDIS_CMD_SORT_FOR_SCRIPT 256       /* \"S\" flag */\n#define REDIS_CMD_LOADING 512               /* \"l\" flag */\n#define REDIS_CMD_STALE 1024                /* \"t\" flag */\n#define REDIS_CMD_SKIP_MONITOR 2048         /* \"M\" flag */\n#define REDIS_CMD_ASKING 4096               /* \"k\" flag */\n\n/* Object types */\n// 对象类型\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/* 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// 对象编码\n#define REDIS_ENCODING_RAW 0     /* Raw representation */\n#define REDIS_ENCODING_INT 1     /* Encoded as integer */\n#define REDIS_ENCODING_HT 2      /* Encoded as hash table */\n#define REDIS_ENCODING_ZIPMAP 3  /* Encoded as zipmap */\n#define REDIS_ENCODING_LINKEDLIST 4 /* Encoded as regular linked list */\n#define REDIS_ENCODING_ZIPLIST 5 /* Encoded as ziplist */\n#define REDIS_ENCODING_INTSET 6  /* Encoded as intset */\n#define REDIS_ENCODING_SKIPLIST 7  /* Encoded as skiplist */\n#define REDIS_ENCODING_EMBSTR 8  /* Embedded sds string encoding */\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 REDIS_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 REDIS_RDB_6BITLEN 0\n#define REDIS_RDB_14BITLEN 1\n#define REDIS_RDB_32BITLEN 2\n#define REDIS_RDB_ENCVAL 3\n#define REDIS_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 REDIS_RDB_ENC_INT8 0        /* 8 bit signed integer */\n#define REDIS_RDB_ENC_INT16 1       /* 16 bit signed integer */\n#define REDIS_RDB_ENC_INT32 2       /* 32 bit signed integer */\n#define REDIS_RDB_ENC_LZF 3         /* string compressed with FASTLZ */\n\n/* AOF states */\n#define REDIS_AOF_OFF 0             /* AOF is off */\n#define REDIS_AOF_ON 1              /* AOF is on */\n#define REDIS_AOF_WAIT_REWRITE 2    /* AOF waits rewrite to start appending */\n\n/* Client flags */\n#define REDIS_SLAVE (1<<0)   /* This client is a slave server */\n#define REDIS_MASTER (1<<1)  /* This client is a master server */\n#define REDIS_MONITOR (1<<2) /* This client is a slave monitor, see MONITOR */\n#define REDIS_MULTI (1<<3)   /* This client is in a MULTI context */\n#define REDIS_BLOCKED (1<<4) /* The client is waiting in a blocking operation */\n#define REDIS_DIRTY_CAS (1<<5) /* Watched keys modified. EXEC will fail. */\n#define REDIS_CLOSE_AFTER_REPLY (1<<6) /* Close after writing entire reply. */\n#define REDIS_UNBLOCKED (1<<7) /* This client was unblocked and is stored in\n                                  server.unblocked_clients */\n#define REDIS_LUA_CLIENT (1<<8) /* This is a non connected client used by Lua */\n#define REDIS_ASKING (1<<9)     /* Client issued the ASKING command */\n#define REDIS_CLOSE_ASAP (1<<10)/* Close this client ASAP */\n#define REDIS_UNIX_SOCKET (1<<11) /* Client connected via Unix domain socket */\n#define REDIS_DIRTY_EXEC (1<<12)  /* EXEC will fail for errors while queueing */\n#define REDIS_MASTER_FORCE_REPLY (1<<13)  /* Queue replies even if is master */\n#define REDIS_FORCE_AOF (1<<14)   /* Force AOF propagation of current cmd. */\n#define REDIS_FORCE_REPL (1<<15)  /* Force replication of current cmd. */\n#define REDIS_PRE_PSYNC (1<<16)   /* Instance don't understand PSYNC. */\n#define REDIS_READONLY (1<<17)    /* Cluster client is in read-only state. */\n\n/* Client block type (btype field in client structure)\n * if REDIS_BLOCKED flag is set. */\n#define REDIS_BLOCKED_NONE 0    /* Not blocked, no REDIS_BLOCKED flag set. */\n#define REDIS_BLOCKED_LIST 1    /* BLPOP & co. */\n#define REDIS_BLOCKED_WAIT 2    /* WAIT for synchronous replication. */\n\n/* Client request types */\n#define REDIS_REQ_INLINE 1\n#define REDIS_REQ_MULTIBULK 2\n\n/* Client classes for client limits, currently used only for\n * the max-client-output-buffer limit implementation. */\n#define REDIS_CLIENT_LIMIT_CLASS_NORMAL 0\n#define REDIS_CLIENT_LIMIT_CLASS_SLAVE 1\n#define REDIS_CLIENT_LIMIT_CLASS_PUBSUB 2\n#define REDIS_CLIENT_LIMIT_NUM_CLASSES 3\n\n/* Slave replication state - from the point of view of the slave. */\n#define REDIS_REPL_NONE 0 /* No active replication */\n#define REDIS_REPL_CONNECT 1 /* Must connect to master */\n#define REDIS_REPL_CONNECTING 2 /* Connecting to master */\n#define REDIS_REPL_RECEIVE_PONG 3 /* Wait for PING reply */\n#define REDIS_REPL_TRANSFER 4 /* Receiving .rdb from master */\n#define REDIS_REPL_CONNECTED 5 /* Connected to master */\n\n/* Slave replication state - from the point of view of the master.\n * In SEND_BULK and ONLINE state the slave receives new updates\n * in its output queue. In the WAIT_BGSAVE state instead the server is waiting\n * to start the next background saving in order to send updates to it. */\n#define REDIS_REPL_WAIT_BGSAVE_START 6 /* We need to produce a new RDB file. */\n#define REDIS_REPL_WAIT_BGSAVE_END 7 /* Waiting RDB file creation to finish. */\n#define REDIS_REPL_SEND_BULK 8 /* Sending RDB file to slave. */\n#define REDIS_REPL_ONLINE 9 /* RDB file transmitted, sending just updates. */\n\n/* Synchronous read timeout - slave side */\n#define REDIS_REPL_SYNCIO_TIMEOUT 5\n\n/* List related stuff */\n#define REDIS_HEAD 0\n#define REDIS_TAIL 1\n\n/* Sort operations */\n#define REDIS_SORT_GET 0\n#define REDIS_SORT_ASC 1\n#define REDIS_SORT_DESC 2\n#define REDIS_SORTKEY_MAX 1024\n\n/* Log levels */\n#define REDIS_DEBUG 0\n#define REDIS_VERBOSE 1\n#define REDIS_NOTICE 2\n#define REDIS_WARNING 3\n#define REDIS_LOG_RAW (1<<10) /* Modifier to log without timestamp */\n#define REDIS_DEFAULT_VERBOSITY REDIS_NOTICE\n\n/* Anti-warning macro... */\n#define REDIS_NOTUSED(V) ((void) V)\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/* Append only defines */\n#define AOF_FSYNC_NO 0\n#define AOF_FSYNC_ALWAYS 1\n#define AOF_FSYNC_EVERYSEC 2\n#define REDIS_DEFAULT_AOF_FSYNC AOF_FSYNC_EVERYSEC\n\n/* Zip structure related defaults */\n#define REDIS_HASH_MAX_ZIPLIST_ENTRIES 512\n#define REDIS_HASH_MAX_ZIPLIST_VALUE 64\n#define REDIS_LIST_MAX_ZIPLIST_ENTRIES 512\n#define REDIS_LIST_MAX_ZIPLIST_VALUE 64\n#define REDIS_SET_MAX_INTSET_ENTRIES 512\n#define REDIS_ZSET_MAX_ZIPLIST_ENTRIES 128\n#define REDIS_ZSET_MAX_ZIPLIST_VALUE 64\n\n/* HyperLogLog defines */\n#define REDIS_DEFAULT_HLL_SPARSE_MAX_BYTES 3000\n\n/* Sets operations codes */\n#define REDIS_OP_UNION 0\n#define REDIS_OP_DIFF 1\n#define REDIS_OP_INTER 2\n\n/* Redis maxmemory strategies */\n#define REDIS_MAXMEMORY_VOLATILE_LRU 0\n#define REDIS_MAXMEMORY_VOLATILE_TTL 1\n#define REDIS_MAXMEMORY_VOLATILE_RANDOM 2\n#define REDIS_MAXMEMORY_ALLKEYS_LRU 3\n#define REDIS_MAXMEMORY_ALLKEYS_RANDOM 4\n#define REDIS_MAXMEMORY_NO_EVICTION 5\n#define REDIS_DEFAULT_MAXMEMORY_POLICY REDIS_MAXMEMORY_NO_EVICTION\n\n/* Scripting */\n#define REDIS_LUA_TIME_LIMIT 5000 /* milliseconds */\n\n/* Units */\n#define UNIT_SECONDS 0\n#define UNIT_MILLISECONDS 1\n\n/* SHUTDOWN flags */\n#define REDIS_SHUTDOWN_SAVE 1       /* Force SAVE on SHUTDOWN even if no save\n                                       points are configured. */\n#define REDIS_SHUTDOWN_NOSAVE 2     /* Don't SAVE on SHUTDOWN. */\n\n/* Command call flags, see call() function */\n#define REDIS_CALL_NONE 0\n#define REDIS_CALL_SLOWLOG 1\n#define REDIS_CALL_STATS 2\n#define REDIS_CALL_PROPAGATE 4\n#define REDIS_CALL_FULL (REDIS_CALL_SLOWLOG | REDIS_CALL_STATS | REDIS_CALL_PROPAGATE)\n\n/* Command propagation flags, see propagate() function */\n#define REDIS_PROPAGATE_NONE 0\n#define REDIS_PROPAGATE_AOF 1\n#define REDIS_PROPAGATE_REPL 2\n\n/* Keyspace changes notification classes. Every class is associated with a\n * character for configuration purposes. */\n#define REDIS_NOTIFY_KEYSPACE (1<<0)    /* K */\n#define REDIS_NOTIFY_KEYEVENT (1<<1)    /* E */\n#define REDIS_NOTIFY_GENERIC (1<<2)     /* g */\n#define REDIS_NOTIFY_STRING (1<<3)      /* $ */\n#define REDIS_NOTIFY_LIST (1<<4)        /* l */\n#define REDIS_NOTIFY_SET (1<<5)         /* s */\n#define REDIS_NOTIFY_HASH (1<<6)        /* h */\n#define REDIS_NOTIFY_ZSET (1<<7)        /* z */\n#define REDIS_NOTIFY_EXPIRED (1<<8)     /* x */\n#define REDIS_NOTIFY_EVICTED (1<<9)     /* e */\n#define REDIS_NOTIFY_ALL (REDIS_NOTIFY_GENERIC | REDIS_NOTIFY_STRING | REDIS_NOTIFY_LIST | REDIS_NOTIFY_SET | REDIS_NOTIFY_HASH | REDIS_NOTIFY_ZSET | REDIS_NOTIFY_EXPIRED | REDIS_NOTIFY_EVICTED)      /* A */\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_) if ((_ms_ <= 1000/server.hz) || !(server.cronloops%((_ms_)/(1000/server.hz))))\n\n/* We can print the stacktrace, so our assert is defined this way: */\n#define redisAssertWithInfo(_c,_o,_e) ((_e)?(void)0 : (_redisAssertWithInfo(_c,_o,#_e,__FILE__,__LINE__),_exit(1)))\n#define redisAssert(_e) ((_e)?(void)0 : (_redisAssert(#_e,__FILE__,__LINE__),_exit(1)))\n#define redisPanic(_e) _redisPanic(#_e,__FILE__,__LINE__),_exit(1)\n\n/*-----------------------------------------------------------------------------\n * Data types\n *----------------------------------------------------------------------------*/\n\ntypedef long long mstime_t; /* millisecond time type. */\n\n/* A redis object, that is a type able to hold a string / list / set */\n\n/* The actual Redis Object */\n/*\n * Redis 对象\n */\n#define REDIS_LRU_BITS 24\n#define REDIS_LRU_CLOCK_MAX ((1<<REDIS_LRU_BITS)-1) /* Max value of obj->lru */\n#define REDIS_LRU_CLOCK_RESOLUTION 1000 /* LRU clock resolution in ms */\ntypedef struct redisObject {\n\n    // 类型\n    unsigned type:4;\n\n    // 编码\n    unsigned encoding:4;\n\n    // 对象最后一次被访问的时间\n    unsigned lru:REDIS_LRU_BITS; /* lru time (relative to server.lruclock) */\n\n    // 引用计数\n    int refcount;\n\n    // 指向实际值的指针\n    void *ptr;\n\n} robj;\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 <= REDIS_LRU_CLOCK_RESOLUTION) ? server.lruclock : getLRUClock())\n\n/* Macro used to initialize a Redis object allocated on the stack.\n * Note that this macro is taken near the structure definition to make sure\n * we'll update it when the structure is changed, to avoid bugs like\n * bug #85 introduced exactly in this way. */\n#define initStaticStringObject(_var,_ptr) do { \\\n    _var.refcount = 1; \\\n    _var.type = REDIS_STRING; \\\n    _var.encoding = REDIS_ENCODING_RAW; \\\n    _var.ptr = _ptr; \\\n} while(0);\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 REDIS_EVICTION_POOL_SIZE 16\nstruct evictionPoolEntry {\n    unsigned long long idle;    /* Object idle time. */\n    sds key;                    /* Key name. */\n};\n\n/* Redis 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\n    // 数据库键空间，保存着数据库中的所有键值对\n    dict *dict;                 /* The keyspace for this DB */\n\n    // 键的过期时间，字典的键为键，字典的值为过期事件 UNIX 时间戳\n    dict *expires;              /* Timeout of keys with a timeout set */\n\n    // 正处于阻塞状态的键\n    dict *blocking_keys;        /* Keys with clients waiting for data (BLPOP) */\n\n    // 可以解除阻塞的键\n    dict *ready_keys;           /* Blocked keys that received a PUSH */\n\n    // 正在被 WATCH 命令监视的键\n    dict *watched_keys;         /* WATCHED keys for MULTI/EXEC CAS */\n\n    struct evictionPoolEntry *eviction_pool;    /* Eviction pool of keys */\n\n    // 数据库号码\n    int id;                     /* Database ID */\n\n    // 数据库的键的平均 TTL ，统计信息\n    long long avg_ttl;          /* Average TTL, just for stats */\n\n} redisDb;\n\n/* Client MULTI/EXEC state */\n\n/*\n * 事务命令\n */\ntypedef struct multiCmd {\n\n    // 参数\n    robj **argv;\n\n    // 参数数量\n    int argc;\n\n    // 命令指针\n    struct redisCommand *cmd;\n\n} multiCmd;\n\n/*\n * 事务状态\n */\ntypedef struct multiState {\n\n    // 事务队列，FIFO 顺序\n    multiCmd *commands;     /* Array of MULTI commands */\n\n    // 已入队命令计数\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\n/* This structure holds the blocking operation state for a client.\n * The fields used depend on client->btype. */\n// 阻塞状态\ntypedef struct blockingState {\n\n    /* Generic fields. */\n    // 阻塞时限\n    mstime_t timeout;       /* Blocking operation timeout. If UNIX current time\n                             * is > timeout then the operation timed out. */\n\n    /* REDIS_BLOCK_LIST */\n    // 造成阻塞的键\n    dict *keys;             /* The keys we are waiting to terminate a blocking\n                             * operation such as BLPOP. Otherwise NULL. */\n    // 在被阻塞的键有新元素进入时，需要将这些新元素添加到哪里的目标键\n    // 用于 BRPOPLPUSH 命令\n    robj *target;           /* The key that should receive the element,\n                             * for BRPOPLPUSH. */\n\n    /* REDIS_BLOCK_WAIT */\n    // 等待 ACK 的复制节点数量\n    int numreplicas;        /* Number of replicas we are waiting for ACK. */\n    // 复制偏移量\n    long long reploffset;   /* Replication offset to reach. */\n\n} blockingState;\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. */\n// 记录解除了客户端的阻塞状态的键，以及键所在的数据库。\ntypedef struct readyList {\n    redisDb *db;\n    robj *key;\n} readyList;\n\n/* With multiplexing we need to take per-client state.\n * Clients are taken in a liked list.\n *\n * 因为 I/O 复用的缘故，需要为每个客户端维持一个状态。\n *\n * 多个客户端状态被服务器用链表连接起来。\n */\ntypedef struct redisClient {\n\n    // 套接字描述符\n    int fd;\n\n    // 当前正在使用的数据库\n    redisDb *db;\n\n    // 当前正在使用的数据库的 id （号码）\n    int dictid;\n\n    // 客户端的名字\n    robj *name;             /* As set by CLIENT SETNAME */\n\n    // 查询缓冲区\n    sds querybuf;\n\n    // 查询缓冲区长度峰值\n    size_t querybuf_peak;   /* Recent (100ms or more) peak of querybuf size */\n\n    // 参数数量\n    int argc;\n\n    // 参数对象数组\n    robj **argv;\n\n    // 记录被客户端执行的命令\n    struct redisCommand *cmd, *lastcmd;\n\n    // 请求的类型：内联命令还是多条命令\n    int reqtype;\n\n    // 剩余未读取的命令内容数量\n    int multibulklen;       /* number of multi bulk arguments left to read */\n\n    // 命令内容的长度\n    long bulklen;           /* length of bulk argument in multi bulk request */\n\n    // 回复链表\n    list *reply;\n\n    // 回复链表中对象的总大小\n    unsigned long reply_bytes; /* Tot bytes of objects in reply list */\n\n    // 已发送字节，处理 short write 用\n    int sentlen;            /* Amount of bytes already sent in the current\n                               buffer or object being sent. */\n\n    // 创建客户端的时间\n    time_t ctime;           /* Client creation time */\n\n    // 客户端最后一次和服务器互动的时间\n    time_t lastinteraction; /* time of the last interaction, used for timeout */\n\n    // 客户端的输出缓冲区超过软性限制的时间\n    time_t obuf_soft_limit_reached_time;\n\n    // 客户端状态标志\n    int flags;              /* REDIS_SLAVE | REDIS_MONITOR | REDIS_MULTI ... */\n\n    // 当 server.requirepass 不为 NULL 时\n    // 代表认证的状态\n    // 0 代表未认证， 1 代表已认证\n    int authenticated;      /* when requirepass is non-NULL */\n\n    // 复制状态\n    int replstate;          /* replication state if this is a slave */\n    // 用于保存主服务器传来的 RDB 文件的文件描述符\n    int repldbfd;           /* replication DB file descriptor */\n\n    // 读取主服务器传来的 RDB 文件的偏移量\n    off_t repldboff;        /* replication DB file offset */\n    // 主服务器传来的 RDB 文件的大小\n    off_t repldbsize;       /* replication DB file size */\n    \n    sds replpreamble;       /* replication DB preamble. */\n\n    // 主服务器的复制偏移量\n    long long reploff;      /* replication offset if this is our master */\n    // 从服务器最后一次发送 REPLCONF ACK 时的偏移量\n    long long repl_ack_off; /* replication ack offset, if this is a slave */\n    // 从服务器最后一次发送 REPLCONF ACK 的时间\n    long long repl_ack_time;/* replication ack time, if this is a slave */\n    // 主服务器的 master run ID\n    // 保存在客户端，用于执行部分重同步\n    char replrunid[REDIS_RUN_ID_SIZE+1]; /* master run id if this is a master */\n    // 从服务器的监听端口号\n    int slave_listening_port; /* As configured with: SLAVECONF listening-port */\n\n    // 事务状态\n    multiState mstate;      /* MULTI/EXEC state */\n\n    // 阻塞类型\n    int btype;              /* Type of blocking op if REDIS_BLOCKED. */\n    // 阻塞状态\n    blockingState bpop;     /* blocking state */\n\n    // 最后被写入的全局复制偏移量\n    long long woff;         /* Last write global replication offset. */\n\n    // 被监视的键\n    list *watched_keys;     /* Keys WATCHED for MULTI/EXEC CAS */\n\n    // 这个字典记录了客户端所有订阅的频道\n    // 键为频道名字，值为 NULL\n    // 也即是，一个频道的集合\n    dict *pubsub_channels;  /* channels a client is interested in (SUBSCRIBE) */\n\n    // 链表，包含多个 pubsubPattern 结构\n    // 记录了所有订阅频道的客户端的信息\n    // 新 pubsubPattern 结构总是被添加到表尾\n    list *pubsub_patterns;  /* patterns a client is interested in (SUBSCRIBE) */\n    sds peerid;             /* Cached peer ID. */\n\n    /* Response buffer */\n    // 回复偏移量\n    int bufpos;\n    // 回复缓冲区\n    char buf[REDIS_REPLY_CHUNK_BYTES];\n\n} redisClient;\n\n// 服务器的保存条件（BGSAVE 自动执行的条件）\nstruct saveparam {\n\n    // 多少秒之内\n    time_t seconds;\n\n    // 发生多少次修改\n    int changes;\n\n};\n\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, *noreplicaserr,\n    *busykeyerr, *oomerr, *plus, *messagebulk, *pmessagebulk, *subscribebulk,\n    *unsubscribebulk, *psubscribebulk, *punsubscribebulk, *del, *rpop, *lpop,\n    *lpush, *emptyscan, *minstring, *maxstring,\n    *select[REDIS_SHARED_SELECT_CMDS],\n    *integers[REDIS_SHARED_INTEGERS],\n    *mbulkhdr[REDIS_SHARED_BULKHDR_LEN], /* \"*<value>\\r\\n\" */\n    *bulkhdr[REDIS_SHARED_BULKHDR_LEN];  /* \"$<value>\\r\\n\" */\n};\n\n/* ZSETs use a specialized version of Skiplists */\n/*\n * 跳跃表节点\n */\ntypedef struct zskiplistNode {\n\n    // 成员对象\n    robj *obj;\n\n    // 分值\n    double score;\n\n    // 后退指针\n    struct zskiplistNode *backward;\n\n    // 层\n    struct zskiplistLevel {\n\n        // 前进指针\n        struct zskiplistNode *forward;\n\n        // 跨度\n        unsigned int span;\n\n    } level[];\n\n} zskiplistNode;\n\n/*\n * 跳跃表\n */\ntypedef struct zskiplist {\n\n    // 表头节点和表尾节点\n    struct zskiplistNode *header, *tail;\n\n    // 表中节点的数量\n    unsigned long length;\n\n    // 表中层数最大的节点的层数\n    int level;\n\n} zskiplist;\n\n/*\n * 有序集合\n */\ntypedef struct zset {\n\n    // 字典，键为成员，值为分值\n    // 用于支持 O(1) 复杂度的按成员取分值操作\n    dict *dict;\n\n    // 跳跃表，按分值排序成员\n    // 用于支持平均复杂度为 O(log N) 的按分值定位成员操作\n    // 以及范围操作\n    zskiplist *zsl;\n\n} zset;\n\n// 客户端缓冲区限制\ntypedef struct clientBufferLimitsConfig {\n    // 硬限制\n    unsigned long long hard_limit_bytes;\n    // 软限制\n    unsigned long long soft_limit_bytes;\n    // 软限制时限\n    time_t soft_limit_seconds;\n} clientBufferLimitsConfig;\n\n// 限制可以有多个\nextern clientBufferLimitsConfig clientBufferLimitsDefaults[REDIS_CLIENT_LIMIT_NUM_CLASSES];\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 * (REDIS_PROPAGATE_*), and command pointer.\n *\n * redisOp 结构定义了一个 Redis 操作，\n * 它包含指向被执行命令的指针、命令的参数、数据库 ID 、传播目标（REDIS_PROPAGATE_*）。\n *\n * Currently only used to additionally propagate more commands to AOF/Replication\n * after the propagation of the executed command. \n *\n * 目前只用于在传播被执行命令之后，传播附加的其他命令到 AOF 或 Replication 中。\n */\ntypedef struct redisOp {\n\n    // 参数\n    robj **argv;\n\n    // 参数数量、数据库 ID 、传播目标\n    int argc, dbid, target;\n\n    // 被执行命令的指针\n    struct redisCommand *cmd;\n\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\n/*-----------------------------------------------------------------------------\n * Global server state\n *----------------------------------------------------------------------------*/\n\nstruct clusterState;\n\nstruct redisServer {\n\n    /* General */\n\n    // 配置文件的绝对路径\n    char *configfile;           /* Absolute config file path, or NULL */\n\n    // serverCron() 每秒调用的次数\n    int hz;                     /* serverCron() calls frequency in hertz */\n\n    // 数据库\n    redisDb *db;\n\n    // 命令表（受到 rename 配置选项的作用）\n    dict *commands;             /* Command table */\n    // 命令表（无 rename 配置选项的作用）\n    dict *orig_commands;        /* Command table before command renaming. */\n\n    // 事件状态\n    aeEventLoop *el;\n\n    // 最近一次使用时钟\n    unsigned lruclock:REDIS_LRU_BITS; /* Clock for LRU eviction */\n\n    // 关闭服务器的标识\n    int shutdown_asap;          /* SHUTDOWN needed ASAP */\n\n    // 在执行 serverCron() 时进行渐进式 rehash\n    int activerehashing;        /* Incremental rehash in serverCron() */\n\n    // 是否设置了密码\n    char *requirepass;          /* Pass for AUTH command, or NULL */\n\n    // PID 文件\n    char *pidfile;              /* PID file path */\n\n    // 架构类型\n    int arch_bits;              /* 32 or 64 depending on sizeof(long) */\n\n    // serverCron() 函数的运行次数计数器\n    int cronloops;              /* Number of times the cron function run */\n\n    // 本服务器的 RUN ID\n    char runid[REDIS_RUN_ID_SIZE+1];  /* ID always different at every exec. */\n\n    // 服务器是否运行在 SENTINEL 模式\n    int sentinel_mode;          /* True if this instance is a Sentinel. */\n\n\n    /* Networking */\n\n    // TCP 监听端口\n    int port;                   /* TCP listening port */\n\n    int tcp_backlog;            /* TCP listen() backlog */\n\n    // 地址\n    char *bindaddr[REDIS_BINDADDR_MAX]; /* Addresses we should bind to */\n    // 地址数量\n    int bindaddr_count;         /* Number of addresses in server.bindaddr[] */\n\n    // UNIX 套接字\n    char *unixsocket;           /* UNIX socket path */\n    mode_t unixsocketperm;      /* UNIX socket permission */\n\n    // 描述符\n    int ipfd[REDIS_BINDADDR_MAX]; /* TCP socket file descriptors */\n    // 描述符数量\n    int ipfd_count;             /* Used slots in ipfd[] */\n\n    // UNIX 套接字文件描述符\n    int sofd;                   /* Unix socket file descriptor */\n\n    int cfd[REDIS_BINDADDR_MAX];/* Cluster bus listening socket */\n    int cfd_count;              /* Used slots in cfd[] */\n\n    // 一个链表，保存了所有客户端状态结构\n    list *clients;              /* List of active clients */\n    // 链表，保存了所有待关闭的客户端\n    list *clients_to_close;     /* Clients to close asynchronously */\n\n    // 链表，保存了所有从服务器，以及所有监视器\n    list *slaves, *monitors;    /* List of slaves and MONITORs */\n\n    // 服务器的当前客户端，仅用于崩溃报告\n    redisClient *current_client; /* Current client, only used on crash report */\n\n    int clients_paused;         /* True if clients are currently paused */\n    mstime_t clients_pause_end_time; /* Time when we undo clients_paused */\n\n    // 网络错误\n    char neterr[ANET_ERR_LEN];   /* Error buffer for anet.c */\n\n    // MIGRATE 缓存\n    dict *migrate_cached_sockets;/* MIGRATE cached sockets */\n\n\n    /* RDB / AOF loading information */\n\n    // 这个值为真时，表示服务器正在进行载入\n    int loading;                /* We are loading data from disk if true */\n\n    // 正在载入的数据的大小\n    off_t loading_total_bytes;\n\n    // 已载入数据的大小\n    off_t loading_loaded_bytes;\n\n    // 开始进行载入的时间\n    time_t loading_start_time;\n    off_t loading_process_events_interval_bytes;\n\n    /* Fast pointers to often looked up command */\n    // 常用命令的快捷连接\n    struct redisCommand *delCommand, *multiCommand, *lpushCommand, *lpopCommand,\n                        *rpopCommand;\n\n\n    /* Fields used only for stats */\n\n    // 服务器启动时间\n    time_t stat_starttime;          /* Server start time */\n\n    // 已处理命令的数量\n    long long stat_numcommands;     /* Number of processed commands */\n\n    // 服务器接到的连接请求数量\n    long long stat_numconnections;  /* Number of connections received */\n\n    // 已过期的键数量\n    long long stat_expiredkeys;     /* Number of expired keys */\n\n    // 因为回收内存而被释放的过期键的数量\n    long long stat_evictedkeys;     /* Number of evicted keys (maxmemory) */\n\n    // 成功查找键的次数\n    long long stat_keyspace_hits;   /* Number of successful lookups of keys */\n\n    // 查找键失败的次数\n    long long stat_keyspace_misses; /* Number of failed lookups of keys */\n\n    // 已使用内存峰值\n    size_t stat_peak_memory;        /* Max used memory record */\n\n    // 最后一次执行 fork() 时消耗的时间\n    long long stat_fork_time;       /* Time needed to perform latest fork() */\n\n    // 服务器因为客户端数量过多而拒绝客户端连接的次数\n    long long stat_rejected_conn;   /* Clients rejected because of maxclients */\n\n    // 执行 full sync 的次数\n    long long stat_sync_full;       /* Number of full resyncs with slaves. */\n\n    // PSYNC 成功执行的次数\n    long long stat_sync_partial_ok; /* Number of accepted PSYNC requests. */\n\n    // PSYNC 执行失败的次数\n    long long stat_sync_partial_err;/* Number of unaccepted PSYNC requests. */\n\n\n    /* slowlog */\n\n    // 保存了所有慢查询日志的链表\n    list *slowlog;                  /* SLOWLOG list of commands */\n\n    // 下一条慢查询日志的 ID\n    long long slowlog_entry_id;     /* SLOWLOG current entry ID */\n\n    // 服务器配置 slowlog-log-slower-than 选项的值\n    long long slowlog_log_slower_than; /* SLOWLOG time limit (to get logged) */\n\n    // 服务器配置 slowlog-max-len 选项的值\n    unsigned long slowlog_max_len;     /* SLOWLOG max number of items logged */\n    size_t resident_set_size;       /* RSS sampled in serverCron(). */\n    /* The following two are used to track instantaneous \"load\" in terms\n     * of operations per second. */\n    // 最后一次进行抽样的时间\n    long long ops_sec_last_sample_time; /* Timestamp of last sample (in ms) */\n    // 最后一次抽样时，服务器已执行命令的数量\n    long long ops_sec_last_sample_ops;  /* numcommands in last sample */\n    // 抽样结果\n    long long ops_sec_samples[REDIS_OPS_SEC_SAMPLES];\n    // 数组索引，用于保存抽样结果，并在需要时回绕到 0\n    int ops_sec_idx;\n\n\n    /* Configuration */\n\n    // 日志可见性\n    int verbosity;                  /* Loglevel in redis.conf */\n\n    // 客户端最大空转时间\n    int maxidletime;                /* Client timeout in seconds */\n\n    // 是否开启 SO_KEEPALIVE 选项\n    int tcpkeepalive;               /* Set SO_KEEPALIVE if non-zero. */\n    int active_expire_enabled;      /* Can be disabled for testing purposes. */\n    size_t client_max_querybuf_len; /* Limit for client query buffer length */\n    int dbnum;                      /* Total number of configured DBs */\n    int daemonize;                  /* True if running as a daemon */\n    // 客户端输出缓冲区大小限制\n    // 数组的元素有 REDIS_CLIENT_LIMIT_NUM_CLASSES 个\n    // 每个代表一类客户端：普通、从服务器、pubsub，诸如此类\n    clientBufferLimitsConfig client_obuf_limits[REDIS_CLIENT_LIMIT_NUM_CLASSES];\n\n\n    /* AOF persistence */\n\n    // AOF 状态（开启/关闭/可写）\n    int aof_state;                  /* REDIS_AOF_(ON|OFF|WAIT_REWRITE) */\n\n    // 所使用的 fsync 策略（每个写入/每秒/从不）\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\n    // 最后一次执行 BGREWRITEAOF 时， AOF 文件的大小\n    off_t aof_rewrite_base_size;    /* AOF size on latest startup or rewrite. */\n\n    // AOF 文件的当前字节大小\n    off_t aof_current_size;         /* AOF current size. */\n    int aof_rewrite_scheduled;      /* Rewrite once BGSAVE terminates. */\n\n    // 负责进行 AOF 重写的子进程 ID\n    pid_t aof_child_pid;            /* PID if rewriting process */\n\n    // AOF 重写缓存链表，链接着多个缓存块\n    list *aof_rewrite_buf_blocks;   /* Hold changes during an AOF rewrite. */\n\n    // AOF 缓冲区\n    sds aof_buf;      /* AOF buffer, written before entering the event loop */\n\n    // AOF 文件的描述符\n    int aof_fd;       /* File descriptor of currently selected AOF file */\n\n    // AOF 的当前目标数据库\n    int aof_selected_db; /* Currently selected DB in AOF */\n\n    // 推迟 write 操作的时间\n    time_t aof_flush_postponed_start; /* UNIX time of postponed AOF flush */\n\n    // 最后一直执行 fsync 的时间\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\n    // AOF 重写的开始时间\n    time_t aof_rewrite_time_start;  /* Current AOF rewrite start time. */\n\n    // 最后一次执行 BGREWRITEAOF 的结果\n    int aof_lastbgrewrite_status;   /* REDIS_OK or REDIS_ERR */\n\n    // 记录 AOF 的 write 操作被推迟了多少次\n    unsigned long aof_delayed_fsync;  /* delayed AOF fsync() counter */\n\n    // 指示是否需要每写入一定量的数据，就主动执行一次 fsync()\n    int aof_rewrite_incremental_fsync;/* fsync incrementally while rewriting? */\n    int aof_last_write_status;      /* REDIS_OK or REDIS_ERR */\n    int aof_last_write_errno;       /* Valid if aof_last_write_status is ERR */\n    /* RDB persistence */\n\n    // 自从上次 SAVE 执行以来，数据库被修改的次数\n    long long dirty;                /* Changes to DB from the last save */\n\n    // BGSAVE 执行前的数据库被修改次数\n    long long dirty_before_bgsave;  /* Used to restore dirty on failed BGSAVE */\n\n    // 负责执行 BGSAVE 的子进程的 ID\n    // 没在执行 BGSAVE 时，设为 -1\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\n    // 最后一次完成 SAVE 的时间\n    time_t lastsave;                /* Unix time of last successful save */\n\n    // 最后一次尝试执行 BGSAVE 的时间\n    time_t lastbgsave_try;          /* Unix time of last attempted bgsave */\n\n    // 最近一次 BGSAVE 执行耗费的时间\n    time_t rdb_save_time_last;      /* Time used by last RDB save run. */\n\n    // 数据库最近一次开始执行 BGSAVE 的时间\n    time_t rdb_save_time_start;     /* Current RDB save start time. */\n\n    // 最后一次执行 SAVE 的状态\n    int lastbgsave_status;          /* REDIS_OK or REDIS_ERR */\n    int stop_writes_on_bgsave_err;  /* Don't allow writes if can't BGSAVE */\n\n\n    /* Propagation of commands in AOF / replication */\n    redisOpArray also_propagate;    /* Additional command to propagate. */\n\n\n    /* Logging */\n    char *logfile;                  /* Path of log file */\n    int syslog_enabled;             /* Is syslog enabled? */\n    char *syslog_ident;             /* Syslog ident */\n    int syslog_facility;            /* Syslog facility */\n\n\n    /* Replication (master) */\n    int slaveseldb;                 /* Last SELECTed DB in replication output */\n    // 全局复制偏移量（一个累计值）\n    long long master_repl_offset;   /* Global replication offset */\n    // 主服务器发送 PING 的频率\n    int repl_ping_slave_period;     /* Master pings the slave every N seconds */\n\n    // backlog 本身\n    char *repl_backlog;             /* Replication backlog for partial syncs */\n    // backlog 的长度\n    long long repl_backlog_size;    /* Backlog circular buffer size */\n    // backlog 中数据的长度\n    long long repl_backlog_histlen; /* Backlog actual data length */\n    // backlog 的当前索引\n    long long repl_backlog_idx;     /* Backlog circular buffer current offset */\n    // backlog 中可以被还原的第一个字节的偏移量\n    long long repl_backlog_off;     /* Replication offset of first byte in the\n                                       backlog buffer. */\n    // backlog 的过期时间\n    time_t repl_backlog_time_limit; /* Time without slaves after the backlog\n                                       gets released. */\n\n    // 距离上一次有从服务器的时间\n    time_t repl_no_slaves_since;    /* We have no slaves since that time.\n                                       Only valid if server.slaves len is 0. */\n\n    // 是否开启最小数量从服务器写入功能\n    int repl_min_slaves_to_write;   /* Min number of slaves to write. */\n    // 定义最小数量从服务器的最大延迟值\n    int repl_min_slaves_max_lag;    /* Max lag of <count> slaves to write. */\n    // 延迟良好的从服务器的数量\n    int repl_good_slaves_count;     /* Number of slaves with lag <= max_lag. */\n\n\n    /* Replication (slave) */\n    // 主服务器的验证密码\n    char *masterauth;               /* AUTH with this password with master */\n    // 主服务器的地址\n    char *masterhost;               /* Hostname of master */\n    // 主服务器的端口\n    int masterport;                 /* Port of master */\n    // 超时时间\n    int repl_timeout;               /* Timeout after N seconds of master idle */\n    // 主服务器所对应的客户端\n    redisClient *master;     /* Client that is master for this slave */\n    // 被缓存的主服务器，PSYNC 时使用\n    redisClient *cached_master; /* Cached master to be reused for PSYNC. */\n    int repl_syncio_timeout; /* Timeout for synchronous I/O calls */\n    // 复制的状态（服务器是从服务器时使用）\n    int repl_state;          /* Replication status if the instance is a slave */\n    // RDB 文件的大小\n    off_t repl_transfer_size; /* Size of RDB to read from master during sync. */\n    // 已读 RDB 文件内容的字节数\n    off_t repl_transfer_read; /* Amount of RDB read from master during sync. */\n    // 最近一次执行 fsync 时的偏移量\n    // 用于 sync_file_range 函数\n    off_t repl_transfer_last_fsync_off; /* Offset when we fsync-ed last time. */\n    // 主服务器的套接字\n    int repl_transfer_s;     /* Slave -> Master SYNC socket */\n    // 保存 RDB 文件的临时文件的描述符\n    int repl_transfer_fd;    /* Slave -> Master SYNC temp file descriptor */\n    // 保存 RDB 文件的临时文件名字\n    char *repl_transfer_tmpfile; /* Slave-> master SYNC temp file name */\n    // 最近一次读入 RDB 内容的时间\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    // 是否只读从服务器？\n    int repl_slave_ro;          /* Slave is read only? */\n    // 连接断开的时长\n    time_t repl_down_since; /* Unix time at which link with master went down */\n    // 是否要在 SYNC 之后关闭 NODELAY ？\n    int repl_disable_tcp_nodelay;   /* Disable TCP_NODELAY after SYNC? */\n    // 从服务器优先级\n    int slave_priority;             /* Reported in INFO and used by Sentinel. */\n    // 本服务器（从服务器）当前主服务器的 RUN ID\n    char repl_master_runid[REDIS_RUN_ID_SIZE+1];  /* Master run id for PSYNC. */\n    // 初始化偏移量\n    long long repl_master_initial_offset;         /* Master PSYNC offset. */\n\n\n    /* Replication script cache. */\n    // 复制脚本缓存\n    // 字典\n    dict *repl_scriptcache_dict;        /* SHA1 all slaves are aware of. */\n    // FIFO 队列\n    list *repl_scriptcache_fifo;        /* First in, first out LRU eviction. */\n    // 缓存的大小\n    int repl_scriptcache_size;          /* Max number of elements. */\n\n    /* Synchronous replication. */\n    list *clients_waiting_acks;         /* Clients waiting in WAIT command. */\n    int get_ack_from_slaves;            /* If true we send REPLCONF GETACK. */\n    /* Limits */\n    int maxclients;                 /* Max number of simultaneous clients */\n    unsigned 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\n\n    /* Blocked clients */\n    unsigned int bpop_blocked_clients; /* Number of clients blocked by lists */\n    list *unblocked_clients; /* list of clients to unblock before next loop */\n    list *ready_keys;        /* List of readyList structures for BLPOP & co */\n\n\n    /* Sort parameters - qsort_r() is only available under BSD so we\n     * have to take this state global, in order to pass it to sortCompare() */\n    int sort_desc;\n    int sort_alpha;\n    int sort_bypattern;\n    int sort_store;\n\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 list_max_ziplist_entries;\n    size_t list_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    time_t unixtime;        /* Unix time sampled every cron cycle. */\n    long long mstime;       /* Like 'unixtime' but with milliseconds resolution. */\n\n\n    /* Pubsub */\n    // 字典，键为频道，值为链表\n    // 链表中保存了所有订阅某个频道的客户端\n    // 新客户端总是被添加到链表的表尾\n    dict *pubsub_channels;  /* Map channels to list of subscribed clients */\n\n    // 这个链表记录了客户端订阅的所有模式的名字\n    list *pubsub_patterns;  /* A list of pubsub_patterns */\n\n    int notify_keyspace_events; /* Events to propagate via Pub/Sub. This is an\n                                   xor of REDIS_NOTIFY... flags. */\n\n\n    /* Cluster */\n\n    int cluster_enabled;      /* Is cluster enabled? */\n    mstime_t cluster_node_timeout; /* Cluster node timeout. */\n    char *cluster_configfile; /* Cluster auto-generated config file name. */\n    struct clusterState *cluster;  /* State of the cluster */\n\n    int cluster_migration_barrier; /* Cluster replicas migration barrier. */\n    /* Scripting */\n\n    // Lua 环境\n    lua_State *lua; /* The Lua interpreter. We use just one for all clients */\n    \n    // 复制执行 Lua 脚本中的 Redis 命令的伪客户端\n    redisClient *lua_client;   /* The \"fake client\" to query Redis from Lua */\n\n    // 当前正在执行 EVAL 命令的客户端，如果没有就是 NULL\n    redisClient *lua_caller;   /* The client running EVAL right now, or NULL */\n\n    // 一个字典，值为 Lua 脚本，键为脚本的 SHA1 校验和\n    dict *lua_scripts;         /* A dictionary of SHA1 -> Lua scripts */\n    // Lua 脚本的执行时限\n    mstime_t lua_time_limit;  /* Script timeout in milliseconds */\n    // 脚本开始执行的时间\n    mstime_t lua_time_start;  /* Start time of script, milliseconds time */\n\n    // 脚本是否执行过写命令\n    int lua_write_dirty;  /* True if a write command was called during the\n                             execution of the current script. */\n\n    // 脚本是否执行过带有随机性质的命令\n    int lua_random_dirty; /* True if a random command was called during the\n                             execution of the current script. */\n\n    // 脚本是否超时\n    int lua_timedout;     /* True if we reached the time limit for script\n                             execution. */\n\n    // 是否要杀死脚本\n    int lua_kill;         /* Kill the script if true. */\n\n\n    /* Assert & bug reporting */\n\n    char *assert_failed;\n    char *assert_file;\n    int assert_line;\n    int bug_report_start; /* True if bug report header was already logged. */\n    int watchdog_period;  /* Software watchdog period in ms. 0 = off */\n};\n\n/*\n * 记录订阅模式的结构\n */\ntypedef struct pubsubPattern {\n\n    // 订阅模式的客户端\n    redisClient *client;\n\n    // 被订阅的模式\n    robj *pattern;\n\n} pubsubPattern;\n\ntypedef void redisCommandProc(redisClient *c);\ntypedef int *redisGetKeysProc(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);\n\n/*\n * Redis 命令\n */\nstruct redisCommand {\n\n    // 命令名字\n    char *name;\n\n    // 实现函数\n    redisCommandProc *proc;\n\n    // 参数个数\n    int arity;\n\n    // 字符串表示的 FLAG\n    char *sflags; /* Flags as string representation, one char per flag. */\n\n    // 实际 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     * Used for Redis Cluster redirect. */\n    // 从命令中判断命令的键参数。在 Redis 集群转向时使用。\n    redisGetKeysProc *getkeys_proc;\n\n    /* What keys should be loaded in background when calling this command? */\n    // 指定哪些参数是 key\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\n    // 统计信息\n    // microseconds 记录了命令执行耗费的总毫微秒数\n    // calls 是命令被执行的总次数\n    long long microseconds, calls;\n};\n\nstruct redisFunctionSym {\n    char *name;\n    unsigned long pointer;\n};\n\n// 用于保存被排序值及其权重的结构\ntypedef struct _redisSortObject {\n\n    // 被排序键的值\n    robj *obj;\n\n    // 权重\n    union {\n\n        // 排序数字值时使用\n        double score;\n\n        // 排序字符串时使用\n        robj *cmpobj;\n\n    } u;\n\n} redisSortObject;\n\n// 排序操作\ntypedef struct _redisSortOperation {\n\n    // 操作的类型，可以是 GET 、 DEL 、INCR 或者 DECR\n    // 目前只实现了 GET\n    int type;\n\n    // 用户给定的模式\n    robj *pattern;\n\n} redisSortOperation;\n\n/* Structure to hold list iteration abstraction.\n *\n * 列表迭代器对象\n */\ntypedef struct {\n\n    // 列表对象\n    robj *subject;\n\n    // 对象所使用的编码\n    unsigned char encoding;\n\n    // 迭代的方向\n    unsigned char direction; /* Iteration direction */\n\n    // ziplist 索引，迭代 ziplist 编码的列表时使用\n    unsigned char *zi;\n\n    // 链表节点的指针，迭代双端链表编码的列表时使用\n    listNode *ln;\n\n} listTypeIterator;\n\n/* Structure for an entry while iterating over a list.\n *\n * 迭代列表时使用的记录结构，\n * 用于保存迭代器，以及迭代器返回的列表节点。\n */\ntypedef struct {\n\n    // 列表迭代器\n    listTypeIterator *li;\n\n    // ziplist 节点索引\n    unsigned char *zi;  /* Entry in ziplist */\n\n    // 双端链表节点指针\n    listNode *ln;       /* Entry in linked list */\n\n} listTypeEntry;\n\n/* Structure to hold set iteration abstraction. */\n/*\n * 多态集合迭代器\n */\ntypedef struct {\n\n    // 被迭代的对象\n    robj *subject;\n\n    // 对象的编码\n    int encoding;\n\n    // 索引值，编码为 intset 时使用\n    int ii; /* intset iterator */\n\n    // 字典迭代器，编码为 HT 时使用\n    dictIterator *di;\n\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. */\n/*\n * 哈希对象的迭代器\n */\ntypedef struct {\n\n    // 被迭代的哈希对象\n    robj *subject;\n\n    // 哈希对象的编码\n    int encoding;\n\n    // 域指针和值指针\n    // 在迭代 ZIPLIST 编码的哈希对象时使用\n    unsigned char *fptr, *vptr;\n\n    // 字典迭代器和指向当前迭代字典节点的指针\n    // 在迭代 HT 编码的哈希对象时使用\n    dictIterator *di;\n    dictEntry *de;\n} hashTypeIterator;\n\n#define REDIS_HASH_KEY 1\n#define REDIS_HASH_VALUE 2\n\n/*-----------------------------------------------------------------------------\n * Extern declarations\n *----------------------------------------------------------------------------*/\n\nextern struct redisServer server;\nextern struct sharedObjectsStruct shared;\nextern dictType setDictType;\nextern dictType zsetDictType;\nextern dictType clusterNodesDictType;\nextern dictType clusterNodesBlackListDictType;\nextern dictType dbDictType;\nextern dictType shaScriptObjectDictType;\nextern double R_Zero, R_PosInf, R_NegInf, R_Nan;\nextern dictType hashDictType;\nextern dictType replScriptCacheDictType;\n\n/*-----------------------------------------------------------------------------\n * Functions prototypes\n *----------------------------------------------------------------------------*/\n\n/* Utils */\nlong long ustime(void);\nlong long mstime(void);\nvoid getRandomHexChars(char *p, unsigned int len);\nuint64_t crc64(uint64_t crc, const unsigned char *s, uint64_t l);\nvoid exitFromChild(int retcode);\nsize_t redisPopcount(void *s, long count);\nvoid redisSetProcTitle(char *title);\n\n/* networking.c -- Networking and Client related operations */\nredisClient *createClient(int fd);\nvoid closeTimedoutClients(void);\nvoid freeClient(redisClient *c);\nvoid freeClientAsync(redisClient *c);\nvoid resetClient(redisClient *c);\nvoid sendReplyToClient(aeEventLoop *el, int fd, void *privdata, int mask);\nvoid addReply(redisClient *c, robj *obj);\nvoid *addDeferredMultiBulkLength(redisClient *c);\nvoid setDeferredMultiBulkLength(redisClient *c, void *node, long length);\nvoid addReplySds(redisClient *c, sds s);\nvoid processInputBuffer(redisClient *c);\nvoid acceptTcpHandler(aeEventLoop *el, int fd, void *privdata, int mask);\nvoid acceptUnixHandler(aeEventLoop *el, int fd, void *privdata, int mask);\nvoid readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask);\nvoid addReplyBulk(redisClient *c, robj *obj);\nvoid addReplyBulkCString(redisClient *c, char *s);\nvoid addReplyBulkCBuffer(redisClient *c, void *p, size_t len);\nvoid addReplyBulkLongLong(redisClient *c, long long ll);\nvoid acceptHandler(aeEventLoop *el, int fd, void *privdata, int mask);\nvoid addReply(redisClient *c, robj *obj);\nvoid addReplySds(redisClient *c, sds s);\nvoid addReplyError(redisClient *c, char *err);\nvoid addReplyStatus(redisClient *c, char *status);\nvoid addReplyDouble(redisClient *c, double d);\nvoid addReplyLongLong(redisClient *c, long long ll);\nvoid addReplyMultiBulkLen(redisClient *c, long length);\nvoid copyClientOutputBuffer(redisClient *dst, redisClient *src);\nvoid *dupClientReplyValue(void *o);\nvoid getClientsMaxBuffers(unsigned long *longest_output_list,\n                          unsigned long *biggest_input_buffer);\nvoid formatPeerId(char *peerid, size_t peerid_len, char *ip, int port);\nchar *getClientPeerId(redisClient *client);\nsds catClientInfoString(sds s, redisClient *client);\nsds getAllClientsInfoString(void);\nvoid rewriteClientCommandVector(redisClient *c, int argc, ...);\nvoid rewriteClientCommandArgument(redisClient *c, int i, robj *newval);\nunsigned long getClientOutputBufferMemoryUsage(redisClient *c);\nvoid freeClientsInAsyncFreeQueue(void);\nvoid asyncCloseClientOnOutputBufferLimitReached(redisClient *c);\nint getClientLimitClassByName(char *name);\nchar *getClientLimitClassName(int class);\nvoid flushSlavesOutputBuffers(void);\nvoid disconnectSlaves(void);\nint listenToPort(int port, int *fds, int *count);\nvoid pauseClients(mstime_t duration);\nint clientsArePaused(void);\nint processEventsWhileBlocked(void);\n\n#ifdef __GNUC__\nvoid addReplyErrorFormat(redisClient *c, const char *fmt, ...)\n    __attribute__((format(printf, 2, 3)));\nvoid addReplyStatusFormat(redisClient *c, const char *fmt, ...)\n    __attribute__((format(printf, 2, 3)));\n#else\nvoid addReplyErrorFormat(redisClient *c, const char *fmt, ...);\nvoid addReplyStatusFormat(redisClient *c, const char *fmt, ...);\n#endif\n\n/* List data type */\nvoid listTypeTryConversion(robj *subject, robj *value);\nvoid listTypePush(robj *subject, robj *value, int where);\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(listTypeEntry *entry);\nvoid listTypeConvert(robj *subject, int enc);\nvoid unblockClientWaitingData(redisClient *c);\nvoid handleClientsBlockedOnLists(void);\nvoid popGenericCommand(redisClient *c, int where);\n\n/* MULTI/EXEC/WATCH... */\nvoid unwatchAllKeys(redisClient *c);\nvoid initClientMultiState(redisClient *c);\nvoid freeClientMultiState(redisClient *c);\nvoid queueMultiCommand(redisClient *c);\nvoid touchWatchedKey(redisDb *db, robj *key);\nvoid touchWatchedKeysOnFlush(int dbid);\nvoid discardTransaction(redisClient *c);\nvoid flagTransaction(redisClient *c);\n\n/* Redis object implementation */\nvoid decrRefCount(robj *o);\nvoid decrRefCountVoid(void *o);\nvoid incrRefCount(robj *o);\nrobj *resetRefCount(robj *obj);\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(char *ptr, size_t len);\nrobj *createRawStringObject(char *ptr, size_t len);\nrobj *createEmbeddedStringObject(char *ptr, size_t len);\nrobj *dupStringObject(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);\nrobj *createListObject(void);\nrobj *createZiplistObject(void);\nrobj *createSetObject(void);\nrobj *createIntsetObject(void);\nrobj *createHashObject(void);\nrobj *createZsetObject(void);\nrobj *createZsetZiplistObject(void);\nint getLongFromObjectOrReply(redisClient *c, robj *o, long *target, const char *msg);\nint checkType(redisClient *c, robj *o, int type);\nint getLongLongFromObjectOrReply(redisClient *c, robj *o, long long *target, const char *msg);\nint getDoubleFromObjectOrReply(redisClient *c, robj *o, double *target, const char *msg);\nint getLongLongFromObject(robj *o, long long *target);\nint getLongDoubleFromObject(robj *o, long double *target);\nint getLongDoubleFromObjectOrReply(redisClient *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#define sdsEncodedObject(objptr) (objptr->encoding == REDIS_ENCODING_RAW || objptr->encoding == REDIS_ENCODING_EMBSTR)\n\n/* Synchronous I/O with timeout */\nssize_t syncWrite(int fd, char *ptr, ssize_t size, long long timeout);\nssize_t syncRead(int fd, char *ptr, ssize_t size, long long timeout);\nssize_t syncReadLine(int fd, char *ptr, ssize_t size, long long timeout);\n\n/* Replication */\nvoid replicationFeedSlaves(list *slaves, int dictid, robj **argv, int argc);\nvoid replicationFeedMonitors(redisClient *c, list *monitors, int dictid, robj **argv, int argc);\nvoid updateSlavesWaitingBgsave(int bgsaveerr);\nvoid replicationCron(void);\nvoid replicationHandleMasterDisconnection(void);\nvoid replicationCacheMaster(redisClient *c);\nvoid resizeReplicationBacklog(long long newsize);\nvoid replicationSetMaster(char *ip, int port);\nvoid replicationUnsetMaster(void);\nvoid refreshGoodSlavesCount(void);\nvoid replicationScriptCacheInit(void);\nvoid replicationScriptCacheFlush(void);\nvoid replicationScriptCacheAdd(sds sha1);\nint replicationScriptCacheExists(sds sha1);\nvoid processClientsWaitingReplicas(void);\nvoid unblockClientWaitingReplicas(redisClient *c);\nint replicationCountAcksByOffset(long long offset);\nvoid replicationSendNewlineToMaster(void);\nlong long replicationGetSlaveOffset(void);\n\n/* Generic persistence functions */\nvoid startLoading(FILE *fp);\nvoid loadingProgress(off_t pos);\nvoid stopLoading(void);\n\n/* RDB persistence */\n#include \"rdb.h\"\n\n/* AOF persistence */\nvoid flushAppendOnlyFile(int force);\nvoid feedAppendOnlyFile(struct redisCommand *cmd, int dictid, robj **argv, int argc);\nvoid aofRemoveTempFile(pid_t childpid);\nint rewriteAppendOnlyFileBackground(void);\nint loadAppendOnlyFile(char *filename);\nvoid stopAppendOnly(void);\nint startAppendOnly(void);\nvoid backgroundRewriteDoneHandler(int exitcode, int bysignal);\nvoid aofRewriteBufferReset(void);\nunsigned long aofRewriteBufferSize(void);\n\n/* Sorted sets data type */\n\n/* Struct to hold a inclusive/exclusive range spec by score comparison. */\n// 表示开区间/闭区间范围的结构\ntypedef struct {\n\n    // 最小值和最大值\n    double min, max;\n\n    // 指示最小值和最大值是否*不*包含在范围之内\n    // 值为 1 表示不包含，值为 0 表示包含\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\nzskiplist *zslCreate(void);\nvoid zslFree(zskiplist *zsl);\nzskiplistNode *zslInsert(zskiplist *zsl, double score, robj *obj);\nunsigned char *zzlInsert(unsigned char *zl, robj *ele, double score);\nint zslDelete(zskiplist *zsl, double score, robj *obj);\nzskiplistNode *zslFirstInRange(zskiplist *zsl, zrangespec *range);\nzskiplistNode *zslLastInRange(zskiplist *zsl, zrangespec *range);\ndouble zzlGetScore(unsigned char *sptr);\nvoid zzlNext(unsigned char *zl, unsigned char **eptr, unsigned char **sptr);\nvoid zzlPrev(unsigned char *zl, unsigned char **eptr, unsigned char **sptr);\nunsigned int zsetLength(robj *zobj);\nvoid zsetConvert(robj *zobj, int encoding);\nunsigned long zslGetRank(zskiplist *zsl, double score, robj *o);\n\n/* Core functions */\nint freeMemoryIfNeeded(void);\nint processCommand(redisClient *c);\nvoid setupSignalHandlers(void);\nstruct redisCommand *lookupCommand(sds name);\nstruct redisCommand *lookupCommandByCString(char *s);\nstruct redisCommand *lookupCommandOrOriginal(sds name);\nvoid call(redisClient *c, int flags);\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(redisClient *c, int flags);\nint prepareForShutdown();\n#ifdef __GNUC__\nvoid redisLog(int level, const char *fmt, ...)\n    __attribute__((format(printf, 2, 3)));\n#else\nvoid redisLog(int level, const char *fmt, ...);\n#endif\nvoid redisLogRaw(int level, const char *msg);\nvoid redisLogFromHandler(int level, const char *msg);\nvoid usage();\nvoid updateDictResizePolicy(void);\nint htNeedsResize(dict *dict);\nvoid oom(const char *msg);\nvoid populateCommandTable(void);\nvoid resetCommandTableStats(void);\nvoid adjustOpenFilesLimit(void);\nvoid closeListeningSockets(int unlink_unix_socket);\nvoid updateCachedTime(void);\nvoid resetServerStats(void);\nunsigned int getLRUClock(void);\n\n/* Set data type */\nrobj *setTypeCreate(robj *value);\nint setTypeAdd(robj *subject, robj *value);\nint setTypeRemove(robj *subject, 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);\nunsigned long setTypeSize(robj *subject);\nvoid setTypeConvert(robj *subject, int enc);\n\n/* Hash data type */\nvoid hashTypeConvert(robj *o, int enc);\nvoid hashTypeTryConversion(robj *subject, robj **argv, int start, int end);\nvoid hashTypeTryObjectEncoding(robj *subject, robj **o1, robj **o2);\nrobj *hashTypeGetObject(robj *o, robj *key);\nint hashTypeExists(robj *o, robj *key);\nint hashTypeSet(robj *o, robj *key, robj *value);\nint hashTypeDelete(robj *o, robj *key);\nunsigned long hashTypeLength(robj *o);\nhashTypeIterator *hashTypeInitIterator(robj *subject);\nvoid hashTypeReleaseIterator(hashTypeIterator *hi);\nint hashTypeNext(hashTypeIterator *hi);\nvoid hashTypeCurrentFromZiplist(hashTypeIterator *hi, int what,\n                                unsigned char **vstr,\n                                unsigned int *vlen,\n                                long long *vll);\nvoid hashTypeCurrentFromHashTable(hashTypeIterator *hi, int what, robj **dst);\nrobj *hashTypeCurrentObject(hashTypeIterator *hi, int what);\nrobj *hashTypeLookupWriteOrCreate(redisClient *c, robj *key);\n\n/* Pub / Sub */\nint pubsubUnsubscribeAllChannels(redisClient *c, int notify);\nint pubsubUnsubscribeAllPatterns(redisClient *c, int notify);\nvoid freePubsubPattern(void *p);\nint listMatchPubsubPattern(void *a, void *b);\nint pubsubPublishMessage(robj *channel, robj *message);\n\n/* Keyspace events notification */\nvoid notifyKeyspaceEvent(int type, char *event, robj *key, int dbid);\nint keyspaceEventsStringToFlags(char *classes);\nsds keyspaceEventsFlagsToString(int flags);\n\n/* Configuration */\nvoid loadServerConfig(char *filename, char *options);\nvoid appendServerSaveParams(time_t seconds, int changes);\nvoid resetServerSaveParams();\nstruct rewriteConfigState; /* Forward declaration to export API. */\nvoid rewriteConfigRewriteLine(struct rewriteConfigState *state, char *option, sds line, int force);\nint rewriteConfig(char *path);\n\n/* db.c -- Keyspace access API */\nint removeExpire(redisDb *db, robj *key);\nvoid propagateExpire(redisDb *db, robj *key);\nint expireIfNeeded(redisDb *db, robj *key);\nlong long getExpire(redisDb *db, robj *key);\nvoid setExpire(redisDb *db, robj *key, long long when);\nrobj *lookupKey(redisDb *db, robj *key);\nrobj *lookupKeyRead(redisDb *db, robj *key);\nrobj *lookupKeyWrite(redisDb *db, robj *key);\nrobj *lookupKeyReadOrReply(redisClient *c, robj *key, robj *reply);\nrobj *lookupKeyWriteOrReply(redisClient *c, robj *key, robj *reply);\nvoid dbAdd(redisDb *db, robj *key, robj *val);\nvoid dbOverwrite(redisDb *db, robj *key, robj *val);\nvoid setKey(redisDb *db, robj *key, robj *val);\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(redisClient *c, int id);\nvoid signalModifiedKey(redisDb *db, robj *key);\nvoid signalFlushedDb(int dbid);\nunsigned int getKeysInSlot(unsigned int hashslot, robj **keys, unsigned int count);\nunsigned int countKeysInSlot(unsigned int hashslot);\nunsigned int delKeysInSlot(unsigned int hashslot);\nint verifyClusterConfigWithData(void);\nvoid scanGenericCommand(redisClient *c, robj *o, unsigned long cursor);\nint parseScanCursorOrReply(redisClient *c, robj *o, unsigned long *cursor);\n\n/* API to get key arguments from commands */\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);\n\n/* Cluster */\nvoid clusterInit(void);\nunsigned short crc16(const char *buf, int len);\nunsigned int keyHashSlot(char *key, int keylen);\nvoid clusterCron(void);\nvoid clusterPropagatePublish(robj *channel, robj *message);\nvoid migrateCloseTimedoutSockets(void);\nvoid clusterBeforeSleep(void);\n\n/* Sentinel */\nvoid initSentinelConfig(void);\nvoid initSentinel(void);\nvoid sentinelTimer(void);\nchar *sentinelHandleConfiguration(char **argv, int argc);\nvoid sentinelIsRunning(void);\n\n/* Scripting */\nvoid scriptingInit(void);\n\n/* Blocked clients */\nvoid processUnblockedClients(void);\nvoid blockClient(redisClient *c, int btype);\nvoid unblockClient(redisClient *c);\nvoid replyToBlockedClientTimedOut(redisClient *c);\nint getTimeoutFromObjectOrReply(redisClient *c, robj *object, mstime_t *timeout, int unit);\n\n/* Git SHA1 */\nchar *redisGitSHA1(void);\nchar *redisGitDirty(void);\nuint64_t redisBuildId(void);\n\n/* Commands prototypes */\nvoid authCommand(redisClient *c);\nvoid pingCommand(redisClient *c);\nvoid echoCommand(redisClient *c);\nvoid setCommand(redisClient *c);\nvoid setnxCommand(redisClient *c);\nvoid setexCommand(redisClient *c);\nvoid psetexCommand(redisClient *c);\nvoid getCommand(redisClient *c);\nvoid delCommand(redisClient *c);\nvoid existsCommand(redisClient *c);\nvoid setbitCommand(redisClient *c);\nvoid getbitCommand(redisClient *c);\nvoid setrangeCommand(redisClient *c);\nvoid getrangeCommand(redisClient *c);\nvoid incrCommand(redisClient *c);\nvoid decrCommand(redisClient *c);\nvoid incrbyCommand(redisClient *c);\nvoid decrbyCommand(redisClient *c);\nvoid incrbyfloatCommand(redisClient *c);\nvoid selectCommand(redisClient *c);\nvoid randomkeyCommand(redisClient *c);\nvoid keysCommand(redisClient *c);\nvoid scanCommand(redisClient *c);\nvoid dbsizeCommand(redisClient *c);\nvoid lastsaveCommand(redisClient *c);\nvoid saveCommand(redisClient *c);\nvoid bgsaveCommand(redisClient *c);\nvoid bgrewriteaofCommand(redisClient *c);\nvoid shutdownCommand(redisClient *c);\nvoid moveCommand(redisClient *c);\nvoid renameCommand(redisClient *c);\nvoid renamenxCommand(redisClient *c);\nvoid lpushCommand(redisClient *c);\nvoid rpushCommand(redisClient *c);\nvoid lpushxCommand(redisClient *c);\nvoid rpushxCommand(redisClient *c);\nvoid linsertCommand(redisClient *c);\nvoid lpopCommand(redisClient *c);\nvoid rpopCommand(redisClient *c);\nvoid llenCommand(redisClient *c);\nvoid lindexCommand(redisClient *c);\nvoid lrangeCommand(redisClient *c);\nvoid ltrimCommand(redisClient *c);\nvoid typeCommand(redisClient *c);\nvoid lsetCommand(redisClient *c);\nvoid saddCommand(redisClient *c);\nvoid sremCommand(redisClient *c);\nvoid smoveCommand(redisClient *c);\nvoid sismemberCommand(redisClient *c);\nvoid scardCommand(redisClient *c);\nvoid spopCommand(redisClient *c);\nvoid srandmemberCommand(redisClient *c);\nvoid sinterCommand(redisClient *c);\nvoid sinterstoreCommand(redisClient *c);\nvoid sunionCommand(redisClient *c);\nvoid sunionstoreCommand(redisClient *c);\nvoid sdiffCommand(redisClient *c);\nvoid sdiffstoreCommand(redisClient *c);\nvoid sscanCommand(redisClient *c);\nvoid syncCommand(redisClient *c);\nvoid flushdbCommand(redisClient *c);\nvoid flushallCommand(redisClient *c);\nvoid sortCommand(redisClient *c);\nvoid lremCommand(redisClient *c);\nvoid rpoplpushCommand(redisClient *c);\nvoid infoCommand(redisClient *c);\nvoid mgetCommand(redisClient *c);\nvoid monitorCommand(redisClient *c);\nvoid expireCommand(redisClient *c);\nvoid expireatCommand(redisClient *c);\nvoid pexpireCommand(redisClient *c);\nvoid pexpireatCommand(redisClient *c);\nvoid getsetCommand(redisClient *c);\nvoid ttlCommand(redisClient *c);\nvoid pttlCommand(redisClient *c);\nvoid persistCommand(redisClient *c);\nvoid slaveofCommand(redisClient *c);\nvoid debugCommand(redisClient *c);\nvoid msetCommand(redisClient *c);\nvoid msetnxCommand(redisClient *c);\nvoid zaddCommand(redisClient *c);\nvoid zincrbyCommand(redisClient *c);\nvoid zrangeCommand(redisClient *c);\nvoid zrangebyscoreCommand(redisClient *c);\nvoid zrevrangebyscoreCommand(redisClient *c);\nvoid zrangebylexCommand(redisClient *c);\nvoid zrevrangebylexCommand(redisClient *c);\nvoid zcountCommand(redisClient *c);\nvoid zlexcountCommand(redisClient *c);\nvoid zrevrangeCommand(redisClient *c);\nvoid zcardCommand(redisClient *c);\nvoid zremCommand(redisClient *c);\nvoid zscoreCommand(redisClient *c);\nvoid zremrangebyscoreCommand(redisClient *c);\nvoid zremrangebylexCommand(redisClient *c);\nvoid multiCommand(redisClient *c);\nvoid execCommand(redisClient *c);\nvoid discardCommand(redisClient *c);\nvoid blpopCommand(redisClient *c);\nvoid brpopCommand(redisClient *c);\nvoid brpoplpushCommand(redisClient *c);\nvoid appendCommand(redisClient *c);\nvoid strlenCommand(redisClient *c);\nvoid zrankCommand(redisClient *c);\nvoid zrevrankCommand(redisClient *c);\nvoid hsetCommand(redisClient *c);\nvoid hsetnxCommand(redisClient *c);\nvoid hgetCommand(redisClient *c);\nvoid hmsetCommand(redisClient *c);\nvoid hmgetCommand(redisClient *c);\nvoid hdelCommand(redisClient *c);\nvoid hlenCommand(redisClient *c);\nvoid zremrangebyrankCommand(redisClient *c);\nvoid zunionstoreCommand(redisClient *c);\nvoid zinterstoreCommand(redisClient *c);\nvoid zscanCommand(redisClient *c);\nvoid hkeysCommand(redisClient *c);\nvoid hvalsCommand(redisClient *c);\nvoid hgetallCommand(redisClient *c);\nvoid hexistsCommand(redisClient *c);\nvoid hscanCommand(redisClient *c);\nvoid configCommand(redisClient *c);\nvoid hincrbyCommand(redisClient *c);\nvoid hincrbyfloatCommand(redisClient *c);\nvoid subscribeCommand(redisClient *c);\nvoid unsubscribeCommand(redisClient *c);\nvoid psubscribeCommand(redisClient *c);\nvoid punsubscribeCommand(redisClient *c);\nvoid publishCommand(redisClient *c);\nvoid pubsubCommand(redisClient *c);\nvoid watchCommand(redisClient *c);\nvoid unwatchCommand(redisClient *c);\nvoid clusterCommand(redisClient *c);\nvoid restoreCommand(redisClient *c);\nvoid migrateCommand(redisClient *c);\nvoid askingCommand(redisClient *c);\nvoid readonlyCommand(redisClient *c);\nvoid readwriteCommand(redisClient *c);\nvoid dumpCommand(redisClient *c);\nvoid objectCommand(redisClient *c);\nvoid clientCommand(redisClient *c);\nvoid evalCommand(redisClient *c);\nvoid evalShaCommand(redisClient *c);\nvoid scriptCommand(redisClient *c);\nvoid timeCommand(redisClient *c);\nvoid bitopCommand(redisClient *c);\nvoid bitcountCommand(redisClient *c);\nvoid bitposCommand(redisClient *c);\nvoid replconfCommand(redisClient *c);\nvoid waitCommand(redisClient *c);\nvoid pfselftestCommand(redisClient *c);\nvoid pfaddCommand(redisClient *c);\nvoid pfcountCommand(redisClient *c);\nvoid pfmergeCommand(redisClient *c);\nvoid pfdebugCommand(redisClient *c);\n\n#if defined(__GNUC__)\nvoid *calloc(size_t count, size_t size) __attribute__ ((deprecated));\nvoid free(void *ptr) __attribute__ ((deprecated));\nvoid *malloc(size_t size) __attribute__ ((deprecated));\nvoid *realloc(void *ptr, size_t size) __attribute__ ((deprecated));\n#endif\n\n/* Debugging stuff */\nvoid _redisAssertWithInfo(redisClient *c, robj *o, char *estr, char *file, int line);\nvoid _redisAssert(char *estr, char *file, int line);\nvoid _redisPanic(char *msg, char *file, int line);\nvoid bugReportStart(void);\nvoid redisLogObjectDebugInfo(robj *o);\nvoid sigsegvHandler(int sig, siginfo_t *info, void *secret);\nsds genRedisInfoString(char *section);\nvoid enableWatchdog(int period);\nvoid disableWatchdog(void);\nvoid watchdogScheduleSignal(int period);\nvoid redisLogHexDump(int level, char *descr, void *value, size_t len);\n\n#define redisDebug(fmt, ...) \\\n    printf(\"DEBUG %s:%d > \" fmt \"\\n\", __FILE__, __LINE__, __VA_ARGS__)\n#define redisDebugMark() \\\n    printf(\"-- MARK %s:%d --\\n\", __FILE__, __LINE__)\n\n#endif\n"
  },
  {
    "path": "src/redisassert.h",
    "content": "/* redisassert.h -- Drop in replacemnet assert.h that prints the stack trace\n *                  in the Redis logs.\n *\n * This file should be included instead of \"assert.h\" inside libraries used by\n * Redis that are using assertions, so instead of Redis disappearing with\n * SIGABORT, we get the details and stack trace inside the log file.\n *\n * ----------------------------------------------------------------------------\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 __REDIS_ASSERT_H__\n#define __REDIS_ASSERT_H__\n\n#include <unistd.h> /* for _exit() */\n\n#define assert(_e) ((_e)?(void)0 : (_redisAssert(#_e,__FILE__,__LINE__),_exit(1)))\n\nvoid _redisAssert(char *estr, char *file, int line);\n\n#endif\n"
  },
  {
    "path": "src/release.c",
    "content": "/*\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/* Every time the Redis Git SHA1 or Dirty status changes only this small \n * file is recompiled, as we access this information in all the other\n * files using this functions. */\n\n#include <string.h>\n\n#include \"release.h\"\n#include \"version.h\"\n#include \"crc64.h\"\n\nchar *redisGitSHA1(void) {\n    return REDIS_GIT_SHA1;\n}\n\nchar *redisGitDirty(void) {\n    return REDIS_GIT_DIRTY;\n}\n\nuint64_t redisBuildId(void) {\n    char *buildid = REDIS_VERSION REDIS_BUILD_ID REDIS_GIT_DIRTY REDIS_GIT_SHA1;\n\n    return crc64(0,(unsigned char*)buildid,strlen(buildid));\n}\n"
  },
  {
    "path": "src/replication.c",
    "content": "/* Asynchronous replication implementation.\n *\n * 异步复制实现\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 \"redis.h\"\n\n#include <sys/time.h>\n#include <unistd.h>\n#include <fcntl.h>\n#include <sys/socket.h>\n#include <sys/stat.h>\n\nvoid replicationDiscardCachedMaster(void);\nvoid replicationResurrectCachedMaster(int newfd);\nvoid replicationSendAck(void);\n\n/* ---------------------------------- MASTER -------------------------------- */\n\n// 创建 backlog\nvoid createReplicationBacklog(void) {\n\n    redisAssert(server.repl_backlog == NULL);\n\n    // backlog\n    server.repl_backlog = zmalloc(server.repl_backlog_size);\n    // 数据长度\n    server.repl_backlog_histlen = 0;\n    // 索引值，增加数据时使用\n    server.repl_backlog_idx = 0;\n    /* When a new backlog buffer is created, we increment the replication\n     * offset by one to make sure we'll not be able to PSYNC with any\n     * previous slave. This is needed because we avoid incrementing the\n     * master_repl_offset if no backlog exists nor slaves are attached. */\n    // 每次创建 backlog 时都将 master_repl_offset 增一\n    // 这是为了防止之前使用过 backlog 的从服务器引发错误的 PSYNC 请求\n    server.master_repl_offset++;\n\n    /* We don't have any data inside our buffer, but virtually the first\n     * byte we have is the next byte that will be generated for the\n     * replication stream. */\n    // 尽管没有任何数据，\n    // 但 backlog 第一个字节的逻辑位置应该是 repl_offset 后的第一个字节\n    server.repl_backlog_off = server.master_repl_offset+1;\n}\n\n/* This function is called when the user modifies the replication backlog\n * size at runtime. It is up to the function to both update the\n * server.repl_backlog_size and to resize the buffer and setup it so that\n * it contains the same data as the previous one (possibly less data, but\n * the most recent bytes, or the same data and more free space in case the\n * buffer is enlarged). */\n// 动态调整 backlog 大小\n// 当 backlog 是被扩大时，原有的数据会被保留，\n// 因为分配空间使用的是 realloc\nvoid resizeReplicationBacklog(long long newsize) {\n\n    // 不能小于最小大小\n    if (newsize < REDIS_REPL_BACKLOG_MIN_SIZE)\n        newsize = REDIS_REPL_BACKLOG_MIN_SIZE;\n\n    // 大小和目前大小相等\n    if (server.repl_backlog_size == newsize) return;\n\n    // 设置新大小\n    server.repl_backlog_size = newsize;\n    if (server.repl_backlog != NULL) {\n        /* What we actually do is to flush the old buffer and realloc a new\n         * empty one. It will refill with new data incrementally.\n         * The reason is that copying a few gigabytes adds latency and even\n         * worse often we need to alloc additional space before freeing the\n         * old buffer. */\n        // 释放 backlog\n        zfree(server.repl_backlog);\n        // 按新大小创建新 backlog\n        server.repl_backlog = zmalloc(server.repl_backlog_size);\n        server.repl_backlog_histlen = 0;\n        server.repl_backlog_idx = 0;\n        /* Next byte we have is... the next since the buffer is emtpy. */\n        server.repl_backlog_off = server.master_repl_offset+1;\n    }\n}\n\n// 释放 backlog\nvoid freeReplicationBacklog(void) {\n    redisAssert(listLength(server.slaves) == 0);\n    zfree(server.repl_backlog);\n    server.repl_backlog = NULL;\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. \n *\n * 添加数据到复制 backlog ，\n * 并且按照添加内容的长度更新 server.master_repl_offset 偏移量。\n */\nvoid feedReplicationBacklog(void *ptr, size_t len) {\n    unsigned char *p = ptr;\n\n    // 将长度累加到全局 offset 中\n    server.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    // 环形 buffer ，每次写尽可能多的数据，并在到达尾部时将 idx 重置到头部\n    while(len) {\n        // 从 idx 到 backlog 尾部的字节数\n        size_t thislen = server.repl_backlog_size - server.repl_backlog_idx;\n        // 如果 idx 到 backlog 尾部这段空间足以容纳要写入的内容\n        // 那么直接将写入数据长度设为 len\n        // 在将这些 len 字节复制之后，这个 while 循环将跳出\n        if (thislen > len) thislen = len;\n        // 将 p 中的 thislen 字节内容复制到 backlog\n        memcpy(server.repl_backlog+server.repl_backlog_idx,p,thislen);\n        // 更新 idx ，指向新写入的数据之后\n        server.repl_backlog_idx += thislen;\n        // 如果写入达到尾部，那么将索引重置到头部\n        if (server.repl_backlog_idx == server.repl_backlog_size)\n            server.repl_backlog_idx = 0;\n        // 减去已写入的字节数\n        len -= thislen;\n        // 将指针移动到已被写入数据的后面，指向未被复制数据的开头\n        p += thislen;\n        // 增加实际长度\n        server.repl_backlog_histlen += thislen;\n    }\n    // histlen 的最大值只能等于 backlog_size\n    // 另外，当 histlen 大于 repl_backlog_size 时，\n    // 表示写入数据的前头有一部分数据被自己的尾部覆盖了\n    // 举个例子，例如 abcde 要写入到一个只有三个字节的环形数组中\n    // 且假设索引为 0\n    // 那么 abc 首先被写入，数组为 [a, b, c] \n    // 然后 de 被写入，数组为 [d, e, c]\n    if (server.repl_backlog_histlen > server.repl_backlog_size)\n        server.repl_backlog_histlen = server.repl_backlog_size;\n    /* Set the offset of the first byte we have in the backlog. */\n    // 记录程序可以依靠 backlog 来还原的数据的第一个字节的偏移量\n    // 比如 master_repl_offset = 10086\n    // repl_backlog_histlen = 30\n    // 那么 backlog 所保存的数据的第一个字节的偏移量为\n    // 10086 - 30 + 1 = 10056 + 1 = 10057\n    // 这说明如果从服务器如果从 10057 至 10086 之间的任何时间断线\n    // 那么从服务器都可以使用 PSYNC\n    server.repl_backlog_off = server.master_repl_offset -\n                              server.repl_backlog_histlen + 1;\n}\n\n/* Wrapper for feedReplicationBacklog() that takes Redis string objects\n * as input. */\n// 将 Redis 对象放进 replication backlog 里面\nvoid feedReplicationBacklogWithObject(robj *o) {\n    char llstr[REDIS_LONGSTR_SIZE];\n    void *p;\n    size_t len;\n\n    if (o->encoding == REDIS_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\n// 将传入的参数发送给从服务器\n// 操作分为三步：\n// 1） 构建协议内容\n// 2） 将协议内容备份到 backlog\n// 3） 将内容发送给各个从服务器\nvoid replicationFeedSlaves(list *slaves, int dictid, robj **argv, int argc) {\n    listNode *ln;\n    listIter li;\n    int j, len;\n    char llstr[REDIS_LONGSTR_SIZE];\n\n    /* If there aren't slaves, and there is no backlog buffer to populate,\n     * we can return ASAP. */\n    // backlog 为空，且没有从服务器，直接返回\n    if (server.repl_backlog == NULL && listLength(slaves) == 0) return;\n\n    /* We can't have slaves attached and no backlog. */\n    redisAssert(!(listLength(slaves) != 0 && server.repl_backlog == NULL));\n\n    /* Send SELECT command to every slave if needed. */\n    // 如果有需要的话，发送 SELECT 命令，指定数据库\n    if (server.slaveseldb != dictid) {\n        robj *selectcmd;\n\n        /* For a few DBs we have pre-computed SELECT command. */\n        if (dictid >= 0 && dictid < REDIS_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(REDIS_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        // 将 SELECT 命令添加到 backlog\n        if (server.repl_backlog) feedReplicationBacklogWithObject(selectcmd);\n\n        /* Send it to slaves. */\n        // 发送给所有从服务器\n        listRewind(slaves,&li);\n        while((ln = listNext(&li))) {\n            redisClient *slave = ln->value;\n            addReply(slave,selectcmd);\n        }\n\n        if (dictid < 0 || dictid >= REDIS_SHARED_SELECT_CMDS)\n            decrRefCount(selectcmd);\n    }\n\n    server.slaveseldb = dictid;\n\n    /* Write the command to the replication backlog if any. */\n    // 将命令写入到backlog\n    if (server.repl_backlog) {\n        char aux[REDIS_LONGSTR_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             * ad add the final CRLF */\n            // 将参数从对象转换成协议格式\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    listRewind(slaves,&li);\n    while((ln = listNext(&li))) {\n\n        // 指向从服务器\n        redisClient *slave = ln->value;\n\n        /* Don't feed slaves that are still waiting for BGSAVE to start */\n        // 不要给正在等待 BGSAVE 开始的从服务器发送命令\n        if (slave->replstate == REDIS_REPL_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        // 向已经接收完和正在接收 RDB 文件的从服务器发送命令\n        // 如果从服务器正在接收主服务器发送的 RDB 文件，\n        // 那么在初次 SYNC 完成之前，主服务器发送的内容会被放进一个缓冲区里面\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// 将协议发给 Monitor\nvoid replicationFeedMonitors(redisClient *c, list *monitors, int dictid, robj **argv, int argc) {\n    listNode *ln;\n    listIter li;\n    int j;\n    sds cmdrepr = sdsnew(\"+\");\n    robj *cmdobj;\n    struct timeval tv;\n\n    // 获取时间戳\n    gettimeofday(&tv,NULL);\n    cmdrepr = sdscatprintf(cmdrepr,\"%ld.%06ld \",(long)tv.tv_sec,(long)tv.tv_usec);\n    if (c->flags & REDIS_LUA_CLIENT) {\n        cmdrepr = sdscatprintf(cmdrepr,\"[%d lua] \",dictid);\n    } else if (c->flags & REDIS_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    // 获取命令和参数\n    for (j = 0; j < argc; j++) {\n        if (argv[j]->encoding == REDIS_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(REDIS_STRING,cmdrepr);\n\n    // 将内容发送给所有 MONITOR \n    listRewind(monitors,&li);\n    while((ln = listNext(&li))) {\n        redisClient *monitor = ln->value;\n        addReply(monitor,cmdobj);\n    }\n    decrRefCount(cmdobj);\n}\n\n/* Feed the slave 'c' with the replication backlog starting from the\n * specified 'offset' up to the end of the backlog. */\n// 向从服务器 c 发送 backlog 中从 offset 到 backlog 尾部之间的数据\nlong long addReplyReplicationBacklog(redisClient *c, long long offset) {\n    long long j, skip, len;\n\n    redisLog(REDIS_DEBUG, \"[PSYNC] Slave request offset: %lld\", offset);\n\n    if (server.repl_backlog_histlen == 0) {\n        redisLog(REDIS_DEBUG, \"[PSYNC] Backlog history len is zero\");\n        return 0;\n    }\n\n    redisLog(REDIS_DEBUG, \"[PSYNC] Backlog size: %lld\",\n             server.repl_backlog_size);\n    redisLog(REDIS_DEBUG, \"[PSYNC] First byte: %lld\",\n             server.repl_backlog_off);\n    redisLog(REDIS_DEBUG, \"[PSYNC] History len: %lld\",\n             server.repl_backlog_histlen);\n    redisLog(REDIS_DEBUG, \"[PSYNC] Current index: %lld\",\n             server.repl_backlog_idx);\n\n    /* Compute the amount of bytes we need to discard. */\n    skip = offset - server.repl_backlog_off;\n    redisLog(REDIS_DEBUG, \"[PSYNC] Skipping: %lld\", skip);\n\n    /* Point j to the oldest byte, that is actaully our\n     * server.repl_backlog_off byte. */\n    j = (server.repl_backlog_idx +\n        (server.repl_backlog_size-server.repl_backlog_histlen)) %\n        server.repl_backlog_size;\n    redisLog(REDIS_DEBUG, \"[PSYNC] Index of first byte: %lld\", j);\n\n    /* Discard the amount of data to seek to the specified 'offset'. */\n    j = (j + skip) % server.repl_backlog_size;\n\n    /* Feed slave with data. Since it is a circular buffer we have to\n     * split the reply in two parts if we are cross-boundary. */\n    len = server.repl_backlog_histlen - skip;\n    redisLog(REDIS_DEBUG, \"[PSYNC] Reply total length: %lld\", len);\n    while(len) {\n        long long thislen =\n            ((server.repl_backlog_size - j) < len) ?\n            (server.repl_backlog_size - j) : len;\n\n        redisLog(REDIS_DEBUG, \"[PSYNC] addReply() length: %lld\", thislen);\n        addReplySds(c,sdsnewlen(server.repl_backlog + j, thislen));\n        len -= thislen;\n        j = 0;\n    }\n    return server.repl_backlog_histlen - skip;\n}\n\n/* This function handles the PSYNC command from the point of view of a\n * master receiving a request for partial resynchronization.\n *\n * On success return REDIS_OK, otherwise REDIS_ERR is returned and we proceed\n * with the usual full resync. */\n// 尝试进行部分 resync ，成功返回 REDIS_OK ，失败返回 REDIS_ERR 。\nint masterTryPartialResynchronization(redisClient *c) {\n    long long psync_offset, psync_len;\n    char *master_runid = c->argv[1]->ptr;\n    char buf[128];\n    int buflen;\n\n    /* Is the runid of this master the same advertised by the wannabe slave\n     * via PSYNC? If runid changed this master is a different instance and\n     * there is no way to continue. */\n    // 检查 master id 是否和 runid 一致，只有一致的情况下才有 PSYNC 的可能\n    if (strcasecmp(master_runid, server.runid)) {\n        /* Run id \"?\" is used by slaves that want to force a full resync. */\n        // 从服务器提供的 run id 和服务器的 run id 不一致\n        if (master_runid[0] != '?') {\n            redisLog(REDIS_NOTICE,\"Partial resynchronization not accepted: \"\n                \"Runid mismatch (Client asked for runid '%s', my runid is '%s')\",\n                master_runid, server.runid);\n        // 从服务器提供的 run id 为 '?' ，表示强制 FULL RESYNC\n        } else {\n            redisLog(REDIS_NOTICE,\"Full resync requested by slave.\");\n        }\n        // 需要 full resync\n        goto need_full_resync;\n    }\n\n    /* We still have the data our slave is asking for? */\n    // 取出 psync_offset 参数\n    if (getLongLongFromObjectOrReply(c,c->argv[2],&psync_offset,NULL) !=\n       REDIS_OK) goto need_full_resync;\n\n        // 如果没有 backlog\n    if (!server.repl_backlog ||\n        // 或者 psync_offset 小于 server.repl_backlog_off\n        // （想要恢复的那部分数据已经被覆盖）\n        psync_offset < server.repl_backlog_off ||\n        // psync offset 大于 backlog 所保存的数据的偏移量\n        psync_offset > (server.repl_backlog_off + server.repl_backlog_histlen))\n    {\n        // 执行 FULL RESYNC\n        redisLog(REDIS_NOTICE,\n            \"Unable to partial resync with the slave for lack of backlog (Slave request was: %lld).\", psync_offset);\n        if (psync_offset > server.master_repl_offset) {\n            redisLog(REDIS_WARNING,\n                \"Warning: slave tried to PSYNC with an offset that is greater than the master replication offset.\");\n        }\n        goto need_full_resync;\n    }\n\n    /* If we reached this point, we are able to perform a partial resync:\n     * 程序运行到这里，说明可以执行 partial resync\n     *\n     * 1) Set client state to make it a slave.\n     *    将客户端状态设为 salve  \n     *\n     * 2) Inform the client we can continue with +CONTINUE\n     *    向 slave 发送 +CONTINUE ，表示 partial resync 的请求被接受\n     *\n     * 3) Send the backlog data (from the offset to the end) to the slave. \n     *    发送 backlog 中，客户端所需要的数据\n     */\n    c->flags |= REDIS_SLAVE;\n    c->replstate = REDIS_REPL_ONLINE;\n    c->repl_ack_time = server.unixtime;\n    listAddNodeTail(server.slaves,c);\n    /* We can't use the connection buffers since they are used to accumulate\n     * new commands at this stage. But we are sure the socket send buffer is\n     * emtpy so this write will never fail actually. */\n    // 向从服务器发送一个同步 +CONTINUE ，表示 PSYNC 可以执行\n    buflen = snprintf(buf,sizeof(buf),\"+CONTINUE\\r\\n\");\n    if (write(c->fd,buf,buflen) != buflen) {\n        freeClientAsync(c);\n        return REDIS_OK;\n    }\n    // 发送 backlog 中的内容（也即是从服务器缺失的那些内容）到从服务器\n    psync_len = addReplyReplicationBacklog(c,psync_offset);\n    redisLog(REDIS_NOTICE,\n        \"Partial resynchronization request accepted. Sending %lld bytes of backlog starting from offset %lld.\", psync_len, psync_offset);\n    /* Note that we don't need to set the selected DB at server.slaveseldb\n     * to -1 to force the master to emit SELECT, since the slave already\n     * has this state from the previous connection with the master. */\n\n    // 刷新低延迟从服务器的数量\n    refreshGoodSlavesCount();\n    return REDIS_OK; /* The caller can return, no full resync needed. */\n\nneed_full_resync:\n    /* We need a full resync for some reason... notify the client. */\n    // 刷新 psync_offset\n    psync_offset = server.master_repl_offset;\n    /* Add 1 to psync_offset if it the replication backlog does not exists\n     * as when it will be created later we'll increment the offset by one. */\n    // 刷新 psync_offset\n    if (server.repl_backlog == NULL) psync_offset++;\n    /* Again, we can't use the connection buffers (see above). */\n    // 发送 +FULLRESYNC ，表示需要完整重同步\n    buflen = snprintf(buf,sizeof(buf),\"+FULLRESYNC %s %lld\\r\\n\",\n                      server.runid,psync_offset);\n    if (write(c->fd,buf,buflen) != buflen) {\n        freeClientAsync(c);\n        return REDIS_OK;\n    }\n    return REDIS_ERR;\n}\n\n/* SYNC ad PSYNC command implemenation. */\nvoid syncCommand(redisClient *c) {\n\n    /* ignore SYNC if already slave or in monitor mode */\n    // 已经是 SLAVE ，或者处于 MONITOR 模式，返回\n    if (c->flags & REDIS_SLAVE) return;\n\n    /* Refuse SYNC requests if we are a slave but the link with our master\n     * is not ok... */\n    // 如果这是一个从服务器，但与主服务器的连接仍未就绪，那么拒绝 SYNC\n    if (server.masterhost && server.repl_state != REDIS_REPL_CONNECTED) {\n        addReplyError(c,\"Can't SYNC while not connected with my master\");\n        return;\n    }\n\n    /* SYNC can't be issued when the server has pending data to send to\n     * the client about already issued commands. We need a fresh reply\n     * buffer registering the differences between the BGSAVE and the current\n     * dataset, so that we can copy to other slaves if needed. */\n    // 在客户端仍有输出数据等待输出，不能 SYNC\n    if (listLength(c->reply) != 0 || c->bufpos != 0) {\n        addReplyError(c,\"SYNC and PSYNC are invalid with pending output\");\n        return;\n    }\n\n    redisLog(REDIS_NOTICE,\"Slave asks for synchronization\");\n\n    /* Try a partial resynchronization if this is a PSYNC command.\n     * 如果这是一个 PSYNC 命令，那么尝试 partial resynchronization 。\n     *\n     * If it fails, we continue with usual full resynchronization, however\n     * when this happens masterTryPartialResynchronization() already\n     * replied with:\n     *\n     * 如果失败，那么使用 full resynchronization ，\n     * 在这种情况下， masterTryPartialResynchronization() 返回以下内容：\n     *\n     * +FULLRESYNC <runid> <offset>\n     *\n     * So the slave knows the new runid and offset to try a PSYNC later\n     * if the connection with the master is lost. \n     *\n     * 这样的话，之后如果主服务器断开，那么从服务器就可以尝试 PSYNC 了。\n     */\n    if (!strcasecmp(c->argv[0]->ptr,\"psync\")) {\n        // 尝试进行 PSYNC\n        if (masterTryPartialResynchronization(c) == REDIS_OK) {\n            // 可执行 PSYNC\n            server.stat_sync_partial_ok++;\n            return; /* No full resync needed, return. */\n        } else {\n            // 不可执行 PSYNC\n            char *master_runid = c->argv[1]->ptr;\n            \n            /* Increment stats for failed PSYNCs, but only if the\n             * runid is not \"?\", as this is used by slaves to force a full\n             * resync on purpose when they are not albe to partially\n             * resync. */\n            if (master_runid[0] != '?') server.stat_sync_partial_err++;\n        }\n    } else {\n        /* If a slave uses SYNC, we are dealing with an old implementation\n         * of the replication protocol (like redis-cli --slave). Flag the client\n         * so that we don't expect to receive REPLCONF ACK feedbacks. */\n        // 旧版实现，设置标识，避免接收 REPLCONF ACK \n        c->flags |= REDIS_PRE_PSYNC;\n    }\n\n    // 以下是完整重同步的情况。。。\n\n    /* Full resynchronization. */\n    // 执行 full resynchronization ，增加计数\n    server.stat_sync_full++;\n\n    /* Here we need to check if there is a background saving operation\n     * in progress, or if it is required to start one */\n    // 检查是否有 BGSAVE 在执行\n    if (server.rdb_child_pid != -1) {\n        /* Ok a background save is in progress. Let's check if it is a good\n         * one for replication, i.e. if there is another slave that is\n         * registering differences since the server forked to save */\n        redisClient *slave;\n        listNode *ln;\n        listIter li;\n\n        // 如果有至少一个 slave 在等待这个 BGSAVE 完成\n        // 那么说明正在进行的 BGSAVE 所产生的 RDB 也可以为其他 slave 所用\n        listRewind(server.slaves,&li);\n        while((ln = listNext(&li))) {\n            slave = ln->value;\n            if (slave->replstate == REDIS_REPL_WAIT_BGSAVE_END) break;\n        }\n\n        if (ln) {\n            /* Perfect, the server is already registering differences for\n             * another slave. Set the right state, and copy the buffer. */\n            // 幸运的情况，可以使用目前 BGSAVE 所生成的 RDB\n            copyClientOutputBuffer(c,slave);\n            c->replstate = REDIS_REPL_WAIT_BGSAVE_END;\n            redisLog(REDIS_NOTICE,\"Waiting for end of BGSAVE for SYNC\");\n        } else {\n            /* No way, we need to wait for the next BGSAVE in order to\n             * register differences */\n            // 不好运的情况，必须等待下个 BGSAVE\n            c->replstate = REDIS_REPL_WAIT_BGSAVE_START;\n            redisLog(REDIS_NOTICE,\"Waiting for next BGSAVE for SYNC\");\n        }\n    } else {\n        /* Ok we don't have a BGSAVE in progress, let's start one */\n        // 没有 BGSAVE 在进行，开始一个新的 BGSAVE\n        redisLog(REDIS_NOTICE,\"Starting BGSAVE for SYNC\");\n        if (rdbSaveBackground(server.rdb_filename) != REDIS_OK) {\n            redisLog(REDIS_NOTICE,\"Replication failed, can't BGSAVE\");\n            addReplyError(c,\"Unable to perform background save\");\n            return;\n        }\n        // 设置状态\n        c->replstate = REDIS_REPL_WAIT_BGSAVE_END;\n        /* Flush the script cache for the new slave. */\n        // 因为新 slave 进入，刷新复制脚本缓存\n        replicationScriptCacheFlush();\n    }\n\n    if (server.repl_disable_tcp_nodelay)\n        anetDisableTcpNoDelay(NULL, c->fd); /* Non critical if it fails. */\n\n    c->repldbfd = -1;\n\n    c->flags |= REDIS_SLAVE;\n\n    server.slaveseldb = -1; /* Force to re-emit the SELECT command. */\n\n    // 添加到 slave 列表中\n    listAddNodeTail(server.slaves,c);\n    // 如果是第一个 slave ，那么初始化 backlog\n    if (listLength(server.slaves) == 1 && server.repl_backlog == NULL)\n        createReplicationBacklog();\n    return;\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 * 由 slave 使用，在 SYNC 之前配置复制进程（process）\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 * 目前这个函数的唯一作用就是，让 slave 告诉 master 它正在监听的端口号\n * 然后 master 就可以在 INFO 命令的输出中打印这个号码了。\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. \n *\n * 将来可能会用这个命令来实现增量式复制，取代 full resync 。\n */\nvoid replconfCommand(redisClient *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\n        // 从服务器发来 REPLCONF listening-port <port> 命令\n        // 主服务器将从服务器监听的端口号记录下来\n        // 也即是 INFO replication 中的 slaveN ..., port = xxx 这一项\n        if (!strcasecmp(c->argv[j]->ptr,\"listening-port\")) {\n            long port;\n\n            if ((getLongFromObjectOrReply(c,c->argv[j+1],\n                    &port,NULL) != REDIS_OK))\n                return;\n            c->slave_listening_port = port;\n\n        // 从服务器发来 REPLCONF ACK <offset> 命令\n        // 告知主服务器，从服务器已处理的复制流的偏移量\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            // 从服务器使用 REPLCONF ACK 告知主服务器，\n            // 从服务器目前已处理的复制流的偏移量\n            // 主服务器更新它的记录值\n            // 也即是 INFO replication 中的  slaveN ..., offset = xxx 这一项\n            long long offset;\n\n            if (!(c->flags & REDIS_SLAVE)) return;\n            if ((getLongLongFromObject(c->argv[j+1], &offset) != REDIS_OK))\n                return;\n            // 如果 offset 已改变，那么更新\n            if (offset > c->repl_ack_off)\n                c->repl_ack_off = offset;\n            // 更新最后一次发送 ack 的时间\n            c->repl_ack_time = server.unixtime;\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 (server.masterhost && server.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// master 将 RDB 文件发送给 slave 的写事件处理器\nvoid sendBulkToSlave(aeEventLoop *el, int fd, void *privdata, int mask) {\n    redisClient *slave = privdata;\n    REDIS_NOTUSED(el);\n    REDIS_NOTUSED(mask);\n    char buf[REDIS_IOBUF_LEN];\n    ssize_t nwritten, buflen;\n\n    /* Before sending the RDB file, we send the preamble as configured by the\n     * replication process. Currently the preamble is just the bulk count of\n     * the file in the form \"$<length>\\r\\n\". */\n    if (slave->replpreamble) {\n        nwritten = write(fd,slave->replpreamble,sdslen(slave->replpreamble));\n        if (nwritten == -1) {\n            redisLog(REDIS_VERBOSE,\"Write error sending RDB preamble to slave: %s\",\n                strerror(errno));\n            freeClient(slave);\n            return;\n        }\n        sdsrange(slave->replpreamble,nwritten,-1);\n        if (sdslen(slave->replpreamble) == 0) {\n            sdsfree(slave->replpreamble);\n            slave->replpreamble = NULL;\n            /* fall through sending data. */\n        } else {\n            return;\n        }\n    }\n\n    /* If the preamble was already transfered, send the RDB bulk data. */\n    lseek(slave->repldbfd,slave->repldboff,SEEK_SET);\n    // 读取 RDB 数据\n    buflen = read(slave->repldbfd,buf,REDIS_IOBUF_LEN);\n    if (buflen <= 0) {\n        redisLog(REDIS_WARNING,\"Read error sending DB to slave: %s\",\n            (buflen == 0) ? \"premature EOF\" : strerror(errno));\n        freeClient(slave);\n        return;\n    }\n    // 写入数据到 slave\n    if ((nwritten = write(fd,buf,buflen)) == -1) {\n        if (errno != EAGAIN) {\n            redisLog(REDIS_WARNING,\"Write error sending DB to slave: %s\",\n                strerror(errno));\n            freeClient(slave);\n        }\n        return;\n    }\n\n    // 如果写入成功，那么更新写入字节数到 repldboff ，等待下次继续写入\n    slave->repldboff += nwritten;\n\n    // 如果写入已经完成\n    if (slave->repldboff == slave->repldbsize) {\n        // 关闭 RDB 文件描述符\n        close(slave->repldbfd);\n        slave->repldbfd = -1;\n        // 删除之前绑定的写事件处理器\n        aeDeleteFileEvent(server.el,slave->fd,AE_WRITABLE);\n        // 将状态更新为 REDIS_REPL_ONLINE\n        slave->replstate = REDIS_REPL_ONLINE;\n        // 更新响应时间\n        slave->repl_ack_time = server.unixtime;\n        // 创建向从服务器发送命令的写事件处理器\n        // 将保存并发送 RDB 期间的回复全部发送给从服务器\n        if (aeCreateFileEvent(server.el, slave->fd, AE_WRITABLE,\n            sendReplyToClient, slave) == AE_ERR) {\n            redisLog(REDIS_WARNING,\"Unable to register writable event for slave bulk transfer: %s\", strerror(errno));\n            freeClient(slave);\n            return;\n        }\n        // 刷新低延迟 slave 数量\n        refreshGoodSlavesCount();\n        redisLog(REDIS_NOTICE,\"Synchronization with slave succeeded\");\n    }\n}\n\n/* This function is called at the end of every background saving.\n * 在每次 BGSAVE 执行完毕之后使用\n *\n * The argument bgsaveerr is REDIS_OK if the background saving succeeded\n * otherwise REDIS_ERR is passed to the function.\n * bgsaveerr 可能是 REDIS_OK 或者 REDIS_ERR ，显示 BGSAVE 的执行结果\n *\n * The goal of this function is to handle slaves waiting for a successful\n * background saving in order to perform non-blocking synchronization. \n * \n * 这个函数是在 BGSAVE 完成之后的异步回调函数，\n * 它指导该怎么执行和 slave 相关的 RDB 下一步工作。\n */\nvoid updateSlavesWaitingBgsave(int bgsaveerr) {\n    listNode *ln;\n    int startbgsave = 0;\n    listIter li;\n\n    // 遍历所有 slave\n    listRewind(server.slaves,&li);\n    while((ln = listNext(&li))) {\n        redisClient *slave = ln->value;\n\n        if (slave->replstate == REDIS_REPL_WAIT_BGSAVE_START) {\n            // 之前的 RDB 文件不能被 slave 使用，\n            // 开始新的 BGSAVE\n            startbgsave = 1;\n            slave->replstate = REDIS_REPL_WAIT_BGSAVE_END;\n        } else if (slave->replstate == REDIS_REPL_WAIT_BGSAVE_END) {\n\n            // 执行到这里，说明有 slave 在等待 BGSAVE 完成\n\n            struct redis_stat buf;\n\n            // 但是 BGSAVE 执行错误\n            if (bgsaveerr != REDIS_OK) {\n                // 释放 slave\n                freeClient(slave);\n                redisLog(REDIS_WARNING,\"SYNC failed. BGSAVE child returned an error\");\n                continue;\n            }\n\n            // 打开 RDB 文件\n            if ((slave->repldbfd = open(server.rdb_filename,O_RDONLY)) == -1 ||\n                redis_fstat(slave->repldbfd,&buf) == -1) {\n                freeClient(slave);\n                redisLog(REDIS_WARNING,\"SYNC failed. Can't open/stat DB after BGSAVE: %s\", strerror(errno));\n                continue;\n            }\n\n            // 设置偏移量，各种值\n            slave->repldboff = 0;\n            slave->repldbsize = buf.st_size;\n            // 更新状态\n            slave->replstate = REDIS_REPL_SEND_BULK;\n\n            slave->replpreamble = sdscatprintf(sdsempty(),\"$%lld\\r\\n\",\n                (unsigned long long) slave->repldbsize);\n\n            // 清空之前的写事件处理器\n            aeDeleteFileEvent(server.el,slave->fd,AE_WRITABLE);\n            // 将 sendBulkToSlave 安装为 slave 的写事件处理器\n            // 它用于将 RDB 文件发送给 slave\n            if (aeCreateFileEvent(server.el, slave->fd, AE_WRITABLE, sendBulkToSlave, slave) == AE_ERR) {\n                freeClient(slave);\n                continue;\n            }\n        }\n    }\n\n    // 需要执行新的 BGSAVE\n    if (startbgsave) {\n        /* Since we are starting a new background save for one or more slaves,\n         * we flush the Replication Script Cache to use EVAL to propagate every\n         * new EVALSHA for the first time, since all the new slaves don't know\n         * about previous scripts. */\n        // 开始行的 BGSAVE ，并清空脚本缓存\n        replicationScriptCacheFlush();\n        if (rdbSaveBackground(server.rdb_filename) != REDIS_OK) {\n            listIter li;\n\n            listRewind(server.slaves,&li);\n            redisLog(REDIS_WARNING,\"SYNC failed. BGSAVE failed\");\n            while((ln = listNext(&li))) {\n                redisClient *slave = ln->value;\n\n                if (slave->replstate == REDIS_REPL_WAIT_BGSAVE_START)\n                    freeClient(slave);\n            }\n        }\n    }\n}\n\n/* ----------------------------------- SLAVE -------------------------------- */\n\n/* Abort the async download of the bulk dataset while SYNC-ing with master */\n// 停止下载 RDB 文件\nvoid replicationAbortSyncTransfer(void) {\n    redisAssert(server.repl_state == REDIS_REPL_TRANSFER);\n\n    aeDeleteFileEvent(server.el,server.repl_transfer_s,AE_READABLE);\n    close(server.repl_transfer_s);\n    close(server.repl_transfer_fd);\n    unlink(server.repl_transfer_tmpfile);\n    zfree(server.repl_transfer_tmpfile);\n    server.repl_state = REDIS_REPL_CONNECT;\n}\n\n/* Avoid the master to detect the slave is timing out while loading the\n * RDB file in initial synchronization. We send a single newline character\n * that is valid protocol but is guaranteed to either be sent entierly or\n * not, since the byte is indivisible.\n *\n * The function is called in two contexts: while we flush the current\n * data with emptyDb(), and while we load the new data received as an\n * RDB file from the master. */\nvoid replicationSendNewlineToMaster(void) {\n    static time_t newline_sent;\n    if (time(NULL) != newline_sent) {\n        newline_sent = time(NULL);\n        if (write(server.repl_transfer_s,\"\\n\",1) == -1) {\n            /* Pinging back in this stage is best-effort. */\n        }\n    }\n}\n\n/* Callback used by emptyDb() while flushing away old data to load\n * the new dataset received by the master. */\nvoid replicationEmptyDbCallback(void *privdata) {\n    REDIS_NOTUSED(privdata);\n    replicationSendNewlineToMaster();\n}\n\n/* Asynchronously read the SYNC payload we receive from a master */\n// 异步 RDB 文件读取函数\n#define REPL_MAX_WRITTEN_BEFORE_FSYNC (1024*1024*8) /* 8 MB */\nvoid readSyncBulkPayload(aeEventLoop *el, int fd, void *privdata, int mask) {\n    char buf[4096];\n    ssize_t nread, readlen;\n    off_t left;\n    REDIS_NOTUSED(el);\n    REDIS_NOTUSED(privdata);\n    REDIS_NOTUSED(mask);\n\n    /* If repl_transfer_size == -1 we still have to read the bulk length\n     * from the master reply. */\n    // 读取 RDB 文件的大小\n    if (server.repl_transfer_size == -1) {\n\n        // 调用读函数\n        if (syncReadLine(fd,buf,1024,server.repl_syncio_timeout*1000) == -1) {\n            redisLog(REDIS_WARNING,\n                \"I/O error reading bulk count from MASTER: %s\",\n                strerror(errno));\n            goto error;\n        }\n\n        // 出错？\n        if (buf[0] == '-') {\n            redisLog(REDIS_WARNING,\n                \"MASTER aborted replication with an error: %s\",\n                buf+1);\n            goto error;\n        } else if (buf[0] == '\\0') {\n            /* At this stage just a newline works as a PING in order to take\n             * the connection live. So we refresh our last interaction\n             * timestamp. */\n            // 只接到了一个作用和 PING 一样的 '\\0'\n            // 更新最后互动时间\n            server.repl_transfer_lastio = server.unixtime;\n            return;\n        } else if (buf[0] != '$') {\n            // 读入的内容出错，和协议格式不符\n            redisLog(REDIS_WARNING,\"Bad protocol from MASTER, the first byte is not '$' (we received '%s'), are you sure the host and port are right?\", buf);\n            goto error;\n        }\n\n        // 分析 RDB 文件大小\n        server.repl_transfer_size = strtol(buf+1,NULL,10);\n\n        redisLog(REDIS_NOTICE,\n            \"MASTER <-> SLAVE sync: receiving %lld bytes from master\",\n            (long long) server.repl_transfer_size);\n        return;\n    }\n\n    /* Read bulk data */\n    // 读数据\n\n    // 还有多少字节要读？\n    left = server.repl_transfer_size - server.repl_transfer_read;\n    readlen = (left < (signed)sizeof(buf)) ? left : (signed)sizeof(buf);\n    // 读取\n    nread = read(fd,buf,readlen);\n    if (nread <= 0) {\n        redisLog(REDIS_WARNING,\"I/O error trying to sync with MASTER: %s\",\n            (nread == -1) ? strerror(errno) : \"connection lost\");\n        replicationAbortSyncTransfer();\n        return;\n    }\n    // 更新最后 RDB 产生的 IO 时间\n    server.repl_transfer_lastio = server.unixtime;\n    if (write(server.repl_transfer_fd,buf,nread) != nread) {\n        redisLog(REDIS_WARNING,\"Write error or short write writing to the DB dump file needed for MASTER <-> SLAVE synchronization: %s\", strerror(errno));\n        goto error;\n    }\n    // 加上刚读取好的字节数\n    server.repl_transfer_read += nread;\n\n    /* Sync data on disk from time to time, otherwise at the end of the transfer\n     * we may suffer a big delay as the memory buffers are copied into the\n     * actual disk. */\n    // 定期将读入的文件 fsync 到磁盘，以免 buffer 太多，一下子写入时撑爆 IO\n    if (server.repl_transfer_read >=\n        server.repl_transfer_last_fsync_off + REPL_MAX_WRITTEN_BEFORE_FSYNC)\n    {\n        off_t sync_size = server.repl_transfer_read -\n                          server.repl_transfer_last_fsync_off;\n        rdb_fsync_range(server.repl_transfer_fd,\n            server.repl_transfer_last_fsync_off, sync_size);\n        server.repl_transfer_last_fsync_off += sync_size;\n    }\n\n    /* Check if the transfer is now complete */\n    // 检查 RDB 是否已经传送完毕\n    if (server.repl_transfer_read == server.repl_transfer_size) {\n\n        // 完毕，将临时文件改名为 dump.rdb\n        if (rename(server.repl_transfer_tmpfile,server.rdb_filename) == -1) {\n            redisLog(REDIS_WARNING,\"Failed trying to rename the temp DB into dump.rdb in MASTER <-> SLAVE synchronization: %s\", strerror(errno));\n            replicationAbortSyncTransfer();\n            return;\n        }\n\n        // 先清空旧数据库\n        redisLog(REDIS_NOTICE, \"MASTER <-> SLAVE sync: Flushing old data\");\n        signalFlushedDb(-1);\n        emptyDb(replicationEmptyDbCallback);\n        /* Before loading the DB into memory we need to delete the readable\n         * handler, otherwise it will get called recursively since\n         * rdbLoad() will call the event loop to process events from time to\n         * time for non blocking loading. */\n        // 先删除主服务器的读事件监听，因为 rdbLoad() 函数也会监听读事件\n        aeDeleteFileEvent(server.el,server.repl_transfer_s,AE_READABLE);\n\n        // 载入 RDB\n        if (rdbLoad(server.rdb_filename) != REDIS_OK) {\n            redisLog(REDIS_WARNING,\"Failed trying to load the MASTER synchronization DB from disk\");\n            replicationAbortSyncTransfer();\n            return;\n        }\n\n        /* Final setup of the connected slave <- master link */\n        // 关闭临时文件\n        zfree(server.repl_transfer_tmpfile);\n        close(server.repl_transfer_fd);\n\n        // 将主服务器设置成一个 redis client\n        // 注意 createClient 会为主服务器绑定事件，为接下来接收命令做好准备\n        server.master = createClient(server.repl_transfer_s);\n        // 标记这个客户端为主服务器\n        server.master->flags |= REDIS_MASTER;\n        // 标记它为已验证身份\n        server.master->authenticated = 1;\n        // 更新复制状态\n        server.repl_state = REDIS_REPL_CONNECTED;\n        // 设置主服务器的复制偏移量\n        server.master->reploff = server.repl_master_initial_offset;\n        // 保存主服务器的 RUN ID\n        memcpy(server.master->replrunid, server.repl_master_runid,\n            sizeof(server.repl_master_runid));\n\n        /* If master offset is set to -1, this master is old and is not\n         * PSYNC capable, so we flag it accordingly. */\n        // 如果 offset 被设置为 -1 ，那么表示主服务器的版本低于 2.8 \n        // 无法使用 PSYNC ，所以需要设置相应的标识值\n        if (server.master->reploff == -1)\n            server.master->flags |= REDIS_PRE_PSYNC;\n        redisLog(REDIS_NOTICE, \"MASTER <-> SLAVE sync: Finished with success\");\n\n        /* Restart the AOF subsystem now that we finished the sync. This\n         * will trigger an AOF rewrite, and when done will start appending\n         * to the new file. */\n        // 如果有开启 AOF 持久化，那么重启 AOF 功能，并强制生成新数据库的 AOF 文件\n        if (server.aof_state != REDIS_AOF_OFF) {\n            int retry = 10;\n\n            // 关闭\n            stopAppendOnly();\n            // 再重启\n            while (retry-- && startAppendOnly() == REDIS_ERR) {\n                redisLog(REDIS_WARNING,\"Failed enabling the AOF after successful master synchronization! Trying it again in one second.\");\n                sleep(1);\n            }\n            if (!retry) {\n                redisLog(REDIS_WARNING,\"FATAL: this slave instance finished the synchronization with its master, but the AOF can't be turned on. Exiting now.\");\n                exit(1);\n            }\n        }\n    }\n\n    return;\n\nerror:\n    replicationAbortSyncTransfer();\n    return;\n}\n\n/* Send a synchronous command to the master. Used to send AUTH and\n * REPLCONF commands before starting the replication with SYNC.\n *\n * The command returns an sds string representing the result of the\n * operation. On error the first byte is a \"-\".\n */\n// Redis 通常情况下是将命令的发送和回复用不同的事件处理器来异步处理的\n// 但这里是同步地发送然后读取\nchar *sendSynchronousCommand(int fd, ...) {\n    va_list ap;\n    sds cmd = sdsempty();\n    char *arg, buf[256];\n\n    /* Create the command to send to the master, we use simple inline\n     * protocol for simplicity as currently we only send simple strings. */\n    va_start(ap,fd);\n    while(1) {\n        arg = va_arg(ap, char*);\n        if (arg == NULL) break;\n\n        if (sdslen(cmd) != 0) cmd = sdscatlen(cmd,\" \",1);\n        cmd = sdscat(cmd,arg);\n    }\n    cmd = sdscatlen(cmd,\"\\r\\n\",2);\n\n    /* Transfer command to the server. */\n    // 发送命令到主服务器\n    if (syncWrite(fd,cmd,sdslen(cmd),server.repl_syncio_timeout*1000) == -1) {\n        sdsfree(cmd);\n        return sdscatprintf(sdsempty(),\"-Writing to master: %s\",\n                strerror(errno));\n    }\n    sdsfree(cmd);\n\n    /* Read the reply from the server. */\n    // 从主服务器中读取回复\n    if (syncReadLine(fd,buf,sizeof(buf),server.repl_syncio_timeout*1000) == -1)\n    {\n        return sdscatprintf(sdsempty(),\"-Reading from master: %s\",\n                strerror(errno));\n    }\n    return sdsnew(buf);\n}\n\n/* Try a partial resynchronization with the master if we are about to reconnect.\n *\n * 在重连接之后，尝试进行部分重同步。\n *\n * If there is no cached master structure, at least try to issue a\n * \"PSYNC ? -1\" command in order to trigger a full resync using the PSYNC\n * command in order to obtain the master run id and the master replication\n * global offset.\n *\n * 如果 master 缓存为空，那么通过 \"PSYNC ? -1\" 命令来触发一次 full resync ，\n * 让主服务器的 run id 和复制偏移量可以传到附属节点里面。\n *\n * This function is designed to be called from syncWithMaster(), so the\n * following assumptions are made:\n *\n * 这个函数由 syncWithMaster() 函数调用，它做了以下假设：\n *\n * 1) We pass the function an already connected socket \"fd\".\n *    一个已连接套接字 fd 会被传入函数\n * 2) This function does not close the file descriptor \"fd\". However in case\n *    of successful partial resynchronization, the function will reuse\n *    'fd' as file descriptor of the server.master client structure.\n *    函数不会关闭 fd 。\n *    当部分同步成功时，函数会将 fd 用作 server.master 客户端结构中的\n *    文件描述符。\n *\n * The function returns:\n * 以下是函数的返回值：\n *\n * PSYNC_CONTINUE: If the PSYNC command succeded and we can continue.\n *                 PSYNC 命令成功，可以继续。\n * PSYNC_FULLRESYNC: If PSYNC is supported but a full resync is needed.\n *                   In this case the master run_id and global replication\n *                   offset is saved.\n *                   主服务器支持 PSYNC 功能，但目前情况需要执行 full resync 。\n *                   在这种情况下， run_id 和全局复制偏移量会被保存。\n * PSYNC_NOT_SUPPORTED: If the server does not understand PSYNC at all and\n *                      the caller should fall back to SYNC.\n *                      主服务器不支持 PSYNC ，调用者应该下降到 SYNC 命令。\n */\n\n#define PSYNC_CONTINUE 0\n#define PSYNC_FULLRESYNC 1\n#define PSYNC_NOT_SUPPORTED 2\nint slaveTryPartialResynchronization(int fd) {\n    char *psync_runid;\n    char psync_offset[32];\n    sds reply;\n\n    /* Initially set repl_master_initial_offset to -1 to mark the current\n     * master run_id and offset as not valid. Later if we'll be able to do\n     * a FULL resync using the PSYNC command we'll set the offset at the\n     * right value, so that this information will be propagated to the\n     * client structure representing the master into server.master. */\n    server.repl_master_initial_offset = -1;\n\n    if (server.cached_master) {\n        // 缓存存在，尝试部分重同步\n        // 命令为 \"PSYNC <master_run_id> <repl_offset>\"\n        psync_runid = server.cached_master->replrunid;\n        snprintf(psync_offset,sizeof(psync_offset),\"%lld\", server.cached_master->reploff+1);\n        redisLog(REDIS_NOTICE,\"Trying a partial resynchronization (request %s:%s).\", psync_runid, psync_offset);\n    } else {\n        // 缓存不存在\n        // 发送 \"PSYNC ? -1\" ，要求完整重同步\n        redisLog(REDIS_NOTICE,\"Partial resynchronization not possible (no cached master)\");\n        psync_runid = \"?\";\n        memcpy(psync_offset,\"-1\",3);\n    }\n\n    /* Issue the PSYNC command */\n    // 向主服务器发送 PSYNC 命令\n    reply = sendSynchronousCommand(fd,\"PSYNC\",psync_runid,psync_offset,NULL);\n\n    // 接收到 FULLRESYNC ，进行 full-resync\n    if (!strncmp(reply,\"+FULLRESYNC\",11)) {\n        char *runid = NULL, *offset = NULL;\n\n        /* FULL RESYNC, parse the reply in order to extract the run id\n         * and the replication offset. */\n        // 分析并记录主服务器的 run id\n        runid = strchr(reply,' ');\n        if (runid) {\n            runid++;\n            offset = strchr(runid,' ');\n            if (offset) offset++;\n        }\n        // 检查 run id 的合法性\n        if (!runid || !offset || (offset-runid-1) != REDIS_RUN_ID_SIZE) {\n            redisLog(REDIS_WARNING,\n                \"Master replied with wrong +FULLRESYNC syntax.\");\n            /* This is an unexpected condition, actually the +FULLRESYNC\n             * reply means that the master supports PSYNC, but the reply\n             * format seems wrong. To stay safe we blank the master\n             * runid to make sure next PSYNCs will fail. */\n            // 主服务器支持 PSYNC ，但是却发来了异常的 run id\n            // 只好将 run id 设为 0 ，让下次 PSYNC 时失败\n            memset(server.repl_master_runid,0,REDIS_RUN_ID_SIZE+1);\n        } else {\n            // 保存 run id\n            memcpy(server.repl_master_runid, runid, offset-runid-1);\n            server.repl_master_runid[REDIS_RUN_ID_SIZE] = '\\0';\n            // 以及 initial offset\n            server.repl_master_initial_offset = strtoll(offset,NULL,10);\n            // 打印日志，这是一个 FULL resync\n            redisLog(REDIS_NOTICE,\"Full resync from master: %s:%lld\",\n                server.repl_master_runid,\n                server.repl_master_initial_offset);\n        }\n        /* We are going to full resync, discard the cached master structure. */\n        // 要开始完整重同步，缓存中的 master 已经没用了，清除它\n        replicationDiscardCachedMaster();\n        sdsfree(reply);\n        \n        // 返回状态\n        return PSYNC_FULLRESYNC;\n    }\n\n    // 接收到 CONTINUE ，进行 partial resync\n    if (!strncmp(reply,\"+CONTINUE\",9)) {\n        /* Partial resync was accepted, set the replication state accordingly */\n        redisLog(REDIS_NOTICE,\n            \"Successful partial resynchronization with master.\");\n        sdsfree(reply);\n        // 将缓存中的 master 设为当前 master\n        replicationResurrectCachedMaster(fd);\n\n        // 返回状态\n        return PSYNC_CONTINUE;\n    }\n\n    /* If we reach this point we receied either an error since the master does\n     * not understand PSYNC, or an unexpected reply from the master.\n     * Return PSYNC_NOT_SUPPORTED to the caller in both cases. */\n\n    // 接收到错误？\n    if (strncmp(reply,\"-ERR\",4)) {\n        /* If it's not an error, log the unexpected event. */\n        redisLog(REDIS_WARNING,\n            \"Unexpected reply to PSYNC from master: %s\", reply);\n    } else {\n        redisLog(REDIS_NOTICE,\n            \"Master does not support PSYNC or is in \"\n            \"error state (reply: %s)\", reply);\n    }\n    sdsfree(reply);\n    replicationDiscardCachedMaster();\n\n    // 主服务器不支持 PSYNC\n    return PSYNC_NOT_SUPPORTED;\n}\n\n// 从服务器用于同步主服务器的回调函数\nvoid syncWithMaster(aeEventLoop *el, int fd, void *privdata, int mask) {\n    char tmpfile[256], *err;\n    int dfd, maxtries = 5;\n    int sockerr = 0, psync_result;\n    socklen_t errlen = sizeof(sockerr);\n    REDIS_NOTUSED(el);\n    REDIS_NOTUSED(privdata);\n    REDIS_NOTUSED(mask);\n\n    /* If this event fired after the user turned the instance into a master\n     * with SLAVEOF NO ONE we must just return ASAP. */\n    // 如果处于 SLAVEOF NO ONE 模式，那么关闭 fd\n    if (server.repl_state == REDIS_REPL_NONE) {\n        close(fd);\n        return;\n    }\n\n    /* Check for errors in the socket. */\n    // 检查套接字错误\n    if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &sockerr, &errlen) == -1)\n        sockerr = errno;\n    if (sockerr) {\n        aeDeleteFileEvent(server.el,fd,AE_READABLE|AE_WRITABLE);\n        redisLog(REDIS_WARNING,\"Error condition on socket for SYNC: %s\",\n            strerror(sockerr));\n        goto error;\n    }\n\n    /* If we were connecting, it's time to send a non blocking PING, we want to\n     * make sure the master is able to reply before going into the actual\n     * replication process where we have long timeouts in the order of\n     * seconds (in the meantime the slave would block). */\n    // 如果状态为 CONNECTING ，那么在进行初次同步之前，\n    // 向主服务器发送一个非阻塞的 PONG \n    // 因为接下来的 RDB 文件发送非常耗时，所以我们想确认主服务器真的能访问\n    if (server.repl_state == REDIS_REPL_CONNECTING) {\n        redisLog(REDIS_NOTICE,\"Non blocking connect for SYNC fired the event.\");\n        /* Delete the writable event so that the readable event remains\n         * registered and we can wait for the PONG reply. */\n        // 手动发送同步 PING ，暂时取消监听写事件\n        aeDeleteFileEvent(server.el,fd,AE_WRITABLE);\n        // 更新状态\n        server.repl_state = REDIS_REPL_RECEIVE_PONG;\n        /* Send the PING, don't check for errors at all, we have the timeout\n         * that will take care about this. */\n        // 同步发送 PING\n        syncWrite(fd,\"PING\\r\\n\",6,100);\n\n        // 返回，等待 PONG 到达\n        return;\n    }\n\n    /* Receive the PONG command. */\n    // 接收 PONG 命令\n    if (server.repl_state == REDIS_REPL_RECEIVE_PONG) {\n        char buf[1024];\n\n        /* Delete the readable event, we no longer need it now that there is\n         * the PING reply to read. */\n        // 手动同步接收 PONG ，暂时取消监听读事件\n        aeDeleteFileEvent(server.el,fd,AE_READABLE);\n\n        /* Read the reply with explicit timeout. */\n        // 尝试在指定时间限制内读取 PONG\n        buf[0] = '\\0';\n        // 同步接收 PONG\n        if (syncReadLine(fd,buf,sizeof(buf),\n            server.repl_syncio_timeout*1000) == -1)\n        {\n            redisLog(REDIS_WARNING,\n                \"I/O error reading PING reply from master: %s\",\n                strerror(errno));\n            goto error;\n        }\n\n        /* We accept only two replies as valid, a positive +PONG reply\n         * (we just check for \"+\") or an authentication error.\n         * Note that older versions of Redis replied with \"operation not\n         * permitted\" instead of using a proper error code, so we test\n         * both. */\n        // 接收到的数据只有两种可能：\n        // 第一种是 +PONG ，第二种是因为未验证而出现的 -NOAUTH 错误\n        if (buf[0] != '+' &&\n            strncmp(buf,\"-NOAUTH\",7) != 0 &&\n            strncmp(buf,\"-ERR operation not permitted\",28) != 0)\n        {\n            // 接收到未验证错误\n            redisLog(REDIS_WARNING,\"Error reply to PING from master: '%s'\",buf);\n            goto error;\n        } else {\n            // 接收到 PONG\n            redisLog(REDIS_NOTICE,\n                \"Master replied to PING, replication can continue...\");\n        }\n    }\n\n    /* AUTH with the master if required. */\n    // 进行身份验证\n    if(server.masterauth) {\n        err = sendSynchronousCommand(fd,\"AUTH\",server.masterauth,NULL);\n        if (err[0] == '-') {\n            redisLog(REDIS_WARNING,\"Unable to AUTH to MASTER: %s\",err);\n            sdsfree(err);\n            goto error;\n        }\n        sdsfree(err);\n    }\n\n    /* Set the slave port, so that Master's INFO command can list the\n     * slave listening port correctly. */\n    // 将从服务器的端口发送给主服务器，\n    // 使得主服务器的 INFO 命令可以显示从服务器正在监听的端口\n    {\n        sds port = sdsfromlonglong(server.port);\n        err = sendSynchronousCommand(fd,\"REPLCONF\",\"listening-port\",port,\n                                         NULL);\n        sdsfree(port);\n        /* Ignore the error if any, not all the Redis versions support\n         * REPLCONF listening-port. */\n        if (err[0] == '-') {\n            redisLog(REDIS_NOTICE,\"(Non critical) Master does not understand REPLCONF listening-port: %s\", err);\n        }\n        sdsfree(err);\n    }\n\n    /* Try a partial resynchonization. If we don't have a cached master\n     * slaveTryPartialResynchronization() will at least try to use PSYNC\n     * to start a full resynchronization so that we get the master run id\n     * and the global offset, to try a partial resync at the next\n     * reconnection attempt. */\n    // 根据返回的结果决定是执行部分 resync ，还是 full-resync\n    psync_result = slaveTryPartialResynchronization(fd);\n\n    // 可以执行部分 resync\n    if (psync_result == PSYNC_CONTINUE) {\n        redisLog(REDIS_NOTICE, \"MASTER <-> SLAVE sync: Master accepted a Partial Resynchronization.\");\n        // 返回\n        return;\n    }\n\n    /* Fall back to SYNC if needed. Otherwise psync_result == PSYNC_FULLRESYNC\n     * and the server.repl_master_runid and repl_master_initial_offset are\n     * already populated. */\n    // 主服务器不支持 PSYNC ，发送 SYNC\n    if (psync_result == PSYNC_NOT_SUPPORTED) {\n        redisLog(REDIS_NOTICE,\"Retrying with SYNC...\");\n        // 向主服务器发送 SYNC 命令\n        if (syncWrite(fd,\"SYNC\\r\\n\",6,server.repl_syncio_timeout*1000) == -1) {\n            redisLog(REDIS_WARNING,\"I/O error writing to MASTER: %s\",\n                strerror(errno));\n            goto error;\n        }\n    }\n\n    // 如果执行到这里，\n    // 那么 psync_result == PSYNC_FULLRESYNC 或 PSYNC_NOT_SUPPORTED\n\n    /* Prepare a suitable temp file for bulk transfer */\n    // 打开一个临时文件，用于写入和保存接下来从主服务器传来的 RDB 文件数据\n    while(maxtries--) {\n        snprintf(tmpfile,256,\n            \"temp-%d.%ld.rdb\",(int)server.unixtime,(long int)getpid());\n        dfd = open(tmpfile,O_CREAT|O_WRONLY|O_EXCL,0644);\n        if (dfd != -1) break;\n        sleep(1);\n    }\n    if (dfd == -1) {\n        redisLog(REDIS_WARNING,\"Opening the temp file needed for MASTER <-> SLAVE synchronization: %s\",strerror(errno));\n        goto error;\n    }\n\n    /* Setup the non blocking download of the bulk file. */\n    // 设置一个读事件处理器，来读取主服务器的 RDB 文件\n    if (aeCreateFileEvent(server.el,fd, AE_READABLE,readSyncBulkPayload,NULL)\n            == AE_ERR)\n    {\n        redisLog(REDIS_WARNING,\n            \"Can't create readable event for SYNC: %s (fd=%d)\",\n            strerror(errno),fd);\n        goto error;\n    }\n\n    // 设置状态\n    server.repl_state = REDIS_REPL_TRANSFER;\n\n    // 更新统计信息\n    server.repl_transfer_size = -1;\n    server.repl_transfer_read = 0;\n    server.repl_transfer_last_fsync_off = 0;\n    server.repl_transfer_fd = dfd;\n    server.repl_transfer_lastio = server.unixtime;\n    server.repl_transfer_tmpfile = zstrdup(tmpfile);\n\n    return;\n\nerror:\n    close(fd);\n    server.repl_transfer_s = -1;\n    server.repl_state = REDIS_REPL_CONNECT;\n    return;\n}\n\n// 以非阻塞方式连接主服务器\nint connectWithMaster(void) {\n    int fd;\n\n    // 连接主服务器\n    fd = anetTcpNonBlockConnect(NULL,server.masterhost,server.masterport);\n    if (fd == -1) {\n        redisLog(REDIS_WARNING,\"Unable to connect to MASTER: %s\",\n            strerror(errno));\n        return REDIS_ERR;\n    }\n\n    // 监听主服务器 fd 的读和写事件，并绑定文件事件处理器\n    if (aeCreateFileEvent(server.el,fd,AE_READABLE|AE_WRITABLE,syncWithMaster,NULL) ==\n            AE_ERR)\n    {\n        close(fd);\n        redisLog(REDIS_WARNING,\"Can't create readable event for SYNC\");\n        return REDIS_ERR;\n    }\n\n    // 初始化统计变量\n    server.repl_transfer_lastio = server.unixtime;\n    server.repl_transfer_s = fd;\n\n    // 将状态改为已连接\n    server.repl_state = REDIS_REPL_CONNECTING;\n\n    return REDIS_OK;\n}\n\n/* This function can be called when a non blocking connection is currently\n * in progress to undo it. */\n// 取消正在进行的连接\nvoid undoConnectWithMaster(void) {\n    int fd = server.repl_transfer_s;\n\n    // 连接必须处于正在连接状态\n    redisAssert(server.repl_state == REDIS_REPL_CONNECTING ||\n                server.repl_state == REDIS_REPL_RECEIVE_PONG);\n    aeDeleteFileEvent(server.el,fd,AE_READABLE|AE_WRITABLE);\n    close(fd);\n    server.repl_transfer_s = -1;\n    // 回到 CONNECT 状态\n    server.repl_state = REDIS_REPL_CONNECT;\n}\n\n/* This function aborts a non blocking replication attempt if there is one\n * in progress, by canceling the non-blocking connect attempt or\n * the initial bulk transfer.\n *\n * 如果有正在进行的非阻塞复制在进行，那么取消它。\n *\n * If there was a replication handshake in progress 1 is returned and\n * the replication state (server.repl_state) set to REDIS_REPL_CONNECT.\n *\n * 如果复制在握手阶段被取消，那么返回 1 ，\n * 并且 server.repl_state 被设置为 REDIS_REPL_CONNECT 。\n *\n * Otherwise zero is returned and no operation is perforemd at all. \n *\n * 否则返回 0 ，并且不执行任何操作。\n */\nint cancelReplicationHandshake(void) {\n    if (server.repl_state == REDIS_REPL_TRANSFER) {\n        replicationAbortSyncTransfer();\n    } else if (server.repl_state == REDIS_REPL_CONNECTING ||\n             server.repl_state == REDIS_REPL_RECEIVE_PONG)\n    {\n        undoConnectWithMaster();\n    } else {\n        return 0;\n    }\n    return 1;\n}\n\n/* Set replication to the specified master address and port. */\n// 将服务器设为指定地址的从服务器\nvoid replicationSetMaster(char *ip, int port) {\n\n    // 清除原有的主服务器地址（如果有的话）\n    sdsfree(server.masterhost);\n\n    // IP\n    server.masterhost = sdsnew(ip);\n\n    // 端口\n    server.masterport = port;\n\n    // 清除原来可能有的主服务器信息。。。\n\n    // 如果之前有其他地址，那么释放它\n    if (server.master) freeClient(server.master);\n    // 断开所有从服务器的连接，强制所有从服务器执行重同步\n    disconnectSlaves(); /* Force our slaves to resync with us as well. */\n    // 清空可能有的 master 缓存，因为已经不会执行 PSYNC 了\n    replicationDiscardCachedMaster(); /* Don't try a PSYNC. */\n    // 释放 backlog ，同理， PSYNC 目前已经不会执行了\n    freeReplicationBacklog(); /* Don't allow our chained slaves to PSYNC. */\n    // 取消之前的复制进程（如果有的话）\n    cancelReplicationHandshake();\n\n    // 进入连接状态（重点）\n    server.repl_state = REDIS_REPL_CONNECT;\n    server.master_repl_offset = 0;\n    server.repl_down_since = 0;\n}\n\n/* Cancel replication, setting the instance as a master itself. */\n// 取消复制，将服务器设置为主服务器\nvoid replicationUnsetMaster(void) {\n\n    if (server.masterhost == NULL) return; /* Nothing to do. */\n\n    sdsfree(server.masterhost);\n    server.masterhost = NULL;\n\n    if (server.master) {\n        if (listLength(server.slaves) == 0) {\n            /* If this instance is turned into a master and there are no\n             * slaves, it inherits the replication offset from the master.\n             * Under certain conditions this makes replicas comparable by\n             * replication offset to understand what is the most updated. */\n            server.master_repl_offset = server.master->reploff;\n            freeReplicationBacklog();\n        }\n        freeClient(server.master);\n    }\n\n    replicationDiscardCachedMaster();\n\n    cancelReplicationHandshake();\n\n    server.repl_state = REDIS_REPL_NONE;\n}\n\nvoid slaveofCommand(redisClient *c) {\n    /* SLAVEOF is not allowed in cluster mode as replication is automatically\n     * configured using the current address of the master node. */\n    // 不允许在集群模式中使用\n    if (server.cluster_enabled) {\n        addReplyError(c,\"SLAVEOF not allowed in cluster mode.\");\n        return;\n    }\n\n    /* The special host/port combination \"NO\" \"ONE\" turns the instance\n     * into a master. Otherwise the new master address is set. */\n    // SLAVEOF NO ONE 让从服务器转为主服务器\n    if (!strcasecmp(c->argv[1]->ptr,\"no\") &&\n        !strcasecmp(c->argv[2]->ptr,\"one\")) {\n        if (server.masterhost) {\n            // 让服务器取消复制，成为主服务器\n            replicationUnsetMaster();\n            redisLog(REDIS_NOTICE,\"MASTER MODE enabled (user request)\");\n        }\n    } else {\n        long port;\n\n        // 获取端口参数\n        if ((getLongFromObjectOrReply(c, c->argv[2], &port, NULL) != REDIS_OK))\n            return;\n\n        /* Check if we are already attached to the specified slave */\n        // 检查输入的 host 和 port 是否服务器目前的主服务器\n        // 如果是的话，向客户端返回 +OK ，不做其他动作\n        if (server.masterhost && !strcasecmp(server.masterhost,c->argv[1]->ptr)\n            && server.masterport == port) {\n            redisLog(REDIS_NOTICE,\"SLAVE OF would result into synchronization with the master we are already connected with. No operation performed.\");\n            addReplySds(c,sdsnew(\"+OK Already connected to specified master\\r\\n\"));\n            return;\n        }\n\n        /* There was no previous master or the user specified a different one,\n         * we can continue. */\n        // 没有前任主服务器，或者客户端指定了新的主服务器\n        // 开始执行复制操作\n        replicationSetMaster(c->argv[1]->ptr, port);\n        redisLog(REDIS_NOTICE,\"SLAVE OF %s:%d enabled (user request)\",\n            server.masterhost, server.masterport);\n    }\n    addReply(c,shared.ok);\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. */\n// 向主服务器发送 REPLCONF AKC ，告知当前处理的偏移量\n// 如果未连接上主服务器，那么这个函数没有实际效果\nvoid replicationSendAck(void) {\n    redisClient *c = server.master;\n\n    if (c != NULL) {\n        c->flags |= REDIS_MASTER_FORCE_REPLY;\n        addReplyMultiBulkLen(c,3);\n        addReplyBulkCString(c,\"REPLCONF\");\n        addReplyBulkCString(c,\"ACK\");\n        // 发送偏移量\n        addReplyBulkLongLong(c,c->reploff);\n        c->flags &= ~REDIS_MASTER_FORCE_REPLY;\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 *\n * 为了实现 partial synchronization ，\n * slave 需要一个 cache 来在 master 断线时将 master 保存到 cache 上。\n *\n * It is cached into server.cached_master and flushed away using the following\n * functions. \n *\n * 以下是该 cache 的设置和清除函数。\n */\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 * 这个函数由 freeClient() 函数调用，它将当前的 master 记录到 master cache 里面，\n * 然后返回。\n *\n * The other functions that will deal with the cached master are:\n *\n * 其他和 master cahce 有关的函数是：\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 * replicationDiscardCachedMaster() 确认清空整个 master ，不对它进行缓存。\n *\n * replicationResurrectCachedMaster() that is used after a successful PSYNC\n * handshake in order to reactivate the cached master.\n *\n * replicationResurrectCachedMaster() 在 PSYNC 成功时将缓存中的 master 提取出来，\n * 重新成为新的 master 。\n */\nvoid replicationCacheMaster(redisClient *c) {\n    listNode *ln;\n\n    redisAssert(server.master != NULL && server.cached_master == NULL);\n    redisLog(REDIS_NOTICE,\"Caching the disconnected master state.\");\n\n    /* Remove from the list of clients, we don't want this client to be\n     * listed by CLIENT LIST or processed in any way by batch operations. */\n    // 从客户端链表中移除主服务器\n    ln = listSearchKey(server.clients,c);\n    redisAssert(ln != NULL);\n    listDelNode(server.clients,ln);\n\n    /* Save the master. Server.master will be set to null later by\n     * replicationHandleMasterDisconnection(). */\n    // 缓存 master\n    server.cached_master = server.master;\n\n    /* Remove the event handlers and close the socket. We'll later reuse\n     * the socket of the new connection with the master during PSYNC. */\n    // 删除事件监视，关闭 socket\n    aeDeleteFileEvent(server.el,c->fd,AE_READABLE);\n    aeDeleteFileEvent(server.el,c->fd,AE_WRITABLE);\n    close(c->fd);\n\n    /* Set fd to -1 so that we can safely call freeClient(c) later. */\n    c->fd = -1;\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    // 重置复制状态，并将 server.master 设为 NULL\n    // 并强制断开这个服务器的所有从服务器，让它们执行 resync \n    replicationHandleMasterDisconnection();\n}\n\n/* Free a cached master, called when there are no longer the conditions for\n * a partial resync on reconnection. \n *\n * 清空 master 缓存，在条件已经不可能执行 partial resync 时执行\n */\nvoid replicationDiscardCachedMaster(void) {\n\n    if (server.cached_master == NULL) return;\n\n    redisLog(REDIS_NOTICE,\"Discarding previously cached master state.\");\n    server.cached_master->flags &= ~REDIS_MASTER;\n    freeClient(server.cached_master);\n    server.cached_master = NULL;\n}\n\n/* Turn the cached master into the current master, using the file descriptor\n * passed as argument as the socket for the new master.\n *\n * 将缓存中的 master 设置为服务器的当前 master 。\n *\n * This funciton is called when successfully setup a partial resynchronization\n * so the stream of data that we'll receive will start from were this\n * master left. \n *\n * 当部分重同步准备就绪之后，调用这个函数。\n * master 断开之前遗留下来的数据可以继续使用。\n */\nvoid replicationResurrectCachedMaster(int newfd) {\n    \n    // 设置 master\n    server.master = server.cached_master;\n    server.cached_master = NULL;\n\n    server.master->fd = newfd;\n\n    server.master->flags &= ~(REDIS_CLOSE_AFTER_REPLY|REDIS_CLOSE_ASAP);\n\n    server.master->authenticated = 1;\n    server.master->lastinteraction = server.unixtime;\n\n    // 回到已连接状态\n    server.repl_state = REDIS_REPL_CONNECTED;\n\n    /* Re-add to the list of clients. */\n    // 将 master 重新加入到客户端列表中\n    listAddNodeTail(server.clients,server.master);\n    // 监听 master 的读事件\n    if (aeCreateFileEvent(server.el, newfd, AE_READABLE,\n                          readQueryFromClient, server.master)) {\n        redisLog(REDIS_WARNING,\"Error resurrecting the cached master, impossible to add the readable handler: %s\", strerror(errno));\n        freeClientAsync(server.master); /* Close ASAP. */\n    }\n\n    /* We may also need to install the write handler as well if there is\n     * pending data in the write buffers. */\n    if (server.master->bufpos || listLength(server.master->reply)) {\n        if (aeCreateFileEvent(server.el, newfd, AE_WRITABLE,\n                          sendReplyToClient, server.master)) {\n            redisLog(REDIS_WARNING,\"Error resurrecting the cached master, impossible to add the writable handler: %s\", strerror(errno));\n            freeClientAsync(server.master); /* Close ASAP. */\n        }\n    }\n}\n\n/* ------------------------- MIN-SLAVES-TO-WRITE  --------------------------- */\n\n/* This function counts the number of slaves with lag <= min-slaves-max-lag.\n * \n * 计算那些延迟值少于等于 min-slaves-max-lag 的从服务器数量。\n *\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). \n *\n * 如果服务器开启了 min-slaves-max-lag 选项，\n * 那么在这个选项所指定的条件达不到时，服务器将阻止写操作执行。\n */\nvoid refreshGoodSlavesCount(void) {\n    listIter li;\n    listNode *ln;\n    int good = 0;\n\n    if (!server.repl_min_slaves_to_write ||\n        !server.repl_min_slaves_max_lag) return;\n\n    listRewind(server.slaves,&li);\n    while((ln = listNext(&li))) {\n        redisClient *slave = ln->value;\n\n        // 计算延迟值\n        time_t lag = server.unixtime - slave->repl_ack_time;\n\n        // 计入 GOOD\n        if (slave->replstate == REDIS_REPL_ONLINE &&\n            lag <= server.repl_min_slaves_max_lag) good++;\n    }\n\n    // 更新状态良好的从服务器数量\n    server.repl_good_slaves_count = good;\n}\n\n/* ----------------------- REPLICATION SCRIPT CACHE --------------------------\n * The goal of this code is to keep track of scripts already sent to every\n * connected slave, in order to be able to replicate EVALSHA as it is without\n * translating it to EVAL every time it is possible.\n *\n * 这部分代码的目的是，\n * 将那些已经发送给所有已连接从服务器的脚本保存到缓存里面，\n * 这样在执行过一次 EVAL 之后，其他时候都可以直接发送 EVALSHA 了。\n *\n * We use a capped collection implemented by a hash table for fast lookup\n * of scripts we can send as EVALSHA, plus a linked list that is used for\n * eviction of the oldest entry when the max number of items is reached.\n *\n * 程序构建了一个固定大小的集合（capped collection），\n * 该集合由哈希结构和一个链表组成，\n * 哈希负责快速查找，而链表则负责形成一个 FIFO 队列，\n * 在脚本的数量超过最大值时，最先保存的脚本将被删除。\n *\n * We don't care about taking a different cache for every different slave\n * since to fill the cache again is not very costly, the goal of this code\n * is to avoid that the same big script is trasmitted a big number of times\n * per second wasting bandwidth and processor speed, but it is not a problem\n * if we need to rebuild the cache from scratch from time to time, every used\n * script will need to be transmitted a single time to reappear in the cache.\n *\n * Redis 我们不是为每个从服务器保存独立的脚本缓存，\n * 而是让所有从服务器都共用一个全局缓存。\n * 这是因为重新填充脚本到缓存中的操作并不昂贵，\n * 这个程序的目的是避免在短时间内发送同一个大脚本多次，\n * 造成带宽和 CPU 浪费，\n * 但时不时重新建立一次缓存的代码并不高昂，\n * 每次将一个脚本添加到缓存中时，都需要发送这个脚本一次。\n *\n * This is how the system works:\n *\n * 以下是这个系统的工作方式：\n *\n * 1) Every time a new slave connects, we flush the whole script cache.\n *    每次有新的从服务器连接时，清空所有脚本缓存。\n *\n * 2) We only send as EVALSHA what was sent to the master as EVALSHA, without\n *    trying to convert EVAL into EVALSHA specifically for slaves.\n *    程序只在主服务器接到 EVALSHA 时才向从服务器发送 EVALSHA ，\n *    它不会主动尝试将 EVAL 转换成 EVALSHA 。\n *\n * 3) Every time we trasmit a script as EVAL to the slaves, we also add the\n *    corresponding SHA1 of the script into the cache as we are sure every\n *    slave knows about the script starting from now.\n *    每次将脚本通过 EVAL 命令发送给所有从服务器时，\n *    将脚本的 SHA1 键保存到脚本字典中，字典的键为 SHA1 ，值为 NULL ，\n *    这样我们就知道，只要脚本的 SHA1 在字典中，\n *    那么这个脚本就存在于所有 slave 中。\n *\n * 4) On SCRIPT FLUSH command, we replicate the command to all the slaves\n *    and at the same time flush the script cache.\n *    当客户端执行 SCRIPT FLUSH 的时候，服务器将该命令复制给所有从服务器，\n *    让它们也刷新自己的脚本缓存。\n *\n * 5) When the last slave disconnects, flush the cache.\n *    当所有从服务器都断开时，清空脚本。\n *\n * 6) We handle SCRIPT LOAD as well since that's how scripts are loaded\n *    in the master sometimes.\n *    SCRIPT LOAD 命令对这个脚本缓存的作用和 EVAL 一样。\n */\n\n/* Initialize the script cache, only called at startup. */\n// 初始化缓存，只在服务器启动时调用\nvoid replicationScriptCacheInit(void) {\n    // 最大缓存脚本数\n    server.repl_scriptcache_size = 10000;\n    // 字典\n    server.repl_scriptcache_dict = dictCreate(&replScriptCacheDictType,NULL);\n    // FIFO 队列\n    server.repl_scriptcache_fifo = listCreate();\n}\n\n/* Empty the script cache. Should be called every time we are no longer sure\n * that every slave knows about all the scripts in our set, or when the\n * current AOF \"context\" is no longer aware of the script. In general we\n * should flush the cache:\n *\n * 清空脚本缓存。\n *\n * 在以下情况下执行：\n *\n * 1) Every time a new slave reconnects to this master and performs a\n *    full SYNC (PSYNC does not require flushing).\n *    有新从服务器连入，并且执行了一次 full SYNC ， PSYNC 无须清空缓存\n * 2) Every time an AOF rewrite is performed.\n *    每次执行 AOF 重写时\n * 3) Every time we are left without slaves at all, and AOF is off, in order\n *    to reclaim otherwise unused memory.\n *    在没有任何从服务器，AOF 关闭的时候，为节约内存而执行清空。\n */\nvoid replicationScriptCacheFlush(void) {\n    dictEmpty(server.repl_scriptcache_dict,NULL);\n    listRelease(server.repl_scriptcache_fifo);\n    server.repl_scriptcache_fifo = listCreate();\n}\n\n/* Add an entry into the script cache, if we reach max number of entries the\n * oldest is removed from the list. \n *\n * 将脚本的 SHA1 添加到缓存中，\n * 如果缓存的数量已达到最大值，那么删除最旧的那个脚本（FIFO）\n */\nvoid replicationScriptCacheAdd(sds sha1) {\n    int retval;\n    sds key = sdsdup(sha1);\n\n    /* Evict oldest. */\n    // 如果大小超过数量限制，那么删除最旧\n    if (listLength(server.repl_scriptcache_fifo) == server.repl_scriptcache_size)\n    {\n        listNode *ln = listLast(server.repl_scriptcache_fifo);\n        sds oldest = listNodeValue(ln);\n\n        retval = dictDelete(server.repl_scriptcache_dict,oldest);\n        redisAssert(retval == DICT_OK);\n        listDelNode(server.repl_scriptcache_fifo,ln);\n    }\n\n    /* Add current. */\n    // 添加 SHA1\n    retval = dictAdd(server.repl_scriptcache_dict,key,NULL);\n    listAddNodeHead(server.repl_scriptcache_fifo,key);\n    redisAssert(retval == DICT_OK);\n}\n\n/* Returns non-zero if the specified entry exists inside the cache, that is,\n * if all the slaves are aware of this script SHA1. */\n// 如果脚本存在于脚本，那么返回 1 ；否则，返回 0 。\nint replicationScriptCacheExists(sds sha1) {\n    return dictFind(server.repl_scriptcache_dict,sha1) != NULL;\n}\n\n/* ----------------------- SYNCHRONOUS REPLICATION --------------------------\n * Redis synchronous replication design can be summarized in points:\n *\n * - Redis masters have a global replication offset, used by PSYNC.\n * - Master increment the offset every time new commands are sent to slaves.\n * - Slaves ping back masters with the offset processed so far.\n *\n * So synchronous replication adds a new WAIT command in the form:\n *\n *   WAIT <num_replicas> <milliseconds_timeout>\n *\n * That returns the number of replicas that processed the query when\n * we finally have at least num_replicas, or when the timeout was\n * reached.\n *\n * The command is implemented in this way:\n *\n * - Every time a client processes a command, we remember the replication\n *   offset after sending that command to the slaves.\n * - When WAIT is called, we ask slaves to send an acknowledgement ASAP.\n *   The client is blocked at the same time (see blocked.c).\n * - Once we receive enough ACKs for a given offset or when the timeout\n *   is reached, the WAIT command is unblocked and the reply sent to the\n *   client.\n */\n\n/* This just set a flag so that we broadcast a REPLCONF GETACK command\n * to all the slaves in the beforeSleep() function. Note that this way\n * we \"group\" all the clients that want to wait for synchronouns replication\n * in a given event loop iteration, and send a single GETACK for them all. */\nvoid replicationRequestAckFromSlaves(void) {\n    server.get_ack_from_slaves = 1;\n}\n\n/* Return the number of slaves that already acknowledged the specified\n * replication offset. */\nint replicationCountAcksByOffset(long long offset) {\n    listIter li;\n    listNode *ln;\n    int count = 0;\n\n    listRewind(server.slaves,&li);\n    while((ln = listNext(&li))) {\n        redisClient *slave = ln->value;\n\n        if (slave->replstate != REDIS_REPL_ONLINE) continue;\n        if (slave->repl_ack_off >= offset) count++;\n    }\n    return count;\n}\n\n/* WAIT for N replicas to acknowledge the processing of our latest\n * write command (and all the previous commands). */\nvoid waitCommand(redisClient *c) {\n    mstime_t timeout;\n    long numreplicas, ackreplicas;\n    long long offset = c->woff;\n\n    /* Argument parsing. */\n    if (getLongFromObjectOrReply(c,c->argv[1],&numreplicas,NULL) != REDIS_OK)\n        return;\n    if (getTimeoutFromObjectOrReply(c,c->argv[2],&timeout,UNIT_MILLISECONDS)\n        != REDIS_OK) return;\n\n    /* First try without blocking at all. */\n    ackreplicas = replicationCountAcksByOffset(c->woff);\n    if (ackreplicas >= numreplicas || c->flags & REDIS_MULTI) {\n        addReplyLongLong(c,ackreplicas);\n        return;\n    }\n\n    /* Otherwise block the client and put it into our list of clients\n     * waiting for ack from slaves. */\n    c->bpop.timeout = timeout;\n    c->bpop.reploffset = offset;\n    c->bpop.numreplicas = numreplicas;\n    listAddNodeTail(server.clients_waiting_acks,c);\n    blockClient(c,REDIS_BLOCKED_WAIT);\n\n    /* Make sure that the server will send an ACK request to all the slaves\n     * before returning to the event loop. */\n    replicationRequestAckFromSlaves();\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(redisClient *c) {\n    listNode *ln = listSearchKey(server.clients_waiting_acks,c);\n    redisAssert(ln != NULL);\n    listDelNode(server.clients_waiting_acks,ln);\n}\n\n/* Check if there are clients blocked in WAIT that can be unblocked since\n * we received enough ACKs from slaves. */\nvoid processClientsWaitingReplicas(void) {\n    long long last_offset = 0;\n    int last_numreplicas = 0;\n\n    listIter li;\n    listNode *ln;\n\n    listRewind(server.clients_waiting_acks,&li);\n    while((ln = listNext(&li))) {\n        redisClient *c = ln->value;\n\n        /* Every time we find a client that is satisfied for a given\n         * offset and number of replicas, we remember it so the next client\n         * may be unblocked without calling replicationCountAcksByOffset()\n         * if the requested offset / replicas were equal or less. */\n        if (last_offset && last_offset > c->bpop.reploffset &&\n                           last_numreplicas > c->bpop.numreplicas)\n        {\n            unblockClient(c);\n            addReplyLongLong(c,last_numreplicas);\n        } else {\n            int numreplicas = replicationCountAcksByOffset(c->bpop.reploffset);\n\n            if (numreplicas >= c->bpop.numreplicas) {\n                last_offset = c->bpop.reploffset;\n                last_numreplicas = numreplicas;\n                unblockClient(c);\n                addReplyLongLong(c,numreplicas);\n            }\n        }\n    }\n}\n\n/* Return the slave replication offset for this instance, that is\n * the offset for which we already processed the master replication stream. */\nlong long replicationGetSlaveOffset(void) {\n    long long offset = 0;\n\n    if (server.masterhost != NULL) {\n        if (server.master) {\n            offset = server.master->reploff;\n        } else if (server.cached_master) {\n            offset = server.cached_master->reploff;\n        }\n    }\n    /* offset may be -1 when the master does not support it at all, however\n     * this function is designed to return an offset that can express the\n     * amount of data processed by the master, so we return a positive\n     * integer. */\n    if (offset < 0) offset = 0;\n    return offset;\n}\n\n/* --------------------------- REPLICATION CRON  ---------------------------- */\n\n/* Replication cron funciton, called 1 time per second. */\n// 复制 cron 函数，每秒调用一次\nvoid replicationCron(void) {\n\n    /* Non blocking connection timeout? */\n    // 尝试连接到主服务器，但超时\n    if (server.masterhost &&\n        (server.repl_state == REDIS_REPL_CONNECTING ||\n         server.repl_state == REDIS_REPL_RECEIVE_PONG) &&\n        (time(NULL)-server.repl_transfer_lastio) > server.repl_timeout)\n    {\n        redisLog(REDIS_WARNING,\"Timeout connecting to the MASTER...\");\n        // 取消连接\n        undoConnectWithMaster();\n    }\n\n    /* Bulk transfer I/O timeout? */\n    // RDB 文件的传送已超时？\n    if (server.masterhost && server.repl_state == REDIS_REPL_TRANSFER &&\n        (time(NULL)-server.repl_transfer_lastio) > server.repl_timeout)\n    {\n        redisLog(REDIS_WARNING,\"Timeout receiving bulk data from MASTER... If the problem persists try to set the 'repl-timeout' parameter in redis.conf to a larger value.\");\n        // 停止传送，并删除临时文件\n        replicationAbortSyncTransfer();\n    }\n\n    /* Timed out master when we are an already connected slave? */\n    // 从服务器曾经连接上主服务器，但现在超时\n    if (server.masterhost && server.repl_state == REDIS_REPL_CONNECTED &&\n        (time(NULL)-server.master->lastinteraction) > server.repl_timeout)\n    {\n        redisLog(REDIS_WARNING,\"MASTER timeout: no data nor PING received...\");\n        // 释放主服务器\n        freeClient(server.master);\n    }\n\n    /* Check if we should connect to a MASTER */\n    // 尝试连接主服务器\n    if (server.repl_state == REDIS_REPL_CONNECT) {\n        redisLog(REDIS_NOTICE,\"Connecting to MASTER %s:%d\",\n            server.masterhost, server.masterport);\n        if (connectWithMaster() == REDIS_OK) {\n            redisLog(REDIS_NOTICE,\"MASTER <-> SLAVE sync started\");\n        }\n    }\n\n    /* Send ACK to master from time to time.\n     * Note that we do not send periodic acks to masters that don't\n     * support PSYNC and replication offsets. */\n    // 定期向主服务器发送 ACK 命令\n    // 不过如果主服务器带有 REDIS_PRE_PSYNC 的话就不发送\n    // 因为带有该标识的版本为 < 2.8 的版本，这些版本不支持 ACK 命令\n    if (server.masterhost && server.master &&\n        !(server.master->flags & REDIS_PRE_PSYNC))\n        replicationSendAck();\n    \n    /* If we have attached slaves, PING them from time to time.\n     *\n     * 如果服务器有从服务器，定时向它们发送 PING 。\n     *\n     * So slaves can implement an explicit timeout to masters, and will\n     * be able to detect a link disconnection even if the TCP connection\n     * will not actually go down. \n     *\n     * 这样从服务器就可以实现显式的 master 超时判断机制，\n     * 即使 TCP 连接未断开也是如此。\n     */\n    if (!(server.cronloops % (server.repl_ping_slave_period * server.hz))) {\n        listIter li;\n        listNode *ln;\n        robj *ping_argv[1];\n\n        /* First, send PING */\n        // 向所有已连接 slave （状态为 ONLINE）发送 PING\n        ping_argv[0] = createStringObject(\"PING\",4);\n        replicationFeedSlaves(server.slaves, server.slaveseldb, ping_argv, 1);\n        decrRefCount(ping_argv[0]);\n\n        /* Second, send a newline to all the slaves in pre-synchronization\n         * stage, that is, slaves waiting for the master to create the RDB file.\n         *\n         * 向那些正在等待 RDB 文件的从服务器（状态为 BGSAVE_START 或 BGSAVE_END）\n         * 发送 \"\\n\"\n         *\n         * The newline will be ignored by the slave but will refresh the\n         * last-io timer preventing a timeout. \n         *\n         * 这个 \"\\n\" 会被从服务器忽略，\n         * 它的作用就是用来防止主服务器因为长期不发送信息而被从服务器误判为超时\n         */\n        listRewind(server.slaves,&li);\n        while((ln = listNext(&li))) {\n            redisClient *slave = ln->value;\n\n            if (slave->replstate == REDIS_REPL_WAIT_BGSAVE_START ||\n                slave->replstate == REDIS_REPL_WAIT_BGSAVE_END) {\n                if (write(slave->fd, \"\\n\", 1) == -1) {\n                    /* Don't worry, it's just a ping. */\n                }\n            }\n        }\n    }\n\n    /* Disconnect timedout slaves. */\n    // 断开超时从服务器\n    if (listLength(server.slaves)) {\n        listIter li;\n        listNode *ln;\n\n        // 遍历所有从服务器\n        listRewind(server.slaves,&li);\n        while((ln = listNext(&li))) {\n            redisClient *slave = ln->value;\n\n            // 略过未 ONLINE 的从服务器\n            if (slave->replstate != REDIS_REPL_ONLINE) continue;\n\n            // 不检查旧版的从服务器\n            if (slave->flags & REDIS_PRE_PSYNC) continue;\n\n            // 释放超时从服务器\n            if ((server.unixtime - slave->repl_ack_time) > server.repl_timeout)\n            {\n                char ip[REDIS_IP_STR_LEN];\n                int port;\n\n                if (anetPeerToString(slave->fd,ip,sizeof(ip),&port) != -1) {\n                    redisLog(REDIS_WARNING,\n                        \"Disconnecting timedout slave: %s:%d\",\n                        ip, slave->slave_listening_port);\n                }\n                \n                // 释放\n                freeClient(slave);\n            }\n        }\n    }\n\n    /* If we have no attached slaves and there is a replication backlog\n     * using memory, free it after some (configured) time. */\n    // 在没有任何从服务器的 N 秒之后，释放 backlog\n    if (listLength(server.slaves) == 0 && server.repl_backlog_time_limit &&\n        server.repl_backlog)\n    {\n        time_t idle = server.unixtime - server.repl_no_slaves_since;\n\n        if (idle > server.repl_backlog_time_limit) {\n            // 释放\n            freeReplicationBacklog();\n            redisLog(REDIS_NOTICE,\n                \"Replication backlog freed after %d seconds \"\n                \"without connected slaves.\",\n                (int) server.repl_backlog_time_limit);\n        }\n    }\n\n    /* If AOF is disabled and we no longer have attached slaves, we can\n     * free our Replication Script Cache as there is no need to propagate\n     * EVALSHA at all. */\n    // 在没有任何从服务器，AOF 关闭的情况下，清空 script 缓存\n    // 因为已经没有传播 EVALSHA 的必要了\n    if (listLength(server.slaves) == 0 &&\n        server.aof_state == REDIS_AOF_OFF &&\n        listLength(server.repl_scriptcache_fifo) != 0)\n    {\n        replicationScriptCacheFlush();\n    }\n\n    /* Refresh the number of slaves with lag <= min-slaves-max-lag. */\n    // 更新符合给定延迟值的从服务器的数量\n    refreshGoodSlavesCount();\n}\n"
  },
  {
    "path": "src/rio.c",
    "content": "/* rio.c is a simple stream-oriented I/O abstraction that provides an interface\n * to write code that can consume/produce data using different concrete input\n * and output devices. \n *\n * RIO 是一个可以面向流、可用于对多种不同的输入\n * （目前是文件和内存字节）进行编程的抽象。\n *\n * For instance the same rdb.c code using the rio\n * abstraction can be used to read and write the RDB format using in-memory\n * buffers or files.\n *\n * 比如说，RIO 可以同时对内存或文件中的 RDB 格式进行读写。\n *\n * A rio object provides the following methods:\n *\n * 一个 RIO 对象提供以下方法：\n *\n *  read: read from stream.\n *        从流中读取\n *\n *  write: write to stream.\n *         写入到流中\n *\n *  tell: get the current offset.\n *        获取当前的偏移量\n *\n * It is also possible to set a 'checksum' method that is used by rio.c in order\n * to compute a checksum of the data written or read, or to query the rio object\n * for the current checksum.\n *\n * 还可以通过设置 checksum 函数，计算写入或读取内容的校验和，\n * 或者为当前的校验和查询 rio 对象。\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\n#include \"fmacros.h\"\n#include <string.h>\n#include <stdio.h>\n#include <unistd.h>\n#include \"rio.h\"\n#include \"util.h\"\n#include \"crc64.h\"\n#include \"config.h\"\n#include \"redis.h\"\n\n/* Returns 1 or 0 for success/failure. \n *\n * 将给定内容 buf 追加到缓存中，长度为 len 。\n *\n * 成功返回 1 ，失败返回 0 。\n */\nstatic size_t rioBufferWrite(rio *r, const void *buf, size_t len) {\n\n    // 追加\n    r->io.buffer.ptr = sdscatlen(r->io.buffer.ptr,(char*)buf,len);\n\n    // 更新偏移量\n    r->io.buffer.pos += len;\n\n    return 1;\n}\n\n/* Returns 1 or 0 for success/failure. \n *\n * 从 r 中读取长度为 len 的内容到 buf 中。\n *\n * 读取成功返回 1 ，否则返回 0 。\n */\nstatic size_t rioBufferRead(rio *r, void *buf, size_t len) {\n\n    // r 中的内容的长度不足 len \n    if (sdslen(r->io.buffer.ptr)-r->io.buffer.pos < len)\n        return 0; /* not enough buffer to return len bytes. */\n    \n    // 复制 r 中的内容到 buf\n    memcpy(buf,r->io.buffer.ptr+r->io.buffer.pos,len);\n    r->io.buffer.pos += len;\n\n    return 1;\n}\n\n/* Returns read/write position in buffer. \n *\n * 返回缓存的当前偏移量\n */\nstatic off_t rioBufferTell(rio *r) {\n    return r->io.buffer.pos;\n}\n\n/* Returns 1 or 0 for success/failure. \n *\n * 将长度为 len 的内容 buf 写入到文件 r 中。\n *\n * 成功返回 1 ，失败返回 0 。\n */\nstatic size_t rioFileWrite(rio *r, const void *buf, size_t len) {\n    size_t retval;\n\n    retval = fwrite(buf,len,1,r->io.file.fp);\n    r->io.file.buffered += len;\n\n    // 检查写入的字节数，看是否需要执行自动 sync\n    if (r->io.file.autosync &&\n        r->io.file.buffered >= r->io.file.autosync)\n    {\n        fflush(r->io.file.fp);\n        aof_fsync(fileno(r->io.file.fp));\n        r->io.file.buffered = 0;\n    }\n\n    return retval;\n}\n\n/* Returns 1 or 0 for success/failure. */\n/*\n * 从文件 r 中读取 len 字节到 buf 中。\n *\n * 返回值为读取的字节数。\n */\nstatic size_t rioFileRead(rio *r, void *buf, size_t len) {\n    return fread(buf,len,1,r->io.file.fp);\n}\n\n/* Returns read/write position in file. \n *\n * 返回文件当前的偏移量\n */\nstatic off_t rioFileTell(rio *r) {\n    return ftello(r->io.file.fp);\n}\n\n/*\n * 流为内存时所使用的结构\n */\nstatic const rio rioBufferIO = {\n    // 读函数\n    rioBufferRead,\n    // 写函数\n    rioBufferWrite,\n    // 偏移量函数\n    rioBufferTell,\n    NULL,           /* update_checksum */\n    0,              /* current checksum */\n    0,              /* bytes read or written */\n    0,              /* read/write chunk size */\n    { { NULL, 0 } } /* union for io-specific vars */\n};\n\n/*\n * 流为文件时所使用的结构\n */\nstatic const rio rioFileIO = {\n    // 读函数\n    rioFileRead,\n    // 写函数\n    rioFileWrite,\n    // 偏移量函数\n    rioFileTell,\n    NULL,           /* update_checksum */\n    0,              /* current checksum */\n    0,              /* bytes read or written */\n    0,              /* read/write chunk size */\n    { { NULL, 0 } } /* union for io-specific vars */\n};\n\n/*\n * 初始化文件流\n */\nvoid rioInitWithFile(rio *r, FILE *fp) {\n    *r = rioFileIO;\n    r->io.file.fp = fp;\n    r->io.file.buffered = 0;\n    r->io.file.autosync = 0;\n}\n\n/*\n * 初始化内存流\n */\nvoid rioInitWithBuffer(rio *r, sds s) {\n    *r = rioBufferIO;\n    r->io.buffer.ptr = s;\n    r->io.buffer.pos = 0;\n}\n\n/* This function can be installed both in memory and file streams when checksum\n * computation is needed. */\n/*\n * 通用校验和计算函数\n */\nvoid rioGenericUpdateChecksum(rio *r, const void *buf, size_t len) {\n    r->cksum = crc64(r->cksum,buf,len);\n}\n\n/* Set the file-based rio object to auto-fsync every 'bytes' file written.\n *\n * 每次通过 rio 写入 bytes 指定的字节数量时，执行一次自动的 fsync 。\n *\n * By default this is set to zero that means no automatic file sync is\n * performed.\n *\n * 默认情况下， bytes 被设为 0 ，表示不执行自动 fsync 。 \n *\n * This feature is useful in a few contexts since when we rely on OS write\n * buffers sometimes the OS buffers way too much, resulting in too many\n * disk I/O concentrated in very little time. When we fsync in an explicit\n * way instead the I/O pressure is more distributed across time. \n *\n * 这个函数是为了防止一次写入过多内容而设置的。\n *\n * 通过显示地、间隔性地调用 fsync ，\n * 可以将写入的 I/O 压力分担到多次 fsync 调用中。\n */\nvoid rioSetAutoSync(rio *r, off_t bytes) {\n    redisAssert(r->read == rioFileIO.read);\n    r->io.file.autosync = bytes;\n}\n\n/* ------------------------------ Higher level interface ---------------------------\n * The following higher level functions use lower level rio.c functions to help\n * generating the Redis protocol for the Append Only File.\n *\n * 以下高层函数通过调用前面的底层函数来生成 AOF 文件所需的协议\n */\n\n/* Write multi bulk count in the format: \"*<count>\\r\\n\". */\n/*\n * 以带 '\\r\\n' 后缀的形式写入字符串表示的 count 到 RIO \n *\n * 成功返回写入的数量，失败返回 0 。\n */\nsize_t rioWriteBulkCount(rio *r, char prefix, int count) {\n    char cbuf[128];\n    int clen;\n\n    // cbuf = prefix ++ count ++ '\\r\\n'\n    // 例如： *123\\r\\n\n    cbuf[0] = prefix;\n    clen = 1+ll2string(cbuf+1,sizeof(cbuf)-1,count);\n    cbuf[clen++] = '\\r';\n    cbuf[clen++] = '\\n';\n\n    // 写入\n    if (rioWrite(r,cbuf,clen) == 0) return 0;\n\n    // 返回写入字节数\n    return clen;\n}\n\n/* Write binary-safe string in the format: \"$<count>\\r\\n<payload>\\r\\n\". \n *\n * 以 \"$<count>\\r\\n<payload>\\r\\n\" 的形式写入二进制安全字符\n *\n * 例如 $3\\r\\nSET\\r\\n\n */\nsize_t rioWriteBulkString(rio *r, const char *buf, size_t len) {\n    size_t nwritten;\n\n    // 写入 $<count>\\r\\n\n    if ((nwritten = rioWriteBulkCount(r,'$',len)) == 0) return 0;\n\n    // 写入 <payload>\n    if (len > 0 && rioWrite(r,buf,len) == 0) return 0;\n\n    // 写入 \\r\\n\n    if (rioWrite(r,\"\\r\\n\",2) == 0) return 0;\n\n    // 返回写入总量\n    return nwritten+len+2;\n}\n\n/* Write a long long value in format: \"$<count>\\r\\n<payload>\\r\\n\". \n *\n * 以 \"$<count>\\r\\n<payload>\\r\\n\" 的格式写入 long long 值\n */\nsize_t rioWriteBulkLongLong(rio *r, long long l) {\n    char lbuf[32];\n    unsigned int llen;\n\n    // 取出 long long 值的字符串形式\n    // 并计算该字符串的长度\n    llen = ll2string(lbuf,sizeof(lbuf),l);\n\n    // 写入 $llen\\r\\nlbuf\\r\\n\n    return rioWriteBulkString(r,lbuf,llen);\n}\n\n/* Write a double value in the format: \"$<count>\\r\\n<payload>\\r\\n\" \n *\n * 以 \"$<count>\\r\\n<payload>\\r\\n\" 的格式写入 double 值\n */\nsize_t rioWriteBulkDouble(rio *r, double d) {\n    char dbuf[128];\n    unsigned int dlen;\n\n    // 取出 double 值的字符串表示（小数点后只保留 17 位）\n    // 并计算字符串的长度\n    dlen = snprintf(dbuf,sizeof(dbuf),\"%.17g\",d);\n\n    // 写入 $dlen\\r\\ndbuf\\r\\n\n    return rioWriteBulkString(r,dbuf,dlen);\n}\n"
  },
  {
    "path": "src/rio.h",
    "content": "/*\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\n#ifndef __REDIS_RIO_H\n#define __REDIS_RIO_H\n\n#include <stdio.h>\n#include <stdint.h>\n#include \"sds.h\"\n\n/*\n * RIO API 接口和状态\n */\nstruct _rio {\n\n    /* Backend functions.\n     * Since this functions do not tolerate short writes or reads the return\n     * value is simplified to: zero on error, non zero on complete success. */\n    // API\n    size_t (*read)(struct _rio *, void *buf, size_t len);\n    size_t (*write)(struct _rio *, const void *buf, size_t len);\n    off_t (*tell)(struct _rio *);\n\n    /* The update_cksum method if not NULL is used to compute the checksum of\n     * all the data that was read or written so far. The method should be\n     * designed so that can be called with the current checksum, and the buf\n     * and len fields pointing to the new block of data to add to the checksum\n     * computation. */\n    // 校验和计算函数，每次有写入/读取新数据时都要计算一次\n    void (*update_cksum)(struct _rio *, const void *buf, size_t len);\n\n    /* The current checksum */\n    // 当前校验和\n    uint64_t cksum;\n\n    /* number of bytes read or written */\n    size_t processed_bytes;\n\n    /* maximum single read or write chunk size */\n    size_t max_processing_chunk;\n\n    /* Backend-specific vars. */\n    union {\n\n        struct {\n            // 缓存指针\n            sds ptr;\n            // 偏移量\n            off_t pos;\n        } buffer;\n\n        struct {\n            // 被打开文件的指针\n            FILE *fp;\n            // 最近一次 fsync() 以来，写入的字节量\n            off_t buffered; /* Bytes written since last fsync. */\n            // 写入多少字节之后，才会自动执行一次 fsync()\n            off_t autosync; /* fsync after 'autosync' bytes written. */\n        } file;\n    } io;\n};\n\ntypedef struct _rio rio;\n\n/* The following functions are our interface with the stream. They'll call the\n * actual implementation of read / write / tell, and will update the checksum\n * if needed. */\n\n/*\n * 将 buf 中的 len 字节写入到 r 中。\n *\n * 写入成功返回实际写入的字节数，写入失败返回 -1 。\n */\nstatic inline size_t rioWrite(rio *r, const void *buf, size_t len) {\n    while (len) {\n        size_t bytes_to_write = (r->max_processing_chunk && r->max_processing_chunk < len) ? r->max_processing_chunk : len;\n        if (r->update_cksum) r->update_cksum(r,buf,bytes_to_write);\n        if (r->write(r,buf,bytes_to_write) == 0)\n            return 0;\n        buf = (char*)buf + bytes_to_write;\n        len -= bytes_to_write;\n        r->processed_bytes += bytes_to_write;\n    }\n    return 1;\n}\n\n/*\n * 从 r 中读取 len 字节，并将内容保存到 buf 中。\n *\n * 读取成功返回 1 ，失败返回 0 。\n */\nstatic inline size_t rioRead(rio *r, void *buf, size_t len) {\n    while (len) {\n        size_t bytes_to_read = (r->max_processing_chunk && r->max_processing_chunk < len) ? r->max_processing_chunk : len;\n        if (r->read(r,buf,bytes_to_read) == 0)\n            return 0;\n        if (r->update_cksum) r->update_cksum(r,buf,bytes_to_read);\n        buf = (char*)buf + bytes_to_read;\n        len -= bytes_to_read;\n        r->processed_bytes += bytes_to_read;\n    }\n    return 1;\n}\n\n/*\n * 返回 r 的当前偏移量。\n */\nstatic inline off_t rioTell(rio *r) {\n    return r->tell(r);\n}\n\nvoid rioInitWithFile(rio *r, FILE *fp);\nvoid rioInitWithBuffer(rio *r, sds s);\n\nsize_t rioWriteBulkCount(rio *r, char prefix, int count);\nsize_t rioWriteBulkString(rio *r, const char *buf, size_t len);\nsize_t rioWriteBulkLongLong(rio *r, long long l);\nsize_t rioWriteBulkDouble(rio *r, double d);\n\nvoid rioGenericUpdateChecksum(rio *r, const void *buf, size_t len);\nvoid rioSetAutoSync(rio *r, off_t bytes);\n\n#endif\n"
  },
  {
    "path": "src/scripting.c",
    "content": "/*\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 \"redis.h\"\n#include \"sha1.h\"\n#include \"rand.h\"\n\n#include <lua.h>\n#include <lauxlib.h>\n#include <lualib.h>\n#include <ctype.h>\n#include <math.h>\n\nchar *redisProtocolToLuaType_Int(lua_State *lua, char *reply);\nchar *redisProtocolToLuaType_Bulk(lua_State *lua, char *reply);\nchar *redisProtocolToLuaType_Status(lua_State *lua, char *reply);\nchar *redisProtocolToLuaType_Error(lua_State *lua, char *reply);\nchar *redisProtocolToLuaType_MultiBulk(lua_State *lua, char *reply);\nint redis_math_random (lua_State *L);\nint redis_math_randomseed (lua_State *L);\nvoid sha1hex(char *digest, char *script, size_t len);\n\n/* Take a Redis reply in the Redis protocol format and convert it into a\n * Lua type. Thanks to this function, and the introduction of not connected\n * clients, it is trivial to implement the redis() lua function.\n *\n * 接收一个 Redis 协议格式的 Redis 回复，并将它转换为 Lua 类型的值。\n *\n * Basically we take the arguments, execute the Redis command in the context\n * of a non connected client, then take the generated reply and convert it\n * into a suitable Lua type. With this trick the scripting feature does not\n * need the introduction of a full Redis internals API. Basically the script\n * is like a normal client that bypasses all the slow I/O paths.\n *\n * 一般来说，程序接收参数，在无网络连接的伪终端中执行 Redis 命令，\n * 然后将所得的回复转换为恰当的 Lua 类型。\n *\n * 这样脚本功能就不用为所有 Redis 命令都实现相应的 API 了。\n *\n * 基本上脚本就是一个没有 I/O 操作的普通客户端。\n *\n * Note: in this function we do not do any sanity check as the reply is\n * generated by Redis directly. This allows us to go faster.\n *\n * 函数没有对回复的正确性进行检查，因为所有回复都直接来自于 Redis 命令的结果。\n * 这使得这个函数的可以更快速地执行。\n *\n * The reply string can be altered during the parsing as it is discarded\n * after the conversion is completed.\n *\n * 回复的内容在分析（Parsing）的过程中可能会被修改，\n * 并且在分析完毕之后，它会被清除。\n *\n * Errors are returned as a table with a single 'err' field set to the\n * error string.\n *\n * 错误返回为一个带有 err 域的表，域的值为出错的字符串内容。\n */\n\nchar *redisProtocolToLuaType(lua_State *lua, char* reply) {\n    char *p = reply;\n\n    switch(*p) {\n    case ':':\n        p = redisProtocolToLuaType_Int(lua,reply);\n        break;\n    case '$':\n        p = redisProtocolToLuaType_Bulk(lua,reply);\n        break;\n    case '+':\n        p = redisProtocolToLuaType_Status(lua,reply);\n        break;\n    case '-':\n        p = redisProtocolToLuaType_Error(lua,reply);\n        break;\n    case '*':\n        p = redisProtocolToLuaType_MultiBulk(lua,reply);\n        break;\n    }\n\n    return p;\n}\n\nchar *redisProtocolToLuaType_Int(lua_State *lua, char *reply) {\n    char *p = strchr(reply+1,'\\r');\n    long long value;\n\n    string2ll(reply+1,p-reply-1,&value);\n    lua_pushnumber(lua,(lua_Number)value);\n    return p+2;\n}\n\nchar *redisProtocolToLuaType_Bulk(lua_State *lua, char *reply) {\n    char *p = strchr(reply+1,'\\r');\n    long long bulklen;\n\n    string2ll(reply+1,p-reply-1,&bulklen);\n    if (bulklen == -1) {\n        lua_pushboolean(lua,0);\n        return p+2;\n    } else {\n        lua_pushlstring(lua,p+2,bulklen);\n        return p+2+bulklen+2;\n    }\n}\n\nchar *redisProtocolToLuaType_Status(lua_State *lua, char *reply) {\n    char *p = strchr(reply+1,'\\r');\n\n    lua_newtable(lua);\n    lua_pushstring(lua,\"ok\");\n    lua_pushlstring(lua,reply+1,p-reply-1);\n    lua_settable(lua,-3);\n    return p+2;\n}\n\nchar *redisProtocolToLuaType_Error(lua_State *lua, char *reply) {\n    char *p = strchr(reply+1,'\\r');\n\n    lua_newtable(lua);\n    lua_pushstring(lua,\"err\");\n    lua_pushlstring(lua,reply+1,p-reply-1);\n    lua_settable(lua,-3);\n    return p+2;\n}\n\nchar *redisProtocolToLuaType_MultiBulk(lua_State *lua, char *reply) {\n    char *p = strchr(reply+1,'\\r');\n    long long mbulklen;\n    int j = 0;\n\n    string2ll(reply+1,p-reply-1,&mbulklen);\n    p += 2;\n    if (mbulklen == -1) {\n        lua_pushboolean(lua,0);\n        return p;\n    }\n    lua_newtable(lua);\n    for (j = 0; j < mbulklen; j++) {\n        lua_pushnumber(lua,j+1);\n        p = redisProtocolToLuaType(lua,p);\n        lua_settable(lua,-3);\n    }\n    return p;\n}\n\nvoid luaPushError(lua_State *lua, char *error) {\n    lua_Debug dbg;\n\n    lua_newtable(lua);\n    lua_pushstring(lua,\"err\");\n\n    /* Attempt to figure out where this function was called, if possible */\n    if(lua_getstack(lua, 1, &dbg) && lua_getinfo(lua, \"nSl\", &dbg)) {\n        sds msg = sdscatprintf(sdsempty(), \"%s: %d: %s\",\n            dbg.source, dbg.currentline, error);\n        lua_pushstring(lua, msg);\n        sdsfree(msg);\n    } else {\n        lua_pushstring(lua, error);\n    }\n    lua_settable(lua,-3);\n}\n\n/* Sort the array currently in the stack. We do this to make the output\n * of commands like KEYS or SMEMBERS something deterministic when called\n * from Lua (to play well with AOf/replication).\n *\n * 对栈中的数组进行排序，从而让 KEYS 、 SMEMBERS 这样的命令的输出变得带有确定性。\n * 这确保了 AOF 和附属节点可以正确地还原数据。\n *\n * The array is sorted using table.sort itself, and assuming all the\n * list elements are strings. \n *\n * 排序由 table.sort 函数执行，并假设所有列表元素均为字符串\n */\nvoid luaSortArray(lua_State *lua) {\n    /* Initial Stack: array */\n    lua_getglobal(lua,\"table\");\n    lua_pushstring(lua,\"sort\");\n    lua_gettable(lua,-2);       /* Stack: array, table, table.sort */\n    lua_pushvalue(lua,-3);      /* Stack: array, table, table.sort, array */\n    if (lua_pcall(lua,1,0,0)) {\n        /* Stack: array, table, error */\n\n        /* We are not interested in the error, we assume that the problem is\n         * that there are 'false' elements inside the array, so we try\n         * again with a slower function but able to handle this case, that\n         * is: table.sort(table, __redis__compare_helper) */\n        lua_pop(lua,1);             /* Stack: array, table */\n        lua_pushstring(lua,\"sort\"); /* Stack: array, table, sort */\n        lua_gettable(lua,-2);       /* Stack: array, table, table.sort */\n        lua_pushvalue(lua,-3);      /* Stack: array, table, table.sort, array */\n        lua_getglobal(lua,\"__redis__compare_helper\");\n        /* Stack: array, table, table.sort, array, __redis__compare_helper */\n        lua_call(lua,2,0);\n    }\n    /* Stack: array (sorted), table */\n    lua_pop(lua,1);             /* Stack: array (sorted) */\n}\n\n/*\n * 用于在 Lua 环境中执行 Redis 命令\n *\n * 执行出错时返回 1 \n */\n#define LUA_CMD_OBJCACHE_SIZE 32\n#define LUA_CMD_OBJCACHE_MAX_LEN 64\nint luaRedisGenericCommand(lua_State *lua, int raise_error) {\n    int j, argc = lua_gettop(lua);\n    struct redisCommand *cmd;\n    redisClient *c = server.lua_client;\n    sds reply;\n\n    /* Cached across calls. */\n    static robj **argv = NULL;\n    static int argv_size = 0;\n    static robj *cached_objects[LUA_CMD_OBJCACHE_SIZE];\n    static int cached_objects_len[LUA_CMD_OBJCACHE_SIZE];\n\n    /* Require at least one argument */\n    // 命令参数检查（命令本身是 argv[0]）\n    if (argc == 0) {\n        luaPushError(lua,\n            \"Please specify at least one argument for redis.call()\");\n        return 1;\n    }\n\n    /* Build the arguments vector */\n    // 构建参数数组\n    if (!argv) {\n        argv = zmalloc(sizeof(robj*)*argc);\n    } else if (argv_size < argc) {\n        argv = zrealloc(argv,sizeof(robj*)*argc);\n        argv_size = argc;\n    }\n\n    for (j = 0; j < argc; j++) {\n        char *obj_s;\n        size_t obj_len;\n\n        obj_s = (char*)lua_tolstring(lua,j+1,&obj_len);\n        if (obj_s == NULL) break; /* Not a string. */\n\n        /* Try to use a cached object. */\n        if (cached_objects[j] && cached_objects_len[j] >= obj_len) {\n            char *s = cached_objects[j]->ptr;\n            struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr)));\n\n            argv[j] = cached_objects[j];\n            cached_objects[j] = NULL;\n            memcpy(s,obj_s,obj_len+1);\n            sh->free += sh->len - obj_len;\n            sh->len = obj_len;\n        } else {\n            argv[j] = createStringObject(obj_s, obj_len);\n        }\n    }\n    \n    /* Check if one of the arguments passed by the Lua script\n     * is not a string or an integer (lua_isstring() return true for\n     * integers as well). \n     *\n     * 对参数进行检查，确保它们只能是字符串或整数\n     */\n    if (j != argc) {\n        j--;\n        while (j >= 0) {\n            decrRefCount(argv[j]);\n            j--;\n        }\n        luaPushError(lua,\n            \"Lua redis() command arguments must be strings or integers\");\n        return 1;\n    }\n\n    /* Setup our fake client for command execution */\n    // 设置参数和参数数量\n    c->argv = argv;\n    c->argc = argc;\n\n    /* Command lookup */\n    // 查找命令的实现函数\n    cmd = lookupCommand(argv[0]->ptr);\n    // 检查参数数量是否正确\n    if (!cmd || ((cmd->arity > 0 && cmd->arity != argc) ||\n                   (argc < -cmd->arity)))\n    {\n        if (cmd)\n            luaPushError(lua,\n                \"Wrong number of args calling Redis command From Lua script\");\n        else\n            luaPushError(lua,\"Unknown Redis command called from Lua script\");\n        goto cleanup;\n    }\n\n    /* There are commands that are not allowed inside scripts. */\n    // 阻止那些不能在脚本中执行的命令\n    if (cmd->flags & REDIS_CMD_NOSCRIPT) {\n        luaPushError(lua, \"This Redis command is not allowed from scripts\");\n        goto cleanup;\n    }\n\n    /* Write commands are forbidden against read-only slaves, or if a\n     * command marked as non-deterministic was already called in the context\n     * of this script. \n     *\n     * 写命令不能在只读附属节点中执行，\n     * 也不能在已经有随机命令执行的情况下执行。\n     */\n    if (cmd->flags & REDIS_CMD_WRITE) {\n\n        // 不能在已经执行过随机命令之后执行\n        if (server.lua_random_dirty) {\n            luaPushError(lua,\n                \"Write commands not allowed after non deterministic commands\");\n            goto cleanup;\n\n        // 不能在只读节点中执行\n        } else if (server.masterhost && server.repl_slave_ro &&\n                   !server.loading &&\n                   !(server.lua_caller->flags & REDIS_MASTER))\n        {\n            luaPushError(lua, shared.roslaveerr->ptr);\n            goto cleanup;\n\n        // 不能在已经有写入出错的情况下继续进行写入\n        } else if (server.stop_writes_on_bgsave_err &&\n                   server.saveparamslen > 0 &&\n                   server.lastbgsave_status == REDIS_ERR)\n        {\n            luaPushError(lua, shared.bgsaveerr->ptr);\n            goto cleanup;\n        }\n    }\n\n    /* If we reached the memory limit configured via maxmemory, commands that\n     * could enlarge the memory usage are not allowed, but only if this is the\n     * first write in the context of this script, otherwise we can't stop\n     * in the middle. \n     *\n     * 如果脚本运行时 Redis 的内存到达了 maxmemory 的限制，\n     * 并且之前没有执行过写操作，\n     * 那么尝试释放内存，如果释放失败的话，就直接退出。\n     */\n    if (server.maxmemory && server.lua_write_dirty == 0 &&\n        (cmd->flags & REDIS_CMD_DENYOOM))\n    {\n        // 尝试释放内存，如果失败的话，直接退出\n        if (freeMemoryIfNeeded() == REDIS_ERR) {\n            luaPushError(lua, shared.oomerr->ptr);\n            goto cleanup;\n        }\n    }\n\n    // 如果将要执行的是随机操作，那么设置 lua_random_dirty 状态\n    if (cmd->flags & REDIS_CMD_RANDOM) server.lua_random_dirty = 1;\n    // 如果将要执行的是写操作，那么设置 lua_write_dirty 状态\n    if (cmd->flags & REDIS_CMD_WRITE) server.lua_write_dirty = 1;\n\n    /* Run the command */\n    // 执行命令\n    c->cmd = cmd;\n    call(c,REDIS_CALL_SLOWLOG | REDIS_CALL_STATS);\n\n    /* Convert the result of the Redis command into a suitable Lua type.\n     *\n     * 将 Redis 命令的结果转换为适当的 Lua 类型。\n     *\n     * The first thing we need is to create a single string from the client\n     * output buffers. \n     *\n     * 首先要做的就是为客户端输出缓存中创建一个字符串\n     */\n    if (listLength(c->reply) == 0 && c->bufpos < REDIS_REPLY_CHUNK_BYTES) {\n        /* This is a fast path for the common case of a reply inside the\n         * client static buffer. Don't create an SDS string but just use\n         * the client buffer directly. */\n        c->buf[c->bufpos] = '\\0';\n        reply = c->buf;\n        c->bufpos = 0;\n    } else {\n        reply = sdsnewlen(c->buf,c->bufpos);\n        c->bufpos = 0;\n        while(listLength(c->reply)) {\n            robj *o = listNodeValue(listFirst(c->reply));\n            reply = sdscatlen(reply,o->ptr,sdslen(o->ptr));\n            listDelNode(c->reply,listFirst(c->reply));\n        }\n    }\n\n    // 检测执行的命令是否出错\n    if (raise_error && reply[0] != '-') raise_error = 0;\n\n    // 将回复转换为 Lua 值，并将这些值推入 Lua 环境\n    redisProtocolToLuaType(lua,reply);\n\n    /* Sort the output array if needed, assuming it is a non-null multi bulk\n     * reply as expected. \n     *\n     * 如果输出是一个 multi bulk reply ，并且它不是一个 null multi bulk reply ，\n     * 那么对它进行排序\n     * (null multi bulk reply 的前缀为 *-1\\r\\n ，具体请参考协议文档）\n     */\n    if ((cmd->flags & REDIS_CMD_SORT_FOR_SCRIPT) &&\n        (reply[0] == '*' && reply[1] != '-')) {\n            // 排序\n            luaSortArray(lua);\n    }\n\n    if (reply != c->buf) sdsfree(reply);\n    c->reply_bytes = 0;\n\ncleanup:\n    /* Clean up. Command code may have changed argv/argc so we use the\n     * argv/argc of the client instead of the local variables. */\n    for (j = 0; j < c->argc; j++) {\n        robj *o = c->argv[j];\n\n        /* Try to cache the object in the cached_objects array.\n         * The object must be small, SDS-encoded, and with refcount = 1\n         * (we must be the only owner) for us to cache it. */\n        if (j < LUA_CMD_OBJCACHE_SIZE &&\n            o->refcount == 1 &&\n            (o->encoding == REDIS_ENCODING_RAW ||\n             o->encoding == REDIS_ENCODING_EMBSTR) &&\n            sdslen(o->ptr) <= LUA_CMD_OBJCACHE_MAX_LEN)\n        {\n            struct sdshdr *sh = (void*)(((char*)(o->ptr))-(sizeof(struct sdshdr)));\n\n            if (cached_objects[j]) decrRefCount(cached_objects[j]);\n            cached_objects[j] = o;\n            cached_objects_len[j] = sh->free + sh->len;\n        } else {\n            decrRefCount(o);\n        }\n    }\n\n    if (c->argv != argv) {\n        zfree(c->argv);\n        argv = NULL;\n    }\n\n    // 返回错误\n    if (raise_error) {\n        /* If we are here we should have an error in the stack, in the\n         * form of a table with an \"err\" field. Extract the string to\n         * return the plain error. */\n        lua_pushstring(lua,\"err\");\n        lua_gettable(lua,-2);\n        return lua_error(lua);\n    }\n\n    return 1;\n}\n\nint luaRedisCallCommand(lua_State *lua) {\n    return luaRedisGenericCommand(lua,1);\n}\n\nint luaRedisPCallCommand(lua_State *lua) {\n    return luaRedisGenericCommand(lua,0);\n}\n\n/* This adds redis.sha1hex(string) to Lua scripts using the same hashing\n * function used for sha1ing lua scripts. */\nint luaRedisSha1hexCommand(lua_State *lua) {\n    int argc = lua_gettop(lua);\n    char digest[41];\n    size_t len;\n    char *s;\n\n    if (argc != 1) {\n        luaPushError(lua, \"wrong number of arguments\");\n        return 1;\n    }\n\n    s = (char*)lua_tolstring(lua,1,&len);\n    sha1hex(digest,s,len);\n    lua_pushstring(lua,digest);\n    return 1;\n}\n\n/* Returns a table with a single field 'field' set to the string value\n * passed as argument. This helper function is handy when returning\n * a Redis Protocol error or status reply from Lua:\n *\n * return redis.error_reply(\"ERR Some Error\")\n * return redis.status_reply(\"ERR Some Error\")\n */\nint luaRedisReturnSingleFieldTable(lua_State *lua, char *field) {\n    if (lua_gettop(lua) != 1 || lua_type(lua,-1) != LUA_TSTRING) {\n        luaPushError(lua, \"wrong number or type of arguments\");\n        return 1;\n    }\n\n    lua_newtable(lua);\n    lua_pushstring(lua, field);\n    lua_pushvalue(lua, -3);\n    lua_settable(lua, -3);\n    return 1;\n}\n\nint luaRedisErrorReplyCommand(lua_State *lua) {\n    return luaRedisReturnSingleFieldTable(lua,\"err\");\n}\n\nint luaRedisStatusReplyCommand(lua_State *lua) {\n    return luaRedisReturnSingleFieldTable(lua,\"ok\");\n}\n\nint luaLogCommand(lua_State *lua) {\n    int j, argc = lua_gettop(lua);\n    int level;\n    sds log;\n\n    if (argc < 2) {\n        luaPushError(lua, \"redis.log() requires two arguments or more.\");\n        return 1;\n    } else if (!lua_isnumber(lua,-argc)) {\n        luaPushError(lua, \"First argument must be a number (log level).\");\n        return 1;\n    }\n    level = lua_tonumber(lua,-argc);\n    if (level < REDIS_DEBUG || level > REDIS_WARNING) {\n        luaPushError(lua, \"Invalid debug level.\");\n        return 1;\n    }\n\n    /* Glue together all the arguments */\n    log = sdsempty();\n    for (j = 1; j < argc; j++) {\n        size_t len;\n        char *s;\n\n        s = (char*)lua_tolstring(lua,(-argc)+j,&len);\n        if (s) {\n            if (j != 1) log = sdscatlen(log,\" \",1);\n            log = sdscatlen(log,s,len);\n        }\n    }\n    redisLogRaw(level,log);\n    sdsfree(log);\n    return 0;\n}\n\n/*\n * 脚本超时钩子\n */\nvoid luaMaskCountHook(lua_State *lua, lua_Debug *ar) {\n    long long elapsed;\n    REDIS_NOTUSED(ar);\n    REDIS_NOTUSED(lua);\n\n    // 计算已执行时间\n    elapsed = mstime() - server.lua_time_start;\n    // 执行已超时\n    if (elapsed >= server.lua_time_limit && server.lua_timedout == 0) {\n\n        // 打印 LOG\n        redisLog(REDIS_WARNING,\"Lua slow script detected: still in execution after %lld milliseconds. You can try killing the script using the SCRIPT KILL command.\",elapsed);\n\n        // 设置状态\n        server.lua_timedout = 1;\n\n        /* Once the script timeouts we reenter the event loop to permit others\n         * to call SCRIPT KILL or SHUTDOWN NOSAVE if needed. For this reason\n         * we need to mask the client executing the script from the event loop.\n         * If we don't do that the client may disconnect and could no longer be\n         * here when the EVAL command will return. */\n         // 当脚本运行超时时，将正在执行的客户端从读事件中移除\n         // 并允许其他客户端执行 SCRIPT KILL 或者 SHUTDOWN NOSAVE\n         aeDeleteFileEvent(server.el, server.lua_caller->fd, AE_READABLE);\n    }\n\n    // 在脚本上下文中，启动文件事件处理（等待 SCRIPT KILL 或 SHUTDOWN NOSAVE）\n    if (server.lua_timedout) processEventsWhileBlocked();\n    // 如果接到 SCRIPT KILL ，那么杀死脚本\n    if (server.lua_kill) {\n        redisLog(REDIS_WARNING,\"Lua script killed by user with SCRIPT KILL.\");\n        lua_pushstring(lua,\"Script killed by user with SCRIPT KILL...\");\n        lua_error(lua);\n    }\n}\n\n/*\n * 将指定函数以指定名字载入到 Lua 环境，并调用\n */\nvoid luaLoadLib(lua_State *lua, const char *libname, lua_CFunction luafunc) {\n  lua_pushcfunction(lua, luafunc);\n  lua_pushstring(lua, libname);\n  lua_call(lua, 1, 0);\n}\n\nLUALIB_API int (luaopen_cjson) (lua_State *L);\nLUALIB_API int (luaopen_struct) (lua_State *L);\nLUALIB_API int (luaopen_cmsgpack) (lua_State *L);\n\n/*\n * 将指定的库载入到 Lua 环境中\n */\nvoid luaLoadLibraries(lua_State *lua) {\n\n    // 基础库\n    luaLoadLib(lua, \"\", luaopen_base);\n\n    // table 库\n    luaLoadLib(lua, LUA_TABLIBNAME, luaopen_table);\n\n    // 字符串库\n    luaLoadLib(lua, LUA_STRLIBNAME, luaopen_string);\n\n    // 数学库\n    luaLoadLib(lua, LUA_MATHLIBNAME, luaopen_math);\n\n    // debug 库\n    luaLoadLib(lua, LUA_DBLIBNAME, luaopen_debug); \n\n    // cjson 库\n    luaLoadLib(lua, \"cjson\", luaopen_cjson);\n\n    // struct 库\n    luaLoadLib(lua, \"struct\", luaopen_struct);\n\n    // cmsgpack 库\n    luaLoadLib(lua, \"cmsgpack\", luaopen_cmsgpack);\n\n#if 0 /* Stuff that we don't load currently, for sandboxing concerns. */\n    luaLoadLib(lua, LUA_LOADLIBNAME, luaopen_package);\n    luaLoadLib(lua, LUA_OSLIBNAME, luaopen_os);\n#endif\n}\n\n/* Remove a functions that we don't want to expose to the Redis scripting\n * environment. \n *\n * 移除所有不想暴露给 Redis 脚本的函数\n */\nvoid luaRemoveUnsupportedFunctions(lua_State *lua) {\n    // 屏蔽 loadfile\n    lua_pushnil(lua);\n    lua_setglobal(lua,\"loadfile\");\n}\n\n/* This function installs metamethods in the global table _G that prevent\n * the creation of globals accidentally.\n *\n * 保护 Lua 脚本中的所有全局变量。\n *\n * It should be the last to be called in the scripting engine initialization\n * sequence, because it may interact with creation of globals. \n *\n * 这个函数应该在脚本环境初始化的最后执行。\n */\nvoid scriptingEnableGlobalsProtection(lua_State *lua) {\n    char *s[32];\n    sds code = sdsempty();\n    int j = 0;\n\n    /* strict.lua from: http://metalua.luaforge.net/src/lib/strict.lua.html.\n     * Modified to be adapted to Redis. */\n    s[j++]=\"local mt = {}\\n\";\n    s[j++]=\"setmetatable(_G, mt)\\n\";\n    s[j++]=\"mt.__newindex = function (t, n, v)\\n\";\n    s[j++]=\"  if debug.getinfo(2) then\\n\";\n    s[j++]=\"    local w = debug.getinfo(2, \\\"S\\\").what\\n\";\n    s[j++]=\"    if w ~= \\\"main\\\" and w ~= \\\"C\\\" then\\n\";\n    s[j++]=\"      error(\\\"Script attempted to create global variable '\\\"..tostring(n)..\\\"'\\\", 2)\\n\";\n    s[j++]=\"    end\\n\";\n    s[j++]=\"  end\\n\";\n    s[j++]=\"  rawset(t, n, v)\\n\";\n    s[j++]=\"end\\n\";\n    s[j++]=\"mt.__index = function (t, n)\\n\";\n    s[j++]=\"  if debug.getinfo(2) and debug.getinfo(2, \\\"S\\\").what ~= \\\"C\\\" then\\n\";\n    s[j++]=\"    error(\\\"Script attempted to access unexisting global variable '\\\"..tostring(n)..\\\"'\\\", 2)\\n\";\n    s[j++]=\"  end\\n\";\n    s[j++]=\"  return rawget(t, n)\\n\";\n    s[j++]=\"end\\n\";\n    s[j++]=NULL;\n\n    for (j = 0; s[j] != NULL; j++) code = sdscatlen(code,s[j],strlen(s[j]));\n    luaL_loadbuffer(lua,code,sdslen(code),\"@enable_strict_lua\");\n    lua_pcall(lua,0,0,0);\n    sdsfree(code);\n}\n\n/* Initialize the scripting environment.\n *\n * 初始化脚本环境。\n *\n * It is possible to call this function to reset the scripting environment\n * assuming that we call scriptingRelease() before.\n *\n * 这个函数也可以在 scriptingRelease() 执行之后使用，用于对脚本环境进行重置。\n *\n * See scriptingReset() for more information. \n *\n * 查看 scriptingReset() 以了解更多信息。\n */\nvoid scriptingInit(void) {\n\n    // 创建一个新的 lua 环境\n    lua_State *lua = lua_open();\n\n    // 载入内置函数库\n    luaLoadLibraries(lua);\n\n    // 屏蔽所有不想暴露给脚本环境的函数（目前只有 loadfile）\n    luaRemoveUnsupportedFunctions(lua);\n\n    /* Initialize a dictionary we use to map SHAs to scripts.\n     *\n     * 创建一个字典，用于将 SHA 校验码映射到脚本\n     *\n     * This is useful for replication, as we need to replicate EVALSHA\n     * as EVAL, so we need to remember the associated script. \n     *\n     * 因为 Redis 目前复制脚本的方法是将 EVALSHA 转换为 EVAL 来进行的，\n     * 所以程序需要保存和 SHA 校验值相对应的脚本。\n     */\n    server.lua_scripts = dictCreate(&shaScriptObjectDictType,NULL);\n\n    /* Register the redis commands table and fields */\n    // 创建 lua 表，并以给定名称将 C 函数注册到表格上\n    lua_newtable(lua);\n\n    /* redis.call */\n    /* 注册 redis.call */\n    lua_pushstring(lua,\"call\");\n    lua_pushcfunction(lua,luaRedisCallCommand);\n    lua_settable(lua,-3);\n\n    /* redis.pcall */\n    /* 注册 redis.pcall */\n    lua_pushstring(lua,\"pcall\");\n    lua_pushcfunction(lua,luaRedisPCallCommand);\n    lua_settable(lua,-3);\n\n    /* redis.log and log levels. */\n    /* 注册 redis.log 和 log levels. */\n    lua_pushstring(lua,\"log\");\n    lua_pushcfunction(lua,luaLogCommand);\n    lua_settable(lua,-3);\n\n    lua_pushstring(lua,\"LOG_DEBUG\");\n    lua_pushnumber(lua,REDIS_DEBUG);\n    lua_settable(lua,-3);\n\n    lua_pushstring(lua,\"LOG_VERBOSE\");\n    lua_pushnumber(lua,REDIS_VERBOSE);\n    lua_settable(lua,-3);\n\n    lua_pushstring(lua,\"LOG_NOTICE\");\n    lua_pushnumber(lua,REDIS_NOTICE);\n    lua_settable(lua,-3);\n\n    lua_pushstring(lua,\"LOG_WARNING\");\n    lua_pushnumber(lua,REDIS_WARNING);\n    lua_settable(lua,-3);\n\n    /* redis.sha1hex */\n    /* 注册 redis.sha1hex */\n    lua_pushstring(lua, \"sha1hex\");\n    lua_pushcfunction(lua, luaRedisSha1hexCommand);\n    lua_settable(lua, -3);\n\n    /* redis.error_reply and redis.status_reply */\n    /* 注册 redis.error_reply and redis.status_reply */\n    lua_pushstring(lua, \"error_reply\");\n    lua_pushcfunction(lua, luaRedisErrorReplyCommand);\n    lua_settable(lua, -3);\n    lua_pushstring(lua, \"status_reply\");\n    lua_pushcfunction(lua, luaRedisStatusReplyCommand);\n    lua_settable(lua, -3);\n\n    /* Finally set the table as 'redis' global var. */\n    // 将 table 设置为 redis 全局变量\n    lua_setglobal(lua,\"redis\");\n\n    /* Replace math.random and math.randomseed with our implementations. \n     *\n     * 使用 Redis 自己的函数来替换 Lua 原来的 math.random 和 math.randomseed 实现\n     *\n     * 使得随机数的生成带有确定性：对于同一个 seed ，总是产生相同的随机数序列\n     */\n    lua_getglobal(lua,\"math\");\n\n    lua_pushstring(lua,\"random\");\n    lua_pushcfunction(lua,redis_math_random);\n    lua_settable(lua,-3);\n\n    lua_pushstring(lua,\"randomseed\");\n    lua_pushcfunction(lua,redis_math_randomseed);\n    lua_settable(lua,-3);\n\n    lua_setglobal(lua,\"math\");\n\n    /* Add a helper function that we use to sort the multi bulk output of non\n     * deterministic commands, when containing 'false' elements. \n     *\n     * 辅助函数，用于对带有不确定性的 multi bulk reply 进行排序\n     */\n    {\n        char *compare_func =    \"function __redis__compare_helper(a,b)\\n\"\n                                \"  if a == false then a = '' end\\n\"\n                                \"  if b == false then b = '' end\\n\"\n                                \"  return a<b\\n\"\n                                \"end\\n\";\n        luaL_loadbuffer(lua,compare_func,strlen(compare_func),\"@cmp_func_def\");\n        lua_pcall(lua,0,0,0);\n    }\n\n    /* Add a helper function we use for pcall error reporting.\n     * Note that when the error is in the C function we want to report the\n     * information about the caller, that's what makes sense from the point\n     * of view of the user debugging a script. \n     *\n     * 创建一个用于报告 pcall 错误的报告函数\n     */\n    {\n        char *errh_func =       \"function __redis__err__handler(err)\\n\"\n                                \"  local i = debug.getinfo(2,'nSl')\\n\"\n                                \"  if i and i.what == 'C' then\\n\"\n                                \"    i = debug.getinfo(3,'nSl')\\n\"\n                                \"  end\\n\"\n                                \"  if i then\\n\"\n                                \"    return i.source .. ':' .. i.currentline .. ': ' .. err\\n\"\n                                \"  else\\n\"\n                                \"    return err\\n\"\n                                \"  end\\n\"\n                                \"end\\n\";\n        luaL_loadbuffer(lua,errh_func,strlen(errh_func),\"@err_handler_def\");\n        lua_pcall(lua,0,0,0);\n    }\n\n    /* Create the (non connected) client that we use to execute Redis commands\n     * inside the Lua interpreter.\n     *\n     * 因为 Redis 的命令只能通过客户端来执行，\n     * 所以创建一个无网络链接的伪客户端来专门执行 Lua 脚本中的 Redis 命令。\n     *\n     * Note: there is no need to create it again when this function is called\n     * by scriptingReset(). \n     *\n     * 伪客户端只需要创建一次即可，因为这个函数会被 scriptingReset() 调用。\n     */\n    if (server.lua_client == NULL) {\n        server.lua_client = createClient(-1);\n        server.lua_client->flags |= REDIS_LUA_CLIENT;\n    }\n\n    /* Lua beginners ofter don't use \"local\", this is likely to introduce\n     * subtle bugs in their code. To prevent problems we protect accesses\n     * to global variables. \n     *\n     * 为了避免全局变量被意外地修改，保护全局变量。\n     */\n    scriptingEnableGlobalsProtection(lua);\n\n    // 将 Lua 环境赋值到服务器属性中\n    server.lua = lua;\n}\n\n/* Release resources related to Lua scripting.\n *\n * 释放脚本环境的相关资源。\n *\n * This function is used in order to reset the scripting environment. \n *\n * 此函数用于重置 Lua 脚本环境。\n */\nvoid scriptingRelease(void) {\n\n    // 释放记录脚本的字典\n    dictRelease(server.lua_scripts);\n\n    // 关闭 Lua 环境\n    lua_close(server.lua);\n}\n\n/* \n * 重置脚本环境\n */\nvoid scriptingReset(void) {\n\n    // 释放环境\n    scriptingRelease();\n\n    // 初始化环境\n    scriptingInit();\n}\n\n/* Perform the SHA1 of the input string. We use this both for hashing script\n * bodies in order to obtain the Lua function name, and in the implementation\n * of redis.sha1().\n *\n * 此函数计算输入字符串的 SHA1 校验和\n *\n * 它用于计算函数名，也用于实现 redis.sha1()\n *\n * 'digest' should point to a 41 bytes buffer: 40 for SHA1 converted into an\n * hexadecimal number, plus 1 byte for null term. \n *\n * digest 参数应该指向一个 41 字节的缓存：\n *\n *  - 40 字节用于将 SHA1 转换为十六进制数字，\n *\n *  - 再加上 1 字节的 NULL 结尾符。\n */\nvoid sha1hex(char *digest, char *script, size_t len) {\n    SHA1_CTX ctx;\n    unsigned char hash[20];\n    char *cset = \"0123456789abcdef\";\n    int j;\n\n    SHA1Init(&ctx);\n    SHA1Update(&ctx,(unsigned char*)script,len);\n    SHA1Final(hash,&ctx);\n\n    for (j = 0; j < 20; j++) {\n        digest[j*2] = cset[((hash[j]&0xF0)>>4)];\n        digest[j*2+1] = cset[(hash[j]&0xF)];\n    }\n    digest[40] = '\\0';\n}\n\n/*\n * 将 Lua 的返回值转换为 Redis 回复\n */\nvoid luaReplyToRedisReply(redisClient *c, lua_State *lua) {\n    int t = lua_type(lua,-1);\n\n    switch(t) {\n    case LUA_TSTRING:\n        addReplyBulkCBuffer(c,(char*)lua_tostring(lua,-1),lua_strlen(lua,-1));\n        break;\n    case LUA_TBOOLEAN:\n        addReply(c,lua_toboolean(lua,-1) ? shared.cone : shared.nullbulk);\n        break;\n    case LUA_TNUMBER:\n        addReplyLongLong(c,(long long)lua_tonumber(lua,-1));\n        break;\n    case LUA_TTABLE:\n        /* We need to check if it is an array, an error, or a status reply.\n         * Error are returned as a single element table with 'err' field.\n         * Status replies are returned as single element table with 'ok' field */\n        lua_pushstring(lua,\"err\");\n        lua_gettable(lua,-2);\n        t = lua_type(lua,-1);\n        if (t == LUA_TSTRING) {\n            sds err = sdsnew(lua_tostring(lua,-1));\n            sdsmapchars(err,\"\\r\\n\",\"  \",2);\n            addReplySds(c,sdscatprintf(sdsempty(),\"-%s\\r\\n\",err));\n            sdsfree(err);\n            lua_pop(lua,2);\n            return;\n        }\n\n        lua_pop(lua,1);\n        lua_pushstring(lua,\"ok\");\n        lua_gettable(lua,-2);\n        t = lua_type(lua,-1);\n        if (t == LUA_TSTRING) {\n            sds ok = sdsnew(lua_tostring(lua,-1));\n            sdsmapchars(ok,\"\\r\\n\",\"  \",2);\n            addReplySds(c,sdscatprintf(sdsempty(),\"+%s\\r\\n\",ok));\n            sdsfree(ok);\n            lua_pop(lua,1);\n        } else {\n            void *replylen = addDeferredMultiBulkLength(c);\n            int j = 1, mbulklen = 0;\n\n            lua_pop(lua,1); /* Discard the 'ok' field value we popped */\n            while(1) {\n                lua_pushnumber(lua,j++);\n                lua_gettable(lua,-2);\n                t = lua_type(lua,-1);\n                if (t == LUA_TNIL) {\n                    lua_pop(lua,1);\n                    break;\n                }\n                luaReplyToRedisReply(c, lua);\n                mbulklen++;\n            }\n            setDeferredMultiBulkLength(c,replylen,mbulklen);\n        }\n        break;\n    default:\n        addReply(c,shared.nullbulk);\n    }\n    lua_pop(lua,1);\n}\n\n/* Set an array of Redis String Objects as a Lua array (table) stored into a\n * global variable. \n *\n * 创建一个数组，并将它设置为指定给定名字的全局变量\n */\nvoid luaSetGlobalArray(lua_State *lua, char *var, robj **elev, int elec) {\n    int j;\n\n    lua_newtable(lua);\n    for (j = 0; j < elec; j++) {\n        lua_pushlstring(lua,(char*)elev[j]->ptr,sdslen(elev[j]->ptr));\n        lua_rawseti(lua,-2,j+1);\n    }\n    lua_setglobal(lua,var);\n}\n\n/* Define a lua function with the specified function name and body.\n *\n * 根据给定函数名和代码体（body），创建 Lua 函数。\n *\n * The function name musts be a 2 characters long string, since all the\n * functions we defined in the Lua context are in the form:\n *\n * 所有函数名称的长度都必须大于 2 ，因为我们使用以下格式来创建函数名：\n *\n *   f_<hex sha1 sum>\n *\n * On success REDIS_OK is returned, and nothing is left on the Lua stack.\n *\n * 创建成功返回 REDIS_OK ，并且 Lua 栈中不会遗留任何数据。\n *\n * On error REDIS_ERR is returned and an appropriate error is set in the\n * client context. \n */\nint luaCreateFunction(redisClient *c, lua_State *lua, char *funcname, robj *body) {\n    sds funcdef = sdsempty();\n\n    // 组成函数定义\n    // 例子：\n    // function sha1_function_name ()\n    //     body\n    // end\n    funcdef = sdscat(funcdef,\"function \");\n    funcdef = sdscatlen(funcdef,funcname,42);\n    funcdef = sdscatlen(funcdef,\"() \",3);\n    funcdef = sdscatlen(funcdef,body->ptr,sdslen(body->ptr));\n    funcdef = sdscatlen(funcdef,\" end\",4);\n\n    // 将函数定义载入到 Lua 中（并编译），但并不执行该定义\n    if (luaL_loadbuffer(lua,funcdef,sdslen(funcdef),\"@user_script\")) {\n\n        // 如果编译出错，那么返回错误\n        addReplyErrorFormat(c,\"Error compiling script (new function): %s\\n\",\n            lua_tostring(lua,-1));\n        lua_pop(lua,1);\n        sdsfree(funcdef);\n\n        return REDIS_ERR;\n    }\n\n    // 释放函数定义内容\n    sdsfree(funcdef);\n\n    // 定义函数\n    if (lua_pcall(lua,0,0,0)) {\n        addReplyErrorFormat(c,\"Error running script (new function): %s\\n\",\n            lua_tostring(lua,-1));\n        lua_pop(lua,1);\n        return REDIS_ERR;\n    }\n\n    /* We also save a SHA1 -> Original script map in a dictionary\n     * so that we can replicate / write in the AOF all the\n     * EVALSHA commands as EVAL using the original script. \n     *\n     * 以 SHA1 值为键，脚本原始内容为值，映射到 server.lua_scripts 字典\n     *\n     * 对脚本进行复制，或者写入到 AOF 文件时使用\n     */\n    {\n        int retval = dictAdd(server.lua_scripts,\n                             // SHA1 值，不包括前缀 f_\n                             // body 为代码体\n                             sdsnewlen(funcname+2,40),body);\n        redisAssertWithInfo(c,NULL,retval == DICT_OK);\n        incrRefCount(body);\n    }\n\n    return REDIS_OK;\n}\n\nvoid evalGenericCommand(redisClient *c, int evalsha) {\n    lua_State *lua = server.lua;\n    char funcname[43];\n    long long numkeys;\n    int delhook = 0, err;\n\n    /* We want the same PRNG sequence at every call so that our PRNG is\n     * not affected by external state. \n     *\n     * 在每次执行 EVAL 时重置随机 seed ，从而保证可以生成相同的随机序列。\n     */\n    redisSrand48(0);\n\n    /* We set this flag to zero to remember that so far no random command\n     * was called. This way we can allow the user to call commands like\n     * SRANDMEMBER or RANDOMKEY from Lua scripts as far as no write command\n     * is called (otherwise the replication and AOF would end with non\n     * deterministic sequences).\n     *\n     * 两个变量：\n     *\n     *  - lua_random_dirty 记录脚本是否执行了随机命令\n     *\n     *  - lua_write_dirty 记录脚本是否进行了写入命令\n     *\n     * Thanks to this flag we'll raise an error every time a write command\n     * is called after a random command was used. \n     *\n     * 通过这两个变量，程序可以在脚本试图在执行随机命令之后执行写入时报错。\n     */\n    server.lua_random_dirty = 0;\n    server.lua_write_dirty = 0;\n\n    /* Get the number of arguments that are keys */\n    // 获取输入键的数量\n    if (getLongLongFromObjectOrReply(c,c->argv[2],&numkeys,NULL) != REDIS_OK)\n        return;\n\n    // 对键的正确性做一个快速检查\n    if (numkeys > (c->argc - 3)) {\n        addReplyError(c,\"Number of keys can't be greater than number of args\");\n        return;\n    }\n\n    /* We obtain the script SHA1, then check if this function is already\n     * defined into the Lua state \n     *\n     * 组合出函数的名字，例如 f_282297a0228f48cd3fc6a55de6316f31422f5d17\n     */\n    funcname[0] = 'f';\n    funcname[1] = '_';\n    if (!evalsha) {\n        /* Hash the code if this is an EVAL call */\n        // 如果执行的是 EVAL 命令，那么计算脚本的 SHA1 校验和\n        sha1hex(funcname+2,c->argv[1]->ptr,sdslen(c->argv[1]->ptr));\n    } else {\n        /* We already have the SHA if it is a EVALSHA */\n        // 如果执行的是 EVALSHA 命令，直接使用传入的 SHA1 值\n        int j;\n        char *sha = c->argv[1]->ptr;\n\n        /* Convert to lowercase. We don't use tolower since the function\n         * managed to always show up in the profiler output consuming\n         * a non trivial amount of time. */\n        for (j = 0; j < 40; j++)\n            funcname[j+2] = (sha[j] >= 'A' && sha[j] <= 'Z') ?\n                sha[j]+('a'-'A') : sha[j];\n        funcname[42] = '\\0';\n    }\n\n    /* Push the pcall error handler function on the stack. */\n    lua_getglobal(lua, \"__redis__err__handler\");\n\n    /* Try to lookup the Lua function */\n    // 根据函数名，在 Lua 环境中检查函数是否已经定义\n    lua_getglobal(lua, funcname);\n\n    if (lua_isnil(lua,-1)) {\n\n        // 没找到脚本相应的脚本函数\n\n        lua_pop(lua,1); /* remove the nil from the stack */\n        /* Function not defined... let's define it if we have the\n         * body of the function. If this is an EVALSHA call we can just\n         * return an error. */\n\n        // 脚本函数不存在，需要根据所执行的命令来做判断。。。\n\n        // 如果执行的是 EVALSHA ，返回脚本未找到错误\n        if (evalsha) {\n            lua_pop(lua,1); /* remove the error handler from the stack. */\n            addReply(c, shared.noscripterr);\n            return;\n        }\n\n        // 如果执行的是 EVAL ，那么创建新函数，然后将代码添加到脚本字典中\n        if (luaCreateFunction(c,lua,funcname,c->argv[1]) == REDIS_ERR) {\n            lua_pop(lua,1); /* remove the error handler from the stack. */\n            /* The error is sent to the client by luaCreateFunction()\n             * itself when it returns REDIS_ERR. */\n            return;\n        }\n        /* Now the following is guaranteed to return non nil */\n        lua_getglobal(lua, funcname);\n        redisAssert(!lua_isnil(lua,-1));\n    }\n\n    /* Populate the argv and keys table accordingly to the arguments that\n     * EVAL received. \n     *\n     * 将用户传入的键数组和参数数组设为 Lua 环境中的 KEYS 全局变量和 ARGV 全局变量\n     */\n    luaSetGlobalArray(lua,\"KEYS\",c->argv+3,numkeys);\n    luaSetGlobalArray(lua,\"ARGV\",c->argv+3+numkeys,c->argc-3-numkeys);\n\n    /* Select the right DB in the context of the Lua client */\n    // 为 Lua 所属的伪客户端设置数据库\n    selectDb(server.lua_client,c->db->id);\n    \n    /* Set a hook in order to be able to stop the script execution if it\n     * is running for too much time.\n     *\n     * 设置一个钩子，用于在脚本运行时间过长时，停止脚本并等待用户的 SCRIPT 命令\n     *\n     * We set the hook only if the time limit is enabled as the hook will\n     * make the Lua script execution slower. \n     *\n     * 只有时间限制功能被开启时才使用钩子，因为它会拖慢脚本的运行速度。\n     */\n    // 调用客户端\n    server.lua_caller = c;\n    // 脚本开始时间\n    server.lua_time_start = mstime();\n    // 是否杀死脚本\n    server.lua_kill = 0;\n    // 只在开启了时间限制功能的时候，才使用钩子\n    if (server.lua_time_limit > 0 && server.masterhost == NULL) {\n        lua_sethook(lua,luaMaskCountHook,LUA_MASKCOUNT,100000);\n        delhook = 1;\n    }\n\n    /* At this point whether this script was never seen before or if it was\n     * already defined, we can call it. We have zero arguments and expect\n     * a single return value. */\n    // 执行脚本对应的 Lua 函数\n    err = lua_pcall(lua,0,1,-2);\n\n    /* Perform some cleanup that we need to do both on error and success. \n     *\n     * 执行一些无论执行是否成功都要执行的清理工作\n     */\n    // 关闭超时钩子\n    if (delhook) lua_sethook(lua,luaMaskCountHook,0,0); /* Disable hook */\n    // 脚本执行已超时\n    if (server.lua_timedout) {\n            // 清除超时 FLAG\n        server.lua_timedout = 0;\n        /* Restore the readable handler that was unregistered when the\n         * script timeout was detected. \n         *\n         * 将超时钩子里删除的读事件重新加上，\n         * 使得服务器可以继续接受当前客户端发来的命令请求\n         */\n        aeCreateFileEvent(server.el,c->fd,AE_READABLE,\n                          readQueryFromClient,c);\n    }\n\n    // 清空调用者\n    server.lua_caller = NULL;\n\n    // 因为客户端的当前数据库在脚本执行之后可能已经改变\n    // 所以这里需要更新客户端\n    selectDb(c,server.lua_client->db->id); /* set DB ID from Lua client */\n\n    /* Call the Lua garbage collector from time to time to avoid a\n     * full cycle performed by Lua, which adds too latency.\n     *\n     * The call is performed every LUA_GC_CYCLE_PERIOD executed commands\n     * (and for LUA_GC_CYCLE_PERIOD collection steps) because calling it\n     * for every command uses too much CPU. */\n    #define LUA_GC_CYCLE_PERIOD 50\n    {\n        static long gc_count = 0;\n\n        gc_count++;\n        if (gc_count == LUA_GC_CYCLE_PERIOD) {\n            lua_gc(lua,LUA_GCSTEP,LUA_GC_CYCLE_PERIOD);\n            gc_count = 0;\n        }\n    }\n\n    // 检查脚本运行是否出错\n    if (err) {\n        // 出错\n        addReplyErrorFormat(c,\"Error running script (call to %s): %s\\n\",\n            funcname, lua_tostring(lua,-1));\n        lua_pop(lua,2); /* Consume the Lua reply and remove error handler. */\n    } else {\n        /* On success convert the Lua return value into Redis protocol, and\n         * send it to * the client.\n         * 执行成功，将结果转换回 Redis 回复，并返回给客户端 */\n        luaReplyToRedisReply(c,lua); /* Convert and consume the reply. */\n        lua_pop(lua,1); /* Remove the error handler. */\n    }\n\n    /* EVALSHA should be propagated to Slave and AOF file as full EVAL, unless\n     * we are sure that the script was already in the context of all the\n     * attached slaves *and* the current AOF file if enabled.\n     *\n     * 除非程序确认脚本已经存在于所有 slave 和 AOF 文件的上下文中，\n     * 否则在传播脚本到 slave 和 AOF 文件时，\n     * 必须将 EVALSHA 当作 EVAL 来传播。\n     *\n     * To do so we use a cache of SHA1s of scripts that we already propagated\n     * as full EVAL, that's called the Replication Script Cache.\n     *\n     * 为此，服务器使用一个字典来作为已传播脚本的缓存，\n     * 这个缓存称之为 Replication Script Cache ，\n     * 缓存字典的键为 SHA1 ，值为 NULL （一个集合）。\n     *\n     * For repliation, everytime a new slave attaches to the master, we need to\n     * flush our cache of scripts that can be replicated as EVALSHA, while\n     * for AOF we need to do so every time we rewrite the AOF file. \n     *\n     * 每次有一个新的 slave 连上主服务器时，\n     * 或者每次进行 AOF 重写时，程序清空缓存字典\n     */\n    if (evalsha) {\n        // replicationScriptCacheExists 如果返回 1 \n        // 那么表示所有 slave 和 AOF 文件中都知道这个脚本\n        // 如果返回 0 ，那么表示这个脚本未被传播到所有 slave 和 AOF 文件中\n        if (!replicationScriptCacheExists(c->argv[1]->ptr)) {\n            /* This script is not in our script cache, replicate it as\n             * EVAL, then add it into the script cache, as from now on\n             * slaves and AOF know about it. */\n            // 将这个脚本加入脚本缓存，并且将以 EVAL 命令的方式传播它\n            // 从现在开始， slaves 和 AOF 文件都知道它\n\n            // 取出脚本 body\n            robj *script = dictFetchValue(server.lua_scripts,c->argv[1]->ptr);\n\n            // 将脚本的 SHA1 值加入到缓存\n            replicationScriptCacheAdd(c->argv[1]->ptr);\n            redisAssertWithInfo(c,NULL,script != NULL);\n            // 将 EVALSHA 命令改写为 EVAL\n            rewriteClientCommandArgument(c,0,\n                resetRefCount(createStringObject(\"EVAL\",4)));\n            rewriteClientCommandArgument(c,1,script);\n            forceCommandPropagation(c,REDIS_PROPAGATE_REPL|REDIS_PROPAGATE_AOF);\n        }\n    }\n}\n\nvoid evalCommand(redisClient *c) {\n    evalGenericCommand(c,0);\n}\n\nvoid evalShaCommand(redisClient *c) {\n    if (sdslen(c->argv[1]->ptr) != 40) {\n        /* We know that a match is not possible if the provided SHA is\n         * not the right length. So we return an error ASAP, this way\n         * evalGenericCommand() can be implemented without string length\n         * sanity check */\n        addReply(c, shared.noscripterr);\n        return;\n    }\n    evalGenericCommand(c,1);\n}\n\n/* We replace math.random() with our implementation that is not affected\n * by specific libc random() implementations and will output the same sequence\n * (for the same seed) in every arch. \n *\n * 通过使用 Redis 自己的实现，在任何架构中，\n * 都可以在给定相同 seed 的情况下产生相同的伪随机序列串\n */\n\n/* The following implementation is the one shipped with Lua itself but with\n * rand() replaced by redisLrand48(). */\nint redis_math_random (lua_State *L) {\n  /* the `%' avoids the (rare) case of r==1, and is needed also because on\n     some systems (SunOS!) `rand()' may return a value larger than RAND_MAX */\n  lua_Number r = (lua_Number)(redisLrand48()%REDIS_LRAND48_MAX) /\n                                (lua_Number)REDIS_LRAND48_MAX;\n  switch (lua_gettop(L)) {  /* check number of arguments */\n    case 0: {  /* no arguments */\n      lua_pushnumber(L, r);  /* Number between 0 and 1 */\n      break;\n    }\n    case 1: {  /* only upper limit */\n      int u = luaL_checkint(L, 1);\n      luaL_argcheck(L, 1<=u, 1, \"interval is empty\");\n      lua_pushnumber(L, floor(r*u)+1);  /* int between 1 and `u' */\n      break;\n    }\n    case 2: {  /* lower and upper limits */\n      int l = luaL_checkint(L, 1);\n      int u = luaL_checkint(L, 2);\n      luaL_argcheck(L, l<=u, 2, \"interval is empty\");\n      lua_pushnumber(L, floor(r*(u-l+1))+l);  /* int between `l' and `u' */\n      break;\n    }\n    default: return luaL_error(L, \"wrong number of arguments\");\n  }\n  return 1;\n}\n\nint redis_math_randomseed (lua_State *L) {\n  redisSrand48(luaL_checkint(L, 1));\n  return 0;\n}\n\n/* ---------------------------------------------------------------------------\n * SCRIPT command for script environment introspection and control\n * ------------------------------------------------------------------------- */\n\nvoid scriptCommand(redisClient *c) {\n\n    // SCRIPT FLUSH 命令\n    if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,\"flush\")) {\n        scriptingReset();\n        addReply(c,shared.ok);\n        replicationScriptCacheFlush();\n        server.dirty++; /* Propagating this command is a good idea. */\n\n    // SCRIPT EXISTS 命令\n    } else if (c->argc >= 2 && !strcasecmp(c->argv[1]->ptr,\"exists\")) {\n        int j;\n\n        addReplyMultiBulkLen(c, c->argc-2);\n        for (j = 2; j < c->argc; j++) {\n            if (dictFind(server.lua_scripts,c->argv[j]->ptr))\n                addReply(c,shared.cone);\n            else\n                addReply(c,shared.czero);\n        }\n\n    // SCRIPT LOAD 命令\n    } else if (c->argc == 3 && !strcasecmp(c->argv[1]->ptr,\"load\")) {\n        char funcname[43];\n        sds sha;\n\n        // 函数名前缀\n        funcname[0] = 'f';\n        funcname[1] = '_';\n\n        // 计算脚本的 SHA1 值\n        sha1hex(funcname+2,c->argv[2]->ptr,sdslen(c->argv[2]->ptr));\n        sha = sdsnewlen(funcname+2,40);\n\n        // 根据 SHA1 值，在字典中查找脚本\n        // 如果脚本未定义，在 Lua 中创建新函数\n        if (dictFind(server.lua_scripts,sha) == NULL) {\n            if (luaCreateFunction(c,server.lua,funcname,c->argv[2])\n                    == REDIS_ERR) {\n                sdsfree(sha);\n                return;\n            }\n        }\n\n        // 返回脚本的 SHA1 值\n        addReplyBulkCBuffer(c,funcname+2,40);\n\n        sdsfree(sha);\n\n        forceCommandPropagation(c,REDIS_PROPAGATE_REPL|REDIS_PROPAGATE_AOF);\n\n    // SCRIPT KILL 命令\n    } else if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,\"kill\")) {\n        if (server.lua_caller == NULL) {\n            addReplySds(c,sdsnew(\"-NOTBUSY No scripts in execution right now.\\r\\n\"));\n        } else if (server.lua_write_dirty) {\n            addReplySds(c,sdsnew(\"-UNKILLABLE Sorry the script already executed write commands against the dataset. You can either wait the script termination or kill the server in a hard way using the SHUTDOWN NOSAVE command.\\r\\n\"));\n        } else {\n            server.lua_kill = 1;\n            addReply(c,shared.ok);\n        }\n\n    } else {\n        addReplyError(c, \"Unknown SCRIPT subcommand or wrong # of args.\");\n    }\n}\n"
  },
  {
    "path": "src/sds.c",
    "content": "/* SDSLib, A C dynamic strings library\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#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <ctype.h>\n#include <assert.h>\n#include \"sds.h\"\n#include \"zmalloc.h\"\n\n/*\n * 根据给定的初始化字符串 init 和字符串长度 initlen\n * 创建一个新的 sds\n *\n * 参数\n *  init ：初始化字符串指针\n *  initlen ：初始化字符串的长度\n *\n * 返回值\n *  sds ：创建成功返回 sdshdr 相对应的 sds\n *        创建失败返回 NULL\n *\n * 复杂度\n *  T = O(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\n    struct sdshdr *sh;\n\n    // 根据是否有初始化内容，选择适当的内存分配方式\n    // T = O(N)\n    if (init) {\n        // zmalloc 不初始化所分配的内存\n        sh = zmalloc(sizeof(struct sdshdr)+initlen+1);\n    } else {\n        // zcalloc 将分配的内存全部初始化为 0\n        sh = zcalloc(sizeof(struct sdshdr)+initlen+1);\n    }\n\n    // 内存分配失败，返回\n    if (sh == NULL) return NULL;\n\n    // 设置初始化长度\n    sh->len = initlen;\n    // 新 sds 不预留任何空间\n    sh->free = 0;\n    // 如果有指定初始化内容，将它们复制到 sdshdr 的 buf 中\n    // T = O(N)\n    if (initlen && init)\n        memcpy(sh->buf, init, initlen);\n    // 以 \\0 结尾\n    sh->buf[initlen] = '\\0';\n\n    // 返回 buf 部分，而不是整个 sdshdr\n    return (char*)sh->buf;\n}\n\n/*\n * 创建并返回一个只保存了空字符串 \"\" 的 sds\n *\n * 返回值\n *  sds ：创建成功返回 sdshdr 相对应的 sds\n *        创建失败返回 NULL\n *\n * 复杂度\n *  T = O(1)\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/*\n * 根据给定字符串 init ，创建一个包含同样字符串的 sds\n *\n * 参数\n *  init ：如果输入为 NULL ，那么创建一个空白 sds\n *         否则，新创建的 sds 中包含和 init 内容相同字符串\n *\n * 返回值\n *  sds ：创建成功返回 sdshdr 相对应的 sds\n *        创建失败返回 NULL\n *\n * 复杂度\n *  T = O(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/*\n * 复制给定 sds 的副本\n *\n * 返回值\n *  sds ：创建成功返回输入 sds 的副本\n *        创建失败返回 NULL\n *\n * 复杂度\n *  T = O(N)\n */\n/* Duplicate an sds string. */\nsds sdsdup(const sds s) {\n    return sdsnewlen(s, sdslen(s));\n}\n\n/*\n * 释放给定的 sds\n *\n * 复杂度\n *  T = O(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    zfree(s-sizeof(struct sdshdr));\n}\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(struct sdshdr)));\n    int reallen = strlen(s);\n    sh->free += (sh->len-reallen);\n    sh->len = reallen;\n}\n\n/*\n * 在不释放 SDS 的字符串空间的情况下，\n * 重置 SDS 所保存的字符串为空字符串。\n *\n * 复杂度\n *  T = O(1)\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\n    // 取出 sdshdr\n    struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));\n\n    // 重新计算属性\n    sh->free += sh->len;\n    sh->len = 0;\n\n    // 将结束符放到最前面（相当于惰性地删除 buf 中的内容）\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. */\n/*\n * 对 sds 中 buf 的长度进行扩展，确保在函数执行之后，\n * buf 至少会有 addlen + 1 长度的空余空间\n * （额外的 1 字节是为 \\0 准备的）\n *\n * 返回值\n *  sds ：扩展成功返回扩展后的 sds\n *        扩展失败返回 NULL\n *\n * 复杂度\n *  T = O(N)\n */\nsds sdsMakeRoomFor(sds s, size_t addlen) {\n\n    struct sdshdr *sh, *newsh;\n\n    // 获取 s 目前的空余空间长度\n    size_t free = sdsavail(s);\n\n    size_t len, newlen;\n\n    // s 目前的空余空间已经足够，无须再进行扩展，直接返回\n    if (free >= addlen) return s;\n\n    // 获取 s 目前已占用空间的长度\n    len = sdslen(s);\n    sh = (void*) (s-(sizeof(struct sdshdr)));\n\n    // s 最少需要的长度\n    newlen = (len+addlen);\n\n    // 根据新长度，为 s 分配新空间所需的大小\n    if (newlen < SDS_MAX_PREALLOC)\n        // 如果新长度小于 SDS_MAX_PREALLOC \n        // 那么为它分配两倍于所需长度的空间\n        newlen *= 2;\n    else\n        // 否则，分配长度为目前长度加上 SDS_MAX_PREALLOC\n        newlen += SDS_MAX_PREALLOC;\n    // T = O(N)\n    newsh = zrealloc(sh, sizeof(struct sdshdr)+newlen+1);\n\n    // 内存不足，分配失败，返回\n    if (newsh == NULL) return NULL;\n\n    // 更新 sds 的空余长度\n    newsh->free = newlen - len;\n\n    // 返回 sds\n    return newsh->buf;\n}\n\n/*\n * 回收 sds 中的空闲空间，\n * 回收不会对 sds 中保存的字符串内容做任何修改。\n *\n * 返回值\n *  sds ：内存调整后的 sds\n *\n * 复杂度\n *  T = O(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(struct sdshdr)));\n\n    // 进行内存重分配，让 buf 的长度仅仅足够保存字符串内容\n    // T = O(N)\n    sh = zrealloc(sh, sizeof(struct sdshdr)+sh->len+1);\n\n    // 空余空间为 0\n    sh->free = 0;\n\n    return sh->buf;\n}\n\n/*\n * 返回给定 sds 分配的内存字节数\n *\n * 复杂度\n *  T = O(1)\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(struct sdshdr)));\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 * 根据 incr 参数，增加 sds 的长度，缩减空余空间，\n * 并将 \\0 放到新字符串的尾端\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 * 这个函数是在调用 sdsMakeRoomFor() 对字符串进行扩展，\n * 然后用户在字符串尾部写入了某些内容之后，\n * 用来正确更新 free 和 len 属性的。\n *\n * Note: it is possible to use a negative increment in order to\n * right-trim the string.\n *\n * 如果 incr 参数为负数，那么对字符串进行右截断操作。\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 * 以下是 sdsIncrLen 的用例：\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 *\n * 复杂度\n *  T = O(1)\n */\nvoid sdsIncrLen(sds s, int incr) {\n    struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));\n\n    // 确保 sds 空间足够\n    assert(sh->free >= incr);\n\n    // 更新属性\n    sh->len += incr;\n    sh->free -= incr;\n\n    // 这个 assert 其实可以忽略\n    // 因为前一个 assert 已经确保 sh->free - incr >= 0 了\n    assert(sh->free >= 0);\n\n    // 放置新的结尾符号\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. */\n/*\n * 将 sds 扩充至指定长度，未使用的空间以 0 字节填充。\n *\n * 返回值\n *  sds ：扩充成功返回新 sds ，失败返回 NULL\n *\n * 复杂度：\n *  T = O(N)\n */\nsds sdsgrowzero(sds s, size_t len) {\n    struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr)));\n    size_t totlen, curlen = sh->len;\n\n    // 如果 len 比字符串的现有长度小，\n    // 那么直接返回，不做动作\n    if (len <= curlen) return s;\n\n    // 扩展 sds\n    // T = O(N)\n    s = sdsMakeRoomFor(s,len-curlen);\n    // 如果内存不足，直接返回\n    if (s == NULL) return NULL;\n\n    /* Make sure added region doesn't contain garbage */\n    // 将新分配的空间用 0 填充，防止出现垃圾内容\n    // T = O(N)\n    sh = (void*)(s-(sizeof(struct sdshdr)));\n    memset(s+curlen,0,(len-curlen+1)); /* also set trailing \\0 byte */\n\n    // 更新属性\n    totlen = sh->len+sh->free;\n    sh->len = len;\n    sh->free = totlen-sh->len;\n\n    // 返回新的 sds\n    return s;\n}\n\n/*\n * 将长度为 len 的字符串 t 追加到 sds 的字符串末尾\n *\n * 返回值\n *  sds ：追加成功返回新 sds ，失败返回 NULL\n *\n * 复杂度\n *  T = O(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    \n    struct sdshdr *sh;\n    \n    // 原有字符串长度\n    size_t curlen = sdslen(s);\n\n    // 扩展 sds 空间\n    // T = O(N)\n    s = sdsMakeRoomFor(s,len);\n\n    // 内存不足？直接返回\n    if (s == NULL) return NULL;\n\n    // 复制 t 中的内容到字符串后部\n    // T = O(N)\n    sh = (void*) (s-(sizeof(struct sdshdr)));\n    memcpy(s+curlen, t, len);\n\n    // 更新属性\n    sh->len = curlen+len;\n    sh->free = sh->free-len;\n\n    // 添加新结尾符号\n    s[curlen+len] = '\\0';\n\n    // 返回新 sds\n    return s;\n}\n\n/*\n * 将给定字符串 t 追加到 sds 的末尾\n * \n * 返回值\n *  sds ：追加成功返回新 sds ，失败返回 NULL\n *\n * 复杂度\n *  T = O(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/*\n * 将另一个 sds 追加到一个 sds 的末尾\n * \n * 返回值\n *  sds ：追加成功返回新 sds ，失败返回 NULL\n *\n * 复杂度\n *  T = O(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/*\n * 将字符串 t 的前 len 个字符复制到 sds s 当中，\n * 并在字符串的最后添加终结符。\n *\n * 如果 sds 的长度少于 len 个字符，那么扩展 sds\n *\n * 复杂度\n *  T = O(N)\n *\n * 返回值\n *  sds ：复制成功返回新的 sds ，否则返回 NULL\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\n    struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));\n\n    // sds 现有 buf 的长度\n    size_t totlen = sh->free+sh->len;\n\n    // 如果 s 的 buf 长度不满足 len ，那么扩展它\n    if (totlen < len) {\n        // T = O(N)\n        s = sdsMakeRoomFor(s,len-sh->len);\n        if (s == NULL) return NULL;\n        sh = (void*) (s-(sizeof(struct sdshdr)));\n        totlen = sh->free+sh->len;\n    }\n\n    // 复制内容\n    // T = O(N)\n    memcpy(s, t, len);\n\n    // 添加终结符号\n    s[len] = '\\0';\n\n    // 更新属性\n    sh->len = len;\n    sh->free = totlen-len;\n\n    // 返回新的 sds\n    return s;\n}\n\n/*\n * 将字符串复制到 sds 当中，\n * 覆盖原有的字符。\n *\n * 如果 sds 的长度少于字符串的长度，那么扩展 sds 。\n *\n * 复杂度\n *  T = O(N)\n *\n * 返回值\n *  sds ：复制成功返回新的 sds ，否则返回 NULL\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/* Create an sds string from a long long value. It is much faster than:\n *\n * sdscatprintf(sdsempty(),\"%lld\\n\", value);\n */\n// 根据输入的 long long 值 value ，创建一个 SDS\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/* \n * 打印函数，被 sdscatprintf 所调用\n *\n * T = O(N^2)\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 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 = zmalloc(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        // T = O(N)\n        vsnprintf(buf, buflen, fmt, cpy);\n        if (buf[buflen-2] != '\\0') {\n            if (buf != staticbuf) zfree(buf);\n            buflen *= 2;\n            buf = zmalloc(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) zfree(buf);\n    return t;\n}\n\n/*\n * 打印任意数量个字符串，并将这些字符串追加到给定 sds 的末尾\n *\n * T = O(N^2)\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 = sdsempty(\"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 = O(N^2)\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    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        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 (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                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 (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 * 对 sds 左右两端进行修剪，清除其中 cset 指定的所有字符\n *\n * 比如 sdsstrim(xxyyabcyyxy, \"xy\") 将返回 \"abc\"\n *\n * 复杂性：\n *  T = O(M*N)，M 为 SDS 长度， N 为 cset 长度。\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 */\nsds sdstrim(sds s, const char *cset) {\n    struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));\n    char *start, *end, *sp, *ep;\n    size_t len;\n\n    // 设置和记录指针\n    sp = start = s;\n    ep = end = s+sdslen(s)-1;\n\n    // 修剪, T = O(N^2)\n    while(sp <= end && strchr(cset, *sp)) sp++;\n    while(ep > start && strchr(cset, *ep)) ep--;\n\n    // 计算 trim 完毕之后剩余的字符串长度\n    len = (sp > ep) ? 0 : ((ep-sp)+1);\n    \n    // 如果有需要，前移字符串内容\n    // T = O(N)\n    if (sh->buf != sp) memmove(sh->buf, sp, len);\n\n    // 添加终结符\n    sh->buf[len] = '\\0';\n\n    // 更新属性\n    sh->free = sh->free+(sh->len-len);\n    sh->len = len;\n\n    // 返回修剪后的 sds\n    return s;\n}\n\n/*\n * 按索引对截取 sds 字符串的其中一段\n * start 和 end 都是闭区间（包含在内）\n *\n * 索引从 0 开始，最大为 sdslen(s) - 1\n * 索引可以是负数， sdslen(s) - 1 == -1\n *\n * 复杂度\n *  T = O(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(struct sdshdr)));\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\n    // 如果有需要，对字符串进行移动\n    // T = O(N)\n    if (start && newlen) memmove(sh->buf, sh->buf+start, newlen);\n\n    // 添加终结符\n    sh->buf[newlen] = 0;\n\n    // 更新属性\n    sh->free = sh->free+(sh->len-newlen);\n    sh->len = newlen;\n}\n\n/*\n * 将 sds 字符串中的所有字符转换为小写\n *\n * T = O(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/*\n * 将 sds 字符串中的所有字符转换为大写\n *\n * T = O(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/*\n * 对比两个 sds ， strcmp 的 sds 版本\n *\n * 返回值\n *  int ：相等返回 0 ，s1 较大返回正数， s2 较大返回负数\n *\n * T = O(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\n    if (cmp == 0) return l1-l2;\n\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 * 使用分隔符 sep 对 s 进行分割，返回一个 sds 字符串的数组。\n * *count 会被设置为返回数组元素的数量。\n *\n * On out of memory, zero length string, zero length\n * separator, NULL is returned.\n *\n * 如果出现内存不足、字符串长度为 0 或分隔符长度为 0\n * 的情况，返回 NULL\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 * 注意分隔符可以的是包含多个字符的字符串\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 *\n * 这个函数接受 len 参数，因此它是二进制安全的。\n * （文档中提到的 sdssplit() 已废弃）\n *\n * T = O(N^2)\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 = zmalloc(sizeof(sds)*slots);\n    if (tokens == NULL) return NULL;\n\n    if (len == 0) {\n        *count = 0;\n        return tokens;\n    }\n    \n    // T = O(N^2)\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 = zrealloc(tokens,sizeof(sds)*slots);\n            if (newtokens == NULL) goto cleanup;\n            tokens = newtokens;\n        }\n        /* search the separator */\n        // T = O(N)\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        zfree(tokens);\n        *count = 0;\n        return NULL;\n    }\n}\n\n/*\n * 释放 tokens 数组中 count 个 sds\n *\n * T = O(N^2)\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    zfree(tokens);\n}\n\n/*\n * 将长度为 len 的字符串 p 以带引号（quoted）的格式\n * 追加到给定 sds 的末尾\n *\n * T = O(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\n    s = sdscatlen(s,\"\\\"\",1);\n\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\n    return sdscatlen(s,\"\\\"\",1);\n}\n\n/* Helper function for sdssplitargs() that returns non zero if 'c'\n * is a valid hex digit. */\n/*\n * 如果 c 为十六进制符号的其中一个，返回正数\n *\n * T = O(1)\n */\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 */\n/*\n * 将十六进制符号转换为 10 进制\n *\n * T = O(1)\n */\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 * 将一行文本分割成多个参数，每个参数可以有以下的类编程语言 REPL 格式：\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 * 参数的个数会保存在 *argc 中，函数返回一个 sds 数组。\n *\n * The caller should free the resulting array of sds strings with\n * sdsfreesplitres().\n *\n * 调用者应该使用 sdsfreesplitres() 来释放函数返回的 sds 数组。\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 * sdscatrepr() 可以将一个字符串转换为一个带引号（quoted）的字符串，\n * 这个带引号的字符串可以被 sdssplitargs() 分析。\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 *\n * 即使输入出现空字符串， NULL ，或者输入带有未对应的括号，\n * 函数都会将已成功处理的字符串先返回。\n *\n * 这个函数主要用于 config.c 中对配置文件进行分析。\n * 例子：\n *  sds *arr = sdssplitargs(\"timeout 10086\\r\\nport 123321\\r\\n\");\n * 会得出\n *  arr[0] = \"timeout\"\n *  arr[1] = \"10086\"\n *  arr[2] = \"port\"\n *  arr[3] = \"123321\"\n *\n * T = O(N^2)\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\n        /* skip blanks */\n        // 跳过空白\n        // T = O(N)\n        while(*p && isspace(*p)) p++;\n\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\n            // T = O(N)\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            // T = O(N)\n            vector = zrealloc(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 = zmalloc(sizeof(void*));\n            return vector;\n        }\n    }\n\nerr:\n    while((*argc)--)\n        sdsfree(vector[*argc]);\n    zfree(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 * 将字符串 s 中，\n * 所有在 from 中出现的字符，替换成 to 中的字符\n *\n * For instance: sdsmapchars(mystring, \"ho\", \"01\", 2)\n * will have the effect of turning the string \"hello\" into \"0ell1\".\n *\n * 比如调用 sdsmapchars(mystring, \"ho\", \"01\", 2)\n * 就会将 \"hello\" 转换为 \"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. \n * 因为无须对 sds 进行大小调整，\n * 所以返回的 sds 输入的 sds 一样\n *\n * T = O(N^2)\n */\nsds sdsmapchars(sds s, const char *from, const char *to, size_t setlen) {\n    size_t j, i, l = sdslen(s);\n\n    // 遍历输入字符串\n    for (j = 0; j < l; j++) {\n        // 遍历映射\n        for (i = 0; i < setlen; i++) {\n            // 替换字符串\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#ifdef SDS_TEST_MAIN\n#include <stdio.h>\n#include \"testhelp.h\"\n#include \"limits.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(\"--\");\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\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(\"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": "src/sds.h",
    "content": "/* SDSLib, A C dynamic strings library\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#ifndef __SDS_H\n#define __SDS_H\n\n/*\n * 最大预分配长度\n */\n#define SDS_MAX_PREALLOC (1024*1024)\n\n#include <sys/types.h>\n#include <stdarg.h>\n\n/*\n * 类型别名，用于指向 sdshdr 的 buf 属性\n */\ntypedef char *sds;\n\n/*\n * 保存字符串对象的结构\n */\nstruct sdshdr {\n    \n    // buf 中已占用空间的长度\n    int len;\n\n    // buf 中剩余可用空间的长度\n    int free;\n\n    // 数据空间\n    char buf[];\n};\n\n/*\n * 返回 sds 实际保存的字符串的长度\n *\n * T = O(1)\n */\nstatic inline size_t sdslen(const sds s) {\n    struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr)));\n    return sh->len;\n}\n\n/*\n * 返回 sds 可用空间的长度\n *\n * T = O(1)\n */\nstatic inline size_t sdsavail(const sds s) {\n    struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr)));\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, ...);\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);\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": "src/sentinel.c",
    "content": "/* Redis Sentinel implementation\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#include \"redis.h\"\n#include \"hiredis.h\"\n#include \"async.h\"\n\n#include <ctype.h>\n#include <arpa/inet.h>\n#include <sys/socket.h>\n#include <sys/wait.h>\n#include <fcntl.h>\n\nextern char **environ;\n\n// sentinel 的默认端口号\n#define REDIS_SENTINEL_PORT 26379\n\n/* ======================== Sentinel global state =========================== */\n\n/* Address object, used to describe an ip:port pair. */\n/* 地址对象，用于保存 IP 地址和端口 */\ntypedef struct sentinelAddr {\n    char *ip;\n    int port;\n} sentinelAddr;\n\n/* A Sentinel Redis Instance object is monitoring. */\n/* 每个被监视的 Redis 实例都会创建一个 sentinelRedisInstance 结构\n * 而每个结构的 flags 值会是以下常量的一个或多个的并 */\n// 实例是一个主服务器\n#define SRI_MASTER  (1<<0)\n// 实例是一个从服务器\n#define SRI_SLAVE   (1<<1)\n// 实例是一个 Sentinel\n#define SRI_SENTINEL (1<<2)\n// 实例已断线\n#define SRI_DISCONNECTED (1<<3)\n// 实例已处于 SDOWN 状态\n#define SRI_S_DOWN (1<<4)   /* Subjectively down (no quorum). */\n// 实例已处于 ODOWN 状态\n#define SRI_O_DOWN (1<<5)   /* Objectively down (confirmed by others). */\n// Sentinel 认为主服务器已下线\n#define SRI_MASTER_DOWN (1<<6) /* A Sentinel with this flag set thinks that\n                                   its master is down. */\n// 正在对主服务器进行故障迁移\n#define SRI_FAILOVER_IN_PROGRESS (1<<7) /* Failover is in progress for\n                                           this master. */\n// 实例是被选中的新主服务器（目前仍是从服务器）\n#define SRI_PROMOTED (1<<8)            /* Slave selected for promotion. */\n// 向从服务器发送 SLAVEOF 命令，让它们转向复制新主服务器\n#define SRI_RECONF_SENT (1<<9)     /* SLAVEOF <newmaster> sent. */\n// 从服务器正在与新主服务器进行同步\n#define SRI_RECONF_INPROG (1<<10)   /* Slave synchronization in progress. */\n// 从服务器与新主服务器同步完毕，开始复制新主服务器\n#define SRI_RECONF_DONE (1<<11)     /* Slave synchronized with new master. */\n// 对主服务器强制执行故障迁移操作\n#define SRI_FORCE_FAILOVER (1<<12)  /* Force failover with master up. */\n// 已经对返回 -BUSY 的服务器发送 SCRIPT KILL 命令\n#define SRI_SCRIPT_KILL_SENT (1<<13) /* SCRIPT KILL already sent on -BUSY */\n\n/* Note: times are in milliseconds. */\n/* 各种时间常量，以毫秒为单位 */\n// 发送 INFO 命令的间隔\n#define SENTINEL_INFO_PERIOD 10000\n// 发送 PING 命令的间隔\n#define SENTINEL_PING_PERIOD 1000\n// 发送 ASK 命令的间隔\n#define SENTINEL_ASK_PERIOD 1000\n// 发送 PUBLISH 命令的间隔\n#define SENTINEL_PUBLISH_PERIOD 2000\n// 默认的判断服务器已下线的时长\n#define SENTINEL_DEFAULT_DOWN_AFTER 30000\n// 默认的信息频道\n#define SENTINEL_HELLO_CHANNEL \"__sentinel__:hello\"\n// 默认的 TILT 触发时长\n#define SENTINEL_TILT_TRIGGER 2000\n// 默认的 TILT 环境时长（要多久才能退出 TITL 模式）\n#define SENTINEL_TILT_PERIOD (SENTINEL_PING_PERIOD*30)\n// 默认从服务器优先级\n#define SENTINEL_DEFAULT_SLAVE_PRIORITY 100\n#define SENTINEL_SLAVE_RECONF_TIMEOUT 10000\n// 默认的同时对新主服务器进行复制的从服务器个数\n#define SENTINEL_DEFAULT_PARALLEL_SYNCS 1\n// 默认的最少重连接间隔\n#define SENTINEL_MIN_LINK_RECONNECT_PERIOD 15000\n// 默认的故障迁移执行时长\n#define SENTINEL_DEFAULT_FAILOVER_TIMEOUT (60*3*1000)\n// 默认的最大积压命令数量\n#define SENTINEL_MAX_PENDING_COMMANDS 100\n// 默认的选举超时时长\n#define SENTINEL_ELECTION_TIMEOUT 10000\n#define SENTINEL_MAX_DESYNC 1000\n\n/* Failover machine different states. */\n/* 故障转移时的状态 */\n// 没在执行故障迁移\n#define SENTINEL_FAILOVER_STATE_NONE 0  /* No failover in progress. */\n// 正在等待开始故障迁移\n#define SENTINEL_FAILOVER_STATE_WAIT_START 1  /* Wait for failover_start_time*/ \n// 正在挑选作为新主服务器的从服务器\n#define SENTINEL_FAILOVER_STATE_SELECT_SLAVE 2 /* Select slave to promote */\n// 向被选中的从服务器发送 SLAVEOF no one\n#define SENTINEL_FAILOVER_STATE_SEND_SLAVEOF_NOONE 3 /* Slave -> Master */\n// 等待从服务器转变成主服务器 \n#define SENTINEL_FAILOVER_STATE_WAIT_PROMOTION 4 /* Wait slave to change role */\n// 向已下线主服务器的其他从服务器发送 SLAVEOF 命令\n// 让它们复制新的主服务器\n#define SENTINEL_FAILOVER_STATE_RECONF_SLAVES 5 /* SLAVEOF newmaster */\n// 监视被升级的从服务器\n#define SENTINEL_FAILOVER_STATE_UPDATE_CONFIG 6 /* Monitor promoted slave. */\n\n/* 主从服务器之间的连接状态 */\n// 连接正常\n#define SENTINEL_MASTER_LINK_STATUS_UP 0\n// 连接断开\n#define SENTINEL_MASTER_LINK_STATUS_DOWN 1\n\n/* Generic flags that can be used with different functions.\n * They use higher bits to avoid colliding with the function specific\n * flags. */\n/* 可以用于多个函数的通用标识。\n * 使用高位来避免与一般标识冲突。 */\n// 没有标识\n#define SENTINEL_NO_FLAGS 0\n// 生成事件\n#define SENTINEL_GENERATE_EVENT (1<<16)\n// 领头\n#define SENTINEL_LEADER (1<<17)\n// 观察者\n#define SENTINEL_OBSERVER (1<<18)\n\n/* Script execution flags and limits. */\n/* 脚本执行状态和限制 */\n// 脚本目前没有被执行\n#define SENTINEL_SCRIPT_NONE 0\n// 脚本正在执行\n#define SENTINEL_SCRIPT_RUNNING 1\n// 脚本队列保存脚本数量的最大值\n#define SENTINEL_SCRIPT_MAX_QUEUE 256\n// 同一时间可执行脚本的最大数量\n#define SENTINEL_SCRIPT_MAX_RUNNING 16\n// 脚本的最大执行时长\n#define SENTINEL_SCRIPT_MAX_RUNTIME 60000 /* 60 seconds max exec time. */\n// 脚本最大重试数量\n#define SENTINEL_SCRIPT_MAX_RETRY 10\n// 脚本重试之前的延迟时间\n#define SENTINEL_SCRIPT_RETRY_DELAY 30000 /* 30 seconds between retries. */\n\n// Sentinel 会为每个被监视的 Redis 实例创建相应的 sentinelRedisInstance 实例\n// （被监视的实例可以是主服务器、从服务器、或者其他 Sentinel ）\ntypedef struct sentinelRedisInstance {\n    \n    // 标识值，记录了实例的类型，以及该实例的当前状态\n    int flags;      /* See SRI_... defines */\n    \n    // 实例的名字\n    // 主服务器的名字由用户在配置文件中设置\n    // 从服务器以及 Sentinel 的名字由 Sentinel 自动设置\n    // 格式为 ip:port ，例如 \"127.0.0.1:26379\"\n    char *name;     /* Master name from the point of view of this sentinel. */\n\n    // 实例的运行 ID\n    char *runid;    /* run ID of this instance. */\n\n    // 配置纪元，用于实现故障转移\n    uint64_t config_epoch;  /* Configuration epoch. */\n\n    // 实例的地址\n    sentinelAddr *addr; /* Master host. */\n\n    // 用于发送命令的异步连接\n    redisAsyncContext *cc; /* Hiredis context for commands. */\n\n    // 用于执行 SUBSCRIBE 命令、接收频道信息的异步连接\n    // 仅在实例为主服务器时使用\n    redisAsyncContext *pc; /* Hiredis context for Pub / Sub. */\n\n    // 已发送但尚未回复的命令数量\n    int pending_commands;   /* Number of commands sent waiting for a reply. */\n\n    // cc 连接的创建时间\n    mstime_t cc_conn_time; /* cc connection time. */\n    \n    // pc 连接的创建时间\n    mstime_t pc_conn_time; /* pc connection time. */\n\n    // 最后一次从这个实例接收信息的时间\n    mstime_t pc_last_activity; /* Last time we received any message. */\n\n    // 实例最后一次返回正确的 PING 命令回复的时间\n    mstime_t last_avail_time; /* Last time the instance replied to ping with\n                                 a reply we consider valid. */\n    // 实例最后一次发送 PING 命令的时间\n    mstime_t last_ping_time;  /* Last time a pending ping was sent in the\n                                 context of the current command connection\n                                 with the instance. 0 if still not sent or\n                                 if pong already received. */\n    // 实例最后一次返回 PING 命令的时间，无论内容正确与否\n    mstime_t last_pong_time;  /* Last time the instance replied to ping,\n                                 whatever the reply was. That's used to check\n                                 if the link is idle and must be reconnected. */\n\n    // 最后一次向频道发送问候信息的时间\n    // 只在当前实例为 sentinel 时使用\n    mstime_t last_pub_time;   /* Last time we sent hello via Pub/Sub. */\n\n    // 最后一次接收到这个 sentinel 发来的问候信息的时间\n    // 只在当前实例为 sentinel 时使用\n    mstime_t last_hello_time; /* Only used if SRI_SENTINEL is set. Last time\n                                 we received a hello from this Sentinel\n                                 via Pub/Sub. */\n\n    // 最后一次回复 SENTINEL is-master-down-by-addr 命令的时间\n    // 只在当前实例为 sentinel 时使用\n    mstime_t last_master_down_reply_time; /* Time of last reply to\n                                             SENTINEL is-master-down command. */\n\n    // 实例被判断为 SDOWN 状态的时间\n    mstime_t s_down_since_time; /* Subjectively down since time. */\n\n    // 实例被判断为 ODOWN 状态的时间\n    mstime_t o_down_since_time; /* Objectively down since time. */\n\n    // SENTINEL down-after-milliseconds 选项所设定的值\n    // 实例无响应多少毫秒之后才会被判断为主观下线（subjectively down）\n    mstime_t down_after_period; /* Consider it down after that period. */\n\n    // 从实例获取 INFO 命令的回复的时间\n    mstime_t info_refresh;  /* Time at which we received INFO output from it. */\n\n    /* Role and the first time we observed it.\n     * This is useful in order to delay replacing what the instance reports\n     * with our own configuration. We need to always wait some time in order\n     * to give a chance to the leader to report the new configuration before\n     * we do silly things. */\n    // 实例的角色\n    int role_reported;\n    // 角色的更新时间\n    mstime_t role_reported_time;\n\n    // 最后一次从服务器的主服务器地址变更的时间\n    mstime_t slave_conf_change_time; /* Last time slave master addr changed. */\n\n    /* Master specific. */\n    /* 主服务器实例特有的属性 -------------------------------------------------------------*/\n\n    // 其他同样监控这个主服务器的所有 sentinel\n    dict *sentinels;    /* Other sentinels monitoring the same master. */\n\n    // 如果这个实例代表的是一个主服务器\n    // 那么这个字典保存着主服务器属下的从服务器\n    // 字典的键是从服务器的名字，字典的值是从服务器对应的 sentinelRedisInstance 结构\n    dict *slaves;       /* Slaves for this master instance. */\n\n    // SENTINEL monitor <master-name> <IP> <port> <quorum> 选项中的 quorum 参数\n    // 判断这个实例为客观下线（objectively down）所需的支持投票数量\n    int quorum;         /* Number of sentinels that need to agree on failure. */\n\n    // SENTINEL parallel-syncs <master-name> <number> 选项的值\n    // 在执行故障转移操作时，可以同时对新的主服务器进行同步的从服务器数量\n    int parallel_syncs; /* How many slaves to reconfigure at same time. */\n\n    // 连接主服务器和从服务器所需的密码\n    char *auth_pass;    /* Password to use for AUTH against master & slaves. */\n\n    /* Slave specific. */\n    /* 从服务器实例特有的属性 -------------------------------------------------------------*/\n\n    // 主从服务器连接断开的时间\n    mstime_t master_link_down_time; /* Slave replication link down time. */\n\n    // 从服务器优先级\n    int slave_priority; /* Slave priority according to its INFO output. */\n\n    // 执行故障转移操作时，从服务器发送 SLAVEOF <new-master> 命令的时间\n    mstime_t slave_reconf_sent_time; /* Time at which we sent SLAVE OF <new> */\n\n    // 主服务器的实例（在本实例为从服务器时使用）\n    struct sentinelRedisInstance *master; /* Master instance if it's slave. */\n\n    // INFO 命令的回复中记录的主服务器 IP\n    char *slave_master_host;    /* Master host as reported by INFO */\n    \n    // INFO 命令的回复中记录的主服务器端口号\n    int slave_master_port;      /* Master port as reported by INFO */\n\n    // INFO 命令的回复中记录的主从服务器连接状态\n    int slave_master_link_status; /* Master link status as reported by INFO */\n\n    // 从服务器的复制偏移量\n    unsigned long long slave_repl_offset; /* Slave replication offset. */\n\n    /* Failover */\n    /* 故障转移相关属性 -------------------------------------------------------------------*/\n\n\n    // 如果这是一个主服务器实例，那么 leader 将是负责进行故障转移的 Sentinel 的运行 ID 。\n    // 如果这是一个 Sentinel 实例，那么 leader 就是被选举出来的领头 Sentinel 。\n    // 这个域只在 Sentinel 实例的 flags 属性的 SRI_MASTER_DOWN 标志处于打开状态时才有效。\n    char *leader;       /* If this is a master instance, this is the runid of\n                           the Sentinel that should perform the failover. If\n                           this is a Sentinel, this is the runid of the Sentinel\n                           that this Sentinel voted as leader. */\n    // 领头的纪元\n    uint64_t leader_epoch; /* Epoch of the 'leader' field. */\n    // 当前执行中的故障转移的纪元\n    uint64_t failover_epoch; /* Epoch of the currently started failover. */\n    // 故障转移操作的当前状态\n    int failover_state; /* See SENTINEL_FAILOVER_STATE_* defines. */\n\n    // 状态改变的时间\n    mstime_t failover_state_change_time;\n\n    // 最后一次进行故障迁移的时间\n    mstime_t failover_start_time;   /* Last failover attempt start time. */\n\n    // SENTINEL failover-timeout <master-name> <ms> 选项的值\n    // 刷新故障迁移状态的最大时限\n    mstime_t failover_timeout;      /* Max time to refresh failover state. */\n\n    mstime_t failover_delay_logged; /* For what failover_start_time value we\n                                       logged the failover delay. */\n    // 指向被提升为新主服务器的从服务器的指针\n    struct sentinelRedisInstance *promoted_slave; /* Promoted slave instance. */\n\n    /* Scripts executed to notify admin or reconfigure clients: when they\n     * are set to NULL no script is executed. */\n    // 一个文件路径，保存着 WARNING 级别的事件发生时执行的，\n    // 用于通知管理员的脚本的地址\n    char *notification_script;\n\n    // 一个文件路径，保存着故障转移执行之前、之后、或者被中止时，\n    // 需要执行的脚本的地址\n    char *client_reconfig_script;\n\n} sentinelRedisInstance;\n\n/* Main state. */\n/* Sentinel 的状态结构 */\nstruct sentinelState {\n\n    // 当前纪元\n    uint64_t current_epoch;     /* Current epoch. */\n\n    // 保存了所有被这个 sentinel 监视的主服务器\n    // 字典的键是主服务器的名字\n    // 字典的值则是一个指向 sentinelRedisInstance 结构的指针\n    dict *masters;      /* Dictionary of master sentinelRedisInstances.\n                           Key is the instance name, value is the\n                           sentinelRedisInstance structure pointer. */\n\n    // 是否进入了 TILT 模式？\n    int tilt;           /* Are we in TILT mode? */\n\n    // 目前正在执行的脚本的数量\n    int running_scripts;    /* Number of scripts in execution right now. */\n\n    // 进入 TILT 模式的时间\n    mstime_t tilt_start_time;   /* When TITL started. */\n\n    // 最后一次执行时间处理器的时间\n    mstime_t previous_time;     /* Last time we ran the time handler. */\n\n    // 一个 FIFO 队列，包含了所有需要执行的用户脚本\n    list *scripts_queue;    /* Queue of user scripts to execute. */\n\n} sentinel;\n\n/* A script execution job. */\n// 脚本运行状态\ntypedef struct sentinelScriptJob {\n\n    // 标志，记录了脚本是否运行\n    int flags;              /* Script job flags: SENTINEL_SCRIPT_* */\n\n    // 该脚本的已尝试执行次数\n    int retry_num;          /* Number of times we tried to execute it. */\n\n    // 要传给脚本的参数\n    char **argv;            /* Arguments to call the script. */\n\n    // 开始运行脚本的时间\n    mstime_t start_time;    /* Script execution time if the script is running,\n                               otherwise 0 if we are allowed to retry the\n                               execution at any time. If the script is not\n                               running and it's not 0, it means: do not run\n                               before the specified time. */\n\n    // 脚本由子进程执行，该属性记录子进程的 pid\n    pid_t pid;              /* Script execution pid. */\n\n} sentinelScriptJob;\n\n/* ======================= hiredis ae.c adapters =============================\n * Note: this implementation is taken from hiredis/adapters/ae.h, however\n * we have our modified copy for Sentinel in order to use our allocator\n * and to have full control over how the adapter works. */\n\n// 客户端适配器（adapter）结构\ntypedef struct redisAeEvents {\n\n    // 客户端连接上下文\n    redisAsyncContext *context;\n\n    // 服务器的事件循环\n    aeEventLoop *loop;\n\n    // 套接字\n    int fd;\n\n    // 记录读事件以及写事件是否就绪\n    int reading, writing;\n\n} redisAeEvents;\n\n// 读事件处理器 \nstatic void redisAeReadEvent(aeEventLoop *el, int fd, void *privdata, int mask) {\n    ((void)el); ((void)fd); ((void)mask);\n\n    redisAeEvents *e = (redisAeEvents*)privdata;\n    // 从连接中进行读取\n    redisAsyncHandleRead(e->context);\n}\n\n// 写事件处理器\nstatic void redisAeWriteEvent(aeEventLoop *el, int fd, void *privdata, int mask) {\n    ((void)el); ((void)fd); ((void)mask);\n\n    redisAeEvents *e = (redisAeEvents*)privdata;\n    // 从连接中进行写入\n    redisAsyncHandleWrite(e->context);\n}\n\n// 将读事件处理器安装到事件循环中\nstatic void redisAeAddRead(void *privdata) {\n    redisAeEvents *e = (redisAeEvents*)privdata;\n    aeEventLoop *loop = e->loop;\n    // 如果读事件处理器未安装，那么进行安装\n    if (!e->reading) {\n        e->reading = 1;\n        aeCreateFileEvent(loop,e->fd,AE_READABLE,redisAeReadEvent,e);\n    }\n}\n\n// 从事件循环中删除读事件处理器\nstatic void redisAeDelRead(void *privdata) {\n    redisAeEvents *e = (redisAeEvents*)privdata;\n    aeEventLoop *loop = e->loop;\n    // 仅在读事件处理器已安装的情况下进行删除\n    if (e->reading) {\n        e->reading = 0;\n        aeDeleteFileEvent(loop,e->fd,AE_READABLE);\n    }\n}\n\n// 将写事件处理器安装到事件循环中\nstatic void redisAeAddWrite(void *privdata) {\n    redisAeEvents *e = (redisAeEvents*)privdata;\n    aeEventLoop *loop = e->loop;\n    if (!e->writing) {\n        e->writing = 1;\n        aeCreateFileEvent(loop,e->fd,AE_WRITABLE,redisAeWriteEvent,e);\n    }\n}\n\n// 从事件循环中删除写事件处理器\nstatic void redisAeDelWrite(void *privdata) {\n    redisAeEvents *e = (redisAeEvents*)privdata;\n    aeEventLoop *loop = e->loop;\n    if (e->writing) {\n        e->writing = 0;\n        aeDeleteFileEvent(loop,e->fd,AE_WRITABLE);\n    }\n}\n\n// 清理事件\nstatic void redisAeCleanup(void *privdata) {\n    redisAeEvents *e = (redisAeEvents*)privdata;\n    redisAeDelRead(privdata);\n    redisAeDelWrite(privdata);\n    zfree(e);\n}\n\n// 为上下文 ae 和事件循环 loop 创建 hiredis 适配器\n// 并设置相关的异步处理函数\nstatic int redisAeAttach(aeEventLoop *loop, redisAsyncContext *ac) {\n    redisContext *c = &(ac->c);\n    redisAeEvents *e;\n\n    /* Nothing should be attached when something is already attached */\n    if (ac->ev.data != NULL)\n        return REDIS_ERR;\n\n    /* Create container for context and r/w events */\n    // 创建适配器\n    e = (redisAeEvents*)zmalloc(sizeof(*e));\n    e->context = ac;\n    e->loop = loop;\n    e->fd = c->fd;\n    e->reading = e->writing = 0;\n\n    /* Register functions to start/stop listening for events */\n    // 设置异步调用函数\n    ac->ev.addRead = redisAeAddRead;\n    ac->ev.delRead = redisAeDelRead;\n    ac->ev.addWrite = redisAeAddWrite;\n    ac->ev.delWrite = redisAeDelWrite;\n    ac->ev.cleanup = redisAeCleanup;\n    ac->ev.data = e;\n\n    return REDIS_OK;\n}\n\n/* ============================= Prototypes ================================= */\n\nvoid sentinelLinkEstablishedCallback(const redisAsyncContext *c, int status);\nvoid sentinelDisconnectCallback(const redisAsyncContext *c, int status);\nvoid sentinelReceiveHelloMessages(redisAsyncContext *c, void *reply, void *privdata);\nsentinelRedisInstance *sentinelGetMasterByName(char *name);\nchar *sentinelGetSubjectiveLeader(sentinelRedisInstance *master);\nchar *sentinelGetObjectiveLeader(sentinelRedisInstance *master);\nint yesnotoi(char *s);\nvoid sentinelDisconnectInstanceFromContext(const redisAsyncContext *c);\nvoid sentinelKillLink(sentinelRedisInstance *ri, redisAsyncContext *c);\nconst char *sentinelRedisInstanceTypeStr(sentinelRedisInstance *ri);\nvoid sentinelAbortFailover(sentinelRedisInstance *ri);\nvoid sentinelEvent(int level, char *type, sentinelRedisInstance *ri, const char *fmt, ...);\nsentinelRedisInstance *sentinelSelectSlave(sentinelRedisInstance *master);\nvoid sentinelScheduleScriptExecution(char *path, ...);\nvoid sentinelStartFailover(sentinelRedisInstance *master);\nvoid sentinelDiscardReplyCallback(redisAsyncContext *c, void *reply, void *privdata);\nint sentinelSendSlaveOf(sentinelRedisInstance *ri, char *host, int port);\nchar *sentinelVoteLeader(sentinelRedisInstance *master, uint64_t req_epoch, char *req_runid, uint64_t *leader_epoch);\nvoid sentinelFlushConfig(void);\nvoid sentinelGenerateInitialMonitorEvents(void);\nint sentinelSendPing(sentinelRedisInstance *ri);\n\n/* ========================= Dictionary types =============================== */\n\nunsigned int dictSdsHash(const void *key);\nint dictSdsKeyCompare(void *privdata, const void *key1, const void *key2);\nvoid releaseSentinelRedisInstance(sentinelRedisInstance *ri);\n\nvoid dictInstancesValDestructor (void *privdata, void *obj) {\n    releaseSentinelRedisInstance(obj);\n}\n\n/* Instance name (sds) -> instance (sentinelRedisInstance pointer)\n *\n * also used for: sentinelRedisInstance->sentinels dictionary that maps\n * sentinels ip:port to last seen time in Pub/Sub hello message. */\n// 这个字典类型有两个作用：\n// 1） 将实例名字映射到一个 sentinelRedisInstance 指针\n// 2） 将 sentinelRedisInstance 指针映射到一个字典，\n//     字典的键是 Sentinel 的 ip:port 地址，\n//     字典的值是该 Sentinel 最后一次向频道发送信息的时间\ndictType instancesDictType = {\n    dictSdsHash,               /* hash function */\n    NULL,                      /* key dup */\n    NULL,                      /* val dup */\n    dictSdsKeyCompare,         /* key compare */\n    NULL,                      /* key destructor */\n    dictInstancesValDestructor /* val destructor */\n};\n\n/* Instance runid (sds) -> votes (long casted to void*)\n *\n * This is useful into sentinelGetObjectiveLeader() function in order to\n * count the votes and understand who is the leader. */\n// 将一个运行 ID 映射到一个 cast 成 void* 类型的 long 值的投票数量上\n// 用于统计客观 leader sentinel\ndictType leaderVotesDictType = {\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/* =========================== Initialization =============================== */\n\nvoid sentinelCommand(redisClient *c);\nvoid sentinelInfoCommand(redisClient *c);\nvoid sentinelSetCommand(redisClient *c);\nvoid sentinelPublishCommand(redisClient *c);\n\n// 服务器在 sentinel 模式下可执行的命令\nstruct redisCommand sentinelcmds[] = {\n    {\"ping\",pingCommand,1,\"\",0,NULL,0,0,0,0,0},\n    {\"sentinel\",sentinelCommand,-2,\"\",0,NULL,0,0,0,0,0},\n    {\"subscribe\",subscribeCommand,-2,\"\",0,NULL,0,0,0,0,0},\n    {\"unsubscribe\",unsubscribeCommand,-1,\"\",0,NULL,0,0,0,0,0},\n    {\"psubscribe\",psubscribeCommand,-2,\"\",0,NULL,0,0,0,0,0},\n    {\"punsubscribe\",punsubscribeCommand,-1,\"\",0,NULL,0,0,0,0,0},\n    {\"publish\",sentinelPublishCommand,3,\"\",0,NULL,0,0,0,0,0},\n    {\"info\",sentinelInfoCommand,-1,\"\",0,NULL,0,0,0,0,0},\n    {\"shutdown\",shutdownCommand,-1,\"\",0,NULL,0,0,0,0,0}\n};\n\n/* This function overwrites a few normal Redis config default with Sentinel\n * specific defaults. */\n// 这个函数会用 Sentinel 所属的属性覆盖服务器默认的属性\nvoid initSentinelConfig(void) {\n    server.port = REDIS_SENTINEL_PORT;\n}\n\n/* Perform the Sentinel mode initialization. */\n// 以 Sentinel 模式初始化服务器\nvoid initSentinel(void) {\n    int j;\n\n    /* Remove usual Redis commands from the command table, then just add\n     * the SENTINEL command. */\n\n    // 清空 Redis 服务器的命令表（该表用于普通模式）\n    dictEmpty(server.commands,NULL);\n    // 将 SENTINEL 模式所用的命令添加进命令表\n    for (j = 0; j < sizeof(sentinelcmds)/sizeof(sentinelcmds[0]); j++) {\n        int retval;\n        struct redisCommand *cmd = sentinelcmds+j;\n\n        retval = dictAdd(server.commands, sdsnew(cmd->name), cmd);\n        redisAssert(retval == DICT_OK);\n    }\n\n    /* Initialize various data structures. */\n    /* 初始化 Sentinel 的状态 */\n    // 初始化纪元\n    sentinel.current_epoch = 0;\n\n    // 初始化保存主服务器信息的字典\n    sentinel.masters = dictCreate(&instancesDictType,NULL);\n\n    // 初始化 TILT 模式的相关选项\n    sentinel.tilt = 0;\n    sentinel.tilt_start_time = 0;\n    sentinel.previous_time = mstime();\n\n    // 初始化脚本相关选项\n    sentinel.running_scripts = 0;\n    sentinel.scripts_queue = listCreate();\n}\n\n/* This function gets called when the server is in Sentinel mode, started,\n * loaded the configuration, and is ready for normal operations. */\n// 这个函数在 Sentinel 准备就绪，可以执行操作时执行\nvoid sentinelIsRunning(void) {\n    redisLog(REDIS_WARNING,\"Sentinel runid is %s\", server.runid);\n\n    // Sentinel 不能在没有配置文件的情况下执行\n    if (server.configfile == NULL) {\n        redisLog(REDIS_WARNING,\n            \"Sentinel started without a config file. Exiting...\");\n        exit(1);\n    } else if (access(server.configfile,W_OK) == -1) {\n        redisLog(REDIS_WARNING,\n            \"Sentinel config file %s is not writable: %s. Exiting...\",\n            server.configfile,strerror(errno));\n        exit(1);\n    }\n\n    /* We want to generate a +monitor event for every configured master\n     * at startup. */\n    sentinelGenerateInitialMonitorEvents();\n}\n\n/* ============================== sentinelAddr ============================== */\n\n/* Create a sentinelAddr object and return it on success.\n *\n * 创建一个 sentinel 地址对象，并在创建成功时返回该对象。\n *\n * On error NULL is returned and errno is set to:\n *\n * 函数在出错时返回 NULL ，并将 errnor 设为以下值：\n *\n *  ENOENT: Can't resolve the hostname.\n *          不能解释 hostname\n *\n *  EINVAL: Invalid port number.\n *          端口号不正确\n */\nsentinelAddr *createSentinelAddr(char *hostname, int port) {\n    char buf[32];\n    sentinelAddr *sa;\n\n    // 检查端口号\n    if (port <= 0 || port > 65535) {\n        errno = EINVAL;\n        return NULL;\n    }\n\n    // 检查并创建地址\n    if (anetResolve(NULL,hostname,buf,sizeof(buf)) == ANET_ERR) {\n        errno = ENOENT;\n        return NULL;\n    }\n\n    // 创建并返回地址结构\n    sa = zmalloc(sizeof(*sa));\n    sa->ip = sdsnew(buf);\n    sa->port = port;\n    return sa;\n}\n\n/* Return a duplicate of the source address. */\n// 复制并返回给定地址的一个副本\nsentinelAddr *dupSentinelAddr(sentinelAddr *src) {\n    sentinelAddr *sa;\n\n    sa = zmalloc(sizeof(*sa));\n    sa->ip = sdsnew(src->ip);\n    sa->port = src->port;\n    return sa;\n}\n\n/* Free a Sentinel address. Can't fail. */\n// 释放 Sentinel 地址\nvoid releaseSentinelAddr(sentinelAddr *sa) {\n    sdsfree(sa->ip);\n    zfree(sa);\n}\n\n/* Return non-zero if two addresses are equal. */\n// 如果两个地址相同，那么返回 0\nint sentinelAddrIsEqual(sentinelAddr *a, sentinelAddr *b) {\n    return a->port == b->port && !strcasecmp(a->ip,b->ip);\n}\n\n/* =========================== Events notification ========================== */\n\n/* Send an event to log, pub/sub, user notification script.\n *\n * 将事件发送到日志、频道，以及用户提醒脚本。\n * \n * 'level' is the log level for logging. Only REDIS_WARNING events will trigger\n * the execution of the user notification script.\n *\n * level 是日志的级别。只有 REDIS_WARNING 级别的日志会触发用户提醒脚本。\n *\n * 'type' is the message type, also used as a pub/sub channel name.\n *\n * type 是信息的类型，也用作频道的名字。\n *\n * 'ri', is the redis instance target of this event if applicable, and is\n * used to obtain the path of the notification script to execute.\n *\n * ri 是引发事件的 Redis 实例，它可以用来获取可执行的用户脚本。\n *\n * The remaining arguments are printf-alike.\n *\n * 剩下的都是类似于传给 printf 函数的参数。\n *\n * If the format specifier starts with the two characters \"%@\" then ri is\n * not NULL, and the message is prefixed with an instance identifier in the\n * following format:\n *\n * 如果格式指定以 \"%@\" 两个字符开头，并且 ri 不为空，\n * 那么信息将使用以下实例标识符为开头：\n *\n *  <instance type> <instance name> <ip> <port>\n *\n *  If the instance type is not master, than the additional string is\n *  added to specify the originating master:\n *\n *  如果实例的类型不是主服务器，那么以下内容会被追加到信息的后面，\n *  用于指定目标主服务器：\n *\n *  @ <master name> <master ip> <master port>\n *\n *  Any other specifier after \"%@\" is processed by printf itself.\n *\n * \"%@\" 之后的其他指派器（specifier）都和 printf 函数所使用的指派器一样。\n */\nvoid sentinelEvent(int level, char *type, sentinelRedisInstance *ri,\n                   const char *fmt, ...) {\n    va_list ap;\n    // 日志字符串\n    char msg[REDIS_MAX_LOGMSG_LEN];\n    robj *channel, *payload;\n\n    /* Handle %@ */\n    // 处理 %@\n    if (fmt[0] == '%' && fmt[1] == '@') {\n\n        // 如果 ri 实例是主服务器，那么 master 就是 NULL \n        // 否则 ri 就是一个从服务器或者 sentinel ，而 master 就是该实例的主服务器\n        //\n        // sentinelRedisInstance *master = NULL;\n        // if (~(ri->flags & SRI_MASTER))\n        //     master = ri->master;\n        sentinelRedisInstance *master = (ri->flags & SRI_MASTER) ?\n                                         NULL : ri->master;\n\n        if (master) {\n            \n            // ri 不是主服务器\n\n            snprintf(msg, sizeof(msg), \"%s %s %s %d @ %s %s %d\",\n                // 打印 ri 的类型\n                sentinelRedisInstanceTypeStr(ri),\n                // 打印 ri 的名字、IP 和端口号\n                ri->name, ri->addr->ip, ri->addr->port,\n                // 打印 ri 的主服务器的名字、 IP 和端口号\n                master->name, master->addr->ip, master->addr->port);\n        } else {\n\n            // ri 是主服务器\n\n            snprintf(msg, sizeof(msg), \"%s %s %s %d\",\n                // 打印 ri 的类型\n                sentinelRedisInstanceTypeStr(ri),\n                // 打印 ri 的名字、IP 和端口号\n                ri->name, ri->addr->ip, ri->addr->port);\n        }\n\n        // 跳过已处理的 \"%@\" 字符\n        fmt += 2;\n\n    } else {\n        msg[0] = '\\0';\n    }\n\n    /* Use vsprintf for the rest of the formatting if any. */\n    // 打印之后的内容，格式和平常的 printf 一样\n    if (fmt[0] != '\\0') {\n        va_start(ap, fmt);\n        vsnprintf(msg+strlen(msg), sizeof(msg)-strlen(msg), fmt, ap);\n        va_end(ap);\n    }\n\n    /* Log the message if the log level allows it to be logged. */\n    // 如果日志的级别足够高的话，那么记录到日志中\n    if (level >= server.verbosity)\n        redisLog(level,\"%s %s\",type,msg);\n\n    /* Publish the message via Pub/Sub if it's not a debugging one. */\n    // 如果日志不是 DEBUG 日志，那么将它发送到频道中\n    if (level != REDIS_DEBUG) {\n        // 频道\n        channel = createStringObject(type,strlen(type));\n        // 内容\n        payload = createStringObject(msg,strlen(msg));\n        // 发送信息\n        pubsubPublishMessage(channel,payload);\n        decrRefCount(channel);\n        decrRefCount(payload);\n    }\n\n    /* Call the notification script if applicable. */\n    // 如果有需要的话，调用提醒脚本\n    if (level == REDIS_WARNING && ri != NULL) {\n        sentinelRedisInstance *master = (ri->flags & SRI_MASTER) ?\n                                         ri : ri->master;\n        if (master->notification_script) {\n            sentinelScheduleScriptExecution(master->notification_script,\n                type,msg,NULL);\n        }\n    }\n}\n\n/* This function is called only at startup and is used to generate a\n * +monitor event for every configured master. The same events are also\n * generated when a master to monitor is added at runtime via the\n * SENTINEL MONITOR command. */\n// 在 Sentinel 启动时执行，用于创建并生成 +monitor 事件\nvoid sentinelGenerateInitialMonitorEvents(void) {\n    dictIterator *di;\n    dictEntry *de;\n\n    di = dictGetIterator(sentinel.masters);\n    while((de = dictNext(di)) != NULL) {\n        sentinelRedisInstance *ri = dictGetVal(de);\n        sentinelEvent(REDIS_WARNING,\"+monitor\",ri,\"%@ quorum %d\",ri->quorum);\n    }\n    dictReleaseIterator(di);\n}\n\n/* ============================ script execution ============================ */\n\n/* Release a script job structure and all the associated data. */\n// 释放一个脚本任务结构，以及该任务的相关数据。\nvoid sentinelReleaseScriptJob(sentinelScriptJob *sj) {\n    int j = 0;\n\n    while(sj->argv[j]) sdsfree(sj->argv[j++]);\n    zfree(sj->argv);\n    zfree(sj);\n}\n\n// 将给定参数和脚本放入队列\n#define SENTINEL_SCRIPT_MAX_ARGS 16\nvoid sentinelScheduleScriptExecution(char *path, ...) {\n    va_list ap;\n    char *argv[SENTINEL_SCRIPT_MAX_ARGS+1];\n    int argc = 1;\n    sentinelScriptJob *sj;\n\n    // 生成参数\n    va_start(ap, path);\n    while(argc < SENTINEL_SCRIPT_MAX_ARGS) {\n        argv[argc] = va_arg(ap,char*);\n        if (!argv[argc]) break;\n        argv[argc] = sdsnew(argv[argc]); /* Copy the string. */\n        argc++;\n    }\n    va_end(ap);\n    argv[0] = sdsnew(path);\n    \n    // 初始化脚本结构\n    sj = zmalloc(sizeof(*sj));\n    sj->flags = SENTINEL_SCRIPT_NONE;\n    sj->retry_num = 0;\n    sj->argv = zmalloc(sizeof(char*)*(argc+1));\n    sj->start_time = 0;\n    sj->pid = 0;\n    memcpy(sj->argv,argv,sizeof(char*)*(argc+1));\n\n    // 添加到等待执行脚本队列的末尾， FIFO\n    listAddNodeTail(sentinel.scripts_queue,sj);\n\n    /* Remove the oldest non running script if we already hit the limit. */\n    // 如果入队的脚本数量太多，那么移除最旧的未执行脚本\n    if (listLength(sentinel.scripts_queue) > SENTINEL_SCRIPT_MAX_QUEUE) {\n        listNode *ln;\n        listIter li;\n\n        listRewind(sentinel.scripts_queue,&li);\n        while ((ln = listNext(&li)) != NULL) {\n            sj = ln->value;\n\n            // 不删除正在运行的脚本\n            if (sj->flags & SENTINEL_SCRIPT_RUNNING) continue;\n            /* The first node is the oldest as we add on tail. */\n            listDelNode(sentinel.scripts_queue,ln);\n            sentinelReleaseScriptJob(sj);\n            break;\n        }\n        redisAssert(listLength(sentinel.scripts_queue) <=\n                    SENTINEL_SCRIPT_MAX_QUEUE);\n    }\n}\n\n/* Lookup a script in the scripts queue via pid, and returns the list node\n * (so that we can easily remove it from the queue if needed). */\n// 根据 pid ，查找正在运行中的脚本\nlistNode *sentinelGetScriptListNodeByPid(pid_t pid) {\n    listNode *ln;\n    listIter li;\n\n    listRewind(sentinel.scripts_queue,&li);\n    while ((ln = listNext(&li)) != NULL) {\n        sentinelScriptJob *sj = ln->value;\n\n        if ((sj->flags & SENTINEL_SCRIPT_RUNNING) && sj->pid == pid)\n            return ln;\n    }\n    return NULL;\n}\n\n/* Run pending scripts if we are not already at max number of running\n * scripts. */\n// 运行等待执行的脚本\nvoid sentinelRunPendingScripts(void) {\n    listNode *ln;\n    listIter li;\n    mstime_t now = mstime();\n\n    /* Find jobs that are not running and run them, from the top to the\n     * tail of the queue, so we run older jobs first. */\n    // 如果运行的脚本数量未超过最大值，\n    // 那么从 FIFO 队列中取出未运行的脚本，并运行该脚本\n    listRewind(sentinel.scripts_queue,&li);\n    while (sentinel.running_scripts < SENTINEL_SCRIPT_MAX_RUNNING &&\n           (ln = listNext(&li)) != NULL)\n    {\n        sentinelScriptJob *sj = ln->value;\n        pid_t pid;\n\n        /* Skip if already running. */\n        // 跳过已运行脚本\n        if (sj->flags & SENTINEL_SCRIPT_RUNNING) continue;\n\n        /* Skip if it's a retry, but not enough time has elapsed. */\n        // 这是一个重试脚本，但它刚刚执行完，稍后再重试\n        if (sj->start_time && sj->start_time > now) continue;\n\n        // 打开运行标记\n        sj->flags |= SENTINEL_SCRIPT_RUNNING;\n        // 记录开始时间\n        sj->start_time = mstime();\n        // 增加重试计数器\n        sj->retry_num++;\n\n        // 创建子进程\n        pid = fork();\n\n        if (pid == -1) {\n            \n            // 创建子进程失败\n\n            /* Parent (fork error).\n             * We report fork errors as signal 99, in order to unify the\n             * reporting with other kind of errors. */\n            sentinelEvent(REDIS_WARNING,\"-script-error\",NULL,\n                          \"%s %d %d\", sj->argv[0], 99, 0);\n            sj->flags &= ~SENTINEL_SCRIPT_RUNNING;\n            sj->pid = 0;\n        } else if (pid == 0) {\n\n            // 子进程执行脚本\n\n            /* Child */\n            execve(sj->argv[0],sj->argv,environ);\n            /* If we are here an error occurred. */\n            _exit(2); /* Don't retry execution. */\n        } else {\n\n            // 父进程\n            \n            // 增加运行脚本计数器\n            sentinel.running_scripts++;\n\n            // 记录 pid\n            sj->pid = pid;\n\n            // 发送脚本运行信号\n            sentinelEvent(REDIS_DEBUG,\"+script-child\",NULL,\"%ld\",(long)pid);\n        }\n    }\n}\n\n/* How much to delay the execution of a script that we need to retry after\n * an error?\n *\n * We double the retry delay for every further retry we do. So for instance\n * if RETRY_DELAY is set to 30 seconds and the max number of retries is 10\n * starting from the second attempt to execute the script the delays are:\n * 30 sec, 60 sec, 2 min, 4 min, 8 min, 16 min, 32 min, 64 min, 128 min. */\n// 计算重试脚本前的延迟时间\nmstime_t sentinelScriptRetryDelay(int retry_num) {\n    mstime_t delay = SENTINEL_SCRIPT_RETRY_DELAY;\n\n    while (retry_num-- > 1) delay *= 2;\n    return delay;\n}\n\n/* Check for scripts that terminated, and remove them from the queue if the\n * script terminated successfully. If instead the script was terminated by\n * a signal, or returned exit code \"1\", it is scheduled to run again if\n * the max number of retries did not already elapsed. */\n// 检查脚本的退出状态，并在脚本成功退出时，将脚本从队列中删除。\n// 如果脚本被信号终结，或者返回退出代码 1 ，那么只要该脚本的重试次数未超过限制\n// 那么该脚本就会被调度，并等待重试\nvoid sentinelCollectTerminatedScripts(void) {\n    int statloc;\n    pid_t pid;\n\n    // 获取子进程信号\n    while ((pid = wait3(&statloc,WNOHANG,NULL)) > 0) {\n        int exitcode = WEXITSTATUS(statloc);\n        int bysignal = 0;\n        listNode *ln;\n        sentinelScriptJob *sj;\n\n        // 发送脚本终结信号\n        if (WIFSIGNALED(statloc)) bysignal = WTERMSIG(statloc);\n        sentinelEvent(REDIS_DEBUG,\"-script-child\",NULL,\"%ld %d %d\",\n            (long)pid, exitcode, bysignal);\n        \n        // 在队列中安 pid 查找脚本\n        ln = sentinelGetScriptListNodeByPid(pid);\n        if (ln == NULL) {\n            redisLog(REDIS_WARNING,\"wait3() returned a pid (%ld) we can't find in our scripts execution queue!\", (long)pid);\n            continue;\n        }\n        sj = ln->value;\n\n        /* If the script was terminated by a signal or returns an\n         * exit code of \"1\" (that means: please retry), we reschedule it\n         * if the max number of retries is not already reached. */\n        if ((bysignal || exitcode == 1) &&\n            sj->retry_num != SENTINEL_SCRIPT_MAX_RETRY)\n        {\n            // 重试脚本\n\n            sj->flags &= ~SENTINEL_SCRIPT_RUNNING;\n            sj->pid = 0;\n            sj->start_time = mstime() +\n                             sentinelScriptRetryDelay(sj->retry_num);\n        } else {\n            /* Otherwise let's remove the script, but log the event if the\n             * execution did not terminated in the best of the ways. */\n\n            // 发送脚本执行错误事件\n            if (bysignal || exitcode != 0) {\n                sentinelEvent(REDIS_WARNING,\"-script-error\",NULL,\n                              \"%s %d %d\", sj->argv[0], bysignal, exitcode);\n            }\n\n            // 将脚本从队列中删除\n            listDelNode(sentinel.scripts_queue,ln);\n            sentinelReleaseScriptJob(sj);\n            sentinel.running_scripts--;\n        }\n    }\n}\n\n/* Kill scripts in timeout, they'll be collected by the\n * sentinelCollectTerminatedScripts() function. */\n// 杀死超时脚本，这些脚本会被 sentinelCollectTerminatedScripts 函数回收处理\nvoid sentinelKillTimedoutScripts(void) {\n    listNode *ln;\n    listIter li;\n    mstime_t now = mstime();\n\n    // 遍历队列中的所有脚本\n    listRewind(sentinel.scripts_queue,&li);\n    while ((ln = listNext(&li)) != NULL) {\n        sentinelScriptJob *sj = ln->value;\n\n        // 选出那些正在执行，并且执行时间超过限制的脚本\n        if (sj->flags & SENTINEL_SCRIPT_RUNNING &&\n            (now - sj->start_time) > SENTINEL_SCRIPT_MAX_RUNTIME)\n        {\n            // 发送脚本超时事件\n            sentinelEvent(REDIS_WARNING,\"-script-timeout\",NULL,\"%s %ld\",\n                sj->argv[0], (long)sj->pid);\n\n            // 杀死脚本进程\n            kill(sj->pid,SIGKILL);\n        }\n    }\n}\n\n/* Implements SENTINEL PENDING-SCRIPTS command. */\n// 打印脚本队列中所有脚本的状态\nvoid sentinelPendingScriptsCommand(redisClient *c) {\n    listNode *ln;\n    listIter li;\n\n    addReplyMultiBulkLen(c,listLength(sentinel.scripts_queue));\n    listRewind(sentinel.scripts_queue,&li);\n    while ((ln = listNext(&li)) != NULL) {\n        sentinelScriptJob *sj = ln->value;\n        int j = 0;\n\n        addReplyMultiBulkLen(c,10);\n\n        addReplyBulkCString(c,\"argv\");\n        while (sj->argv[j]) j++;\n        addReplyMultiBulkLen(c,j);\n        j = 0;\n        while (sj->argv[j]) addReplyBulkCString(c,sj->argv[j++]);\n\n        addReplyBulkCString(c,\"flags\");\n        addReplyBulkCString(c,\n            (sj->flags & SENTINEL_SCRIPT_RUNNING) ? \"running\" : \"scheduled\");\n\n        addReplyBulkCString(c,\"pid\");\n        addReplyBulkLongLong(c,sj->pid);\n\n        if (sj->flags & SENTINEL_SCRIPT_RUNNING) {\n            addReplyBulkCString(c,\"run-time\");\n            addReplyBulkLongLong(c,mstime() - sj->start_time);\n        } else {\n            mstime_t delay = sj->start_time ? (sj->start_time-mstime()) : 0;\n            if (delay < 0) delay = 0;\n            addReplyBulkCString(c,\"run-delay\");\n            addReplyBulkLongLong(c,delay);\n        }\n\n        addReplyBulkCString(c,\"retry-num\");\n        addReplyBulkLongLong(c,sj->retry_num);\n    }\n}\n\n/* This function calls, if any, the client reconfiguration script with the\n * following parameters:\n *\n * 当该函数执行时，使用以下格式的参数调用客户端重配置脚本\n *\n * <master-name> <role> <state> <from-ip> <from-port> <to-ip> <to-port>\n *\n * It is called every time a failover is performed.\n *\n * 这个函数在每次执行故障迁移时都会执行一次。\n *\n * <state> is currently always \"failover\".\n * <role> is either \"leader\" or \"observer\".\n *\n * <state> 总是 \"failover\" ，而 <role> 可以是 \"leader\" 或者 \"observer\"\n *\n * from/to fields are respectively master -> promoted slave addresses for\n * \"start\" and \"end\". \n */\nvoid sentinelCallClientReconfScript(sentinelRedisInstance *master, int role, char *state, sentinelAddr *from, sentinelAddr *to) {\n    char fromport[32], toport[32];\n\n    if (master->client_reconfig_script == NULL) return;\n    ll2string(fromport,sizeof(fromport),from->port);\n    ll2string(toport,sizeof(toport),to->port);\n    // 将给定参数和脚本放进度列，等待执行\n    sentinelScheduleScriptExecution(master->client_reconfig_script,\n        master->name,\n        (role == SENTINEL_LEADER) ? \"leader\" : \"observer\",\n        state, from->ip, fromport, to->ip, toport, NULL);\n}\n\n/* ========================== sentinelRedisInstance ========================= */\n\n/* Create a redis instance, the following fields must be populated by the\n * caller if needed:\n *\n * 创建一个 Redis 实例，在有需要时，以下两个域需要从调用者提取：\n *\n * runid: set to NULL but will be populated once INFO output is received.\n *        设置为 NULL ，并在接收到 INFO 命令的回复时设置\n *\n * info_refresh: is set to 0 to mean that we never received INFO so far.\n *               如果这个值为 0 ，那么表示我们未收到过 INFO 信息。\n *\n * If SRI_MASTER is set into initial flags the instance is added to\n * sentinel.masters table.\n *\n * 如果 flags 参数为 SRI_MASTER ，\n * 那么这个实例会被添加到 sentinel.masters 表。\n *\n * if SRI_SLAVE or SRI_SENTINEL is set then 'master' must be not NULL and the\n * instance is added into master->slaves or master->sentinels table.\n *\n * 如果 flags 为 SRI_SLAVE 或者 SRI_SENTINEL ，\n * 那么 master 参数不能为 NULL ，\n * SRI_SLAVE 类型的实例会被添加到 master->slaves 表中，\n * 而 SRI_SENTINEL 类型的实例则会被添加到 master->sentinels 表中。\n *\n * If the instance is a slave or sentinel, the name parameter is ignored and\n * is created automatically as hostname:port.\n *\n * 如果实例是从服务器或者 sentinel ，那么 name 参数会被自动忽略，\n * 实例的名字会被自动设置为 hostname:port 。\n *\n * The function fails if hostname can't be resolved or port is out of range.\n * When this happens NULL is returned and errno is set accordingly to the\n * createSentinelAddr() function.\n *\n * 当 hostname 不能被解释，或者超出范围时，函数将失败。\n * 函数将返回 NULL ，并设置 errno 变量，\n * 具体的出错值请参考 createSentinelAddr() 函数。\n *\n * The function may also fail and return NULL with errno set to EBUSY if\n * a master or slave with the same name already exists. \n *\n * 当相同名字的主服务器或者从服务器已经存在时，函数返回 NULL ，\n * 并将 errno 设为 EBUSY 。\n */\nsentinelRedisInstance *createSentinelRedisInstance(char *name, int flags, char *hostname, int port, int quorum, sentinelRedisInstance *master) {\n    sentinelRedisInstance *ri;\n    sentinelAddr *addr;\n    dict *table = NULL;\n    char slavename[128], *sdsname;\n\n    redisAssert(flags & (SRI_MASTER|SRI_SLAVE|SRI_SENTINEL));\n    redisAssert((flags & SRI_MASTER) || master != NULL);\n\n    /* Check address validity. */\n    // 保存 IP 地址和端口号到 addr\n    addr = createSentinelAddr(hostname,port);\n    if (addr == NULL) return NULL;\n\n    /* For slaves and sentinel we use ip:port as name. */\n    // 如果实例是从服务器或者 sentinel ，那么使用 ip:port 格式为实例设置名字\n    if (flags & (SRI_SLAVE|SRI_SENTINEL)) {\n        snprintf(slavename,sizeof(slavename),\n            strchr(hostname,':') ? \"[%s]:%d\" : \"%s:%d\",\n            hostname,port);\n        name = slavename;\n    }\n\n    /* Make sure the entry is not duplicated. This may happen when the same\n     * name for a master is used multiple times inside the configuration or\n     * if we try to add multiple times a slave or sentinel with same ip/port\n     * to a master. */\n    // 配置文件中添加了重复的主服务器配置\n    // 或者尝试添加一个相同 ip 或者端口号的从服务器或者 sentinel 时\n    // 就可能出现重复添加同一个实例的情况\n    // 为了避免这种现象，程序在添加新实例之前，需要先检查实例是否已存在\n    // 只有不存在的实例会被添加\n\n    // 选择要添加的表\n    // 注意主服务会被添加到 sentinel.masters 表\n    // 而从服务器和 sentinel 则会被添加到 master 所属的 slaves 表和 sentinels 表中\n    if (flags & SRI_MASTER) table = sentinel.masters;\n    else if (flags & SRI_SLAVE) table = master->slaves;\n    else if (flags & SRI_SENTINEL) table = master->sentinels;\n    sdsname = sdsnew(name);\n    if (dictFind(table,sdsname)) {\n\n        // 实例已存在，函数直接返回\n\n        sdsfree(sdsname);\n        errno = EBUSY;\n        return NULL;\n    }\n\n    /* Create the instance object. */\n    // 创建实例对象\n    ri = zmalloc(sizeof(*ri));\n    /* Note that all the instances are started in the disconnected state,\n     * the event loop will take care of connecting them. */\n    // 所有连接都已断线为起始状态，sentinel 会在需要时自动为它创建连接\n    ri->flags = flags | SRI_DISCONNECTED;\n    ri->name = sdsname;\n    ri->runid = NULL;\n    ri->config_epoch = 0;\n    ri->addr = addr;\n    ri->cc = NULL;\n    ri->pc = NULL;\n    ri->pending_commands = 0;\n    ri->cc_conn_time = 0;\n    ri->pc_conn_time = 0;\n    ri->pc_last_activity = 0;\n    /* We set the last_ping_time to \"now\" even if we actually don't have yet\n     * a connection with the node, nor we sent a ping.\n     * This is useful to detect a timeout in case we'll not be able to connect\n     * with the node at all. */\n    ri->last_ping_time = mstime();\n    ri->last_avail_time = mstime();\n    ri->last_pong_time = mstime();\n    ri->last_pub_time = mstime();\n    ri->last_hello_time = mstime();\n    ri->last_master_down_reply_time = mstime();\n    ri->s_down_since_time = 0;\n    ri->o_down_since_time = 0;\n    ri->down_after_period = master ? master->down_after_period :\n                            SENTINEL_DEFAULT_DOWN_AFTER;\n    ri->master_link_down_time = 0;\n    ri->auth_pass = NULL;\n    ri->slave_priority = SENTINEL_DEFAULT_SLAVE_PRIORITY;\n    ri->slave_reconf_sent_time = 0;\n    ri->slave_master_host = NULL;\n    ri->slave_master_port = 0;\n    ri->slave_master_link_status = SENTINEL_MASTER_LINK_STATUS_DOWN;\n    ri->slave_repl_offset = 0;\n    ri->sentinels = dictCreate(&instancesDictType,NULL);\n    ri->quorum = quorum;\n    ri->parallel_syncs = SENTINEL_DEFAULT_PARALLEL_SYNCS;\n    ri->master = master;\n    ri->slaves = dictCreate(&instancesDictType,NULL);\n    ri->info_refresh = 0;\n\n    /* Failover state. */\n    ri->leader = NULL;\n    ri->leader_epoch = 0;\n    ri->failover_epoch = 0;\n    ri->failover_state = SENTINEL_FAILOVER_STATE_NONE;\n    ri->failover_state_change_time = 0;\n    ri->failover_start_time = 0;\n    ri->failover_timeout = SENTINEL_DEFAULT_FAILOVER_TIMEOUT;\n    ri->failover_delay_logged = 0;\n    ri->promoted_slave = NULL;\n    ri->notification_script = NULL;\n    ri->client_reconfig_script = NULL;\n\n    /* Role */\n    ri->role_reported = ri->flags & (SRI_MASTER|SRI_SLAVE);\n    ri->role_reported_time = mstime();\n    ri->slave_conf_change_time = mstime();\n\n    /* Add into the right table. */\n    // 将实例添加到适当的表中\n    dictAdd(table, ri->name, ri);\n\n    // 返回实例\n    return ri;\n}\n\n/* Release this instance and all its slaves, sentinels, hiredis connections.\n *\n * 释放一个实例，以及它的所有从服务器、sentinel ，以及 hiredis 连接。\n *\n * This function does not take care of unlinking the instance from the main\n * masters table (if it is a master) or from its master sentinels/slaves table\n * if it is a slave or sentinel. \n *\n * 如果这个实例是一个从服务器或者 sentinel ，\n * 那么这个函数也会从该实例所属的主服务器表中删除这个从服务器/sentinel 。\n */\nvoid releaseSentinelRedisInstance(sentinelRedisInstance *ri) {\n\n    /* Release all its slaves or sentinels if any. */\n    // 释放（可能有的）sentinel 和 slave\n    dictRelease(ri->sentinels);\n    dictRelease(ri->slaves);\n\n    /* Release hiredis connections. */\n    // 释放连接\n    if (ri->cc) sentinelKillLink(ri,ri->cc);\n    if (ri->pc) sentinelKillLink(ri,ri->pc);\n\n    /* Free other resources. */\n    // 释放其他资源\n    sdsfree(ri->name);\n    sdsfree(ri->runid);\n    sdsfree(ri->notification_script);\n    sdsfree(ri->client_reconfig_script);\n    sdsfree(ri->slave_master_host);\n    sdsfree(ri->leader);\n    sdsfree(ri->auth_pass);\n    releaseSentinelAddr(ri->addr);\n\n    /* Clear state into the master if needed. */\n    // 清除故障转移带来的状态\n    if ((ri->flags & SRI_SLAVE) && (ri->flags & SRI_PROMOTED) && ri->master)\n        ri->master->promoted_slave = NULL;\n\n    zfree(ri);\n}\n\n/* Lookup a slave in a master Redis instance, by ip and port. */\n// 根据 IP 和端口号，查找主服务器实例的从服务器\nsentinelRedisInstance *sentinelRedisInstanceLookupSlave(\n                sentinelRedisInstance *ri, char *ip, int port)\n{\n    sds key;\n    sentinelRedisInstance *slave;\n  \n    redisAssert(ri->flags & SRI_MASTER);\n    key = sdscatprintf(sdsempty(),\n        strchr(ip,':') ? \"[%s]:%d\" : \"%s:%d\",\n        ip,port);\n    slave = dictFetchValue(ri->slaves,key);\n    sdsfree(key);\n    return slave;\n}\n\n/* Return the name of the type of the instance as a string. */\n// 以字符串形式返回实例的类型\nconst char *sentinelRedisInstanceTypeStr(sentinelRedisInstance *ri) {\n    if (ri->flags & SRI_MASTER) return \"master\";\n    else if (ri->flags & SRI_SLAVE) return \"slave\";\n    else if (ri->flags & SRI_SENTINEL) return \"sentinel\";\n    else return \"unknown\";\n}\n\n/* This function removes all the instances found in the dictionary of\n * sentinels in the specified 'master', having either:\n * \n * 1) The same ip/port as specified.\n *    实例给定的 IP 和端口号和字典中已有的实例相同\n *\n * 2) The same runid.\n *    实例给定的运行 ID 和字典中已有的实例相同\n *\n * \"1\" and \"2\" don't need to verify at the same time, just one is enough.\n *\n * 以上条件任意满足一个，移除操作就会被执行。\n *\n * If \"runid\" is NULL it is not checked.\n * Similarly if \"ip\" is NULL it is not checked.\n *\n * 如果 runid 参数为 NULL ，那么不检查该参数。\n * 如果 ip 参数为 NULL ，那么不检查该参数。\n *\n * This function is useful because every time we add a new Sentinel into\n * a master's Sentinels dictionary, we want to be very sure about not\n * having duplicated instances for any reason. This is important because\n * other sentinels are needed to reach ODOWN quorum, and later to get\n * voted for a given configuration epoch in order to perform the failover.\n *\n * 因为 sentinel 的操作比如故障转移，需要多个 sentinel 投票才能进行。\n * 所以我们必须保证所添加的各个 sentinel 都是不相同、独一无二的，\n * 这样才能确保投票的合法性。\n *\n * The function returns the number of Sentinels removed. \n *\n * 函数的返回值为被移除 sentinel 的数量\n */\nint removeMatchingSentinelsFromMaster(sentinelRedisInstance *master, char *ip, int port, char *runid) {\n    dictIterator *di;\n    dictEntry *de;\n    int removed = 0;\n\n    di = dictGetSafeIterator(master->sentinels);\n    while((de = dictNext(di)) != NULL) {\n        sentinelRedisInstance *ri = dictGetVal(de);\n\n        // 运行 ID 相同，或者 IP 和端口号相同，那么移除该实例\n        if ((ri->runid && runid && strcmp(ri->runid,runid) == 0) ||\n            (ip && strcmp(ri->addr->ip,ip) == 0 && port == ri->addr->port))\n        {\n            dictDelete(master->sentinels,ri->name);\n            removed++;\n        }\n    }\n    dictReleaseIterator(di);\n\n    return removed;\n}\n\n/* Search an instance with the same runid, ip and port into a dictionary\n * of instances. Return NULL if not found, otherwise return the instance\n * pointer.\n *\n * 在给定的实例中查找具有相同 runid 、ip 、port 的实例，\n * 没找到则返回 NULL 。\n *\n * runid or ip can be NULL. In such a case the search is performed only\n * by the non-NULL field. \n *\n * runid 或者 ip 都可以为 NULL ，在这种情况下，函数只检查非空域。\n */\nsentinelRedisInstance *getSentinelRedisInstanceByAddrAndRunID(dict *instances, char *ip, int port, char *runid) {\n    dictIterator *di;\n    dictEntry *de;\n    sentinelRedisInstance *instance = NULL;\n\n    redisAssert(ip || runid);   /* User must pass at least one search param. */\n\n    // 遍历所有输入实例\n    di = dictGetIterator(instances);\n    while((de = dictNext(di)) != NULL) {\n        sentinelRedisInstance *ri = dictGetVal(de);\n\n        // runid 不相同，忽略该实例\n        if (runid && !ri->runid) continue;\n\n        // 检查 ip 和端口号是否相同\n        if ((runid == NULL || strcmp(ri->runid, runid) == 0) &&\n            (ip == NULL || (strcmp(ri->addr->ip, ip) == 0 &&\n                            ri->addr->port == port)))\n        {\n            instance = ri;\n            break;\n        }\n    }\n    dictReleaseIterator(di);\n\n    return instance;\n}\n\n// 根据名字查找主服务器\n/* Master lookup by name */\nsentinelRedisInstance *sentinelGetMasterByName(char *name) {\n    sentinelRedisInstance *ri;\n    sds sdsname = sdsnew(name);\n\n    ri = dictFetchValue(sentinel.masters,sdsname);\n    sdsfree(sdsname);\n    return ri;\n}\n\n/* Add the specified flags to all the instances in the specified dictionary. */\n// 为输入的所有实例打开指定的 flags\nvoid sentinelAddFlagsToDictOfRedisInstances(dict *instances, int flags) {\n    dictIterator *di;\n    dictEntry *de;\n\n    di = dictGetIterator(instances);\n    while((de = dictNext(di)) != NULL) {\n        sentinelRedisInstance *ri = dictGetVal(de);\n        ri->flags |= flags;\n    }\n    dictReleaseIterator(di);\n}\n\n/* Remove the specified flags to all the instances in the specified\n * dictionary. */\n// 从字典中移除所有实例的给定 flags\nvoid sentinelDelFlagsToDictOfRedisInstances(dict *instances, int flags) {\n    dictIterator *di;\n    dictEntry *de;\n\n    // 遍历所有实例\n    di = dictGetIterator(instances);\n    while((de = dictNext(di)) != NULL) {\n        sentinelRedisInstance *ri = dictGetVal(de);\n        // 移除 flags\n        ri->flags &= ~flags;\n    }\n    dictReleaseIterator(di);\n}\n\n/* Reset the state of a monitored master:\n *\n * 重置主服务区的监控状态\n *\n * 1) Remove all slaves.\n *    移除主服务器的所有从服务器\n * 2) Remove all sentinels.\n *    移除主服务器的所有 sentinel\n * 3) Remove most of the flags resulting from runtime operations.\n *    移除大部分运行时操作标志\n * 4) Reset timers to their default value.\n *    重置计时器为默认值\n * 5) In the process of doing this undo the failover if in progress.\n *    如果故障转移正在执行的话，那么取消该它\n * 6) Disconnect the connections with the master (will reconnect automatically).\n *    断开 sentinel 与主服务器的连接（之后会自动重连）\n */\n\n#define SENTINEL_RESET_NO_SENTINELS (1<<0)\nvoid sentinelResetMaster(sentinelRedisInstance *ri, int flags) {\n\n    redisAssert(ri->flags & SRI_MASTER);\n\n    dictRelease(ri->slaves);\n    ri->slaves = dictCreate(&instancesDictType,NULL);\n\n    if (!(flags & SENTINEL_RESET_NO_SENTINELS)) {\n        dictRelease(ri->sentinels);\n        ri->sentinels = dictCreate(&instancesDictType,NULL);\n    }\n\n    if (ri->cc) sentinelKillLink(ri,ri->cc);\n\n    if (ri->pc) sentinelKillLink(ri,ri->pc);\n\n    // 设置标识为断线的主服务器\n    ri->flags &= SRI_MASTER|SRI_DISCONNECTED;\n\n    if (ri->leader) {\n        sdsfree(ri->leader);\n        ri->leader = NULL;\n    }\n\n    ri->failover_state = SENTINEL_FAILOVER_STATE_NONE;\n    ri->failover_state_change_time = 0;\n    ri->failover_start_time = 0;\n    ri->promoted_slave = NULL;\n    sdsfree(ri->runid);\n    sdsfree(ri->slave_master_host);\n    ri->runid = NULL;\n    ri->slave_master_host = NULL;\n    ri->last_ping_time = mstime();\n    ri->last_avail_time = mstime();\n    ri->last_pong_time = mstime();\n    ri->role_reported_time = mstime();\n    ri->role_reported = SRI_MASTER;\n    // 发送主服务器重置事件\n    if (flags & SENTINEL_GENERATE_EVENT)\n        sentinelEvent(REDIS_WARNING,\"+reset-master\",ri,\"%@\");\n}\n\n/* Call sentinelResetMaster() on every master with a name matching the specified\n * pattern. */\n// 重置所有符合给定模式的主服务器\nint sentinelResetMastersByPattern(char *pattern, int flags) {\n    dictIterator *di;\n    dictEntry *de;\n    int reset = 0;\n\n    di = dictGetIterator(sentinel.masters);\n    while((de = dictNext(di)) != NULL) {\n        sentinelRedisInstance *ri = dictGetVal(de);\n\n        if (ri->name) {\n            if (stringmatch(pattern,ri->name,0)) {\n                sentinelResetMaster(ri,flags);\n                reset++;\n            }\n        }\n    }\n    dictReleaseIterator(di);\n    return reset;\n}\n\n/* Reset the specified master with sentinelResetMaster(), and also change\n * the ip:port address, but take the name of the instance unmodified.\n *\n * 将 master 实例的 IP 和端口号修改成给定的 ip 和 port ，\n * 但保留 master 原来的名字。\n *\n * This is used to handle the +switch-master event.\n *\n * 这个函数用于处理 +switch-master 事件\n *\n * The function returns REDIS_ERR if the address can't be resolved for some\n * reason. Otherwise REDIS_OK is returned.  \n *\n * 函数在无法解释地址时返回 REDIS_ERR ，否则返回 REDIS_OK 。\n */\nint sentinelResetMasterAndChangeAddress(sentinelRedisInstance *master, char *ip, int port) {\n    sentinelAddr *oldaddr, *newaddr;\n    sentinelAddr **slaves = NULL;\n    int numslaves = 0, j;\n    dictIterator *di;\n    dictEntry *de;\n\n    // 根据 ip 和 port 参数，创建地址结构\n    newaddr = createSentinelAddr(ip,port);\n    if (newaddr == NULL) return REDIS_ERR;\n\n    /* Make a list of slaves to add back after the reset.\n     * Don't include the one having the address we are switching to. */\n    // 创建一个包含原主服务器所有从服务器实例的数组\n    // 用于在重置地址之后进行检查\n    // 新主服务器（原主服务器的其中一个从服务器）的地址不会包含在这个数组中\n    di = dictGetIterator(master->slaves);\n    while((de = dictNext(di)) != NULL) {\n        sentinelRedisInstance *slave = dictGetVal(de);\n\n        // 跳过新主服务器\n        if (sentinelAddrIsEqual(slave->addr,newaddr)) continue;\n\n        // 将从服务器保存到数组中\n        slaves = zrealloc(slaves,sizeof(sentinelAddr*)*(numslaves+1));\n        slaves[numslaves++] = createSentinelAddr(slave->addr->ip,\n                                                 slave->addr->port);\n    }\n    dictReleaseIterator(di);\n    \n    /* If we are switching to a different address, include the old address\n     * as a slave as well, so that we'll be able to sense / reconfigure\n     * the old master. */\n    // 如果新地址和 master 的地址不相同，\n    // 将 master 的地址也作为从服务器地址添加到保存了所有从服务器地址的数组中\n    // （这样等于将下线主服务器设置为新主服务器的从服务器）\n    if (!sentinelAddrIsEqual(newaddr,master->addr)) {\n        slaves = zrealloc(slaves,sizeof(sentinelAddr*)*(numslaves+1));\n        slaves[numslaves++] = createSentinelAddr(master->addr->ip,\n                                                 master->addr->port);\n    }\n\n    /* Reset and switch address. */\n    // 重置 master 实例结构\n    sentinelResetMaster(master,SENTINEL_RESET_NO_SENTINELS);\n    oldaddr = master->addr;\n    // 为 master 实例设置新的地址\n    master->addr = newaddr;\n    master->o_down_since_time = 0;\n    master->s_down_since_time = 0;\n\n    /* Add slaves back. */\n    // 为实例加回之前保存的所有从服务器\n    for (j = 0; j < numslaves; j++) {\n        sentinelRedisInstance *slave;\n\n        slave = createSentinelRedisInstance(NULL,SRI_SLAVE,slaves[j]->ip,\n                    slaves[j]->port, master->quorum, master);\n\n        releaseSentinelAddr(slaves[j]);\n\n        if (slave) {\n            sentinelEvent(REDIS_NOTICE,\"+slave\",slave,\"%@\");\n            sentinelFlushConfig();\n        }\n    }\n    zfree(slaves);\n\n    /* Release the old address at the end so we are safe even if the function\n     * gets the master->addr->ip and master->addr->port as arguments. */\n    // 释放旧地址\n    releaseSentinelAddr(oldaddr);\n    sentinelFlushConfig();\n    return REDIS_OK;\n}\n\n/* Return non-zero if there was no SDOWN or ODOWN error associated to this\n * instance in the latest 'ms' milliseconds. */\n// 如果实例在给定 ms 中没有出现过 SDOWN 或者 ODOWN 状态\n// 那么函数返回一个非零值\nint sentinelRedisInstanceNoDownFor(sentinelRedisInstance *ri, mstime_t ms) {\n    mstime_t most_recent;\n\n    most_recent = ri->s_down_since_time;\n    if (ri->o_down_since_time > most_recent)\n        most_recent = ri->o_down_since_time;\n    return most_recent == 0 || (mstime() - most_recent) > ms;\n}\n\n/* Return the current master address, that is, its address or the address\n * of the promoted slave if already operational. */\n// 返回当前主服务器的地址\n// 如果 Sentinel 正在对主服务器进行故障迁移，那么返回新主服务器的地址\nsentinelAddr *sentinelGetCurrentMasterAddress(sentinelRedisInstance *master) {\n    /* If we are failing over the master, and the state is already\n     * SENTINEL_FAILOVER_STATE_RECONF_SLAVES or greater, it means that we\n     * already have the new configuration epoch in the master, and the\n     * slave acknowledged the configuration switch. Advertise the new\n     * address. */\n    if ((master->flags & SRI_FAILOVER_IN_PROGRESS) &&\n        master->promoted_slave &&\n        master->failover_state >= SENTINEL_FAILOVER_STATE_RECONF_SLAVES)\n    {\n        return master->promoted_slave->addr;\n    } else {\n        return master->addr;\n    }\n}\n\n/* This function sets the down_after_period field value in 'master' to all\n * the slaves and sentinel instances connected to this master. */\nvoid sentinelPropagateDownAfterPeriod(sentinelRedisInstance *master) {\n    dictIterator *di;\n    dictEntry *de;\n    int j;\n    dict *d[] = {master->slaves, master->sentinels, NULL};\n\n    for (j = 0; d[j]; j++) {\n        di = dictGetIterator(d[j]);\n        while((de = dictNext(di)) != NULL) {\n            sentinelRedisInstance *ri = dictGetVal(de);\n            ri->down_after_period = master->down_after_period;\n        }\n        dictReleaseIterator(di);\n    }\n}\n\n/* ============================ Config handling ============================= */\n\n// Sentinel 配置文件分析器\nchar *sentinelHandleConfiguration(char **argv, int argc) {\n    sentinelRedisInstance *ri;\n\n    // SENTINEL monitor 选项\n    if (!strcasecmp(argv[0],\"monitor\") && argc == 5) {\n        /* monitor <name> <host> <port> <quorum> */\n\n        // 读入 quorum 参数\n        int quorum = atoi(argv[4]);\n\n        // 检查 quorum 参数必须大于 0\n        if (quorum <= 0) return \"Quorum must be 1 or greater.\";\n\n        // 创建主服务器实例\n        if (createSentinelRedisInstance(argv[1],SRI_MASTER,argv[2],\n                                        atoi(argv[3]),quorum,NULL) == NULL)\n        {\n            switch(errno) {\n            case EBUSY: return \"Duplicated master name.\";\n            case ENOENT: return \"Can't resolve master instance hostname.\";\n            case EINVAL: return \"Invalid port number\";\n            }\n        }\n\n    // SENTINEL down-after-milliseconds 选项\n    } else if (!strcasecmp(argv[0],\"down-after-milliseconds\") && argc == 3) {\n\n        /* down-after-milliseconds <name> <milliseconds> */\n\n        // 查找主服务器\n        ri = sentinelGetMasterByName(argv[1]);\n        if (!ri) return \"No such master with specified name.\";\n\n        // 设置选项\n        ri->down_after_period = atoi(argv[2]);\n        if (ri->down_after_period <= 0)\n            return \"negative or zero time parameter.\";\n\n        sentinelPropagateDownAfterPeriod(ri);\n\n    // SENTINEL failover-timeout 选项\n    } else if (!strcasecmp(argv[0],\"failover-timeout\") && argc == 3) {\n\n        /* failover-timeout <name> <milliseconds> */\n\n        // 查找主服务器\n        ri = sentinelGetMasterByName(argv[1]);\n        if (!ri) return \"No such master with specified name.\";\n\n        // 设置选项\n        ri->failover_timeout = atoi(argv[2]);\n        if (ri->failover_timeout <= 0)\n            return \"negative or zero time parameter.\";\n\n   // Sentinel parallel-syncs 选项\n   } else if (!strcasecmp(argv[0],\"parallel-syncs\") && argc == 3) {\n\n        /* parallel-syncs <name> <milliseconds> */\n\n        // 查找主服务器\n        ri = sentinelGetMasterByName(argv[1]);\n        if (!ri) return \"No such master with specified name.\";\n\n        // 设置选项\n        ri->parallel_syncs = atoi(argv[2]);\n\n    // SENTINEL notification-script 选项\n   } else if (!strcasecmp(argv[0],\"notification-script\") && argc == 3) {\n\n        /* notification-script <name> <path> */\n        \n        // 查找主服务器\n        ri = sentinelGetMasterByName(argv[1]);\n        if (!ri) return \"No such master with specified name.\";\n\n        // 检查给定路径所指向的文件是否存在，以及是否可执行\n        if (access(argv[2],X_OK) == -1)\n            return \"Notification script seems non existing or non executable.\";\n\n        // 设置选项\n        ri->notification_script = sdsnew(argv[2]);\n\n    // SENTINEL client-reconfig-script 选项\n   } else if (!strcasecmp(argv[0],\"client-reconfig-script\") && argc == 3) {\n\n        /* client-reconfig-script <name> <path> */\n\n        // 查找主服务器\n        ri = sentinelGetMasterByName(argv[1]);\n        if (!ri) return \"No such master with specified name.\";\n        // 检查给定路径所指向的文件是否存在，以及是否可执行\n        if (access(argv[2],X_OK) == -1)\n            return \"Client reconfiguration script seems non existing or \"\n                   \"non executable.\";\n\n        // 设置选项\n        ri->client_reconfig_script = sdsnew(argv[2]);\n\n    // 设置 SENTINEL auth-pass 选项\n   } else if (!strcasecmp(argv[0],\"auth-pass\") && argc == 3) {\n\n        /* auth-pass <name> <password> */\n\n        // 查找主服务器\n        ri = sentinelGetMasterByName(argv[1]);\n        if (!ri) return \"No such master with specified name.\";\n\n        // 设置选项\n        ri->auth_pass = sdsnew(argv[2]);\n\n    } else if (!strcasecmp(argv[0],\"current-epoch\") && argc == 2) {\n        /* current-epoch <epoch> */\n        unsigned long long current_epoch = strtoull(argv[1],NULL,10);\n        if (current_epoch > sentinel.current_epoch)\n            sentinel.current_epoch = current_epoch;\n\n    // SENTINEL config-epoch 选项\n    } else if (!strcasecmp(argv[0],\"config-epoch\") && argc == 3) {\n\n        /* config-epoch <name> <epoch> */\n\n        ri = sentinelGetMasterByName(argv[1]);\n        if (!ri) return \"No such master with specified name.\";\n\n        ri->config_epoch = strtoull(argv[2],NULL,10);\n        /* The following update of current_epoch is not really useful as\n         * now the current epoch is persisted on the config file, but\n         * we leave this check here for redundancy. */\n        if (ri->config_epoch > sentinel.current_epoch)\n            sentinel.current_epoch = ri->config_epoch;\n\n    } else if (!strcasecmp(argv[0],\"leader-epoch\") && argc == 3) {\n        /* leader-epoch <name> <epoch> */\n        ri = sentinelGetMasterByName(argv[1]);\n        if (!ri) return \"No such master with specified name.\";\n        ri->leader_epoch = strtoull(argv[2],NULL,10);\n\n    // SENTINEL known-slave 选项\n    } else if (!strcasecmp(argv[0],\"known-slave\") && argc == 4) {\n        sentinelRedisInstance *slave;\n\n        /* known-slave <name> <ip> <port> */\n\n        ri = sentinelGetMasterByName(argv[1]);\n        if (!ri) return \"No such master with specified name.\";\n        if ((slave = createSentinelRedisInstance(NULL,SRI_SLAVE,argv[2],\n                    atoi(argv[3]), ri->quorum, ri)) == NULL)\n        {\n            return \"Wrong hostname or port for slave.\";\n        }\n\n    // SENTINEL known-sentinel 选项\n    } else if (!strcasecmp(argv[0],\"known-sentinel\") &&\n               (argc == 4 || argc == 5)) {\n        sentinelRedisInstance *si;\n\n        /* known-sentinel <name> <ip> <port> [runid] */\n\n        ri = sentinelGetMasterByName(argv[1]);\n        if (!ri) return \"No such master with specified name.\";\n        if ((si = createSentinelRedisInstance(NULL,SRI_SENTINEL,argv[2],\n                    atoi(argv[3]), ri->quorum, ri)) == NULL)\n        {\n            return \"Wrong hostname or port for sentinel.\";\n        }\n        if (argc == 5) si->runid = sdsnew(argv[4]);\n\n    } else {\n        return \"Unrecognized sentinel configuration statement.\";\n    }\n    return NULL;\n}\n\n/* Implements CONFIG REWRITE for \"sentinel\" option.\n * This is used not just to rewrite the configuration given by the user\n * (the configured masters) but also in order to retain the state of\n * Sentinel across restarts: config epoch of masters, associated slaves\n * and sentinel instances, and so forth. */\n// CONFIG REWIRTE 命令中和 sentinel 选项有关的部分\n// 这个函数不仅用于用户执行 CONFIG REWRITE 的时候，\n// 也用于保存 Sentinel 状态，以备 Sentinel 重启时载入状态使用\nvoid rewriteConfigSentinelOption(struct rewriteConfigState *state) {\n    dictIterator *di, *di2;\n    dictEntry *de;\n    sds line;\n\n    /* For every master emit a \"sentinel monitor\" config entry. */\n    di = dictGetIterator(sentinel.masters);\n    while((de = dictNext(di)) != NULL) {\n        sentinelRedisInstance *master, *ri;\n        sentinelAddr *master_addr;\n\n        /* sentinel monitor */\n        master = dictGetVal(de);\n        master_addr = sentinelGetCurrentMasterAddress(master);\n        line = sdscatprintf(sdsempty(),\"sentinel monitor %s %s %d %d\",\n            master->name, master_addr->ip, master_addr->port,\n            master->quorum);\n        rewriteConfigRewriteLine(state,\"sentinel\",line,1);\n\n        /* sentinel down-after-milliseconds */\n        if (master->down_after_period != SENTINEL_DEFAULT_DOWN_AFTER) {\n            line = sdscatprintf(sdsempty(),\n                \"sentinel down-after-milliseconds %s %ld\",\n                master->name, (long) master->down_after_period);\n            rewriteConfigRewriteLine(state,\"sentinel\",line,1);\n        }\n\n        /* sentinel failover-timeout */\n        if (master->failover_timeout != SENTINEL_DEFAULT_FAILOVER_TIMEOUT) {\n            line = sdscatprintf(sdsempty(),\n                \"sentinel failover-timeout %s %ld\",\n                master->name, (long) master->failover_timeout);\n            rewriteConfigRewriteLine(state,\"sentinel\",line,1);\n        }\n\n        /* sentinel parallel-syncs */\n        if (master->parallel_syncs != SENTINEL_DEFAULT_PARALLEL_SYNCS) {\n            line = sdscatprintf(sdsempty(),\n                \"sentinel parallel-syncs %s %d\",\n                master->name, master->parallel_syncs);\n            rewriteConfigRewriteLine(state,\"sentinel\",line,1);\n        }\n\n        /* sentinel notification-script */\n        if (master->notification_script) {\n            line = sdscatprintf(sdsempty(),\n                \"sentinel notification-script %s %s\",\n                master->name, master->notification_script);\n            rewriteConfigRewriteLine(state,\"sentinel\",line,1);\n        }\n\n        /* sentinel client-reconfig-script */\n        if (master->client_reconfig_script) {\n            line = sdscatprintf(sdsempty(),\n                \"sentinel client-reconfig-script %s %s\",\n                master->name, master->client_reconfig_script);\n            rewriteConfigRewriteLine(state,\"sentinel\",line,1);\n        }\n\n        /* sentinel auth-pass */\n        if (master->auth_pass) {\n            line = sdscatprintf(sdsempty(),\n                \"sentinel auth-pass %s %s\",\n                master->name, master->auth_pass);\n            rewriteConfigRewriteLine(state,\"sentinel\",line,1);\n        }\n\n        /* sentinel config-epoch */\n        line = sdscatprintf(sdsempty(),\n            \"sentinel config-epoch %s %llu\",\n            master->name, (unsigned long long) master->config_epoch);\n        rewriteConfigRewriteLine(state,\"sentinel\",line,1);\n\n        /* sentinel leader-epoch */\n        line = sdscatprintf(sdsempty(),\n            \"sentinel leader-epoch %s %llu\",\n            master->name, (unsigned long long) master->leader_epoch);\n        rewriteConfigRewriteLine(state,\"sentinel\",line,1);\n\n        /* sentinel known-slave */\n        di2 = dictGetIterator(master->slaves);\n        while((de = dictNext(di2)) != NULL) {\n            sentinelAddr *slave_addr;\n\n            ri = dictGetVal(de);\n            slave_addr = ri->addr;\n\n            /* If master_addr (obtained using sentinelGetCurrentMasterAddress()\n             * so it may be the address of the promoted slave) is equal to this\n             * slave's address, a failover is in progress and the slave was\n             * already successfully promoted. So as the address of this slave\n             * we use the old master address instead. */\n            if (sentinelAddrIsEqual(slave_addr,master_addr))\n                slave_addr = master->addr;\n            line = sdscatprintf(sdsempty(),\n                \"sentinel known-slave %s %s %d\",\n                master->name, ri->addr->ip, ri->addr->port);\n            rewriteConfigRewriteLine(state,\"sentinel\",line,1);\n        }\n        dictReleaseIterator(di2);\n\n        /* sentinel known-sentinel */\n        di2 = dictGetIterator(master->sentinels);\n        while((de = dictNext(di2)) != NULL) {\n            ri = dictGetVal(de);\n            line = sdscatprintf(sdsempty(),\n                \"sentinel known-sentinel %s %s %d%s%s\",\n                master->name, ri->addr->ip, ri->addr->port,\n                ri->runid ? \" \" : \"\",\n                ri->runid ? ri->runid : \"\");\n            rewriteConfigRewriteLine(state,\"sentinel\",line,1);\n        }\n        dictReleaseIterator(di2);\n    }\n\n    /* sentinel current-epoch is a global state valid for all the masters. */\n    line = sdscatprintf(sdsempty(),\n        \"sentinel current-epoch %llu\", (unsigned long long) sentinel.current_epoch);\n    rewriteConfigRewriteLine(state,\"sentinel\",line,1);\n\n    dictReleaseIterator(di);\n}\n\n/* This function uses the config rewriting Redis engine in order to persist\n * the state of the Sentinel in the current configuration file.\n *\n * 使用 CONFIG REWRITE 功能，将当前 Sentinel 的状态持久化到配置文件里面。\n *\n * Before returning the function calls fsync() against the generated\n * configuration file to make sure changes are committed to disk.\n *\n * 在函数返回之前，程序会调用一次 fsync() ，确保文件已经被保存到磁盘里面。\n *\n * On failure the function logs a warning on the Redis log. \n *\n * 如果保存失败，那么打印一条警告日志。\n */\nvoid sentinelFlushConfig(void) {\n    int fd = -1;\n    int saved_hz = server.hz;\n    int rewrite_status;\n\n    server.hz = REDIS_DEFAULT_HZ;\n    rewrite_status = rewriteConfig(server.configfile);\n    server.hz = saved_hz;\n\n    if (rewrite_status == -1) goto werr;\n    if ((fd = open(server.configfile,O_RDONLY)) == -1) goto werr;\n    if (fsync(fd) == -1) goto werr;\n    if (close(fd) == EOF) goto werr;\n    return;\n\nwerr:\n    if (fd != -1) close(fd);\n    redisLog(REDIS_WARNING,\"WARNING: Sentinel was not able to save the new configuration on disk!!!: %s\", strerror(errno));\n}\n\n/* ====================== hiredis connection handling ======================= */\n\n/* Completely disconnect a hiredis link from an instance. */\n// 断开实例的连接\nvoid sentinelKillLink(sentinelRedisInstance *ri, redisAsyncContext *c) {\n    if (ri->cc == c) {\n        ri->cc = NULL;\n        ri->pending_commands = 0;\n    }\n    if (ri->pc == c) ri->pc = NULL;\n    c->data = NULL;\n\n    // 打开断线标志\n    ri->flags |= SRI_DISCONNECTED;\n\n    // 断开连接\n    redisAsyncFree(c);\n}\n\n/* This function takes a hiredis context that is in an error condition\n * and make sure to mark the instance as disconnected performing the\n * cleanup needed.\n *\n * 函数将一个出错连接设置正确的断线标志，并执行清理操作\n *\n * Note: we don't free the hiredis context as hiredis will do it for us\n * for async connections. \n *\n * 这个函数没有手动释放连接，因为异步连接会自动释放\n */\nvoid sentinelDisconnectInstanceFromContext(const redisAsyncContext *c) {\n    sentinelRedisInstance *ri = c->data;\n    int pubsub;\n\n    if (ri == NULL) return; /* The instance no longer exists. */\n\n    // 发送断线事件\n    pubsub = (ri->pc == c);\n    sentinelEvent(REDIS_DEBUG, pubsub ? \"-pubsub-link\" : \"-cmd-link\", ri,\n        \"%@ #%s\", c->errstr);\n\n    if (pubsub)\n        ri->pc = NULL;\n    else\n        ri->cc = NULL;\n\n    // 打开标志\n    ri->flags |= SRI_DISCONNECTED;\n}\n\n// 异步连接的连接回调函数\nvoid sentinelLinkEstablishedCallback(const redisAsyncContext *c, int status) {\n    if (status != REDIS_OK) {\n        sentinelDisconnectInstanceFromContext(c);\n    } else {\n        sentinelRedisInstance *ri = c->data;\n        int pubsub = (ri->pc == c);\n\n        // 发送连接事件\n        sentinelEvent(REDIS_DEBUG, pubsub ? \"+pubsub-link\" : \"+cmd-link\", ri,\n            \"%@\");\n    }\n}\n\n// 异步连接的断线回调函数\nvoid sentinelDisconnectCallback(const redisAsyncContext *c, int status) {\n    sentinelDisconnectInstanceFromContext(c);\n}\n\n/* Send the AUTH command with the specified master password if needed.\n * Note that for slaves the password set for the master is used.\n *\n * 如果 sentinel 设置了 auth-pass 选项，那么向主服务器或者从服务器发送验证密码。\n * 注意从服务器使用的是主服务器的密码。\n *\n * We don't check at all if the command was successfully transmitted\n * to the instance as if it fails Sentinel will detect the instance down,\n * will disconnect and reconnect the link and so forth. \n *\n * 函数不检查命令是否被成功发送，因为如果目标服务器掉线了的话， sentinel 会识别到，\n * 并对它进行重连接，然后又重新发送 AUTH 命令。\n */\nvoid sentinelSendAuthIfNeeded(sentinelRedisInstance *ri, redisAsyncContext *c) {\n\n    // 如果 ri 是主服务器，那么使用实例自己的密码\n    // 如果 ri 是从服务器，那么使用主服务器的密码\n    char *auth_pass = (ri->flags & SRI_MASTER) ? ri->auth_pass :\n                                                 ri->master->auth_pass;\n\n    // 发送 AUTH 命令\n    if (auth_pass) {\n        if (redisAsyncCommand(c, sentinelDiscardReplyCallback, NULL, \"AUTH %s\",\n            auth_pass) == REDIS_OK) ri->pending_commands++;\n    }\n}\n\n/* Use CLIENT SETNAME to name the connection in the Redis instance as\n * sentinel-<first_8_chars_of_runid>-<connection_type>\n * The connection type is \"cmd\" or \"pubsub\" as specified by 'type'.\n *\n * This makes it possible to list all the sentinel instances connected\n * to a Redis servewr with CLIENT LIST, grepping for a specific name format. */\n// 使用 CLIENT SETNAME 命令，为给定的客户端设置名字。\nvoid sentinelSetClientName(sentinelRedisInstance *ri, redisAsyncContext *c, char *type) {\n    char name[64];\n\n    snprintf(name,sizeof(name),\"sentinel-%.8s-%s\",server.runid,type);\n    if (redisAsyncCommand(c, sentinelDiscardReplyCallback, NULL,\n        \"CLIENT SETNAME %s\", name) == REDIS_OK)\n    {\n        ri->pending_commands++;\n    }\n}\n\n/* Create the async connections for the specified instance if the instance\n * is disconnected. Note that the SRI_DISCONNECTED flag is set even if just\n * one of the two links (commands and pub/sub) is missing. */\n// 如果 sentinel 与实例处于断线（未连接）状态，那么创建连向实例的异步连接。\nvoid sentinelReconnectInstance(sentinelRedisInstance *ri) {\n\n    // 示例未断线（已连接），返回\n    if (!(ri->flags & SRI_DISCONNECTED)) return;\n\n    /* Commands connection. */\n    // 对所有实例创建一个用于发送 Redis 命令的连接\n    if (ri->cc == NULL) {\n\n        // 连接实例\n        ri->cc = redisAsyncConnect(ri->addr->ip,ri->addr->port);\n\n        // 连接出错\n        if (ri->cc->err) {\n            sentinelEvent(REDIS_DEBUG,\"-cmd-link-reconnection\",ri,\"%@ #%s\",\n                ri->cc->errstr);\n            sentinelKillLink(ri,ri->cc);\n\n        // 连接成功\n        } else {\n            // 设置连接属性\n            ri->cc_conn_time = mstime();\n            ri->cc->data = ri;\n            redisAeAttach(server.el,ri->cc);\n            // 设置连线 callback\n            redisAsyncSetConnectCallback(ri->cc,\n                                            sentinelLinkEstablishedCallback);\n            // 设置断线 callback\n            redisAsyncSetDisconnectCallback(ri->cc,\n                                            sentinelDisconnectCallback);\n            // 发送 AUTH 命令，验证身份\n            sentinelSendAuthIfNeeded(ri,ri->cc);\n            sentinelSetClientName(ri,ri->cc,\"cmd\");\n\n            /* Send a PING ASAP when reconnecting. */\n            sentinelSendPing(ri);\n        }\n    }\n\n    /* Pub / Sub */\n    // 对主服务器和从服务器，创建一个用于订阅频道的连接\n    if ((ri->flags & (SRI_MASTER|SRI_SLAVE)) && ri->pc == NULL) {\n\n        // 连接实例\n        ri->pc = redisAsyncConnect(ri->addr->ip,ri->addr->port);\n\n        // 连接出错\n        if (ri->pc->err) {\n            sentinelEvent(REDIS_DEBUG,\"-pubsub-link-reconnection\",ri,\"%@ #%s\",\n                ri->pc->errstr);\n            sentinelKillLink(ri,ri->pc);\n\n        // 连接成功\n        } else {\n            int retval;\n\n            // 设置连接属性\n            ri->pc_conn_time = mstime();\n            ri->pc->data = ri;\n            redisAeAttach(server.el,ri->pc);\n            // 设置连接 callback\n            redisAsyncSetConnectCallback(ri->pc,\n                                            sentinelLinkEstablishedCallback);\n            // 设置断线 callback\n            redisAsyncSetDisconnectCallback(ri->pc,\n                                            sentinelDisconnectCallback);\n            // 发送 AUTH 命令，验证身份\n            sentinelSendAuthIfNeeded(ri,ri->pc);\n\n            // 为客户但设置名字 \"pubsub\"\n            sentinelSetClientName(ri,ri->pc,\"pubsub\");\n\n            /* Now we subscribe to the Sentinels \"Hello\" channel. */\n            // 发送 SUBSCRIBE __sentinel__:hello 命令，订阅频道\n            retval = redisAsyncCommand(ri->pc,\n                sentinelReceiveHelloMessages, NULL, \"SUBSCRIBE %s\",\n                    SENTINEL_HELLO_CHANNEL);\n            \n            // 订阅出错，断开连接\n            if (retval != REDIS_OK) {\n                /* If we can't subscribe, the Pub/Sub connection is useless\n                 * and we can simply disconnect it and try again. */\n                sentinelKillLink(ri,ri->pc);\n                return;\n            }\n        }\n    }\n\n    /* Clear the DISCONNECTED flags only if we have both the connections\n     * (or just the commands connection if this is a sentinel instance). */\n    // 如果实例是主服务器或者从服务器，那么当 cc 和 pc 两个连接都创建成功时，关闭 DISCONNECTED 标识\n    // 如果实例是 Sentinel ，那么当 cc 连接创建成功时，关闭 DISCONNECTED 标识\n    if (ri->cc && (ri->flags & SRI_SENTINEL || ri->pc))\n        ri->flags &= ~SRI_DISCONNECTED;\n}\n\n/* ======================== Redis instances pinging  ======================== */\n\n/* Return true if master looks \"sane\", that is:\n *\n * 如果主服务器看上去是合理（sane），那么返回真。判断是否合理的条件如下：\n *\n * 1) It is actually a master in the current configuration.\n *    它在当前配置中的角色为主服务器\n * 2) It reports itself as a master.\n *    它报告自己是一个主服务器\n * 3) It is not SDOWN or ODOWN.\n *    这个主服务器不处于 SDOWN 或者 ODOWN 状态\n * 4) We obtained last INFO no more than two times the INFO period time ago. \n *    主服务器最近一次刷新 INFO 信息距离现在不超过 SENTINEL_INFO_PERIOD 的两倍时间\n */\nint sentinelMasterLooksSane(sentinelRedisInstance *master) {\n    return\n        master->flags & SRI_MASTER &&\n        master->role_reported == SRI_MASTER &&\n        (master->flags & (SRI_S_DOWN|SRI_O_DOWN)) == 0 &&\n        (mstime() - master->info_refresh) < SENTINEL_INFO_PERIOD*2;\n}\n\n/* Process the INFO output from masters. */\n// 从主服务器或者从服务器所返回的 INFO 命令的回复中分析相关信息\n// （上面的英文注释错了，这个函数不仅处理主服务器的 INFO 回复，还处理从服务器的 INFO 回复）\nvoid sentinelRefreshInstanceInfo(sentinelRedisInstance *ri, const char *info) {\n    sds *lines;\n    int numlines, j;\n    int role = 0;\n\n    /* The following fields must be reset to a given value in the case they\n     * are not found at all in the INFO output. */\n    // 将该变量重置为 0 ，避免 INFO 回复中无该值的情况\n    ri->master_link_down_time = 0;\n\n    /* Process line by line. */\n    // 对 INFO 命令的回复进行逐行分析\n    lines = sdssplitlen(info,strlen(info),\"\\r\\n\",2,&numlines);\n    for (j = 0; j < numlines; j++) {\n        sentinelRedisInstance *slave;\n        sds l = lines[j];\n\n        /* run_id:<40 hex chars>*/\n        // 读取并分析 runid\n        if (sdslen(l) >= 47 && !memcmp(l,\"run_id:\",7)) {\n\n            // 新设置 runid\n            if (ri->runid == NULL) {\n                ri->runid = sdsnewlen(l+7,40);\n            } else {\n                // RUNID 不同，说明服务器已重启\n                if (strncmp(ri->runid,l+7,40) != 0) {\n                    sentinelEvent(REDIS_NOTICE,\"+reboot\",ri,\"%@\");\n\n                    // 释放旧 ID ，设置新 ID\n                    sdsfree(ri->runid);\n                    ri->runid = sdsnewlen(l+7,40);\n                }\n            }\n        }\n\n        // 读取从服务器的 ip 和端口号\n        /* old versions: slave0:<ip>,<port>,<state>\n         * new versions: slave0:ip=127.0.0.1,port=9999,... */\n        if ((ri->flags & SRI_MASTER) &&\n            sdslen(l) >= 7 &&\n            !memcmp(l,\"slave\",5) && isdigit(l[5]))\n        {\n            char *ip, *port, *end;\n\n            if (strstr(l,\"ip=\") == NULL) {\n                /* Old format. */\n                ip = strchr(l,':'); if (!ip) continue;\n                ip++; /* Now ip points to start of ip address. */\n                port = strchr(ip,','); if (!port) continue;\n                *port = '\\0'; /* nul term for easy access. */\n                port++; /* Now port points to start of port number. */\n                end = strchr(port,','); if (!end) continue;\n                *end = '\\0'; /* nul term for easy access. */\n            } else {\n                /* New format. */\n                ip = strstr(l,\"ip=\"); if (!ip) continue;\n                ip += 3; /* Now ip points to start of ip address. */\n                port = strstr(l,\"port=\"); if (!port) continue;\n                port += 5; /* Now port points to start of port number. */\n                /* Nul term both fields for easy access. */\n                end = strchr(ip,','); if (end) *end = '\\0';\n                end = strchr(port,','); if (end) *end = '\\0';\n            }\n\n            /* Check if we already have this slave into our table,\n             * otherwise add it. */\n            // 如果发现有新的从服务器出现，那么为它添加实例\n            if (sentinelRedisInstanceLookupSlave(ri,ip,atoi(port)) == NULL) {\n                if ((slave = createSentinelRedisInstance(NULL,SRI_SLAVE,ip,\n                            atoi(port), ri->quorum, ri)) != NULL)\n                {\n                    sentinelEvent(REDIS_NOTICE,\"+slave\",slave,\"%@\");\n                }\n            }\n        }\n\n        /* master_link_down_since_seconds:<seconds> */\n        // 读取主从服务器的断线时长\n        // 这个只会在实例是从服务器，并且主从连接断开的情况下出现\n        if (sdslen(l) >= 32 &&\n            !memcmp(l,\"master_link_down_since_seconds\",30))\n        {\n            ri->master_link_down_time = strtoll(l+31,NULL,10)*1000;\n        }\n\n        /* role:<role> */\n        // 读取实例的角色\n        if (!memcmp(l,\"role:master\",11)) role = SRI_MASTER;\n        else if (!memcmp(l,\"role:slave\",10)) role = SRI_SLAVE;\n\n        // 处理从服务器\n        if (role == SRI_SLAVE) {\n\n            /* master_host:<host> */\n            // 读入主服务器的 IP\n            if (sdslen(l) >= 12 && !memcmp(l,\"master_host:\",12)) {\n                if (ri->slave_master_host == NULL ||\n                    strcasecmp(l+12,ri->slave_master_host))\n                {\n                    sdsfree(ri->slave_master_host);\n                    ri->slave_master_host = sdsnew(l+12);\n                    ri->slave_conf_change_time = mstime();\n                }\n            }\n\n            /* master_port:<port> */\n            // 读入主服务器的端口号\n            if (sdslen(l) >= 12 && !memcmp(l,\"master_port:\",12)) {\n                int slave_master_port = atoi(l+12);\n\n                if (ri->slave_master_port != slave_master_port) {\n                    ri->slave_master_port = slave_master_port;\n                    ri->slave_conf_change_time = mstime();\n                }\n            }\n            \n            /* master_link_status:<status> */\n            // 读入主服务器的状态\n            if (sdslen(l) >= 19 && !memcmp(l,\"master_link_status:\",19)) {\n                ri->slave_master_link_status =\n                    (strcasecmp(l+19,\"up\") == 0) ?\n                    SENTINEL_MASTER_LINK_STATUS_UP :\n                    SENTINEL_MASTER_LINK_STATUS_DOWN;\n            }\n\n            /* slave_priority:<priority> */\n            // 读入从服务器的优先级\n            if (sdslen(l) >= 15 && !memcmp(l,\"slave_priority:\",15))\n                ri->slave_priority = atoi(l+15);\n\n            /* slave_repl_offset:<offset> */\n            // 读入从服务器的复制偏移量\n            if (sdslen(l) >= 18 && !memcmp(l,\"slave_repl_offset:\",18))\n                ri->slave_repl_offset = strtoull(l+18,NULL,10);\n        }\n    }\n\n    // 更新刷新 INFO 命令回复的时间\n    ri->info_refresh = mstime();\n    sdsfreesplitres(lines,numlines);\n\n    /* ---------------------------- Acting half -----------------------------\n     * Some things will not happen if sentinel.tilt is true, but some will\n     * still be processed. \n     *\n     * 如果 sentinel 进入了 TILT 模式，那么可能只有一部分动作会被执行\n     */\n\n    /* Remember when the role changed. */\n    if (role != ri->role_reported) {\n        ri->role_reported_time = mstime();\n        ri->role_reported = role;\n        if (role == SRI_SLAVE) ri->slave_conf_change_time = mstime();\n        /* Log the event with +role-change if the new role is coherent or\n         * with -role-change if there is a mismatch with the current config. */\n        sentinelEvent(REDIS_VERBOSE,\n            ((ri->flags & (SRI_MASTER|SRI_SLAVE)) == role) ?\n            \"+role-change\" : \"-role-change\",\n            ri, \"%@ new reported role is %s\",\n            role == SRI_MASTER ? \"master\" : \"slave\",\n            ri->flags & SRI_MASTER ? \"master\" : \"slave\");\n    }\n\n    /* None of the following conditions are processed when in tilt mode, so\n     * return asap. */\n    // 如果 Sentinel 正处于 TILT 模式，那么它不能执行以下的语句。\n    if (sentinel.tilt) return;\n\n    /* Handle master -> slave role switch. */\n    // 实例被 Sentinel 标识为主服务器，但根据 INFO 命令的回复\n    // 这个实例的身份为从服务器\n    if ((ri->flags & SRI_MASTER) && role == SRI_SLAVE) {\n        /* Nothing to do, but masters claiming to be slaves are\n         * considered to be unreachable by Sentinel, so eventually\n         * a failover will be triggered. */\n        // 如果一个主服务器变为从服务器，那么 Sentinel 将这个主服务器看作是不可用的\n    }\n\n    /* Handle slave -> master role switch. */\n    // 处理从服务器转变为主服务器的情况\n    if ((ri->flags & SRI_SLAVE) && role == SRI_MASTER) {\n        /* If this is a promoted slave we can change state to the\n         * failover state machine. */\n\n        // 如果这是被选中升级为新主服务器的从服务器\n        // 那么更新相关的故障转移属性\n        if ((ri->master->flags & SRI_FAILOVER_IN_PROGRESS) &&\n            (ri->master->failover_state ==\n                SENTINEL_FAILOVER_STATE_WAIT_PROMOTION))\n        {\n            /* Now that we are sure the slave was reconfigured as a master\n             * set the master configuration epoch to the epoch we won the\n             * election to perform this failover. This will force the other\n             * Sentinels to update their config (assuming there is not\n             * a newer one already available). */\n            // 这是一个被 Sentinel 发送 SLAVEOF no one 之后由从服务器变为主服务器的实例\n            // 将这个新主服务器的配置纪元设置为 Sentinel 赢得领头选举的纪元\n            // 这一操作会强制其他 Sentinel 更新它们自己的配置\n            // （假设没有一个更新的纪元存在的话）\n            // 更新从服务器的主服务器（已下线）的配置纪元\n            ri->master->config_epoch = ri->master->failover_epoch;\n            // 设置从服务器的主服务器（已下线）的故障转移状态\n            // 这个状态会让从服务器开始同步新的主服务器\n            ri->master->failover_state = SENTINEL_FAILOVER_STATE_RECONF_SLAVES;\n            // 更新从服务器的主服务器（已下线）的故障转移状态变更时间\n            ri->master->failover_state_change_time = mstime();\n            // 将当前 Sentinel 状态保存到配置文件里面\n            sentinelFlushConfig();\n            // 发送事件\n            sentinelEvent(REDIS_WARNING,\"+promoted-slave\",ri,\"%@\");\n            sentinelEvent(REDIS_WARNING,\"+failover-state-reconf-slaves\",\n                ri->master,\"%@\");\n            // 执行脚本\n            sentinelCallClientReconfScript(ri->master,SENTINEL_LEADER,\n                \"start\",ri->master->addr,ri->addr);\n\n        // 这个实例由从服务器变为了主服务器，并且没有进入 TILT 模式\n        // （可能是因为重启造成的，或者之前的下线主服务器重新上线了）\n        } else {\n            /* A slave turned into a master. We want to force our view and\n             * reconfigure as slave. Wait some time after the change before\n             * going forward, to receive new configs if any. */\n            // 如果一个从服务器变为了主服务器，那么我们会考虑将它变回一个从服务器\n\n            // 将 PUBLISH 命令的发送时间乘以 4 ，给于一定缓冲时间\n            mstime_t wait_time = SENTINEL_PUBLISH_PERIOD*4;\n\n            // 如果这个实例的主服务器运作正常\n            // 并且实例在一段时间内没有进入过 SDOWN 状态或者 ODOWN 状态\n            // 并且实例报告它是主服务器的时间已经超过 wait_time\n            if (sentinelMasterLooksSane(ri->master) &&\n               sentinelRedisInstanceNoDownFor(ri,wait_time) &&\n               mstime() - ri->role_reported_time > wait_time)\n            {\n                // 重新将实例设置为从服务器\n                int retval = sentinelSendSlaveOf(ri,\n                        ri->master->addr->ip,\n                        ri->master->addr->port);\n                \n                // 发送事件\n                if (retval == REDIS_OK)\n                    sentinelEvent(REDIS_NOTICE,\"+convert-to-slave\",ri,\"%@\");\n            }\n        }\n    }\n\n    /* Handle slaves replicating to a different master address. */\n    // 让从服务器重新复制回正确的主服务器\n    if ((ri->flags & SRI_SLAVE) &&\n        role == SRI_SLAVE &&\n        // 从服务器现在的主服务器地址和 Sentinel 保存的信息不一致\n        (ri->slave_master_port != ri->master->addr->port ||\n         strcasecmp(ri->slave_master_host,ri->master->addr->ip)))\n    {\n        mstime_t wait_time = ri->master->failover_timeout;\n\n        /* Make sure the master is sane before reconfiguring this instance\n         * into a slave. */\n        // 1) 检查实例的主服务器状态是否正常\n        // 2) 检查实例在给定时间内是否进入过 SDOWN 或者 ODOWN 状态\n        // 3) 检查实例身份变更的时长是否已经超过了指定时长\n        // 如果是的话，执行代码。。。\n        if (sentinelMasterLooksSane(ri->master) &&\n            sentinelRedisInstanceNoDownFor(ri,wait_time) &&\n            mstime() - ri->slave_conf_change_time > wait_time)\n        {\n            // 重新将实例指向原本的主服务器\n            int retval = sentinelSendSlaveOf(ri,\n                    ri->master->addr->ip,\n                    ri->master->addr->port);\n\n            if (retval == REDIS_OK)\n                sentinelEvent(REDIS_NOTICE,\"+fix-slave-config\",ri,\"%@\");\n        }\n    }\n\n    /* Detect if the slave that is in the process of being reconfigured\n     * changed state. */\n    // Sentinel 监视的实例为从服务器，并且已经向它发送 SLAVEOF 命令\n    if ((ri->flags & SRI_SLAVE) && role == SRI_SLAVE &&\n        (ri->flags & (SRI_RECONF_SENT|SRI_RECONF_INPROG)))\n    {\n        /* SRI_RECONF_SENT -> SRI_RECONF_INPROG. */\n        // 将 SENT 状态改为 INPROG 状态，表示同步正在进行\n        if ((ri->flags & SRI_RECONF_SENT) &&\n            ri->slave_master_host &&\n            strcmp(ri->slave_master_host,\n                    ri->master->promoted_slave->addr->ip) == 0 &&\n            ri->slave_master_port == ri->master->promoted_slave->addr->port)\n        {\n            ri->flags &= ~SRI_RECONF_SENT;\n            ri->flags |= SRI_RECONF_INPROG;\n            sentinelEvent(REDIS_NOTICE,\"+slave-reconf-inprog\",ri,\"%@\");\n        }\n\n        /* SRI_RECONF_INPROG -> SRI_RECONF_DONE */\n        // 将 INPROG 状态改为 DONE 状态，表示同步已完成\n        if ((ri->flags & SRI_RECONF_INPROG) &&\n            ri->slave_master_link_status == SENTINEL_MASTER_LINK_STATUS_UP)\n        {\n            ri->flags &= ~SRI_RECONF_INPROG;\n            ri->flags |= SRI_RECONF_DONE;\n            sentinelEvent(REDIS_NOTICE,\"+slave-reconf-done\",ri,\"%@\");\n        }\n    }\n}\n\n// 处理 INFO 命令的回复\nvoid sentinelInfoReplyCallback(redisAsyncContext *c, void *reply, void *privdata) {\n    sentinelRedisInstance *ri = c->data;\n    redisReply *r;\n\n    if (ri) ri->pending_commands--;\n    if (!reply || !ri) return;\n    r = reply;\n\n    if (r->type == REDIS_REPLY_STRING) {\n        sentinelRefreshInstanceInfo(ri,r->str);\n    }\n}\n\n/* Just discard the reply. We use this when we are not monitoring the return\n * value of the command but its effects directly. */\n// 这个回调函数用于处理不需要检查回复的命令（只使用命令的副作用）\nvoid sentinelDiscardReplyCallback(redisAsyncContext *c, void *reply, void *privdata) {\n    sentinelRedisInstance *ri = c->data;\n\n    if (ri) ri->pending_commands--;\n}\n\n// 处理 PING 命令的回复\nvoid sentinelPingReplyCallback(redisAsyncContext *c, void *reply, void *privdata) {\n    sentinelRedisInstance *ri = c->data;\n    redisReply *r;\n\n    if (ri) ri->pending_commands--;\n    if (!reply || !ri) return;\n    r = reply;\n\n    if (r->type == REDIS_REPLY_STATUS ||\n        r->type == REDIS_REPLY_ERROR) {\n\n        /* Update the \"instance available\" field only if this is an\n         * acceptable reply. */\n        // 只在实例返回 acceptable 回复时更新 last_avail_time\n        if (strncmp(r->str,\"PONG\",4) == 0 ||\n            strncmp(r->str,\"LOADING\",7) == 0 ||\n            strncmp(r->str,\"MASTERDOWN\",10) == 0)\n        {\n            // 实例运作正常\n            ri->last_avail_time = mstime();\n            ri->last_ping_time = 0; /* Flag the pong as received. */\n        } else {\n\n            // 实例运作不正常\n\n            /* Send a SCRIPT KILL command if the instance appears to be\n             * down because of a busy script. */\n            // 如果服务器因为执行脚本而进入 BUSY 状态，\n            // 那么尝试通过发送 SCRIPT KILL 来恢复服务器\n            if (strncmp(r->str,\"BUSY\",4) == 0 &&\n                (ri->flags & SRI_S_DOWN) &&\n                !(ri->flags & SRI_SCRIPT_KILL_SENT))\n            {\n                if (redisAsyncCommand(ri->cc,\n                        sentinelDiscardReplyCallback, NULL,\n                        \"SCRIPT KILL\") == REDIS_OK)\n                    ri->pending_commands++;\n                ri->flags |= SRI_SCRIPT_KILL_SENT;\n            }\n        }\n    }\n\n    // 更新实例最后一次回复 PING 命令的时间\n    ri->last_pong_time = mstime();\n}\n\n/* This is called when we get the reply about the PUBLISH command we send\n * to the master to advertise this sentinel. */\n// 处理 PUBLISH 命令的回复\nvoid sentinelPublishReplyCallback(redisAsyncContext *c, void *reply, void *privdata) {\n    sentinelRedisInstance *ri = c->data;\n    redisReply *r;\n\n    if (ri) ri->pending_commands--;\n    if (!reply || !ri) return;\n    r = reply;\n\n    /* Only update pub_time if we actually published our message. Otherwise\n     * we'll retry against in 100 milliseconds. */\n    // 如果命令发送成功，那么更新 last_pub_time\n    if (r->type != REDIS_REPLY_ERROR)\n        ri->last_pub_time = mstime();\n}\n\n/* Process an hello message received via Pub/Sub in master or slave instance,\n * or sent directly to this sentinel via the (fake) PUBLISH command of Sentinel.\n *\n * 处理从 Pub/Sub 连接得来的，来自主服务器或者从服务器的 hello 消息。\n * hello 消息也可能是另一个 Sentinel 通过 PUBLISH 命令发送过来的。\n *\n * If the master name specified in the message is not known, the message is\n * discareded. \n *\n * 如果消息里面指定的主服务器的名字是未知的，那么这条消息将被丢弃。\n */\nvoid sentinelProcessHelloMessage(char *hello, int hello_len) {\n    /* Format is composed of 8 tokens:\n     * 0=ip,1=port,2=runid,3=current_epoch,4=master_name,\n     * 5=master_ip,6=master_port,7=master_config_epoch. */\n    int numtokens, port, removed, master_port;\n    uint64_t current_epoch, master_config_epoch;\n    char **token = sdssplitlen(hello, hello_len, \",\", 1, &numtokens);\n    sentinelRedisInstance *si, *master;\n\n    if (numtokens == 8) {\n        /* Obtain a reference to the master this hello message is about */\n        // 获取主服务器的名字，并丢弃和未知主服务器相关的消息。\n        master = sentinelGetMasterByName(token[4]);\n        if (!master) goto cleanup; /* Unknown master, skip the message. */\n\n        /* First, try to see if we already have this sentinel. */\n        // 看这个 Sentinel 是否已经认识发送消息的 Sentinel\n        port = atoi(token[1]);\n        master_port = atoi(token[6]);\n        si = getSentinelRedisInstanceByAddrAndRunID(\n                        master->sentinels,token[0],port,token[2]);\n        current_epoch = strtoull(token[3],NULL,10);\n        master_config_epoch = strtoull(token[7],NULL,10);\n\n        if (!si) {\n\n            // 这个 Sentinel 不认识发送消息的 Sentinel \n            // 将对方加入到 Sentinel 列表中\n\n            /* If not, remove all the sentinels that have the same runid\n             * OR the same ip/port, because it's either a restart or a\n             * network topology change. */\n            removed = removeMatchingSentinelsFromMaster(master,token[0],port,\n                            token[2]);\n            if (removed) {\n                sentinelEvent(REDIS_NOTICE,\"-dup-sentinel\",master,\n                    \"%@ #duplicate of %s:%d or %s\",\n                    token[0],port,token[2]);\n            }\n\n            /* Add the new sentinel. */\n            si = createSentinelRedisInstance(NULL,SRI_SENTINEL,\n                            token[0],port,master->quorum,master);\n            if (si) {\n                sentinelEvent(REDIS_NOTICE,\"+sentinel\",si,\"%@\");\n                /* The runid is NULL after a new instance creation and\n                 * for Sentinels we don't have a later chance to fill it,\n                 * so do it now. */\n                si->runid = sdsnew(token[2]);\n                sentinelFlushConfig();\n            }\n        }\n\n        /* Update local current_epoch if received current_epoch is greater.*/\n        // 如果消息中记录的纪元比 Sentinel 当前的纪元要高，那么更新纪元\n        if (current_epoch > sentinel.current_epoch) {\n            sentinel.current_epoch = current_epoch;\n            sentinelFlushConfig();\n            sentinelEvent(REDIS_WARNING,\"+new-epoch\",master,\"%llu\",\n                (unsigned long long) sentinel.current_epoch);\n        }\n\n        /* Update master info if received configuration is newer. */\n        // 如果消息中记录的配置信息更新，那么对主服务器的信息进行更新\n        if (master->config_epoch < master_config_epoch) {\n            master->config_epoch = master_config_epoch;\n            if (master_port != master->addr->port ||\n                strcmp(master->addr->ip, token[5]))\n            {\n                sentinelAddr *old_addr;\n\n                sentinelEvent(REDIS_WARNING,\"+config-update-from\",si,\"%@\");\n                sentinelEvent(REDIS_WARNING,\"+switch-master\",\n                    master,\"%s %s %d %s %d\",\n                    master->name,\n                    master->addr->ip, master->addr->port,\n                    token[5], master_port);\n\n                old_addr = dupSentinelAddr(master->addr);\n                sentinelResetMasterAndChangeAddress(master, token[5], master_port);\n                sentinelCallClientReconfScript(master,\n                    SENTINEL_OBSERVER,\"start\",\n                    old_addr,master->addr);\n                releaseSentinelAddr(old_addr);\n            }\n        }\n\n        /* Update the state of the Sentinel. */\n        // 更新我方 Sentinel 记录的对方 Sentinel 的信息。\n        if (si) si->last_hello_time = mstime();\n    }\n\ncleanup:\n    sdsfreesplitres(token,numtokens);\n}\n\n\n/* This is our Pub/Sub callback for the Hello channel. It's useful in order\n * to discover other sentinels attached at the same master. */\n// 此回调函数用于处理 Hello 频道的返回值，它可以发现其他正在订阅同一主服务器的 Sentinel\nvoid sentinelReceiveHelloMessages(redisAsyncContext *c, void *reply, void *privdata) {\n    sentinelRedisInstance *ri = c->data;\n    redisReply *r;\n\n    if (!reply || !ri) return;\n    r = reply;\n\n    /* Update the last activity in the pubsub channel. Note that since we\n     * receive our messages as well this timestamp can be used to detect\n     * if the link is probably disconnected even if it seems otherwise. */\n    // 更新最后一次接收频道命令的时间\n    ri->pc_last_activity = mstime();\n   \n    /* Sanity check in the reply we expect, so that the code that follows\n     * can avoid to check for details. */\n    // 只处理频道发来的信息，不处理订阅时和退订时产生的信息\n    if (r->type != REDIS_REPLY_ARRAY ||\n        r->elements != 3 ||\n        r->element[0]->type != REDIS_REPLY_STRING ||\n        r->element[1]->type != REDIS_REPLY_STRING ||\n        r->element[2]->type != REDIS_REPLY_STRING ||\n        strcmp(r->element[0]->str,\"message\") != 0) return;\n\n    /* We are not interested in meeting ourselves */\n    // 只处理非自己发送的信息\n    if (strstr(r->element[2]->str,server.runid) != NULL) return;\n\n    sentinelProcessHelloMessage(r->element[2]->str, r->element[2]->len);\n}\n\n/* Send an \"Hello\" message via Pub/Sub to the specified 'ri' Redis\n * instance in order to broadcast the current configuraiton for this\n * master, and to advertise the existence of this Sentinel at the same time.\n *\n * 向给定 ri 实例的频道发送信息，\n * 从而传播关于给定主服务器的配置，\n * 并向其他 Sentinel 宣告本 Sentinel 的存在。\n *\n * The message has the following format:\n *\n * 发送信息的格式如下： \n *\n * sentinel_ip,sentinel_port,sentinel_runid,current_epoch,\n * master_name,master_ip,master_port,master_config_epoch.\n *\n * Sentinel IP,Sentinel 端口号,Sentinel 的运行 ID,Sentinel 当前的纪元,\n * 主服务器的名称,主服务器的 IP,主服务器的端口号,主服务器的配置纪元.\n *\n * Returns REDIS_OK if the PUBLISH was queued correctly, otherwise\n * REDIS_ERR is returned. \n *\n * PUBLISH 命令成功入队时返回 REDIS_OK ，\n * 否则返回 REDIS_ERR 。\n */\nint sentinelSendHello(sentinelRedisInstance *ri) {\n    char ip[REDIS_IP_STR_LEN];\n    char payload[REDIS_IP_STR_LEN+1024];\n    int retval;\n\n    // 如果实例是主服务器，那么使用此实例的信息\n    // 如果实例是从服务器，那么使用这个从服务器的主服务器的信息\n    sentinelRedisInstance *master = (ri->flags & SRI_MASTER) ? ri : ri->master;\n\n    // 获取地址信息\n    sentinelAddr *master_addr = sentinelGetCurrentMasterAddress(master);\n\n    /* Try to obtain our own IP address. */\n    // 获取实例自身的地址\n    if (anetSockName(ri->cc->c.fd,ip,sizeof(ip),NULL) == -1) return REDIS_ERR;\n    if (ri->flags & SRI_DISCONNECTED) return REDIS_ERR;\n\n    /* Format and send the Hello message. */\n    // 格式化信息\n    snprintf(payload,sizeof(payload),\n        \"%s,%d,%s,%llu,\" /* Info about this sentinel. */\n        \"%s,%s,%d,%llu\", /* Info about current master. */\n        ip, server.port, server.runid,\n        (unsigned long long) sentinel.current_epoch,\n        /* --- */\n        master->name,master_addr->ip,master_addr->port,\n        (unsigned long long) master->config_epoch);\n    \n    // 发送信息\n    retval = redisAsyncCommand(ri->cc,\n        sentinelPublishReplyCallback, NULL, \"PUBLISH %s %s\",\n            SENTINEL_HELLO_CHANNEL,payload);\n\n    if (retval != REDIS_OK) return REDIS_ERR;\n\n    ri->pending_commands++;\n\n    return REDIS_OK;\n}\n\n/* Send a PING to the specified instance and refresh the last_ping_time\n * if it is zero (that is, if we received a pong for the previous ping).\n *\n * On error zero is returned, and we can't consider the PING command\n * queued in the connection. */\n// 向指定的 Sentinel 发送 PING 命令。\nint sentinelSendPing(sentinelRedisInstance *ri) {\n    int retval = redisAsyncCommand(ri->cc,\n        sentinelPingReplyCallback, NULL, \"PING\");\n    if (retval == REDIS_OK) {\n        ri->pending_commands++;\n        /* We update the ping time only if we received the pong for\n         * the previous ping, otherwise we are technically waiting\n         * since the first ping that did not received a reply. */\n        if (ri->last_ping_time == 0) ri->last_ping_time = mstime();\n        return 1;\n    } else {\n        return 0;\n    }\n}\n\n// 根据时间和实例类型等情况，向实例发送命令，比如 INFO 、PING 和 PUBLISH\n// 虽然函数的名字包含 Ping ，但命令并不只发送 PING 命令\n/* Send periodic PING, INFO, and PUBLISH to the Hello channel to\n * the specified master or slave instance. */\nvoid sentinelSendPeriodicCommands(sentinelRedisInstance *ri) {\n    mstime_t now = mstime();\n    mstime_t info_period, ping_period;\n    int retval;\n\n    /* Return ASAP if we have already a PING or INFO already pending, or\n     * in the case the instance is not properly connected. */\n    // 函数不能在网络连接未创建时执行\n    if (ri->flags & SRI_DISCONNECTED) return;\n\n    /* For INFO, PING, PUBLISH that are not critical commands to send we\n     * also have a limit of SENTINEL_MAX_PENDING_COMMANDS. We don't\n     * want to use a lot of memory just because a link is not working\n     * properly (note that anyway there is a redundant protection about this,\n     * that is, the link will be disconnected and reconnected if a long\n     * timeout condition is detected. */\n    // 为了避免 sentinel 在实例处于不正常状态时，发送过多命令\n    // sentinel 只在待发送命令的数量未超过 SENTINEL_MAX_PENDING_COMMANDS 常量时\n    // 才进行命令发送\n    if (ri->pending_commands >= SENTINEL_MAX_PENDING_COMMANDS) return;\n\n    /* If this is a slave of a master in O_DOWN condition we start sending\n     * it INFO every second, instead of the usual SENTINEL_INFO_PERIOD\n     * period. In this state we want to closely monitor slaves in case they\n     * are turned into masters by another Sentinel, or by the sysadmin. */\n    // 对于从服务器来说， sentinel 默认每 SENTINEL_INFO_PERIOD 秒向它发送一次 INFO 命令\n    // 但是，当从服务器的主服务器处于 SDOWN 状态，或者正在执行故障转移时\n    // 为了更快速地捕捉从服务器的变动， sentinel 会将发送 INFO 命令的频率该为每秒一次\n    if ((ri->flags & SRI_SLAVE) &&\n        (ri->master->flags & (SRI_O_DOWN|SRI_FAILOVER_IN_PROGRESS))) {\n        info_period = 1000;\n    } else {\n        info_period = SENTINEL_INFO_PERIOD;\n    }\n\n    /* We ping instances every time the last received pong is older than\n     * the configured 'down-after-milliseconds' time, but every second\n     * anyway if 'down-after-milliseconds' is greater than 1 second. */\n    ping_period = ri->down_after_period;\n    if (ping_period > SENTINEL_PING_PERIOD) ping_period = SENTINEL_PING_PERIOD;\n\n    // 实例不是 Sentinel （主服务器或者从服务器）\n    // 并且以下条件的其中一个成立：\n    // 1）SENTINEL 未收到过这个服务器的 INFO 命令回复\n    // 2）距离上一次该实例回复 INFO 命令已经超过 info_period 间隔\n    // 那么向实例发送 INFO 命令\n    if ((ri->flags & SRI_SENTINEL) == 0 &&\n        (ri->info_refresh == 0 ||\n        (now - ri->info_refresh) > info_period))\n    {\n        /* Send INFO to masters and slaves, not sentinels. */\n        retval = redisAsyncCommand(ri->cc,\n            sentinelInfoReplyCallback, NULL, \"INFO\");\n        if (retval == REDIS_OK) ri->pending_commands++;\n    } else if ((now - ri->last_pong_time) > ping_period) {\n        /* Send PING to all the three kinds of instances. */\n        sentinelSendPing(ri);\n    } else if ((now - ri->last_pub_time) > SENTINEL_PUBLISH_PERIOD) {\n        /* PUBLISH hello messages to all the three kinds of instances. */\n        sentinelSendHello(ri);\n    }\n}\n\n/* =========================== SENTINEL command ============================= */\n\n// 返回字符串表示的故障转移状态\nconst char *sentinelFailoverStateStr(int state) {\n    switch(state) {\n    case SENTINEL_FAILOVER_STATE_NONE: return \"none\";\n    case SENTINEL_FAILOVER_STATE_WAIT_START: return \"wait_start\";\n    case SENTINEL_FAILOVER_STATE_SELECT_SLAVE: return \"select_slave\";\n    case SENTINEL_FAILOVER_STATE_SEND_SLAVEOF_NOONE: return \"send_slaveof_noone\";\n    case SENTINEL_FAILOVER_STATE_WAIT_PROMOTION: return \"wait_promotion\";\n    case SENTINEL_FAILOVER_STATE_RECONF_SLAVES: return \"reconf_slaves\";\n    case SENTINEL_FAILOVER_STATE_UPDATE_CONFIG: return \"update_config\";\n    default: return \"unknown\";\n    }\n}\n\n/* Redis instance to Redis protocol representation. */\n// 以 Redis 协议的形式返回 Redis 实例的情况\nvoid addReplySentinelRedisInstance(redisClient *c, sentinelRedisInstance *ri) {\n    char *flags = sdsempty();\n    void *mbl;\n    int fields = 0;\n\n    mbl = addDeferredMultiBulkLength(c);\n\n    addReplyBulkCString(c,\"name\");\n    addReplyBulkCString(c,ri->name);\n    fields++;\n\n    addReplyBulkCString(c,\"ip\");\n    addReplyBulkCString(c,ri->addr->ip);\n    fields++;\n\n    addReplyBulkCString(c,\"port\");\n    addReplyBulkLongLong(c,ri->addr->port);\n    fields++;\n\n    addReplyBulkCString(c,\"runid\");\n    addReplyBulkCString(c,ri->runid ? ri->runid : \"\");\n    fields++;\n\n    addReplyBulkCString(c,\"flags\");\n    if (ri->flags & SRI_S_DOWN) flags = sdscat(flags,\"s_down,\");\n    if (ri->flags & SRI_O_DOWN) flags = sdscat(flags,\"o_down,\");\n    if (ri->flags & SRI_MASTER) flags = sdscat(flags,\"master,\");\n    if (ri->flags & SRI_SLAVE) flags = sdscat(flags,\"slave,\");\n    if (ri->flags & SRI_SENTINEL) flags = sdscat(flags,\"sentinel,\");\n    if (ri->flags & SRI_DISCONNECTED) flags = sdscat(flags,\"disconnected,\");\n    if (ri->flags & SRI_MASTER_DOWN) flags = sdscat(flags,\"master_down,\");\n    if (ri->flags & SRI_FAILOVER_IN_PROGRESS)\n        flags = sdscat(flags,\"failover_in_progress,\");\n    if (ri->flags & SRI_PROMOTED) flags = sdscat(flags,\"promoted,\");\n    if (ri->flags & SRI_RECONF_SENT) flags = sdscat(flags,\"reconf_sent,\");\n    if (ri->flags & SRI_RECONF_INPROG) flags = sdscat(flags,\"reconf_inprog,\");\n    if (ri->flags & SRI_RECONF_DONE) flags = sdscat(flags,\"reconf_done,\");\n\n    if (sdslen(flags) != 0) sdsrange(flags,0,-2); /* remove last \",\" */\n    addReplyBulkCString(c,flags);\n    sdsfree(flags);\n    fields++;\n\n    addReplyBulkCString(c,\"pending-commands\");\n    addReplyBulkLongLong(c,ri->pending_commands);\n    fields++;\n\n    if (ri->flags & SRI_FAILOVER_IN_PROGRESS) {\n        addReplyBulkCString(c,\"failover-state\");\n        addReplyBulkCString(c,(char*)sentinelFailoverStateStr(ri->failover_state));\n        fields++;\n    }\n\n    addReplyBulkCString(c,\"last-ping-sent\");\n    addReplyBulkLongLong(c,\n        ri->last_ping_time ? (mstime() - ri->last_ping_time) : 0);\n    fields++;\n\n    addReplyBulkCString(c,\"last-ok-ping-reply\");\n    addReplyBulkLongLong(c,mstime() - ri->last_avail_time);\n    fields++;\n\n    addReplyBulkCString(c,\"last-ping-reply\");\n    addReplyBulkLongLong(c,mstime() - ri->last_pong_time);\n    fields++;\n\n    if (ri->flags & SRI_S_DOWN) {\n        addReplyBulkCString(c,\"s-down-time\");\n        addReplyBulkLongLong(c,mstime()-ri->s_down_since_time);\n        fields++;\n    }\n\n    if (ri->flags & SRI_O_DOWN) {\n        addReplyBulkCString(c,\"o-down-time\");\n        addReplyBulkLongLong(c,mstime()-ri->o_down_since_time);\n        fields++;\n    }\n\n    addReplyBulkCString(c,\"down-after-milliseconds\");\n    addReplyBulkLongLong(c,ri->down_after_period);\n    fields++;\n\n    /* Masters and Slaves */\n    if (ri->flags & (SRI_MASTER|SRI_SLAVE)) {\n        addReplyBulkCString(c,\"info-refresh\");\n        addReplyBulkLongLong(c,mstime() - ri->info_refresh);\n        fields++;\n\n        addReplyBulkCString(c,\"role-reported\");\n        addReplyBulkCString(c, (ri->role_reported == SRI_MASTER) ? \"master\" :\n                                                                   \"slave\");\n        fields++;\n\n        addReplyBulkCString(c,\"role-reported-time\");\n        addReplyBulkLongLong(c,mstime() - ri->role_reported_time);\n        fields++;\n    }\n\n    /* Only masters */\n    if (ri->flags & SRI_MASTER) {\n        addReplyBulkCString(c,\"config-epoch\");\n        addReplyBulkLongLong(c,ri->config_epoch);\n        fields++;\n\n        addReplyBulkCString(c,\"num-slaves\");\n        addReplyBulkLongLong(c,dictSize(ri->slaves));\n        fields++;\n\n        addReplyBulkCString(c,\"num-other-sentinels\");\n        addReplyBulkLongLong(c,dictSize(ri->sentinels));\n        fields++;\n\n        addReplyBulkCString(c,\"quorum\");\n        addReplyBulkLongLong(c,ri->quorum);\n        fields++;\n\n        addReplyBulkCString(c,\"failover-timeout\");\n        addReplyBulkLongLong(c,ri->failover_timeout);\n        fields++;\n\n        addReplyBulkCString(c,\"parallel-syncs\");\n        addReplyBulkLongLong(c,ri->parallel_syncs);\n        fields++;\n\n        if (ri->notification_script) {\n            addReplyBulkCString(c,\"notification-script\");\n            addReplyBulkCString(c,ri->notification_script);\n            fields++;\n        }\n\n        if (ri->client_reconfig_script) {\n            addReplyBulkCString(c,\"client-reconfig-script\");\n            addReplyBulkCString(c,ri->client_reconfig_script);\n            fields++;\n        }\n    }\n\n    /* Only slaves */\n    if (ri->flags & SRI_SLAVE) {\n        addReplyBulkCString(c,\"master-link-down-time\");\n        addReplyBulkLongLong(c,ri->master_link_down_time);\n        fields++;\n\n        addReplyBulkCString(c,\"master-link-status\");\n        addReplyBulkCString(c,\n            (ri->slave_master_link_status == SENTINEL_MASTER_LINK_STATUS_UP) ?\n            \"ok\" : \"err\");\n        fields++;\n\n        addReplyBulkCString(c,\"master-host\");\n        addReplyBulkCString(c,\n            ri->slave_master_host ? ri->slave_master_host : \"?\");\n        fields++;\n\n        addReplyBulkCString(c,\"master-port\");\n        addReplyBulkLongLong(c,ri->slave_master_port);\n        fields++;\n\n        addReplyBulkCString(c,\"slave-priority\");\n        addReplyBulkLongLong(c,ri->slave_priority);\n        fields++;\n\n        addReplyBulkCString(c,\"slave-repl-offset\");\n        addReplyBulkLongLong(c,ri->slave_repl_offset);\n        fields++;\n    }\n\n    /* Only sentinels */\n    if (ri->flags & SRI_SENTINEL) {\n        addReplyBulkCString(c,\"last-hello-message\");\n        addReplyBulkLongLong(c,mstime() - ri->last_hello_time);\n        fields++;\n\n        addReplyBulkCString(c,\"voted-leader\");\n        addReplyBulkCString(c,ri->leader ? ri->leader : \"?\");\n        fields++;\n\n        addReplyBulkCString(c,\"voted-leader-epoch\");\n        addReplyBulkLongLong(c,ri->leader_epoch);\n        fields++;\n    }\n\n    setDeferredMultiBulkLength(c,mbl,fields*2);\n}\n\n/* Output a number of instances contained inside a dictionary as\n * Redis protocol. */\n// 打印各个实例的情况\nvoid addReplyDictOfRedisInstances(redisClient *c, dict *instances) {\n    dictIterator *di;\n    dictEntry *de;\n\n    di = dictGetIterator(instances);\n    addReplyMultiBulkLen(c,dictSize(instances));\n    while((de = dictNext(di)) != NULL) {\n        sentinelRedisInstance *ri = dictGetVal(de);\n\n        addReplySentinelRedisInstance(c,ri);\n    }\n    dictReleaseIterator(di);\n}\n\n/* Lookup the named master into sentinel.masters.\n * If the master is not found reply to the client with an error and returns\n * NULL. */\n// 在 sentinel.masters 字典中查找给定名字的 master\n// 没找到则返回 NULL\nsentinelRedisInstance *sentinelGetMasterByNameOrReplyError(redisClient *c,\n                        robj *name)\n{\n    sentinelRedisInstance *ri;\n\n    ri = dictFetchValue(sentinel.masters,c->argv[2]->ptr);\n    if (!ri) {\n        addReplyError(c,\"No such master with that name\");\n        return NULL;\n    }\n    return ri;\n}\n\n// SENTINEL 命令的实现\nvoid sentinelCommand(redisClient *c) {\n    if (!strcasecmp(c->argv[1]->ptr,\"masters\")) {\n        /* SENTINEL MASTERS */\n        if (c->argc != 2) goto numargserr;\n        addReplyDictOfRedisInstances(c,sentinel.masters);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"master\")) {\n        /* SENTINEL MASTER <name> */\n        sentinelRedisInstance *ri;\n\n        if (c->argc != 3) goto numargserr;\n        if ((ri = sentinelGetMasterByNameOrReplyError(c,c->argv[2]))\n            == NULL) return;\n        addReplySentinelRedisInstance(c,ri);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"slaves\")) {\n        /* SENTINEL SLAVES <master-name> */\n        sentinelRedisInstance *ri;\n\n        if (c->argc != 3) goto numargserr;\n        if ((ri = sentinelGetMasterByNameOrReplyError(c,c->argv[2])) == NULL)\n            return;\n        addReplyDictOfRedisInstances(c,ri->slaves);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"sentinels\")) {\n        /* SENTINEL SENTINELS <master-name> */\n        sentinelRedisInstance *ri;\n\n        if (c->argc != 3) goto numargserr;\n        if ((ri = sentinelGetMasterByNameOrReplyError(c,c->argv[2])) == NULL)\n            return;\n        addReplyDictOfRedisInstances(c,ri->sentinels);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"is-master-down-by-addr\")) {\n        /* SENTINEL IS-MASTER-DOWN-BY-ADDR <ip> <port> <current-epoch> <runid>*/\n        sentinelRedisInstance *ri;\n        long long req_epoch;\n        uint64_t leader_epoch = 0;\n        char *leader = NULL;\n        long port;\n        int isdown = 0;\n\n        if (c->argc != 6) goto numargserr;\n        if (getLongFromObjectOrReply(c,c->argv[3],&port,NULL) != REDIS_OK ||\n            getLongLongFromObjectOrReply(c,c->argv[4],&req_epoch,NULL)\n                                                              != REDIS_OK)\n            return;\n        ri = getSentinelRedisInstanceByAddrAndRunID(sentinel.masters,\n            c->argv[2]->ptr,port,NULL);\n\n        /* It exists? Is actually a master? Is subjectively down? It's down.\n         * Note: if we are in tilt mode we always reply with \"0\". */\n        if (!sentinel.tilt && ri && (ri->flags & SRI_S_DOWN) &&\n                                    (ri->flags & SRI_MASTER))\n            isdown = 1;\n\n        /* Vote for the master (or fetch the previous vote) if the request\n         * includes a runid, otherwise the sender is not seeking for a vote. */\n        if (ri && ri->flags & SRI_MASTER && strcasecmp(c->argv[5]->ptr,\"*\")) {\n            leader = sentinelVoteLeader(ri,(uint64_t)req_epoch,\n                                            c->argv[5]->ptr,\n                                            &leader_epoch);\n        }\n\n        /* Reply with a three-elements multi-bulk reply:\n         * down state, leader, vote epoch. */\n        // 多条回复\n        // 1) <down_state>    1 代表下线， 0 代表未下线\n        // 2) <leader_runid>  Sentinel 选举作为领头 Sentinel 的运行 ID\n        // 3) <leader_epoch>  领头 Sentinel 目前的配置纪元\n        addReplyMultiBulkLen(c,3);\n        addReply(c, isdown ? shared.cone : shared.czero);\n        addReplyBulkCString(c, leader ? leader : \"*\");\n        addReplyLongLong(c, (long long)leader_epoch);\n        if (leader) sdsfree(leader);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"reset\")) {\n        /* SENTINEL RESET <pattern> */\n        if (c->argc != 3) goto numargserr;\n        addReplyLongLong(c,sentinelResetMastersByPattern(c->argv[2]->ptr,SENTINEL_GENERATE_EVENT));\n    } else if (!strcasecmp(c->argv[1]->ptr,\"get-master-addr-by-name\")) {\n        /* SENTINEL GET-MASTER-ADDR-BY-NAME <master-name> */\n        sentinelRedisInstance *ri;\n\n        if (c->argc != 3) goto numargserr;\n        ri = sentinelGetMasterByName(c->argv[2]->ptr);\n        if (ri == NULL) {\n            addReply(c,shared.nullmultibulk);\n        } else {\n            sentinelAddr *addr = sentinelGetCurrentMasterAddress(ri);\n\n            addReplyMultiBulkLen(c,2);\n            addReplyBulkCString(c,addr->ip);\n            addReplyBulkLongLong(c,addr->port);\n        }\n    } else if (!strcasecmp(c->argv[1]->ptr,\"failover\")) {\n        /* SENTINEL FAILOVER <master-name> */\n        sentinelRedisInstance *ri;\n\n        if (c->argc != 3) goto numargserr;\n        if ((ri = sentinelGetMasterByNameOrReplyError(c,c->argv[2])) == NULL)\n            return;\n        if (ri->flags & SRI_FAILOVER_IN_PROGRESS) {\n            addReplySds(c,sdsnew(\"-INPROG Failover already in progress\\r\\n\"));\n            return;\n        }\n        if (sentinelSelectSlave(ri) == NULL) {\n            addReplySds(c,sdsnew(\"-NOGOODSLAVE No suitable slave to promote\\r\\n\"));\n            return;\n        }\n        redisLog(REDIS_WARNING,\"Executing user requested FAILOVER of '%s'\",\n            ri->name);\n        sentinelStartFailover(ri);\n        ri->flags |= SRI_FORCE_FAILOVER;\n        addReply(c,shared.ok);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"pending-scripts\")) {\n        /* SENTINEL PENDING-SCRIPTS */\n\n        if (c->argc != 2) goto numargserr;\n        sentinelPendingScriptsCommand(c);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"monitor\")) {\n        /* SENTINEL MONITOR <name> <ip> <port> <quorum> */\n        sentinelRedisInstance *ri;\n        long quorum, port;\n        char buf[32];\n\n        if (c->argc != 6) goto numargserr;\n        if (getLongFromObjectOrReply(c,c->argv[5],&quorum,\"Invalid quorum\")\n            != REDIS_OK) return;\n        if (getLongFromObjectOrReply(c,c->argv[4],&port,\"Invalid port\")\n            != REDIS_OK) return;\n        /* Make sure the IP field is actually a valid IP before passing it\n         * to createSentinelRedisInstance(), otherwise we may trigger a\n         * DNS lookup at runtime. */\n        if (anetResolveIP(NULL,c->argv[3]->ptr,buf,sizeof(buf)) == ANET_ERR) {\n            addReplyError(c,\"Invalid IP address specified\");\n            return;\n        }\n\n        /* Parameters are valid. Try to create the master instance. */\n        ri = createSentinelRedisInstance(c->argv[2]->ptr,SRI_MASTER,\n                c->argv[3]->ptr,port,quorum,NULL);\n        if (ri == NULL) {\n            switch(errno) {\n            case EBUSY:\n                addReplyError(c,\"Duplicated master name\");\n                break;\n            case EINVAL:\n                addReplyError(c,\"Invalid port number\");\n                break;\n            default:\n                addReplyError(c,\"Unspecified error adding the instance\");\n                break;\n            }\n        } else {\n            sentinelFlushConfig();\n            sentinelEvent(REDIS_WARNING,\"+monitor\",ri,\"%@ quorum %d\",ri->quorum);\n            addReply(c,shared.ok);\n        }\n    } else if (!strcasecmp(c->argv[1]->ptr,\"remove\")) {\n        /* SENTINEL REMOVE <name> */\n        sentinelRedisInstance *ri;\n\n        if ((ri = sentinelGetMasterByNameOrReplyError(c,c->argv[2]))\n            == NULL) return;\n        sentinelEvent(REDIS_WARNING,\"-monitor\",ri,\"%@\");\n        dictDelete(sentinel.masters,c->argv[2]->ptr);\n        sentinelFlushConfig();\n        addReply(c,shared.ok);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"set\")) {\n        if (c->argc < 3 || c->argc % 2 == 0) goto numargserr;\n        sentinelSetCommand(c);\n    } else {\n        addReplyErrorFormat(c,\"Unknown sentinel subcommand '%s'\",\n                               (char*)c->argv[1]->ptr);\n    }\n    return;\n\nnumargserr:\n    addReplyErrorFormat(c,\"Wrong number of arguments for 'sentinel %s'\",\n                          (char*)c->argv[1]->ptr);\n}\n\n/* SENTINEL INFO [section] */\n// sentinel 模式下的 INFO 命令实现\nvoid sentinelInfoCommand(redisClient *c) {\n    char *section = c->argc == 2 ? c->argv[1]->ptr : \"default\";\n    sds info = sdsempty();\n    int defsections = !strcasecmp(section,\"default\");\n    int sections = 0;\n\n    if (c->argc > 2) {\n        addReply(c,shared.syntaxerr);\n        return;\n    }\n\n    if (!strcasecmp(section,\"server\") || defsections) {\n        if (sections++) info = sdscat(info,\"\\r\\n\");\n        sds serversection = genRedisInfoString(\"server\");\n        info = sdscatlen(info,serversection,sdslen(serversection));\n        sdsfree(serversection);\n    }\n\n    if (!strcasecmp(section,\"sentinel\") || defsections) {\n        dictIterator *di;\n        dictEntry *de;\n        int master_id = 0;\n\n        if (sections++) info = sdscat(info,\"\\r\\n\");\n        info = sdscatprintf(info,\n            \"# Sentinel\\r\\n\"\n            \"sentinel_masters:%lu\\r\\n\"\n            \"sentinel_tilt:%d\\r\\n\"\n            \"sentinel_running_scripts:%d\\r\\n\"\n            \"sentinel_scripts_queue_length:%ld\\r\\n\",\n            dictSize(sentinel.masters),\n            sentinel.tilt,\n            sentinel.running_scripts,\n            listLength(sentinel.scripts_queue));\n\n        di = dictGetIterator(sentinel.masters);\n        while((de = dictNext(di)) != NULL) {\n            sentinelRedisInstance *ri = dictGetVal(de);\n            char *status = \"ok\";\n\n            if (ri->flags & SRI_O_DOWN) status = \"odown\";\n            else if (ri->flags & SRI_S_DOWN) status = \"sdown\";\n            info = sdscatprintf(info,\n                \"master%d:name=%s,status=%s,address=%s:%d,\"\n                \"slaves=%lu,sentinels=%lu\\r\\n\",\n                master_id++, ri->name, status,\n                ri->addr->ip, ri->addr->port,\n                dictSize(ri->slaves),\n                dictSize(ri->sentinels)+1);\n        }\n        dictReleaseIterator(di);\n    }\n\n    addReplySds(c,sdscatprintf(sdsempty(),\"$%lu\\r\\n\",\n        (unsigned long)sdslen(info)));\n    addReplySds(c,info);\n    addReply(c,shared.crlf);\n}\n\n/* SENTINEL SET <mastername> [<option> <value> ...] */\nvoid sentinelSetCommand(redisClient *c) {\n    sentinelRedisInstance *ri;\n    int j, changes = 0;\n    char *option, *value;\n\n    if ((ri = sentinelGetMasterByNameOrReplyError(c,c->argv[2]))\n        == NULL) return;\n\n    /* Process option - value pairs. */\n    for (j = 3; j < c->argc; j += 2) {\n        option = c->argv[j]->ptr;\n        value = c->argv[j+1]->ptr;\n        robj *o = c->argv[j+1];\n        long long ll;\n\n        if (!strcasecmp(option,\"down-after-milliseconds\")) {\n            /* down-after-millisecodns <milliseconds> */\n            if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll <= 0)\n                goto badfmt;\n            ri->down_after_period = ll;\n            sentinelPropagateDownAfterPeriod(ri);\n            changes++;\n        } else if (!strcasecmp(option,\"failover-timeout\")) {\n            /* failover-timeout <milliseconds> */\n            if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll <= 0)\n                goto badfmt;\n            ri->failover_timeout = ll;\n            changes++;\n       } else if (!strcasecmp(option,\"parallel-syncs\")) {\n            /* parallel-syncs <milliseconds> */\n            if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll <= 0)\n                goto badfmt;\n            ri->parallel_syncs = ll;\n            changes++;\n       } else if (!strcasecmp(option,\"notification-script\")) {\n            /* notification-script <path> */\n            if (strlen(value) && access(value,X_OK) == -1) {\n                addReplyError(c,\n                    \"Notification script seems non existing or non executable\");\n                if (changes) sentinelFlushConfig();\n                return;\n            }\n            sdsfree(ri->notification_script);\n            ri->notification_script = strlen(value) ? sdsnew(value) : NULL;\n            changes++;\n       } else if (!strcasecmp(option,\"client-reconfig-script\")) {\n            /* client-reconfig-script <path> */\n            if (strlen(value) && access(value,X_OK) == -1) {\n                addReplyError(c,\n                    \"Client reconfiguration script seems non existing or \"\n                    \"non executable\");\n                if (changes) sentinelFlushConfig();\n                return;\n            }\n            sdsfree(ri->client_reconfig_script);\n            ri->client_reconfig_script = strlen(value) ? sdsnew(value) : NULL;\n            changes++;\n       } else if (!strcasecmp(option,\"auth-pass\")) {\n            /* auth-pass <password> */\n            sdsfree(ri->auth_pass);\n            ri->auth_pass = strlen(value) ? sdsnew(value) : NULL;\n            changes++;\n       } else if (!strcasecmp(option,\"quorum\")) {\n            /* quorum <count> */\n            if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll <= 0)\n                goto badfmt;\n            ri->quorum = ll;\n            changes++;\n        } else {\n            addReplyErrorFormat(c,\"Unknown option '%s' for SENTINEL SET\",\n                option);\n            if (changes) sentinelFlushConfig();\n            return;\n        }\n        sentinelEvent(REDIS_WARNING,\"+set\",ri,\"%@ %s %s\",option,value);\n    }\n\n    if (changes) sentinelFlushConfig();\n    addReply(c,shared.ok);\n    return;\n\nbadfmt: /* Bad format errors */\n    if (changes) sentinelFlushConfig();\n    addReplyErrorFormat(c,\"Invalid argument '%s' for SENTINEL SET '%s'\",\n            value, option);\n}\n\n/* Our fake PUBLISH command: it is actually useful only to receive hello messages\n * from the other sentinel instances, and publishing to a channel other than\n * SENTINEL_HELLO_CHANNEL is forbidden.\n *\n * Because we have a Sentinel PUBLISH, the code to send hello messages is the same\n * for all the three kind of instances: masters, slaves, sentinels. */\nvoid sentinelPublishCommand(redisClient *c) {\n    if (strcmp(c->argv[1]->ptr,SENTINEL_HELLO_CHANNEL)) {\n        addReplyError(c, \"Only HELLO messages are accepted by Sentinel instances.\");\n        return;\n    }\n    sentinelProcessHelloMessage(c->argv[2]->ptr,sdslen(c->argv[2]->ptr));\n    addReplyLongLong(c,1);\n}\n\n/* ===================== SENTINEL availability checks ======================= */\n\n/* Is this instance down from our point of view? */\n// 检查实例是否以下线（从本 Sentinel 的角度来看）\nvoid sentinelCheckSubjectivelyDown(sentinelRedisInstance *ri) {\n\n    mstime_t elapsed = 0;\n\n    if (ri->last_ping_time)\n        elapsed = mstime() - ri->last_ping_time;\n\n    /* Check if we are in need for a reconnection of one of the \n     * links, because we are detecting low activity.\n     *\n     * 如果检测到连接的活跃度（activity）很低，那么考虑重断开连接，并进行重连\n     *\n     * 1) Check if the command link seems connected, was connected not less\n     *    than SENTINEL_MIN_LINK_RECONNECT_PERIOD, but still we have a\n     *    pending ping for more than half the timeout. */\n    // 考虑断开实例的 cc 连接\n    if (ri->cc &&\n        (mstime() - ri->cc_conn_time) > SENTINEL_MIN_LINK_RECONNECT_PERIOD &&\n        ri->last_ping_time != 0 && /* Ther is a pending ping... */\n        /* The pending ping is delayed, and we did not received\n         * error replies as well. */\n        (mstime() - ri->last_ping_time) > (ri->down_after_period/2) &&\n        (mstime() - ri->last_pong_time) > (ri->down_after_period/2))\n    {\n        sentinelKillLink(ri,ri->cc);\n    }\n\n    /* 2) Check if the pubsub link seems connected, was connected not less\n     *    than SENTINEL_MIN_LINK_RECONNECT_PERIOD, but still we have no\n     *    activity in the Pub/Sub channel for more than\n     *    SENTINEL_PUBLISH_PERIOD * 3.\n     */\n    // 考虑断开实例的 pc 连接\n    if (ri->pc &&\n        (mstime() - ri->pc_conn_time) > SENTINEL_MIN_LINK_RECONNECT_PERIOD &&\n        (mstime() - ri->pc_last_activity) > (SENTINEL_PUBLISH_PERIOD*3))\n    {\n        sentinelKillLink(ri,ri->pc);\n    }\n\n    /* Update the SDOWN flag. We believe the instance is SDOWN if:\n     *\n     * 更新 SDOWN 标识。如果以下条件被满足，那么 Sentinel 认为实例已下线：\n     *\n     * 1) It is not replying.\n     *    它没有回应命令\n     * 2) We believe it is a master, it reports to be a slave for enough time\n     *    to meet the down_after_period, plus enough time to get two times\n     *    INFO report from the instance. \n     *    Sentinel 认为实例是主服务器，这个服务器向 Sentinel 报告它将成为从服务器，\n     *    但在超过给定时限之后，服务器仍然没有完成这一角色转换。\n     */\n    if (elapsed > ri->down_after_period ||\n        (ri->flags & SRI_MASTER &&\n         ri->role_reported == SRI_SLAVE &&\n         mstime() - ri->role_reported_time >\n          (ri->down_after_period+SENTINEL_INFO_PERIOD*2)))\n    {\n        /* Is subjectively down */\n        if ((ri->flags & SRI_S_DOWN) == 0) {\n            // 发送事件\n            sentinelEvent(REDIS_WARNING,\"+sdown\",ri,\"%@\");\n            // 记录进入 SDOWN 状态的时间\n            ri->s_down_since_time = mstime();\n            // 打开 SDOWN 标志\n            ri->flags |= SRI_S_DOWN;\n        }\n    } else {\n        // 移除（可能有的） SDOWN 状态\n        /* Is subjectively up */\n        if (ri->flags & SRI_S_DOWN) {\n            // 发送事件\n            sentinelEvent(REDIS_WARNING,\"-sdown\",ri,\"%@\");\n            // 移除相关标志\n            ri->flags &= ~(SRI_S_DOWN|SRI_SCRIPT_KILL_SENT);\n        }\n    }\n}\n\n\n/* Is this instance down according to the configured quorum?\n *\n * 根据给定数量的 Sentinel 投票，判断实例是否已下线。\n *\n * Note that ODOWN is a weak quorum, it only means that enough Sentinels\n * reported in a given time range that the instance was not reachable.\n *\n * 注意 ODOWN 是一个 weak quorum ，它只意味着有足够多的 Sentinel \n * 在**给定的时间范围内**报告实例不可达。\n *\n * However messages can be delayed so there are no strong guarantees about\n * N instances agreeing at the same time about the down state. \n *\n * 因为 Sentinel 对实例的检测信息可能带有延迟，\n * 所以实际上 N 个 Sentinel **不可能在同一时间内**判断主服务器进入了下线状态。\n */\nvoid sentinelCheckObjectivelyDown(sentinelRedisInstance *master) {\n    dictIterator *di;\n    dictEntry *de;\n    int quorum = 0, odown = 0;\n\n    // 如果当前 Sentinel 将主服务器判断为主观下线\n    // 那么检查是否有其他 Sentinel 同意这一判断\n    // 当同意的数量足够时，将主服务器判断为客观下线\n    if (master->flags & SRI_S_DOWN) {\n        /* Is down for enough sentinels? */\n\n        // 统计同意的 Sentinel 数量（起始的 1 代表本 Sentinel）\n        quorum = 1; /* the current sentinel. */\n\n        /* Count all the other sentinels. */\n        // 统计其他认为 master 进入下线状态的 Sentinel 的数量\n        di = dictGetIterator(master->sentinels);\n        while((de = dictNext(di)) != NULL) {\n            sentinelRedisInstance *ri = dictGetVal(de);\n                \n            // 该 SENTINEL 也认为 master 已下线\n            if (ri->flags & SRI_MASTER_DOWN) quorum++;\n        }\n        dictReleaseIterator(di);\n        \n        // 如果投票得出的支持数目大于等于判断 ODOWN 所需的票数\n        // 那么进入 ODOWN 状态\n        if (quorum >= master->quorum) odown = 1;\n    }\n\n    /* Set the flag accordingly to the outcome. */\n    if (odown) {\n\n        // master 已 ODOWN\n\n        if ((master->flags & SRI_O_DOWN) == 0) {\n            // 发送事件\n            sentinelEvent(REDIS_WARNING,\"+odown\",master,\"%@ #quorum %d/%d\",\n                quorum, master->quorum);\n            // 打开 ODOWN 标志\n            master->flags |= SRI_O_DOWN;\n            // 记录进入 ODOWN 的时间\n            master->o_down_since_time = mstime();\n        }\n    } else {\n\n        // 未进入 ODOWN\n\n        if (master->flags & SRI_O_DOWN) {\n\n            // 如果 master 曾经进入过 ODOWN 状态，那么移除该状态\n\n            // 发送事件\n            sentinelEvent(REDIS_WARNING,\"-odown\",master,\"%@\");\n            // 移除 ODOWN 标志\n            master->flags &= ~SRI_O_DOWN;\n        }\n    }\n}\n\n/* Receive the SENTINEL is-master-down-by-addr reply, see the\n * sentinelAskMasterStateToOtherSentinels() function for more information. */\n// 本回调函数用于处理SENTINEL 接收到其他 SENTINEL \n// 发回的 SENTINEL is-master-down-by-addr 命令的回复\nvoid sentinelReceiveIsMasterDownReply(redisAsyncContext *c, void *reply, void *privdata) {\n    sentinelRedisInstance *ri = c->data;\n    redisReply *r;\n\n    if (ri) ri->pending_commands--;\n    if (!reply || !ri) return;\n    r = reply;\n\n    /* Ignore every error or unexpected reply.\n     * 忽略错误回复\n     * Note that if the command returns an error for any reason we'll\n     * end clearing the SRI_MASTER_DOWN flag for timeout anyway. */\n    if (r->type == REDIS_REPLY_ARRAY && r->elements == 3 &&\n        r->element[0]->type == REDIS_REPLY_INTEGER &&\n        r->element[1]->type == REDIS_REPLY_STRING &&\n        r->element[2]->type == REDIS_REPLY_INTEGER)\n    {\n        // 更新最后一次回复询问的时间\n        ri->last_master_down_reply_time = mstime();\n\n        // 设置 SENTINEL 认为主服务器的状态\n        if (r->element[0]->integer == 1) {\n            // 已下线\n            ri->flags |= SRI_MASTER_DOWN;\n        } else {\n            // 未下线\n            ri->flags &= ~SRI_MASTER_DOWN;\n        }\n\n        // 如果运行 ID 不是 \"*\" 的话，那么这是一个带投票的回复\n        if (strcmp(r->element[1]->str,\"*\")) {\n            /* If the runid in the reply is not \"*\" the Sentinel actually\n             * replied with a vote. */\n            sdsfree(ri->leader);\n            // 打印日志\n            if (ri->leader_epoch != r->element[2]->integer)\n                redisLog(REDIS_WARNING,\n                    \"%s voted for %s %llu\", ri->name,\n                    r->element[1]->str,\n                    (unsigned long long) r->element[2]->integer);\n            // 设置实例的领头\n            ri->leader = sdsnew(r->element[1]->str);\n            ri->leader_epoch = r->element[2]->integer;\n        }\n    }\n}\n\n/* If we think the master is down, we start sending\n * SENTINEL IS-MASTER-DOWN-BY-ADDR requests to other sentinels\n * in order to get the replies that allow to reach the quorum\n * needed to mark the master in ODOWN state and trigger a failover. */\n// 如果 Sentinel 认为主服务器已下线，\n// 那么它会通过向其他 Sentinel 发送 SENTINEL is-master-down-by-addr 命令，\n// 尝试获得足够的票数，将主服务器标记为 ODOWN 状态，并开始一次故障转移操作\n#define SENTINEL_ASK_FORCED (1<<0)\nvoid sentinelAskMasterStateToOtherSentinels(sentinelRedisInstance *master, int flags) {\n    dictIterator *di;\n    dictEntry *de;\n\n    // 遍历正在监视相同 master 的所有 sentinel\n    // 向它们发送 SENTINEL is-master-down-by-addr 命令\n    di = dictGetIterator(master->sentinels);\n    while((de = dictNext(di)) != NULL) {\n        sentinelRedisInstance *ri = dictGetVal(de);\n\n        // 距离该 sentinel 最后一次回复 SENTINEL master-down-by-addr 命令已经过了多久\n        mstime_t elapsed = mstime() - ri->last_master_down_reply_time;\n\n        char port[32];\n        int retval;\n\n        /* If the master state from other sentinel is too old, we clear it. */\n        // 如果目标 Sentinel 关于主服务器的信息已经太久没更新，那么我们清除它\n        if (elapsed > SENTINEL_ASK_PERIOD*5) {\n            ri->flags &= ~SRI_MASTER_DOWN;\n            sdsfree(ri->leader);\n            ri->leader = NULL;\n        }\n\n        /* Only ask if master is down to other sentinels if:\n         *\n         * 只在以下情况满足时，才向其他 sentinel 询问主服务器是否已下线\n         *\n         * 1) We believe it is down, or there is a failover in progress.\n         *    本 sentinel 相信服务器已经下线，或者针对该主服务器的故障转移操作正在执行\n         * 2) Sentinel is connected.\n         *    目标 Sentinel 与本 Sentinel 已连接\n         * 3) We did not received the info within SENTINEL_ASK_PERIOD ms. \n         *    当前 Sentinel 在 SENTINEL_ASK_PERIOD 毫秒内没有获得过目标 Sentinel 发来的信息\n         * 4) 条件 1 和条件 2 满足而条件 3 不满足，但是 flags 参数给定了 SENTINEL_ASK_FORCED 标识\n         */\n        if ((master->flags & SRI_S_DOWN) == 0) continue;\n        if (ri->flags & SRI_DISCONNECTED) continue;\n        if (!(flags & SENTINEL_ASK_FORCED) &&\n            mstime() - ri->last_master_down_reply_time < SENTINEL_ASK_PERIOD)\n            continue;\n\n        /* Ask */\n        // 发送 SENTINEL is-master-down-by-addr 命令\n        ll2string(port,sizeof(port),master->addr->port);\n        retval = redisAsyncCommand(ri->cc,\n                    sentinelReceiveIsMasterDownReply, NULL,\n                    \"SENTINEL is-master-down-by-addr %s %s %llu %s\",\n                    master->addr->ip, port,\n                    sentinel.current_epoch,\n                    // 如果本 Sentinel 已经检测到 master 进入 ODOWN \n                    // 并且要开始一次故障转移，那么向其他 Sentinel 发送自己的运行 ID\n                    // 让对方将给自己投一票（如果对方在这个纪元内还没有投票的话）\n                    (master->failover_state > SENTINEL_FAILOVER_STATE_NONE) ?\n                    server.runid : \"*\");\n        if (retval == REDIS_OK) ri->pending_commands++;\n    }\n    dictReleaseIterator(di);\n}\n\n/* =============================== FAILOVER ================================= */\n\n/* Vote for the sentinel with 'req_runid' or return the old vote if already\n * voted for the specifed 'req_epoch' or one greater.\n *\n * 为运行 ID 为 req_runid 的 Sentinel 投上一票，有两种额外情况可能出现：\n * 1) 如果 Sentinel 在 req_epoch 纪元已经投过票了，那么返回之前投的票。\n * 2) 如果 Sentinel 已经为大于 req_epoch 的纪元投过票了，那么返回更大纪元的投票。\n *\n * If a vote is not available returns NULL, otherwise return the Sentinel\n * runid and populate the leader_epoch with the epoch of the vote. \n *\n * 如果投票暂时不可用，那么返回 NULL 。\n * 否则返回 Sentinel 的运行 ID ，并将被投票的纪元保存到 leader_epoch 指针的值里面。\n */\nchar *sentinelVoteLeader(sentinelRedisInstance *master, uint64_t req_epoch, char *req_runid, uint64_t *leader_epoch) {\n    if (req_epoch > sentinel.current_epoch) {\n        sentinel.current_epoch = req_epoch;\n        sentinelFlushConfig();\n        sentinelEvent(REDIS_WARNING,\"+new-epoch\",master,\"%llu\",\n            (unsigned long long) sentinel.current_epoch);\n    }\n\n    if (master->leader_epoch < req_epoch && sentinel.current_epoch <= req_epoch)\n    {\n        sdsfree(master->leader);\n        master->leader = sdsnew(req_runid);\n        master->leader_epoch = sentinel.current_epoch;\n        sentinelFlushConfig();\n        sentinelEvent(REDIS_WARNING,\"+vote-for-leader\",master,\"%s %llu\",\n            master->leader, (unsigned long long) master->leader_epoch);\n        /* If we did not voted for ourselves, set the master failover start\n         * time to now, in order to force a delay before we can start a\n         * failover for the same master. */\n        if (strcasecmp(master->leader,server.runid))\n            master->failover_start_time = mstime()+rand()%SENTINEL_MAX_DESYNC;\n    }\n\n    *leader_epoch = master->leader_epoch;\n    return master->leader ? sdsnew(master->leader) : NULL;\n}\n\n// 记录客观 leader 投票的结构\nstruct sentinelLeader {\n\n    // sentinel 的运行 id\n    char *runid;\n\n    // 该 sentinel 获得的票数\n    unsigned long votes;\n};\n\n/* Helper function for sentinelGetLeader, increment the counter\n * relative to the specified runid. */\n// 为给定 ID 的 Sentinel 实例增加一票\nint sentinelLeaderIncr(dict *counters, char *runid) {\n    dictEntry *de = dictFind(counters,runid);\n    uint64_t oldval;\n\n    if (de) {\n        oldval = dictGetUnsignedIntegerVal(de);\n        dictSetUnsignedIntegerVal(de,oldval+1);\n        return oldval+1;\n    } else {\n        de = dictAddRaw(counters,runid);\n        redisAssert(de != NULL);\n        dictSetUnsignedIntegerVal(de,1);\n        return 1;\n    }\n}\n\n/* Scan all the Sentinels attached to this master to check if there\n * is a leader for the specified epoch.\n *\n * 扫描所有监视 master 的 Sentinels ，查看是否有 Sentinels 是这个纪元的领头。\n *\n * To be a leader for a given epoch, we should have the majorify of\n * the Sentinels we know that reported the same instance as\n * leader for the same epoch. \n *\n * 要让一个 Sentinel 成为本纪元的领头，\n * 这个 Sentinel 必须让大多数其他 Sentinel 承认它是该纪元的领头才行。\n */\n// 选举出 master 在指定 epoch 上的领头\nchar *sentinelGetLeader(sentinelRedisInstance *master, uint64_t epoch) {\n    dict *counters;\n    dictIterator *di;\n    dictEntry *de;\n    unsigned int voters = 0, voters_quorum;\n    char *myvote;\n    char *winner = NULL;\n    uint64_t leader_epoch;\n    uint64_t max_votes = 0;\n\n    redisAssert(master->flags & (SRI_O_DOWN|SRI_FAILOVER_IN_PROGRESS));\n\n    // 统计器\n    counters = dictCreate(&leaderVotesDictType,NULL);\n\n    /* Count other sentinels votes */\n    // 统计其他 sentinel 的主观 leader 投票\n    di = dictGetIterator(master->sentinels);\n    while((de = dictNext(di)) != NULL) {\n        sentinelRedisInstance *ri = dictGetVal(de);\n\n        // 为目标 Sentinel 选出的领头 Sentinel 增加一票\n        if (ri->leader != NULL && ri->leader_epoch == sentinel.current_epoch)\n            sentinelLeaderIncr(counters,ri->leader);\n\n        // 统计投票数量\n        voters++;\n    }\n    dictReleaseIterator(di);\n\n    /* Check what's the winner. For the winner to win, it needs two conditions:\n     *\n     * 选出领头 leader ，它必须满足以下两个条件：\n     *\n     * 1) Absolute majority between voters (50% + 1).\n     *    有多于一般的 Sentinel 支持\n     * 2) And anyway at least master->quorum votes. \n     *    投票数至少要有 master->quorum 那么多\n     */\n    di = dictGetIterator(counters);\n    while((de = dictNext(di)) != NULL) {\n\n        // 取出票数\n        uint64_t votes = dictGetUnsignedIntegerVal(de);\n\n        // 选出票数最大的人\n        if (votes > max_votes) {\n            max_votes = votes;\n            winner = dictGetKey(de);\n        }\n    }\n    dictReleaseIterator(di);\n\n    /* Count this Sentinel vote:\n     * if this Sentinel did not voted yet, either vote for the most\n     * common voted sentinel, or for itself if no vote exists at all. */\n    // 本 Sentinel 进行投票\n    // 如果 Sentinel 之前还没有进行投票，那么有两种选择：\n    // 1）如果选出了 winner （最多票数支持的 Sentinel ），那么这个 Sentinel 也投 winner 一票\n    // 2）如果没有选出 winner ，那么 Sentinel 投自己一票\n    if (winner)\n        myvote = sentinelVoteLeader(master,epoch,winner,&leader_epoch);\n    else\n        myvote = sentinelVoteLeader(master,epoch,server.runid,&leader_epoch);\n\n    // 领头 Sentinel 已选出，并且领头的纪元和给定的纪元一样\n    if (myvote && leader_epoch == epoch) {\n\n        // 为领头 Sentinel 增加一票（这一票来自本 Sentinel ）\n        uint64_t votes = sentinelLeaderIncr(counters,myvote);\n\n        // 如果投票之后的票数比最大票数要大，那么更换领头 Sentinel\n        if (votes > max_votes) {\n            max_votes = votes;\n            winner = myvote;\n        }\n    }\n    voters++; /* Anyway, count me as one of the voters. */\n\n    // 如果支持领头的投票数量不超过半数\n    // 并且支持票数不超过 master 配置指定的投票数量\n    // 那么这次领头选举无效\n    voters_quorum = voters/2+1;\n    if (winner && (max_votes < voters_quorum || max_votes < master->quorum))\n        winner = NULL;\n\n    // 返回领头 Sentinel ，或者 NULL\n    winner = winner ? sdsnew(winner) : NULL;\n    sdsfree(myvote);\n    dictRelease(counters);\n    return winner;\n}\n\n/* Send SLAVEOF to the specified instance, always followed by a\n * CONFIG REWRITE command in order to store the new configuration on disk\n * when possible (that is, if the Redis instance is recent enough to support\n * config rewriting, and if the server was started with a configuration file).\n *\n * 向指定实例发送 SLAVEOF 命令，并在可能时，执行 CONFIG REWRITE 命令，\n * 将当前配置保存到磁盘中。\n *\n * If Host is NULL the function sends \"SLAVEOF NO ONE\".\n *\n * 如果 host 参数为 NULL ，那么向实例发送 SLAVEOF NO ONE 命令\n *\n * The command returns REDIS_OK if the SLAVEOF command was accepted for\n * (later) delivery otherwise REDIS_ERR. The command replies are just\n * discarded. \n *\n * 命令入队成功（异步发送）时，函数返回 REDIS_OK ，\n * 入队失败时返回 REDIS_ERR ，\n * 命令回复会被丢弃。\n */\nint sentinelSendSlaveOf(sentinelRedisInstance *ri, char *host, int port) {\n    char portstr[32];\n    int retval;\n\n    ll2string(portstr,sizeof(portstr),port);\n\n    if (host == NULL) {\n        host = \"NO\";\n        memcpy(portstr,\"ONE\",4);\n    }\n\n    // 发送 SLAVEOF NO ONE\n    retval = redisAsyncCommand(ri->cc,\n        sentinelDiscardReplyCallback, NULL, \"SLAVEOF %s %s\", host, portstr);\n    if (retval == REDIS_ERR) return retval;\n\n    ri->pending_commands++;\n\n    // 发送 CONFIG REWRITE\n    if (redisAsyncCommand(ri->cc,\n        sentinelDiscardReplyCallback, NULL, \"CONFIG REWRITE\") == REDIS_OK)\n    {\n        ri->pending_commands++;\n    }\n\n    return REDIS_OK;\n}\n\n/* Setup the master state to start a failover. */\n// 设置主服务器的状态，开始一次故障转移\nvoid sentinelStartFailover(sentinelRedisInstance *master) {\n    redisAssert(master->flags & SRI_MASTER);\n\n    // 更新故障转移状态\n    master->failover_state = SENTINEL_FAILOVER_STATE_WAIT_START;\n\n    // 更新主服务器状态\n    master->flags |= SRI_FAILOVER_IN_PROGRESS;\n\n    // 更新纪元\n    master->failover_epoch = ++sentinel.current_epoch;\n\n    sentinelEvent(REDIS_WARNING,\"+new-epoch\",master,\"%llu\",\n        (unsigned long long) sentinel.current_epoch);\n\n    sentinelEvent(REDIS_WARNING,\"+try-failover\",master,\"%@\");\n\n    // 记录故障转移状态的变更时间\n    master->failover_start_time = mstime()+rand()%SENTINEL_MAX_DESYNC;\n    master->failover_state_change_time = mstime();\n}\n\n/* This function checks if there are the conditions to start the failover,\n * that is:\n *\n * 这个函数检查是否需要开始一次故障转移操作：\n *\n * 1) Master must be in ODOWN condition.\n *    主服务器已经计入 ODOWN 状态。\n * 2) No failover already in progress.\n *    当前没有针对同一主服务器的故障转移操作在执行。\n * 3) No failover already attempted recently.\n *    最近时间内，这个主服务器没有尝试过执行故障转移\n *    （应该是为了防止频繁执行）。\n * \n * We still don't know if we'll win the election so it is possible that we\n * start the failover but that we'll not be able to act.\n *\n * 虽然 Sentinel 可以发起一次故障转移，但因为故障转移操作是由领头 Sentinel 执行的，\n * 所以发起故障转移的 Sentinel 不一定就是执行故障转移的 Sentinel 。\n *\n * Return non-zero if a failover was started. \n *\n * 如果故障转移操作成功开始，那么函数返回非 0 值。\n */\nint sentinelStartFailoverIfNeeded(sentinelRedisInstance *master) {\n\n    /* We can't failover if the master is not in O_DOWN state. */\n    if (!(master->flags & SRI_O_DOWN)) return 0;\n\n    /* Failover already in progress? */\n    if (master->flags & SRI_FAILOVER_IN_PROGRESS) return 0;\n\n    /* Last failover attempt started too little time ago? */\n    if (mstime() - master->failover_start_time <\n        master->failover_timeout*2)\n    {\n        if (master->failover_delay_logged != master->failover_start_time) {\n            time_t clock = (master->failover_start_time +\n                            master->failover_timeout*2) / 1000;\n            char ctimebuf[26];\n\n            ctime_r(&clock,ctimebuf);\n            ctimebuf[24] = '\\0'; /* Remove newline. */\n            master->failover_delay_logged = master->failover_start_time;\n            redisLog(REDIS_WARNING,\n                \"Next failover delay: I will not start a failover before %s\",\n                ctimebuf);\n        }\n        return 0;\n    }\n\n    // 开始一次故障转移\n    sentinelStartFailover(master);\n\n    return 1;\n}\n\n/* Select a suitable slave to promote. The current algorithm only uses\n * the following parameters:\n *\n * 在多个从服务器中选出一个作为新的主服务器。\n * 算法使用以下参数：\n *\n * 1) None of the following conditions: S_DOWN, O_DOWN, DISCONNECTED.\n *    带有 S_DOWN 、 O_DOWN 和 DISCONNECTED 状态的从服务器不会被选中。\n * 2) Last time the slave replied to ping no more than 5 times the PING period.\n *    距离最近一次回复 PING 命令超过 5 秒以上的从服务区不会被选中。\n * 3) info_refresh not older than 3 times the INFO refresh period.\n *    距离最后一次回复 INFO 命令的时间超过 info_refresh 时长三倍的从服务器不会被考虑。\n * 4) master_link_down_time no more than:\n *    主从服务器之间的连接断开时间不能超过：\n *     (now - master->s_down_since_time) + (master->down_after_period * 10).\n *    Basically since the master is down from our POV, the slave reports\n *    to be disconnected no more than 10 times the configured down-after-period.\n *    因为从当前 Sentinel 来看，主服务器已经处于下线状态，\n *    所以正常来说，\n *    从服务器与主服务器之间的连接断开时间不应该超过 down-after-period 的十倍。\n *    This is pretty much black magic but the idea is, the master was not\n *    available so the slave may be lagging, but not over a certain time.\n *    这听上去有点像黑魔法，不过这个判断的主意是这样的：\n *    当主服务器下线之后，主从服务器的连接就会断开，但只要先下线的是主服务器而不是从服务器\n *    （换句话说，连接断开是由主服务器而不是从服务器造成的）\n *    那么主从服务器之间的连接断开时间就不会太长。\n *    Anyway we'll select the best slave according to replication offset.\n *    不过这只是一个辅助手段，因为最终我们都会使用复制偏移量来挑选从服务器。\n * 5) Slave priority can't be zero, otherwise the slave is discarded.\n *    从服务器的优先级不能为 0 ，优先级为 0 的从服务器表示被禁用。\n *\n * Among all the slaves matching the above conditions we select the slave\n * with, in order of sorting key:\n *\n * 符合以上条件的从服务器，我们会按以下条件对从服务器进行排序：\n *\n * - lower slave_priority.\n *   优先级更小的从服务器优先。\n * - bigger processed replication offset.\n *   复制偏移量较大的从服务器优先。\n * - lexicographically smaller runid.\n *   运行 ID 较小的从服务器优先。\n *\n * Basically if runid is the same, the slave that processed more commands\n * from the master is selected.\n *\n * 如果运行 ID 相同，那么执行命令数量较多的那个从服务器会被选中。\n *\n * The function returns the pointer to the selected slave, otherwise\n * NULL if no suitable slave was found.\n *\n * sentinelSelectSlave 函数返回被选中从服务器的实例指针，\n * 如果没有何时的从服务器，那么返回 NULL 。\n */\n\n/* Helper for sentinelSelectSlave(). This is used by qsort() in order to\n * sort suitable slaves in a \"better first\" order, to take the first of\n * the list. */\n// 排序函数，用于选出更好的从服务器\nint compareSlavesForPromotion(const void *a, const void *b) {\n    sentinelRedisInstance **sa = (sentinelRedisInstance **)a,\n                          **sb = (sentinelRedisInstance **)b;\n    char *sa_runid, *sb_runid;\n\n    // 优先级较小的从服务器胜出\n    if ((*sa)->slave_priority != (*sb)->slave_priority)\n        return (*sa)->slave_priority - (*sb)->slave_priority;\n\n    /* If priority is the same, select the slave with greater replication\n     * offset (processed more data frmo the master). */\n    // 如果优先级相同，那么复制偏移量较大的那个从服务器胜出\n    // （偏移量较大表示从主服务器获取的数据更多，更完整）\n    if ((*sa)->slave_repl_offset > (*sb)->slave_repl_offset) {\n        return -1; /* a < b */\n    } else if ((*sa)->slave_repl_offset < (*sb)->slave_repl_offset) {\n        return 1; /* b > a */\n    }\n\n    /* If the replication offset is the same select the slave with that has\n     * the lexicographically smaller runid. Note that we try to handle runid\n     * == NULL as there are old Redis versions that don't publish runid in\n     * INFO. A NULL runid is considered bigger than any other runid. */\n    // 如果复制偏移量也相同，那么选出运行 ID 较低的那个从服务器\n    // 注意，对于没有运行 ID 的旧版 Redis 来说，默认的运行 ID 为 NULL\n    sa_runid = (*sa)->runid;\n    sb_runid = (*sb)->runid;\n    if (sa_runid == NULL && sb_runid == NULL) return 0;\n    else if (sa_runid == NULL) return 1;  /* a > b */\n    else if (sb_runid == NULL) return -1; /* a < b */\n    return strcasecmp(sa_runid, sb_runid);\n}\n\n// 从主服务器的所有从服务器中，挑选一个作为新的主服务器\n// 如果没有合格的新主服务器，那么返回 NULL\nsentinelRedisInstance *sentinelSelectSlave(sentinelRedisInstance *master) {\n\n    sentinelRedisInstance **instance =\n        zmalloc(sizeof(instance[0])*dictSize(master->slaves));\n    sentinelRedisInstance *selected = NULL;\n    int instances = 0;\n    dictIterator *di;\n    dictEntry *de;\n    mstime_t max_master_down_time = 0;\n\n    // 计算可以接收的，从服务器与主服务器之间的最大下线时间\n    // 这个值可以保证被选中的从服务器的数据库不会太旧\n    if (master->flags & SRI_S_DOWN)\n        max_master_down_time += mstime() - master->s_down_since_time;\n    max_master_down_time += master->down_after_period * 10;\n\n    // 遍历所有从服务器\n    di = dictGetIterator(master->slaves);\n    while((de = dictNext(di)) != NULL) {\n\n        // 从服务器实例\n        sentinelRedisInstance *slave = dictGetVal(de);\n        mstime_t info_validity_time;\n\n        // 忽略所有 SDOWN 、ODOWN 或者已断线的从服务器\n        if (slave->flags & (SRI_S_DOWN|SRI_O_DOWN|SRI_DISCONNECTED)) continue;\n        if (mstime() - slave->last_avail_time > SENTINEL_PING_PERIOD*5) continue;\n        if (slave->slave_priority == 0) continue;\n\n        /* If the master is in SDOWN state we get INFO for slaves every second.\n         * Otherwise we get it with the usual period so we need to account for\n         * a larger delay. */\n        // 如果主服务器处于 SDOWN 状态，那么 Sentinel 以每秒一次的频率向从服务器发送 INFO 命令\n        // 否则以平常频率向从服务器发送 INFO 命令\n        // 这里要检查 INFO 命令的返回值是否合法，检查的时间会乘以一个倍数，以计算延迟\n        if (master->flags & SRI_S_DOWN)\n            info_validity_time = SENTINEL_PING_PERIOD*5;\n        else\n            info_validity_time = SENTINEL_INFO_PERIOD*3;\n\n        // INFO 回复已过期，不考虑\n        if (mstime() - slave->info_refresh > info_validity_time) continue;\n\n        // 从服务器下线的时间过长，不考虑\n        if (slave->master_link_down_time > max_master_down_time) continue;\n\n        // 将被选中的 slave 保存到数组中\n        instance[instances++] = slave;\n    }\n    dictReleaseIterator(di);\n\n    if (instances) {\n\n        // 对被选中的从服务器进行排序\n        qsort(instance,instances,sizeof(sentinelRedisInstance*),\n            compareSlavesForPromotion);\n        \n        // 分值最低的从服务器为被选中服务器\n        selected = instance[0];\n    }\n    zfree(instance);\n\n    // 返回被选中的从服务区\n    return selected;\n}\n\n/* ---------------- Failover state machine implementation ------------------- */\n\n// 准备执行故障转移\nvoid sentinelFailoverWaitStart(sentinelRedisInstance *ri) {\n    char *leader;\n    int isleader;\n\n    /* Check if we are the leader for the failover epoch. */\n    // 获取给定纪元的领头 Sentinel\n    leader = sentinelGetLeader(ri, ri->failover_epoch);\n    // 本 Sentinel 是否为领头 Sentinel ？\n    isleader = leader && strcasecmp(leader,server.runid) == 0;\n    sdsfree(leader);\n\n    /* If I'm not the leader, and it is not a forced failover via\n     * SENTINEL FAILOVER, then I can't continue with the failover. */\n    // 如果本 Sentinel 不是领头，并且这次故障迁移不是一次强制故障迁移操作\n    // 那么本 Sentinel 不做动作\n    if (!isleader && !(ri->flags & SRI_FORCE_FAILOVER)) {\n        int election_timeout = SENTINEL_ELECTION_TIMEOUT;\n\n        /* The election timeout is the MIN between SENTINEL_ELECTION_TIMEOUT\n         * and the configured failover timeout. */\n        // 当选的时长（类似于任期）是 SENTINEL_ELECTION_TIMEOUT\n        // 和 Sentinel 设置的故障迁移时长之间的较小那个值\n        if (election_timeout > ri->failover_timeout)\n            election_timeout = ri->failover_timeout;\n\n        /* Abort the failover if I'm not the leader after some time. */\n        // Sentinel 的当选时间已过，取消故障转移计划\n        if (mstime() - ri->failover_start_time > election_timeout) {\n            sentinelEvent(REDIS_WARNING,\"-failover-abort-not-elected\",ri,\"%@\");\n            // 取消故障转移\n            sentinelAbortFailover(ri);\n        }\n        return;\n    }\n\n    // 本 Sentinel 作为领头，开始执行故障迁移操作...\n\n    sentinelEvent(REDIS_WARNING,\"+elected-leader\",ri,\"%@\");\n\n    // 进入选择从服务器状态\n    ri->failover_state = SENTINEL_FAILOVER_STATE_SELECT_SLAVE;\n    ri->failover_state_change_time = mstime();\n\n    sentinelEvent(REDIS_WARNING,\"+failover-state-select-slave\",ri,\"%@\");\n}\n\n// 选择合适的从服务器作为新的主服务器\nvoid sentinelFailoverSelectSlave(sentinelRedisInstance *ri) {\n\n    // 在旧主服务器所属的从服务器中，选择新服务器\n    sentinelRedisInstance *slave = sentinelSelectSlave(ri);\n\n    /* We don't handle the timeout in this state as the function aborts\n     * the failover or go forward in the next state. */\n    // 没有合适的从服务器，直接终止故障转移操作\n    if (slave == NULL) {\n\n        // 没有可用的从服务器可以提升为新主服务器，故障转移操作无法执行\n        sentinelEvent(REDIS_WARNING,\"-failover-abort-no-good-slave\",ri,\"%@\");\n\n        // 中止故障转移\n        sentinelAbortFailover(ri);\n\n    } else {\n\n        // 成功选定新主服务器\n\n        // 发送事件\n        sentinelEvent(REDIS_WARNING,\"+selected-slave\",slave,\"%@\");\n\n        // 打开实例的升级标记\n        slave->flags |= SRI_PROMOTED;\n\n        // 记录被选中的从服务器\n        ri->promoted_slave = slave;\n\n        // 更新故障转移状态\n        ri->failover_state = SENTINEL_FAILOVER_STATE_SEND_SLAVEOF_NOONE;\n\n        // 更新状态改变时间\n        ri->failover_state_change_time = mstime();\n\n        // 发送事件\n        sentinelEvent(REDIS_NOTICE,\"+failover-state-send-slaveof-noone\",\n            slave, \"%@\");\n    }\n}\n\n// 向被选中的从服务器发送 SLAVEOF no one 命令\n// 将它升级为新的主服务器\nvoid sentinelFailoverSendSlaveOfNoOne(sentinelRedisInstance *ri) {\n    int retval;\n\n    /* We can't send the command to the promoted slave if it is now\n     * disconnected. Retry again and again with this state until the timeout\n     * is reached, then abort the failover. */\n    // 如果选中的从服务器断线了，那么在给定的时间内重试\n    // 如果给定时间内选中的从服务器也没有上线，那么终止故障迁移操作\n    // （一般来说出现这种情况的机会很小，因为在选择新的主服务器时，\n    // 已经断线的从服务器是不会被选中的，所以这种情况只会出现在\n    // 从服务器被选中，并且发送 SLAVEOF NO ONE 命令之前的这段时间内）\n    if (ri->promoted_slave->flags & SRI_DISCONNECTED) {\n\n        // 如果超过时限，就不再重试\n        if (mstime() - ri->failover_state_change_time > ri->failover_timeout) {\n            sentinelEvent(REDIS_WARNING,\"-failover-abort-slave-timeout\",ri,\"%@\");\n            sentinelAbortFailover(ri);\n        }\n        return;\n    }\n\n    /* Send SLAVEOF NO ONE command to turn the slave into a master.\n     *\n     * 向被升级的从服务器发送 SLAVEOF NO ONE 命令，将它变为一个主服务器。\n     *\n     * We actually register a generic callback for this command as we don't\n     * really care about the reply. We check if it worked indirectly observing\n     * if INFO returns a different role (master instead of slave). \n     *\n     * 这里没有为命令回复关联一个回调函数，因为从服务器是否已经转变为主服务器可以\n     * 通过向从服务器发送 INFO 命令来确认\n     */\n    retval = sentinelSendSlaveOf(ri->promoted_slave,NULL,0);\n    if (retval != REDIS_OK) return;\n    sentinelEvent(REDIS_NOTICE, \"+failover-state-wait-promotion\",\n        ri->promoted_slave,\"%@\");\n\n    // 更新状态\n    // 这个状态会让 Sentinel 等待被选中的从服务器升级为主服务器\n    ri->failover_state = SENTINEL_FAILOVER_STATE_WAIT_PROMOTION;\n\n    // 更新状态改变的时间\n    ri->failover_state_change_time = mstime();\n}\n\n/* We actually wait for promotion indirectly checking with INFO when the\n * slave turns into a master. */\n// Sentinel 会通过 INFO 命令的回复检查从服务器是否已经转变为主服务器\n// 这里只负责检查时限\nvoid sentinelFailoverWaitPromotion(sentinelRedisInstance *ri) {\n    /* Just handle the timeout. Switching to the next state is handled\n     * by the function parsing the INFO command of the promoted slave. */\n    if (mstime() - ri->failover_state_change_time > ri->failover_timeout) {\n        sentinelEvent(REDIS_WARNING,\"-failover-abort-slave-timeout\",ri,\"%@\");\n        sentinelAbortFailover(ri);\n    }\n}\n\n// 判断故障转移操作是否结束\n// 结束可以因为超时，也可以因为所有从服务器已经同步到新主服务器\nvoid sentinelFailoverDetectEnd(sentinelRedisInstance *master) {\n    int not_reconfigured = 0, timeout = 0;\n    dictIterator *di;\n    dictEntry *de;\n\n    // 上次 failover 状态更新以来，经过的时间\n    mstime_t elapsed = mstime() - master->failover_state_change_time;\n\n    /* We can't consider failover finished if the promoted slave is\n     * not reachable. */\n    // 如果新主服务器已经下线，那么故障转移操作不成功\n    if (master->promoted_slave == NULL ||\n        master->promoted_slave->flags & SRI_S_DOWN) return;\n\n    /* The failover terminates once all the reachable slaves are properly\n     * configured. */\n    // 计算未完成同步的从服务器的数量\n    di = dictGetIterator(master->slaves);\n    while((de = dictNext(di)) != NULL) {\n        sentinelRedisInstance *slave = dictGetVal(de);\n\n        // 新主服务器和已完成同步的从服务器不计算在内\n        if (slave->flags & (SRI_PROMOTED|SRI_RECONF_DONE)) continue;\n\n        // 已下线的从服务器不计算在内\n        if (slave->flags & SRI_S_DOWN) continue;\n\n        // 增一\n        not_reconfigured++;\n    }\n    dictReleaseIterator(di);\n\n    /* Force end of failover on timeout. */\n    // 故障操作因为超时而结束\n    if (elapsed > master->failover_timeout) {\n        // 忽略未完成的从服务器\n        not_reconfigured = 0;\n        // 打开超时标志\n        timeout = 1;\n        // 发送超时事件\n        sentinelEvent(REDIS_WARNING,\"+failover-end-for-timeout\",master,\"%@\");\n    }\n\n    // 所有从服务器都已完成同步，故障转移结束\n    if (not_reconfigured == 0) {\n        sentinelEvent(REDIS_WARNING,\"+failover-end\",master,\"%@\");\n        // 更新故障转移状态\n        // 这一状态将告知 Sentinel ，所有从服务器都已经同步到新主服务器\n        master->failover_state = SENTINEL_FAILOVER_STATE_UPDATE_CONFIG;\n        // 更新状态改变的时间\n        master->failover_state_change_time = mstime();\n    }\n\n    /* If I'm the leader it is a good idea to send a best effort SLAVEOF\n     * command to all the slaves still not reconfigured to replicate with\n     * the new master. */\n    if (timeout) {\n        dictIterator *di;\n        dictEntry *de;\n\n        // 遍历所有从服务器\n        di = dictGetIterator(master->slaves);\n        while((de = dictNext(di)) != NULL) {\n            sentinelRedisInstance *slave = dictGetVal(de);\n            int retval;\n\n            // 跳过已发送 SLAVEOF 命令，以及已经完成同步的所有从服务器\n            if (slave->flags &\n                (SRI_RECONF_DONE|SRI_RECONF_SENT|SRI_DISCONNECTED)) continue;\n\n            // 发送命令\n            retval = sentinelSendSlaveOf(slave,\n                    master->promoted_slave->addr->ip,\n                    master->promoted_slave->addr->port);\n            if (retval == REDIS_OK) {\n                sentinelEvent(REDIS_NOTICE,\"+slave-reconf-sent-be\",slave,\"%@\");\n                // 打开从服务器的 SLAVEOF 命令已发送标记\n                slave->flags |= SRI_RECONF_SENT;\n            }\n        }\n        dictReleaseIterator(di);\n    }\n}\n\n/* Send SLAVE OF <new master address> to all the remaining slaves that\n * still don't appear to have the configuration updated. */\n// 向所有尚未同步新主服务器的从服务器发送 SLAVEOF <new-master-address> 命令\nvoid sentinelFailoverReconfNextSlave(sentinelRedisInstance *master) {\n    dictIterator *di;\n    dictEntry *de;\n    int in_progress = 0;\n\n    // 计算正在同步新主服务器的从服务器数量\n    di = dictGetIterator(master->slaves);\n    while((de = dictNext(di)) != NULL) {\n        sentinelRedisInstance *slave = dictGetVal(de);\n\n        // SLAVEOF 命令已发送，或者同步正在进行\n        if (slave->flags & (SRI_RECONF_SENT|SRI_RECONF_INPROG))\n            in_progress++;\n    }\n    dictReleaseIterator(di);\n\n    // 如果正在同步的从服务器的数量少于 parallel-syncs 选项的值\n    // 那么继续遍历从服务器，并让从服务器对新主服务器进行同步\n    di = dictGetIterator(master->slaves);\n    while(in_progress < master->parallel_syncs &&\n          (de = dictNext(di)) != NULL)\n    {\n        sentinelRedisInstance *slave = dictGetVal(de);\n        int retval;\n\n        /* Skip the promoted slave, and already configured slaves. */\n        // 跳过新主服务器，以及已经完成了同步的从服务器\n        if (slave->flags & (SRI_PROMOTED|SRI_RECONF_DONE)) continue;\n\n        /* If too much time elapsed without the slave moving forward to\n         * the next state, consider it reconfigured even if it is not.\n         * Sentinels will detect the slave as misconfigured and fix its\n         * configuration later. */\n        if ((slave->flags & SRI_RECONF_SENT) &&\n            (mstime() - slave->slave_reconf_sent_time) >\n            SENTINEL_SLAVE_RECONF_TIMEOUT)\n        {\n            // 发送重拾同步事件\n            sentinelEvent(REDIS_NOTICE,\"-slave-reconf-sent-timeout\",slave,\"%@\");\n            // 清除已发送 SLAVEOF 命令的标记\n            slave->flags &= ~SRI_RECONF_SENT;\n            slave->flags |= SRI_RECONF_DONE;\n        }\n\n        /* Nothing to do for instances that are disconnected or already\n         * in RECONF_SENT state. */\n        // 如果已向从服务器发送 SLAVEOF 命令，或者同步正在进行\n        // 又或者从服务器已断线，那么略过该服务器\n        if (slave->flags & (SRI_DISCONNECTED|SRI_RECONF_SENT|SRI_RECONF_INPROG))\n            continue;\n\n        /* Send SLAVEOF <new master>. */\n        // 向从服务器发送 SLAVEOF 命令，让它同步新主服务器\n        retval = sentinelSendSlaveOf(slave,\n                master->promoted_slave->addr->ip,\n                master->promoted_slave->addr->port);\n        if (retval == REDIS_OK) {\n\n            // 将状态改为 SLAVEOF 命令已发送\n            slave->flags |= SRI_RECONF_SENT;\n            // 更新发送 SLAVEOF 命令的时间\n            slave->slave_reconf_sent_time = mstime();\n            sentinelEvent(REDIS_NOTICE,\"+slave-reconf-sent\",slave,\"%@\");\n            // 增加当前正在同步的从服务器的数量\n            in_progress++;\n        }\n    }\n    dictReleaseIterator(di);\n\n    /* Check if all the slaves are reconfigured and handle timeout. */\n    // 判断是否所有从服务器的同步都已经完成\n    sentinelFailoverDetectEnd(master);\n}\n\n/* This function is called when the slave is in\n * SENTINEL_FAILOVER_STATE_UPDATE_CONFIG state. In this state we need\n * to remove it from the master table and add the promoted slave instead. */\n// 这个函数在 master 已下线，并且对这个 master 的故障迁移操作已经完成时调用\n// 这个 master 会被移除出 master 表格，并由新的主服务器代替\nvoid sentinelFailoverSwitchToPromotedSlave(sentinelRedisInstance *master) {\n\n    /// 选出要添加的 master\n    sentinelRedisInstance *ref = master->promoted_slave ?\n                                 master->promoted_slave : master;\n\n    // 发送更新 master 事件\n    sentinelEvent(REDIS_WARNING,\"+switch-master\",master,\"%s %s %d %s %d\",\n        // 原 master 信息\n        master->name, master->addr->ip, master->addr->port,\n        // 新 master 信息\n        ref->addr->ip, ref->addr->port);\n\n    // 用新主服务器的信息代替原 master 的信息\n    sentinelResetMasterAndChangeAddress(master,ref->addr->ip,ref->addr->port);\n}\n\n// 执行故障转移\nvoid sentinelFailoverStateMachine(sentinelRedisInstance *ri) {\n    redisAssert(ri->flags & SRI_MASTER);\n\n    // master 未进入故障转移状态，直接返回\n    if (!(ri->flags & SRI_FAILOVER_IN_PROGRESS)) return;\n\n    switch(ri->failover_state) {\n\n        // 等待故障转移开始\n        case SENTINEL_FAILOVER_STATE_WAIT_START:\n            sentinelFailoverWaitStart(ri);\n            break;\n\n        // 选择新主服务器\n        case SENTINEL_FAILOVER_STATE_SELECT_SLAVE:\n            sentinelFailoverSelectSlave(ri);\n            break;\n        \n        // 升级被选中的从服务器为新主服务器\n        case SENTINEL_FAILOVER_STATE_SEND_SLAVEOF_NOONE:\n            sentinelFailoverSendSlaveOfNoOne(ri);\n            break;\n\n        // 等待升级生效，如果升级超时，那么重新选择新主服务器\n        // 具体情况请看 sentinelRefreshInstanceInfo 函数\n        case SENTINEL_FAILOVER_STATE_WAIT_PROMOTION:\n            sentinelFailoverWaitPromotion(ri);\n            break;\n\n        // 向从服务器发送 SLAVEOF 命令，让它们同步新主服务器\n        case SENTINEL_FAILOVER_STATE_RECONF_SLAVES:\n            sentinelFailoverReconfNextSlave(ri);\n            break;\n    }\n}\n\n/* Abort a failover in progress:\n *\n * 关于中途停止执行故障转移：\n *\n * This function can only be called before the promoted slave acknowledged\n * the slave -> master switch. Otherwise the failover can't be aborted and\n * will reach its end (possibly by timeout). \n *\n * 这个函数只能在被选中的从服务器升级为新的主服务器之前调用，\n * 否则故障转移就不能中途停止，\n * 并且会一直执行到结束。\n */\nvoid sentinelAbortFailover(sentinelRedisInstance *ri) {\n    redisAssert(ri->flags & SRI_FAILOVER_IN_PROGRESS);\n    redisAssert(ri->failover_state <= SENTINEL_FAILOVER_STATE_WAIT_PROMOTION);\n\n    // 移除相关标识\n    ri->flags &= ~(SRI_FAILOVER_IN_PROGRESS|SRI_FORCE_FAILOVER);\n\n    // 清除状态\n    ri->failover_state = SENTINEL_FAILOVER_STATE_NONE;\n    ri->failover_state_change_time = mstime();\n\n    // 清除新主服务器的升级标识\n    if (ri->promoted_slave) {\n        ri->promoted_slave->flags &= ~SRI_PROMOTED;\n        // 清空新服务器\n        ri->promoted_slave = NULL;\n    }\n}\n\n/* ======================== SENTINEL timer handler ==========================\n * This is the \"main\" our Sentinel, being sentinel completely non blocking\n * in design. The function is called every second.\n * -------------------------------------------------------------------------- */\n\n/* Perform scheduled operations for the specified Redis instance. */\n// 对给定的实例执行定期操作\nvoid sentinelHandleRedisInstance(sentinelRedisInstance *ri) {\n\n    /* ========== MONITORING HALF ============ */\n    /* ==========     监控操作    =========*/\n\n    /* Every kind of instance */\n    /* 对所有类型实例进行处理 */\n\n    // 如果有需要的话，创建连向实例的网络连接\n    sentinelReconnectInstance(ri);\n\n    // 根据情况，向实例发送 PING、 INFO 或者 PUBLISH 命令\n    sentinelSendPeriodicCommands(ri);\n\n    /* ============== ACTING HALF ============= */\n    /* ==============  故障检测   ============= */\n\n    /* We don't proceed with the acting half if we are in TILT mode.\n     * TILT happens when we find something odd with the time, like a\n     * sudden change in the clock. */\n    // 如果 Sentinel 处于 TILT 模式，那么不执行故障检测。\n    if (sentinel.tilt) {\n\n        // 如果 TILI 模式未解除，那么不执行动作\n        if (mstime()-sentinel.tilt_start_time < SENTINEL_TILT_PERIOD) return;\n\n        // 时间已过，退出 TILT 模式\n        sentinel.tilt = 0;\n        sentinelEvent(REDIS_WARNING,\"-tilt\",NULL,\"#tilt mode exited\");\n    }\n\n    /* Every kind of instance */\n    // 检查给定实例是否进入 SDOWN 状态\n    sentinelCheckSubjectivelyDown(ri);\n\n    /* Masters and slaves */\n    if (ri->flags & (SRI_MASTER|SRI_SLAVE)) {\n        /* Nothing so far. */\n    }\n\n    /* Only masters */\n    /* 对主服务器进行处理 */\n    if (ri->flags & SRI_MASTER) {\n\n        // 判断 master 是否进入 ODOWN 状态\n        sentinelCheckObjectivelyDown(ri);\n\n        // 如果主服务器进入了 ODOWN 状态，那么开始一次故障转移操作\n        if (sentinelStartFailoverIfNeeded(ri))\n            // 强制向其他 Sentinel 发送 SENTINEL is-master-down-by-addr 命令\n            // 刷新其他 Sentinel 关于主服务器的状态\n            sentinelAskMasterStateToOtherSentinels(ri,SENTINEL_ASK_FORCED);\n\n        // 执行故障转移\n        sentinelFailoverStateMachine(ri);\n\n        // 如果有需要的话，向其他 Sentinel 发送 SENTINEL is-master-down-by-addr 命令\n        // 刷新其他 Sentinel 关于主服务器的状态\n        // 这一句是对那些没有进入 if(sentinelStartFailoverIfNeeded(ri)) { /* ... */ }\n        // 语句的主服务器使用的\n        sentinelAskMasterStateToOtherSentinels(ri,SENTINEL_NO_FLAGS);\n    }\n}\n\n/* Perform scheduled operations for all the instances in the dictionary.\n * Recursively call the function against dictionaries of slaves. */\n// 对被 Sentinel 监视的所有实例（包括主服务器、从服务器和其他 Sentinel ）\n// 进行定期操作\n//\n//  sentinelHandleRedisInstance\n//              |\n//              |\n//              v\n//            master\n//             /  \\\n//            /    \\\n//           v      v\n//       slaves    sentinels\nvoid sentinelHandleDictOfRedisInstances(dict *instances) {\n    dictIterator *di;\n    dictEntry *de;\n    sentinelRedisInstance *switch_to_promoted = NULL;\n\n    /* There are a number of things we need to perform against every master. */\n    // 遍历多个实例，这些实例可以是多个主服务器、多个从服务器或者多个 sentinel\n    di = dictGetIterator(instances);\n    while((de = dictNext(di)) != NULL) {\n\n        // 取出实例对应的实例结构\n        sentinelRedisInstance *ri = dictGetVal(de);\n\n        // 执行调度操作\n        sentinelHandleRedisInstance(ri);\n\n        // 如果被遍历的是主服务器，那么递归地遍历该主服务器的所有从服务器\n        // 以及所有 sentinel\n        if (ri->flags & SRI_MASTER) {\n\n            // 所有从服务器\n            sentinelHandleDictOfRedisInstances(ri->slaves);\n\n            // 所有 sentinel\n            sentinelHandleDictOfRedisInstances(ri->sentinels);\n\n            // 对已下线主服务器（ri）的故障迁移已经完成\n            // ri 的所有从服务器都已经同步到新主服务器\n            if (ri->failover_state == SENTINEL_FAILOVER_STATE_UPDATE_CONFIG) {\n                // 已选出新的主服务器\n                switch_to_promoted = ri;\n            }\n        }\n    }\n\n    // 将原主服务器（已下线）从主服务器表格中移除，并使用新主服务器代替它\n    if (switch_to_promoted)\n        sentinelFailoverSwitchToPromotedSlave(switch_to_promoted);\n\n    dictReleaseIterator(di);\n}\n\n/* This function checks if we need to enter the TITL mode.\n *\n * 这个函数检查 sentinel 是否需要进入 TITL 模式。\n *\n * The TILT mode is entered if we detect that between two invocations of the\n * timer interrupt, a negative amount of time, or too much time has passed.\n *\n * 当程序发现两次执行 sentinel 之间的时间差为负值，或者过大时，\n * 就会进入 TILT 模式。\n *\n * Note that we expect that more or less just 100 milliseconds will pass\n * if everything is fine. However we'll see a negative number or a\n * difference bigger than SENTINEL_TILT_TRIGGER milliseconds if one of the\n * following conditions happen:\n *\n * 通常来说，两次执行 sentinel 之间的差会在 100 毫秒左右，\n * 但当出现以下内容时，这个差就可能会出现异常：\n *\n * 1) The Sentinel process for some time is blocked, for every kind of\n * random reason: the load is huge, the computer was frozen for some time\n * in I/O or alike, the process was stopped by a signal. Everything.\n *    sentinel 进程因为某些原因而被阻塞，比如载入的数据太大，计算机 I/O 任务繁重，\n *    进程被信号停止，诸如此类。\n *\n * 2) The system clock was altered significantly.\n *    系统的时钟产生了非常明显的变化。\n *\n * Under both this conditions we'll see everything as timed out and failing\n * without good reasons. Instead we enter the TILT mode and wait\n * for SENTINEL_TILT_PERIOD to elapse before starting to act again.\n *\n * 当出现以上两种情况时， sentinel 可能会将任何实例都视为掉线，并无原因地判断实例为失效。\n * 为了避免这种情况，我们让 sentinel 进入 TILT 模式，\n * 停止进行任何动作，并等待 SENTINEL_TILT_PERIOD 秒钟。 \n *\n * During TILT time we still collect information, we just do not act. \n *\n * TILT 模式下的 sentinel 仍然会进行监控并收集信息，\n * 它只是不执行诸如故障转移、下线判断之类的操作而已。\n */\nvoid sentinelCheckTiltCondition(void) {\n\n    // 计算当前时间\n    mstime_t now = mstime();\n\n    // 计算上次运行 sentinel 和当前时间的差\n    mstime_t delta = now - sentinel.previous_time;\n\n    // 如果差为负数，或者大于 2 秒钟，那么进入 TILT 模式\n    if (delta < 0 || delta > SENTINEL_TILT_TRIGGER) {\n        // 打开标记\n        sentinel.tilt = 1;\n        // 记录进入 TILT 模式的开始时间\n        sentinel.tilt_start_time = mstime();\n        // 打印事件\n        sentinelEvent(REDIS_WARNING,\"+tilt\",NULL,\"#tilt mode entered\");\n    }\n\n    // 更新最后一次 sentinel 运行时间\n    sentinel.previous_time = mstime();\n}\n\n// sentinel 模式的主函数，由 redis.c/serverCron 函数调用\nvoid sentinelTimer(void) {\n\n    // 记录本次 sentinel 调用的事件，\n    // 并判断是否需要进入 TITL 模式\n    sentinelCheckTiltCondition();\n\n    // 执行定期操作\n    // 比如 PING 实例、分析主服务器和从服务器的 INFO 命令\n    // 向其他监视相同主服务器的 sentinel 发送问候信息\n    // 并接收其他 sentinel 发来的问候信息\n    // 执行故障转移操作，等等\n    sentinelHandleDictOfRedisInstances(sentinel.masters);\n\n    // 运行等待执行的脚本\n    sentinelRunPendingScripts();\n\n    // 清理已执行完毕的脚本，并重试出错的脚本\n    sentinelCollectTerminatedScripts();\n\n    // 杀死运行超时的脚本\n    sentinelKillTimedoutScripts();\n\n    /* We continuously change the frequency of the Redis \"timer interrupt\"\n     * in order to desynchronize every Sentinel from every other.\n     * This non-determinism avoids that Sentinels started at the same time\n     * exactly continue to stay synchronized asking to be voted at the\n     * same time again and again (resulting in nobody likely winning the\n     * election because of split brain voting). */\n    server.hz = REDIS_DEFAULT_HZ + rand() % REDIS_DEFAULT_HZ;\n}\n"
  },
  {
    "path": "src/setproctitle.c",
    "content": "/* ==========================================================================\n * setproctitle.c - Linux/Darwin setproctitle.\n * --------------------------------------------------------------------------\n * Copyright (C) 2010  William Ahern\n * Copyright (C) 2013  Salvatore Sanfilippo\n * Copyright (C) 2013  Stam He\n *\n * Permission is hereby granted, free of charge, to any person obtaining a\n * copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to permit\n * persons to whom the Software is furnished to do so, subject to the\n * following conditions:\n *\n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\n * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN\n * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,\n * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\n * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE\n * USE OR OTHER DEALINGS IN THE SOFTWARE.\n * ==========================================================================\n */\n#ifndef _GNU_SOURCE\n#define _GNU_SOURCE\n#endif\n\n#include <stddef.h>\t/* NULL size_t */\n#include <stdarg.h>\t/* va_list va_start va_end */\n#include <stdlib.h>\t/* malloc(3) setenv(3) clearenv(3) setproctitle(3) getprogname(3) */\n#include <stdio.h>\t/* vsnprintf(3) snprintf(3) */\n\n#include <string.h>\t/* strlen(3) strchr(3) strdup(3) memset(3) memcpy(3) */\n\n#include <errno.h>\t/* errno program_invocation_name program_invocation_short_name */\n\n#if !defined(HAVE_SETPROCTITLE)\n#define HAVE_SETPROCTITLE (defined __NetBSD__ || defined __FreeBSD__ || defined __OpenBSD__)\n#endif\n\n\n#if !HAVE_SETPROCTITLE\n#if (defined __linux || defined __APPLE__)\n\nextern char **environ;\n\nstatic struct {\n\t/* original value */\n\tconst char *arg0;\n\n\t/* title space available */\n\tchar *base, *end;\n\n\t /* pointer to original nul character within base */\n\tchar *nul;\n\n\t_Bool reset;\n\tint error;\n} SPT;\n\n\n#ifndef SPT_MIN\n#define SPT_MIN(a, b) (((a) < (b))? (a) : (b))\n#endif\n\nstatic inline size_t spt_min(size_t a, size_t b) {\n\treturn SPT_MIN(a, b);\n} /* spt_min() */\n\n\n/*\n * For discussion on the portability of the various methods, see\n * http://lists.freebsd.org/pipermail/freebsd-stable/2008-June/043136.html\n */\nstatic int spt_clearenv(void) {\n#if __GLIBC__\n\tclearenv();\n\n\treturn 0;\n#else\n\textern char **environ;\n\tstatic char **tmp;\n\n\tif (!(tmp = malloc(sizeof *tmp)))\n\t\treturn errno;\n\n\ttmp[0]  = NULL;\n\tenviron = tmp;\n\n\treturn 0;\n#endif\n} /* spt_clearenv() */\n\n\nstatic int spt_copyenv(char *oldenv[]) {\n\textern char **environ;\n\tchar *eq;\n\tint i, error;\n\n\tif (environ != oldenv)\n\t\treturn 0;\n\n\tif ((error = spt_clearenv()))\n\t\tgoto error;\n\n\tfor (i = 0; oldenv[i]; i++) {\n\t\tif (!(eq = strchr(oldenv[i], '=')))\n\t\t\tcontinue;\n\n\t\t*eq = '\\0';\n\t\terror = (0 != setenv(oldenv[i], eq + 1, 1))? errno : 0;\n\t\t*eq = '=';\n\n\t\tif (error)\n\t\t\tgoto error;\n\t}\n\n\treturn 0;\nerror:\n\tenviron = oldenv;\n\n\treturn error;\n} /* spt_copyenv() */\n\n\nstatic int spt_copyargs(int argc, char *argv[]) {\n\tchar *tmp;\n\tint i;\n\n\tfor (i = 1; i < argc || (i >= argc && argv[i]); i++) {\n\t\tif (!argv[i])\n\t\t\tcontinue;\n\n\t\tif (!(tmp = strdup(argv[i])))\n\t\t\treturn errno;\n\n\t\targv[i] = tmp;\n\t}\n\n\treturn 0;\n} /* spt_copyargs() */\n\n\nvoid spt_init(int argc, char *argv[]) {\n        char **envp = environ;\n\tchar *base, *end, *nul, *tmp;\n\tint i, error;\n\n\tif (!(base = argv[0]))\n\t\treturn;\n\n\tnul = &base[strlen(base)];\n\tend = nul + 1;\n\n\tfor (i = 0; i < argc || (i >= argc && argv[i]); i++) {\n\t\tif (!argv[i] || argv[i] < end)\n\t\t\tcontinue;\n\n\t\tend = argv[i] + strlen(argv[i]) + 1;\n\t}\n\n\tfor (i = 0; envp[i]; i++) {\n\t\tif (envp[i] < end)\n\t\t\tcontinue;\n\n\t\tend = envp[i] + strlen(envp[i]) + 1;\n\t}\n\n\tif (!(SPT.arg0 = strdup(argv[0])))\n\t\tgoto syerr;\n\n#if __GLIBC__\n\tif (!(tmp = strdup(program_invocation_name)))\n\t\tgoto syerr;\n\n\tprogram_invocation_name = tmp;\n\n\tif (!(tmp = strdup(program_invocation_short_name)))\n\t\tgoto syerr;\n\n\tprogram_invocation_short_name = tmp;\n#elif __APPLE__\n\tif (!(tmp = strdup(getprogname())))\n\t\tgoto syerr;\n\n\tsetprogname(tmp);\n#endif\n\n\n\tif ((error = spt_copyenv(envp)))\n\t\tgoto error;\n\n\tif ((error = spt_copyargs(argc, argv)))\n\t\tgoto error;\n\n\tSPT.nul  = nul;\n\tSPT.base = base;\n\tSPT.end  = end;\n\n\treturn;\nsyerr:\n\terror = errno;\nerror:\n\tSPT.error = error;\n} /* spt_init() */\n\n\n#ifndef SPT_MAXTITLE\n#define SPT_MAXTITLE 255\n#endif\n\nvoid setproctitle(const char *fmt, ...) {\n\tchar buf[SPT_MAXTITLE + 1]; /* use buffer in case argv[0] is passed */\n\tva_list ap;\n\tchar *nul;\n\tint len, error;\n\n\tif (!SPT.base)\n\t\treturn;\n\n\tif (fmt) {\n\t\tva_start(ap, fmt);\n\t\tlen = vsnprintf(buf, sizeof buf, fmt, ap);\n\t\tva_end(ap);\n\t} else {\n\t\tlen = snprintf(buf, sizeof buf, \"%s\", SPT.arg0);\n\t}\n\n\tif (len <= 0)\n\t\t{ error = errno; goto error; }\n\n\tif (!SPT.reset) {\n\t\tmemset(SPT.base, 0, SPT.end - SPT.base);\n\t\tSPT.reset = 1;\n\t} else {\n\t\tmemset(SPT.base, 0, spt_min(sizeof buf, SPT.end - SPT.base));\n\t}\n\n\tlen = spt_min(len, spt_min(sizeof buf, SPT.end - SPT.base) - 1);\n\tmemcpy(SPT.base, buf, len);\n\tnul = &SPT.base[len];\n\n\tif (nul < SPT.nul) {\n\t\t*SPT.nul = '.';\n\t} else if (nul == SPT.nul && &nul[1] < SPT.end) {\n\t\t*SPT.nul = ' ';\n\t\t*++nul = '\\0';\n\t}\n\n\treturn;\nerror:\n\tSPT.error = error;\n} /* setproctitle() */\n\n\n#endif /* __linux || __APPLE__ */\n#endif /* !HAVE_SETPROCTITLE */\n"
  },
  {
    "path": "src/sha1.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 <sys/types.h>\t/* for u_int*_t */\n#if defined(__sun)\n#include \"solarisfixes.h\"\n#endif\n#include \"sha1.h\"\n#include \"config.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#if BYTE_ORDER == LITTLE_ENDIAN\n#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \\\n    |(rol(block->l[i],8)&0x00FF00FF))\n#elif BYTE_ORDER == BIG_ENDIAN\n#define blk0(i) block->l[i]\n#else\n#error \"Endianness not defined!\"\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(u_int32_t state[5], const unsigned char buffer[64])\n{\n    u_int32_t a, b, c, d, e;\n    typedef union {\n        unsigned char c[64];\n        u_int32_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, u_int32_t len)\n{\n    u_int32_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        u_int32_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\n#if 0\n#define BUFSIZE 4096\n\nint\nmain(int argc, char **argv)\n{\n    SHA1_CTX ctx;\n    unsigned char hash[20], buf[BUFSIZE];\n    int i;\n\n    for(i=0;i<BUFSIZE;i++)\n        buf[i] = i;\n\n    SHA1Init(&ctx);\n    for(i=0;i<1000;i++)\n        SHA1Update(&ctx, buf, BUFSIZE);\n    SHA1Final(hash, &ctx);\n\n    printf(\"SHA1=\");\n    for(i=0;i<20;i++)\n        printf(\"%02x\", hash[i]);\n    printf(\"\\n\");\n    return 0;\n}\n\n#endif\n\n"
  },
  {
    "path": "src/sha1.h",
    "content": "/* ================ sha1.h ================ */\n/*\nSHA-1 in C\nBy Steve Reid <steve@edmweb.com>\n100% Public Domain\n*/\n\ntypedef struct {\n    u_int32_t state[5];\n    u_int32_t count[2];\n    unsigned char buffer[64];\n} SHA1_CTX;\n\nvoid SHA1Transform(u_int32_t state[5], const unsigned char buffer[64]);\nvoid SHA1Init(SHA1_CTX* context);\nvoid SHA1Update(SHA1_CTX* context, const unsigned char* data, u_int32_t len);\nvoid SHA1Final(unsigned char digest[20], SHA1_CTX* context);\n"
  },
  {
    "path": "src/slowlog.c",
    "content": "/* Slowlog implements a system that is able to remember the latest N\n * queries that took more than M microseconds to execute.\n *\n * Slowlog 用于记录最新 N 条执行时间超过 M 毫秒的命令。\n *\n * The execution time to reach to be logged in the slow log is set\n * using the 'slowlog-log-slower-than' config directive, that is also\n * readable and writable using the CONFIG SET/GET command.\n *\n * 上限时间由选项 slowlog-log-slower-than 决定，\n * 可以使用 CONFIG SET/GET 命令来设置/获取这个选项的值。\n *\n * The slow queries log is actually not \"logged\" in the Redis log file\n * but is accessible thanks to the SLOWLOG command.\n *\n * 慢查询日志保存在内存而不是文件中，\n * 这确保了慢查询日志本身不会成为速度的瓶颈。\n *\n * ----------------------------------------------------------------------------\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 \"redis.h\"\n#include \"slowlog.h\"\n\n/* Create a new slowlog entry.\n *\n * 创建一条新的慢查询日志\n *\n * Incrementing the ref count of all the objects retained is up to\n * this function. \n *\n * 函数负责增加所有记录对象的引用计数\n */\nslowlogEntry *slowlogCreateEntry(robj **argv, int argc, long long duration) {\n    slowlogEntry *se = zmalloc(sizeof(*se));\n    int j, slargc = argc;\n\n    // 如果参数过多，那么只记录服务器允许的最大参数数量\n    if (slargc > SLOWLOG_ENTRY_MAX_ARGC) slargc = SLOWLOG_ENTRY_MAX_ARGC;\n\n    // 记录参数数量\n    se->argc = slargc;\n\n    // 遍历并记录命令的参数\n    se->argv = zmalloc(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        // 当参数的数量超过服务器允许的最大参数数量时，\n        // 用最后一个参数记录省略提示\n        if (slargc != argc && j == slargc-1) {\n            se->argv[j] = createObject(REDIS_STRING,\n                sdscatprintf(sdsempty(),\"... (%d more arguments)\",\n                argc-slargc+1));\n        } else {\n            /* Trim too long strings as well... */\n            // 如果参数太长，那么进行截断\n            if (argv[j]->type == REDIS_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\n                se->argv[j] = createObject(REDIS_STRING,s);\n            } else {\n                se->argv[j] = argv[j];\n                incrRefCount(argv[j]);\n            }\n        }\n    }\n\n    // 命令的执行时间\n    se->time = time(NULL);\n\n    // 执行命令耗费的时间\n    se->duration = duration;\n\n    // 设置慢查询 id\n    se->id = server.slowlog_entry_id++;\n\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 * 释放给定的慢查询日志\n *\n * 因为函数参数的类型为 void* ，所以它可以用作 adlist.c 中的 free 方法。\n *\n * This function will take care to release all the retained object. \n *\n * 这个函数负责对所有记录对象进行引用计数减一。\n */\nvoid slowlogFreeEntry(void *septr) {\n    slowlogEntry *se = septr;\n    int j;\n\n    // 释放参数\n    for (j = 0; j < se->argc; j++)\n        decrRefCount(se->argv[j]);\n\n    zfree(se->argv);\n\n    zfree(se);\n}\n\n/* Initialize the slow log. This function should be called a single time\n * at server startup. \n *\n * 初始化服务器慢查询功能。\n *\n * 这个函数只应该在服务器启动时执行一次。\n */\nvoid slowlogInit(void) {\n\n    // 保存日志的链表，FIFO 顺序\n    server.slowlog = listCreate();\n\n    // 日志数量计数器\n    server.slowlog_entry_id = 0;\n\n    // 日志链表的释构函数\n    listSetFreeMethod(server.slowlog,slowlogFreeEntry);\n}\n\n/* Push a new entry into the slow log.\n *\n * 如果参数 duration 超过服务器设置的上限时间，\n * 那么将一个新条目以 FIFO 顺序推入到慢查询日志中。\n *\n * This function will make sure to trim the slow log accordingly to the\n * configured max length. \n *\n * 根据服务器设置的最大日志长度，可能会对日志进行截断（trim）\n */\nvoid slowlogPushEntryIfNeeded(robj **argv, int argc, long long duration) {\n\n    // 慢查询功能未开启，直接返回\n    if (server.slowlog_log_slower_than < 0) return; /* Slowlog disabled */\n\n    // 如果执行时间超过服务器设置的上限，那么将命令添加到慢查询日志\n    if (duration >= server.slowlog_log_slower_than)\n        // 新日志添加到链表表头\n        listAddNodeHead(server.slowlog,slowlogCreateEntry(argv,argc,duration));\n\n    /* Remove old entries if needed. */\n    // 如果日志数量过多，那么进行删除\n    while (listLength(server.slowlog) > server.slowlog_max_len)\n        listDelNode(server.slowlog,listLast(server.slowlog));\n}\n\n/* Remove all the entries from the current slow log. \n *\n * 删除所有慢查询日志\n */\nvoid slowlogReset(void) {\n    while (listLength(server.slowlog) > 0)\n        listDelNode(server.slowlog,listLast(server.slowlog));\n}\n\n/* The SLOWLOG command. Implements all the subcommands needed to handle the\n * Redis slow log. \n *\n * SLOWLOG 命令的实现，支持 GET / RESET 和 LEN 参数\n */\nvoid slowlogCommand(redisClient *c) {\n\n    // 重置\n    if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,\"reset\")) {\n        slowlogReset();\n        addReply(c,shared.ok);\n\n    // 返回长度\n    } else if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,\"len\")) {\n        addReplyLongLong(c,listLength(server.slowlog));\n\n    // 获取某条或者全部日志\n    } else if ((c->argc == 2 || c->argc == 3) &&\n               !strcasecmp(c->argv[1]->ptr,\"get\"))\n    {\n        long count = 10, sent = 0;\n        listIter li;\n        void *totentries;\n        listNode *ln;\n        slowlogEntry *se;\n\n        if (c->argc == 3 &&\n            getLongFromObjectOrReply(c,c->argv[2],&count,NULL) != REDIS_OK)\n            return;\n\n        // 遍历日志，取出指定数量的日志\n        listRewind(server.slowlog,&li);\n        totentries = addDeferredMultiBulkLength(c);\n        while(count-- && (ln = listNext(&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        setDeferredMultiBulkLength(c,totentries,sent);\n    } else {\n        addReplyError(c,\n            \"Unknown SLOWLOG subcommand or wrong # of args. Try GET, RESET, LEN.\");\n    }\n}\n"
  },
  {
    "path": "src/slowlog.h",
    "content": "/*\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#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 */\n/*\n * 慢查询日志\n */\ntypedef struct slowlogEntry {\n\n    // 命令与命令参数\n    robj **argv;\n\n    // 命令与命令参数的数量\n    int argc;\n\n    // 唯一标识符\n    long long id;       /* Unique entry identifier. */\n\n    // 执行命令消耗的时间，以微秒为单位\n    // 注释里说的 nanoseconds 是错误的\n    long long duration; /* Time spent by the query, in nanoseconds. */\n\n    // 命令执行时的时间，格式为 UNIX 时间戳\n    time_t time;        /* Unix time at which the query was executed. */\n\n} slowlogEntry;\n\n/* Exported API */\nvoid slowlogInit(void);\nvoid slowlogPushEntryIfNeeded(robj **argv, int argc, long long duration);\n\n/* Exported commands */\nvoid slowlogCommand(redisClient *c);\n"
  },
  {
    "path": "src/solarisfixes.h",
    "content": "/* Solaris specific fixes.\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#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"
  },
  {
    "path": "src/sort.c",
    "content": "/* SORT command and helper functions.\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 \"redis.h\"\n#include \"pqsort.h\" /* Partial qsort for SORT+LIMIT */\n#include <math.h> /* isnan() */\n\nzskiplistNode* zslGetElementByRank(zskiplist *zsl, unsigned long rank);\n\n// 创建一次 SORT 操作\nredisSortOperation *createSortOperation(int type, robj *pattern) {\n\n    redisSortOperation *so = zmalloc(sizeof(*so));\n\n    so->type = type;\n    so->pattern = pattern;\n\n    return so;\n}\n\n/* Return the value associated to the key with a name obtained using\n * the following rules:\n *\n * 根据以下规则，返回给定名字的键所关联的值：\n *\n * 1) The first occurrence of '*' in 'pattern' is substituted with 'subst'.\n *\t  模式中出现的第一个 '*' 字符被替换为 subst\n *\n * 2) If 'pattern' matches the \"->\" string, everything on the left of\n *    the arrow is treated as the name of a hash field, and the part on the\n *    left as the key name containing a hash. The value of the specified\n *    field is returned.\n *\t  如果模式中包含一个 \"->\" 字符串，\n *    那么字符串的左边部分会被看作是一个 Hash 键的名字，\n *    而字符串的右边部分会被看作是 Hash 键中的域名（field name）。\n *    给定域所对应的值会被返回。\n *\n * 3) If 'pattern' equals \"#\", the function simply returns 'subst' itself so\n *    that the SORT command can be used like: SORT key GET # to retrieve\n *    the Set/List elements directly.\n *    如果模式等于 \"#\" ，那么函数直接返回 subst 本身，\n *\t  这种用法使得 SORT 命令可以使用 SORT key GET # 的方式来直接获取集合或者列表的元素。\n *\n * 4) 如果 pattern 不是 \"#\" ，并且不包含 '*' 字符，那么直接返回 NULL 。\n *\n * The returned object will always have its refcount increased by 1\n * when it is non-NULL. \n *\n * 如果返回的对象不是 NULL ，那么这个对象的引用计数总是被增一的。\n */\nrobj *lookupKeyByPattern(redisDb *db, robj *pattern, robj *subst) {\n    char *p, *f, *k;\n    sds spat, ssub;\n    robj *keyobj, *fieldobj = NULL, *o;\n    int prefixlen, sublen, postfixlen, fieldlen;\n\n    /* If the pattern is \"#\" return the substitution object itself in order\n     * to implement the \"SORT ... GET #\" feature. */\n\t// 如果模式是 # ，那么直接返回 subst\n    spat = pattern->ptr;\n    if (spat[0] == '#' && spat[1] == '\\0') {\n        incrRefCount(subst);\n        return subst;\n    }\n\n    /* The substitution object may be specially encoded. If so we create\n     * a decoded object on the fly. Otherwise getDecodedObject will just\n     * increment the ref count, that we'll decrement later. */\n    // 获取解码后的 subst\n    subst = getDecodedObject(subst);\n    // 指向 subst 所保存的字符串\n    ssub = subst->ptr;\n\n    /* If we can't find '*' in the pattern we return NULL as to GET a\n     * fixed key does not make sense. */\n\t// 如果模式不是 \"#\" ，并且模式中不带 '*' ，那么直接返回 NULL\n    // 因为一直返回固定的键是没有意义的\n    p = strchr(spat,'*');\n    if (!p) {\n        decrRefCount(subst);\n        return NULL;\n    }\n\n    /* Find out if we're dealing with a hash dereference. */\n\t// 检查指定的是字符串键还是 Hash 键\n    if ((f = strstr(p+1, \"->\")) != NULL && *(f+2) != '\\0') {\n\t\t// Hash 键\n        // 域的长度\n        fieldlen = sdslen(spat)-(f-spat)-2;\n        // 域的对象\n        fieldobj = createStringObject(f+2,fieldlen);\n    } else {\n\t\t// 字符串键，没有域\n        fieldlen = 0;\n    }\n\n    /* Perform the '*' substitution. */\n\t// 根据模式，进行替换\n\t// 比如说， subst 为 www ，模式为 nono_happ_*\n\t// 那么替换结果就是 nono_happ_www\n    // 又比如说， subst 为 peter ，模式为 *-info->age\n    // 那么替换结果就是 peter-info->age\n    prefixlen = p-spat;\n    sublen = sdslen(ssub);\n    postfixlen = sdslen(spat)-(prefixlen+1)-(fieldlen ? fieldlen+2 : 0);\n    keyobj = createStringObject(NULL,prefixlen+sublen+postfixlen);\n    k = keyobj->ptr;\n    memcpy(k,spat,prefixlen);\n    memcpy(k+prefixlen,ssub,sublen);\n    memcpy(k+prefixlen+sublen,p+1,postfixlen);\n    decrRefCount(subst); /* Incremented by decodeObject() */\n\n    /* Lookup substituted key */\n\t// 查找替换 key\n    o = lookupKeyRead(db,keyobj);\n    if (o == NULL) goto noobj;\n\n    // 这是一个 Hash 键\n    if (fieldobj) {\n        if (o->type != REDIS_HASH) goto noobj;\n\n        /* Retrieve value from hash by the field name. This operation\n         * already increases the refcount of the returned object. */\n\t\t// 从 Hash 键的指定域中获取值\n        o = hashTypeGetObject(o, fieldobj);\n\n    // 这是一个字符串键\n    } else {\n        if (o->type != REDIS_STRING) goto noobj;\n\n        /* Every object that this function returns needs to have its refcount\n         * increased. sortCommand decreases it again. */\n\t\t// 增一字符串键的计数\n        incrRefCount(o);\n    }\n    decrRefCount(keyobj);\n    if (fieldobj) decrRefCount(fieldobj);\n\n    // 返回值\n    return o;\n\nnoobj:\n    decrRefCount(keyobj);\n    if (fieldlen) decrRefCount(fieldobj);\n    return NULL;\n}\n\n/* sortCompare() is used by qsort in sortCommand(). Given that qsort_r with\n * the additional parameter is not standard but a BSD-specific we have to\n * pass sorting parameters via the global 'server' structure */\n// 排序算法所使用的对比函数\nint sortCompare(const void *s1, const void *s2) {\n    const redisSortObject *so1 = s1, *so2 = s2;\n    int cmp;\n\n    if (!server.sort_alpha) {\n\n        /* Numeric sorting. Here it's trivial as we precomputed scores */\n\t\t// 数值排序\n\n        if (so1->u.score > so2->u.score) {\n            cmp = 1;\n        } else if (so1->u.score < so2->u.score) {\n            cmp = -1;\n        } else {\n            /* Objects have the same score, but we don't want the comparison\n             * to be undefined, so we compare objects lexicographically.\n             * This way the result of SORT is deterministic. */\n\t\t\t// 两个元素的分值一样，但为了让排序的结果是确定性的（deterministic）\n\t\t\t// 我们对元素的字符串本身进行字典序排序\n            cmp = compareStringObjects(so1->obj,so2->obj);\n        }\n    } else {\n\n        /* Alphanumeric sorting */\n\t\t// 字符排序\n\n        if (server.sort_bypattern) {\n\n\t\t    // 以模式进行对比\n\n\t\t\t// 有至少一个对象为 NULL\n            if (!so1->u.cmpobj || !so2->u.cmpobj) {\n                /* At least one compare object is NULL */\n                if (so1->u.cmpobj == so2->u.cmpobj)\n                    cmp = 0;\n                else if (so1->u.cmpobj == NULL)\n                    cmp = -1;\n                else\n                    cmp = 1;\n            } else {\n                /* We have both the objects, compare them. */\n\t\t\t\t// 两个对象都不为 NULL\n\n                if (server.sort_store) {\n\t\t\t\t\t// 以二进制方式对比两个模式\n                    cmp = compareStringObjects(so1->u.cmpobj,so2->u.cmpobj);\n                } else {\n                    /* Here we can use strcoll() directly as we are sure that\n                     * the objects are decoded string objects. */\n\t\t\t\t\t// 以本地编码对比两个模式\n                    cmp = strcoll(so1->u.cmpobj->ptr,so2->u.cmpobj->ptr);\n                }\n            }\n\n        } else {\n\n\n            /* Compare elements directly. */\n\t\t\t// 对比字符串本身\n\n            if (server.sort_store) {\n\t\t\t\t// 以二进制方式对比字符串对象\n                cmp = compareStringObjects(so1->obj,so2->obj);\n            } else {\n\t\t\t\t// 以本地编码对比字符串对象\n                cmp = collateStringObjects(so1->obj,so2->obj);\n            }\n        }\n    }\n\n    return server.sort_desc ? -cmp : cmp;\n}\n\n/* The SORT command is the most complex command in Redis. Warning: this code\n * is optimized for speed and a bit less for readability */\nvoid sortCommand(redisClient *c) {\n    list *operations;\n    unsigned int outputlen = 0;\n    int desc = 0, alpha = 0;\n    long limit_start = 0, limit_count = -1, start, end;\n    int j, dontsort = 0, vectorlen;\n    int getop = 0; /* GET operation counter */\n    int int_convertion_error = 0;\n    int syntax_error = 0;\n    robj *sortval, *sortby = NULL, *storekey = NULL;\n    redisSortObject *vector; /* Resulting vector to sort */\n\n    /* Lookup the key to sort. It must be of the right types */\n\t// 获取要排序的键，并检查他是否可以被排序的类型\n    sortval = lookupKeyRead(c->db,c->argv[1]);\n    if (sortval && sortval->type != REDIS_SET &&\n                   sortval->type != REDIS_LIST &&\n                   sortval->type != REDIS_ZSET)\n    {\n        addReply(c,shared.wrongtypeerr);\n        return;\n    }\n\n    /* Create a list of operations to perform for every sorted element.\n     * Operations can be GET/DEL/INCR/DECR */\n\t// 创建一个链表，链表中保存了要对所有已排序元素执行的操作\n\t// 操作可以是 GET 、 DEL 、 INCR 或者 DECR\n    operations = listCreate();\n    listSetFreeMethod(operations,zfree);\n\n\t// 指向参数位置\n    j = 2; /* options start at argv[2] */\n\n    /* Now we need to protect sortval incrementing its count, in the future\n     * SORT may have options able to overwrite/delete keys during the sorting\n     * and the sorted key itself may get destroyed */\n\t// 为 sortval 的引用计数增一\n\t// 在将来， SORT 命令可以在排序某个键的过程中，覆盖或者删除那个键\n    if (sortval)\n        incrRefCount(sortval);\n    else\n        sortval = createListObject();\n\n    /* The SORT command has an SQL-alike syntax, parse it */\n\t// 读入并分析 SORT 命令的选项\n    while(j < c->argc) {\n\n        int leftargs = c->argc-j-1;\n\n\t\t// ASC 选项\n        if (!strcasecmp(c->argv[j]->ptr,\"asc\")) {\n            desc = 0;\n\n\t\t// DESC 选项\n        } else if (!strcasecmp(c->argv[j]->ptr,\"desc\")) {\n            desc = 1;\n\n\t\t// ALPHA 选项\n        } else if (!strcasecmp(c->argv[j]->ptr,\"alpha\")) {\n            alpha = 1;\n\n\t\t// LIMIT 选项\n        } else if (!strcasecmp(c->argv[j]->ptr,\"limit\") && leftargs >= 2) {\n\t\t\t// start 参数和 count 参数\n            if ((getLongFromObjectOrReply(c, c->argv[j+1], &limit_start, NULL)\n                 != REDIS_OK) ||\n                (getLongFromObjectOrReply(c, c->argv[j+2], &limit_count, NULL)\n                 != REDIS_OK))\n            {\n                syntax_error++;\n                break;\n            }\n            j+=2;\n\n\t\t// STORE 选项\n        } else if (!strcasecmp(c->argv[j]->ptr,\"store\") && leftargs >= 1) {\n\t\t\t// 目标键\n            storekey = c->argv[j+1];\n            j++;\n\n\t\t// BY 选项\n        } else if (!strcasecmp(c->argv[j]->ptr,\"by\") && leftargs >= 1) {\n\n\t\t\t// 排序的顺序由这个模式决定\n            sortby = c->argv[j+1];\n\n            /* If the BY pattern does not contain '*', i.e. it is constant,\n             * we don't need to sort nor to lookup the weight keys. */\n\t\t\t// 如果 sortby 模式里面不包含 '*' 符号，\n            // 那么无须执行排序操作\n            if (strchr(c->argv[j+1]->ptr,'*') == NULL) {\n                dontsort = 1;\n            } else {\n                /* If BY is specified with a real patter, we can't accept\n                 * it in cluster mode. */\n                if (server.cluster_enabled) {\n                    addReplyError(c,\"BY option of SORT denied in Cluster mode.\");\n                    syntax_error++;\n                    break;\n                }\n            }\n            j++;\n\n\t\t// GET 选项\n        } else if (!strcasecmp(c->argv[j]->ptr,\"get\") && leftargs >= 1) {\n\n\t\t\t// 创建一个 GET 操作\n\n            // 不能在集群模式下使用 GET 选项\n            if (server.cluster_enabled) {\n                addReplyError(c,\"GET option of SORT denied in Cluster mode.\");\n                syntax_error++;\n                break;\n            }\n            listAddNodeTail(operations,createSortOperation(\n                REDIS_SORT_GET,c->argv[j+1]));\n            getop++;\n            j++;\n\n\t\t// 未知选项，语法出错\n        } else {\n            addReply(c,shared.syntaxerr);\n            syntax_error++;\n            break;\n        }\n\n        j++;\n    }\n\n    /* Handle syntax errors set during options parsing. */\n    if (syntax_error) {\n        decrRefCount(sortval);\n        listRelease(operations);\n        return;\n    }\n\n    /* For the STORE option, or when SORT is called from a Lua script,\n     * we want to force a specific ordering even when no explicit ordering\n     * was asked (SORT BY nosort). This guarantees that replication / AOF\n     * is deterministic.\n\t *\n\t * 对于 STORE 选项，以及从 Lua 脚本中调用 SORT 命令的情况来看，\n\t * 我们想即使在没有指定排序方式的情况下，也强制指定一个排序方法。\n\t * 这可以保证复制/AOF 是确定性的。\n     *\n     * However in the case 'dontsort' is true, but the type to sort is a\n     * sorted set, we don't need to do anything as ordering is guaranteed\n     * in this special case. \n\t *\n\t * 在 dontsort 为真，并且被排序的键不是有序集合时，\n\t * 我们才需要为排序指定排序方式，\n     * 因为有序集合的成员已经是有序的了。\n\t */\n    if ((storekey || c->flags & REDIS_LUA_CLIENT) &&\n        (dontsort && sortval->type != REDIS_ZSET))\n    {\n        /* Force ALPHA sorting */\n\t\t// 强制 ALPHA 排序\n        dontsort = 0;\n        alpha = 1;\n        sortby = NULL;\n    }\n\n    /* Destructively convert encoded sorted sets for SORT. */\n\t// 被排序的有序集合必须是 SKIPLIST 编码的\n    // 如果不是的话，那么将它转换成 SKIPLIST 编码\n    if (sortval->type == REDIS_ZSET)\n        zsetConvert(sortval, REDIS_ENCODING_SKIPLIST);\n\n    /* Objtain the length of the object to sort. */\n\t// 获取要排序对象的长度\n    switch(sortval->type) {\n    case REDIS_LIST: vectorlen = listTypeLength(sortval); break;\n    case REDIS_SET: vectorlen =  setTypeSize(sortval); break;\n    case REDIS_ZSET: vectorlen = dictSize(((zset*)sortval->ptr)->dict); break;\n    default: vectorlen = 0; redisPanic(\"Bad SORT type\"); /* Avoid GCC warning */\n    }\n\n    /* Perform LIMIT start,count sanity checking. */\n\t// 对 LIMIT 选项的 start 和 count 参数进行检查\n    start = (limit_start < 0) ? 0 : limit_start;\n    end = (limit_count < 0) ? vectorlen-1 : start+limit_count-1;\n    if (start >= vectorlen) {\n        start = vectorlen-1;\n        end = vectorlen-2;\n    }\n    if (end >= vectorlen) end = vectorlen-1;\n\n    /* Optimization:\n\t * 优化\n     *\n     * 1) if the object to sort is a sorted set.\n\t *    如果排序的对象是有序集合\n     * 2) There is nothing to sort as dontsort is true (BY <constant string>).\n\t *\t  dontsort 为真，表示没有什么需要排序\n     * 3) We have a LIMIT option that actually reduces the number of elements\n     *    to fetch.\n\t *\t  LIMIT 选项所设置的范围比起有序集合的长度要小\n     *\n     * In this case to load all the objects in the vector is a huge waste of\n     * resources. We just allocate a vector that is big enough for the selected\n     * range length, and make sure to load just this part in the vector. \n\t *\n\t * 在这种情况下，不需要载入有序集合中的所有元素，只要载入给定范围（range）内的元素就可以了。\n\t */\n    if (sortval->type == REDIS_ZSET &&\n        dontsort &&\n        (start != 0 || end != vectorlen-1))\n    {\n        vectorlen = end-start+1;\n    }\n\n    /* Load the sorting vector with all the objects to sort */\n\t// 创建 redisSortObject 数组\n    vector = zmalloc(sizeof(redisSortObject)*vectorlen);\n    j = 0;\n\n\t// 将列表项放入数组\n    if (sortval->type == REDIS_LIST) {\n        listTypeIterator *li = listTypeInitIterator(sortval,0,REDIS_TAIL);\n        listTypeEntry entry;\n        while(listTypeNext(li,&entry)) {\n            vector[j].obj = listTypeGet(&entry);\n            vector[j].u.score = 0;\n            vector[j].u.cmpobj = NULL;\n            j++;\n        }\n        listTypeReleaseIterator(li);\n\n\t// 将集合元素放入数组\n    } else if (sortval->type == REDIS_SET) {\n        setTypeIterator *si = setTypeInitIterator(sortval);\n        robj *ele;\n        while((ele = setTypeNextObject(si)) != NULL) {\n            vector[j].obj = ele;\n            vector[j].u.score = 0;\n            vector[j].u.cmpobj = NULL;\n            j++;\n        }\n        setTypeReleaseIterator(si);\n\n\t// 在 dontsort 为真的情况下\n\t// 将有序集合的部分成员放进数组\n    } else if (sortval->type == REDIS_ZSET && dontsort) {\n        /* Special handling for a sorted set, if 'dontsort' is true.\n         * This makes sure we return elements in the sorted set original\n         * ordering, accordingly to DESC / ASC options.\n         *\n         * Note that in this case we also handle LIMIT here in a direct\n         * way, just getting the required range, as an optimization. */\n\n\t\t// 这是前面说过的，可以进行优化的 case\n\n        zset *zs = sortval->ptr;\n        zskiplist *zsl = zs->zsl;\n        zskiplistNode *ln;\n        robj *ele;\n        int rangelen = vectorlen;\n\n        /* Check if starting point is trivial, before doing log(N) lookup. */\n\t\t// 根据 desc 或者 asc 排序，指向初始节点\n        if (desc) {\n            long zsetlen = dictSize(((zset*)sortval->ptr)->dict);\n\n            ln = zsl->tail;\n            if (start > 0)\n                ln = zslGetElementByRank(zsl,zsetlen-start);\n        } else {\n            ln = zsl->header->level[0].forward;\n            if (start > 0)\n                ln = zslGetElementByRank(zsl,start+1);\n        }\n\n\t\t// 遍历范围中的所有节点，并放进数组\n        while(rangelen--) {\n            redisAssertWithInfo(c,sortval,ln != NULL);\n            ele = ln->obj;\n            vector[j].obj = ele;\n            vector[j].u.score = 0;\n            vector[j].u.cmpobj = NULL;\n            j++;\n            ln = desc ? ln->backward : ln->level[0].forward;\n        }\n        /* The code producing the output does not know that in the case of\n         * sorted set, 'dontsort', and LIMIT, we are able to get just the\n         * range, already sorted, so we need to adjust \"start\" and \"end\"\n         * to make sure start is set to 0. */\n        end -= start;\n        start = 0;\n\n\t// 普通情况下的有序集合，将所有集合成员放进数组\n    } else if (sortval->type == REDIS_ZSET) {\n        dict *set = ((zset*)sortval->ptr)->dict;\n        dictIterator *di;\n        dictEntry *setele;\n        di = dictGetIterator(set);\n        while((setele = dictNext(di)) != NULL) {\n            vector[j].obj = dictGetKey(setele);\n            vector[j].u.score = 0;\n            vector[j].u.cmpobj = NULL;\n            j++;\n        }\n        dictReleaseIterator(di);\n    } else {\n        redisPanic(\"Unknown type\");\n    }\n    redisAssertWithInfo(c,sortval,j == vectorlen);\n\n    /* Now it's time to load the right scores in the sorting vector */\n\t// 载入权重值\n    if (dontsort == 0) {\n\n        for (j = 0; j < vectorlen; j++) {\n            robj *byval;\n\n\t\t\t// 如果使用了 BY 选项，那么就根据指定的对象作为权重\n            if (sortby) {\n                /* lookup value to sort by */\n                byval = lookupKeyByPattern(c->db,sortby,vector[j].obj);\n                if (!byval) continue;\n\t\t\t// 如果没有使用 BY 选项，那么使用对象本身作为权重\n            } else {\n                /* use object itself to sort by */\n                byval = vector[j].obj;\n            }\n\n\t\t\t// 如果是 ALPHA 排序，那么将对比对象改为解码后的 byval\n            if (alpha) {\n                if (sortby) vector[j].u.cmpobj = getDecodedObject(byval);\n\t\t\t// 否则，将字符串对象转换成 double 类型\n            } else {\n                if (sdsEncodedObject(byval)) {\n                    char *eptr;\n\t\t\t\t\t// 将字符串转换成 double 类型\n                    vector[j].u.score = strtod(byval->ptr,&eptr);\n                    if (eptr[0] != '\\0' || errno == ERANGE ||\n                        isnan(vector[j].u.score))\n                    {\n                        int_convertion_error = 1;\n                    }\n                } else if (byval->encoding == REDIS_ENCODING_INT) {\n                    /* Don't need to decode the object if it's\n                     * integer-encoded (the only encoding supported) so\n                     * far. We can just cast it */\n\t\t\t\t\t// 直接将整数设置为权重\n                    vector[j].u.score = (long)byval->ptr;\n                } else {\n                    redisAssertWithInfo(c,sortval,1 != 1);\n                }\n            }\n\n            /* when the object was retrieved using lookupKeyByPattern,\n             * its refcount needs to be decreased. */\n            if (sortby) {\n                decrRefCount(byval);\n            }\n        }\n\n    }\n\n\t// 排序\n    if (dontsort == 0) {\n        server.sort_desc = desc;\n        server.sort_alpha = alpha;\n        server.sort_bypattern = sortby ? 1 : 0;\n        server.sort_store = storekey ? 1 : 0;\n\n        if (sortby && (start != 0 || end != vectorlen-1))\n            pqsort(vector,vectorlen,sizeof(redisSortObject),sortCompare, start,end);\n        else\n            qsort(vector,vectorlen,sizeof(redisSortObject),sortCompare);\n    }\n\n    /* Send command output to the output buffer, performing the specified\n     * GET/DEL/INCR/DECR operations if any. */\n\t// 将命令的输出放到输出缓冲区\n\t// 然后执行给定的 GET / DEL / INCR 或 DECR 操作\n    outputlen = getop ? getop*(end-start+1) : end-start+1;\n    if (int_convertion_error) {\n        addReplyError(c,\"One or more scores can't be converted into double\");\n    } else if (storekey == NULL) {\n\n        /* STORE option not specified, sent the sorting result to client */\n\t\t// STORE 选项未使用，直接将排序结果发送给客户端\n\n        addReplyMultiBulkLen(c,outputlen);\n        for (j = start; j <= end; j++) {\n            listNode *ln;\n            listIter li;\n\n\t\t\t// 没有设置 GET 选项，直接将结果添加到回复\n            if (!getop) addReplyBulk(c,vector[j].obj);\n\n            // 有设置 GET 选项。。。\n\n\t\t\t// 遍历设置的操作\n            listRewind(operations,&li);\n            while((ln = listNext(&li))) {\n                redisSortOperation *sop = ln->value;\n\n\t\t\t\t// 解释并查找键\n                robj *val = lookupKeyByPattern(c->db,sop->pattern,\n                    vector[j].obj);\n\n\t\t\t\t// 执行 GET 操作，将指定键的值添加到回复\n                if (sop->type == REDIS_SORT_GET) {\n                    if (!val) {\n                        addReply(c,shared.nullbulk);\n                    } else {\n                        addReplyBulk(c,val);\n                        decrRefCount(val);\n                    }\n\n\t\t\t\t// DEL 、INCR 和 DECR 操作都尚未实现\n                } else {\n                    /* Always fails */\n                    redisAssertWithInfo(c,sortval,sop->type == REDIS_SORT_GET);\n                }\n            }\n        }\n    } else {\n        robj *sobj = createZiplistObject();\n\n        /* STORE option specified, set the sorting result as a List object */\n\t\t// 已设置 STORE 选项，将排序结果保存到列表对象\n\n        for (j = start; j <= end; j++) {\n            listNode *ln;\n            listIter li;\n\n\t\t\t// 没有 GET ，直接返回排序元素\n            if (!getop) {\n                listTypePush(sobj,vector[j].obj,REDIS_TAIL);\n\n\t\t\t// 有 GET ，获取指定的键\n            } else {\n                listRewind(operations,&li);\n                while((ln = listNext(&li))) {\n                    redisSortOperation *sop = ln->value;\n                    robj *val = lookupKeyByPattern(c->db,sop->pattern,\n                        vector[j].obj);\n\n                    if (sop->type == REDIS_SORT_GET) {\n                        if (!val) val = createStringObject(\"\",0);\n\n                        /* listTypePush does an incrRefCount, so we should take care\n                         * care of the incremented refcount caused by either\n                         * lookupKeyByPattern or createStringObject(\"\",0) */\n                        listTypePush(sobj,val,REDIS_TAIL);\n                        decrRefCount(val);\n                    } else {\n                        /* Always fails */\n                        redisAssertWithInfo(c,sortval,sop->type == REDIS_SORT_GET);\n                    }\n                }\n            }\n        }\n\n\t\t// 如果排序结果不为空，那么将结果列表关联到数据库键，并发送事件\n        if (outputlen) {\n            setKey(c->db,storekey,sobj);\n            notifyKeyspaceEvent(REDIS_NOTIFY_LIST,\"sortstore\",storekey,\n                                c->db->id);\n            server.dirty += outputlen;\n\t\t// 如果排序结果为空，那么只要删除 storekey 就可以了，因为没有结果可以保存\n        } else if (dbDelete(c->db,storekey)) {\n            signalModifiedKey(c->db,storekey);\n            notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,\"del\",storekey,c->db->id);\n            server.dirty++;\n        }\n        decrRefCount(sobj);\n        addReplyLongLong(c,outputlen);\n    }\n\n    /* Cleanup */\n    if (sortval->type == REDIS_LIST || sortval->type == REDIS_SET)\n        for (j = 0; j < vectorlen; j++)\n            decrRefCount(vector[j].obj);\n    decrRefCount(sortval);\n    listRelease(operations);\n    for (j = 0; j < vectorlen; j++) {\n        if (alpha && vector[j].u.cmpobj)\n            decrRefCount(vector[j].u.cmpobj);\n    }\n    zfree(vector);\n}\n"
  },
  {
    "path": "src/syncio.c",
    "content": "/* Synchronous socket and file I/O operations useful across the core.\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#include \"redis.h\"\n\n/* ----------------- Blocking sockets I/O with timeouts --------------------- */\n\n/* Redis performs most of the I/O in a nonblocking way, with the exception\n * of the SYNC command where the slave does it in a blocking way, and\n * the MIGRATE command that must be blocking in order to be atomic from the\n * point of view of the two instances (one migrating the key and one receiving\n * the key). This is why need the following blocking I/O functions.\n *\n * All the functions take the timeout in milliseconds. */\n\n#define REDIS_SYNCIO_RESOLUTION 10 /* Resolution in milliseconds */\n\n/* Write the specified payload to 'fd'. If writing the whole payload will be\n * done within 'timeout' milliseconds the operation succeeds and 'size' is\n * returned. Otherwise the operation fails, -1 is returned, and an unspecified\n * partial write could be performed against the file descriptor. */\nssize_t syncWrite(int fd, char *ptr, ssize_t size, long long timeout) {\n    ssize_t nwritten, ret = size;\n    long long start = mstime();\n    long long remaining = timeout;\n\n    while(1) {\n        long long wait = (remaining > REDIS_SYNCIO_RESOLUTION) ?\n                          remaining : REDIS_SYNCIO_RESOLUTION;\n        long long elapsed;\n\n        /* Optimistically try to write before checking if the file descriptor\n         * is actually writable. At worst we get EAGAIN. */\n        nwritten = write(fd,ptr,size);\n        if (nwritten == -1) {\n            if (errno != EAGAIN) return -1;\n        } else {\n            ptr += nwritten;\n            size -= nwritten;\n        }\n        if (size == 0) return ret;\n\n        /* Wait */\n        aeWait(fd,AE_WRITABLE,wait);\n        elapsed = mstime() - start;\n        if (elapsed >= timeout) {\n            errno = ETIMEDOUT;\n            return -1;\n        }\n        remaining = timeout - elapsed;\n    }\n}\n\n/* Read the specified amount of bytes from 'fd'. If all the bytes are read\n * within 'timeout' milliseconds the operation succeed and 'size' is returned.\n * Otherwise the operation fails, -1 is returned, and an unspecified amount of\n * data could be read from the file descriptor. */\nssize_t syncRead(int fd, char *ptr, ssize_t size, long long timeout) {\n    ssize_t nread, totread = 0;\n    long long start = mstime();\n    long long remaining = timeout;\n\n    if (size == 0) return 0;\n    while(1) {\n        long long wait = (remaining > REDIS_SYNCIO_RESOLUTION) ?\n                          remaining : REDIS_SYNCIO_RESOLUTION;\n        long long elapsed;\n\n        /* Optimistically try to read before checking if the file descriptor\n         * is actually readable. At worst we get EAGAIN. */\n        nread = read(fd,ptr,size);\n        if (nread == 0) return -1; /* short read. */\n        if (nread == -1) {\n            if (errno != EAGAIN) return -1;\n        } else {\n            ptr += nread;\n            size -= nread;\n            totread += nread;\n        }\n        if (size == 0) return totread;\n\n        /* Wait */\n        aeWait(fd,AE_READABLE,wait);\n        elapsed = mstime() - start;\n        if (elapsed >= timeout) {\n            errno = ETIMEDOUT;\n            return -1;\n        }\n        remaining = timeout - elapsed;\n    }\n}\n\n/* Read a line making sure that every char will not require more than 'timeout'\n * milliseconds to be read.\n * \n * On success the number of bytes read is returned, otherwise -1.\n * On success the string is always correctly terminated with a 0 byte. */\nssize_t syncReadLine(int fd, char *ptr, ssize_t size, long long timeout) {\n    ssize_t nread = 0;\n\n    size--;\n    while(size) {\n        char c;\n\n        if (syncRead(fd,&c,1,timeout) == -1) return -1;\n        if (c == '\\n') {\n            *ptr = '\\0';\n            if (nread && *(ptr-1) == '\\r') *(ptr-1) = '\\0';\n            return nread;\n        } else {\n            *ptr++ = c;\n            *ptr = '\\0';\n            nread++;\n        }\n    }\n    return nread;\n}\n"
  },
  {
    "path": "src/t_hash.c",
    "content": "/*\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 \"redis.h\"\n#include <math.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. \n *\n * 对 argv 数组中的多个对象进行检查，\n * 看是否需要将对象的编码从 REDIS_ENCODING_ZIPLIST 转换成 REDIS_ENCODING_HT\n *\n * Note that we only check string encoded objects\n * as their string length can be queried in constant time. \n *\n * 注意程序只检查字符串值，因为它们的长度可以在常数时间内取得。\n */\nvoid hashTypeTryConversion(robj *o, robj **argv, int start, int end) {\n    int i;\n\n    // 如果对象不是 ziplist 编码，那么直接返回\n    if (o->encoding != REDIS_ENCODING_ZIPLIST) return;\n\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            // 将对象的编码转换成 REDIS_ENCODING_HT\n            hashTypeConvert(o, REDIS_ENCODING_HT);\n            break;\n        }\n    }\n}\n\n/* Encode given objects in-place when the hash uses a dict. \n *\n * 当 subject 的编码为 REDIS_ENCODING_HT 时，\n * 尝试对对象 o1 和 o2 进行编码，\n * 以节省更多内存。\n */\nvoid hashTypeTryObjectEncoding(robj *subject, robj **o1, robj **o2) {\n    if (subject->encoding == REDIS_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. \n *\n * 从 ziplist 编码的 hash 中取出和 field 相对应的值。\n *\n * 参数：\n *  field   域\n *  vstr    值是字符串时，将它保存到这个指针\n *  vlen    保存字符串的长度\n *  ll      值是整数时，将它保存到这个指针\n *\n * 查找失败时，函数返回 -1 。\n * 查找成功时，返回 0 。\n */\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\n    // 确保编码正确\n    redisAssert(o->encoding == REDIS_ENCODING_ZIPLIST);\n\n    // 取出未编码的域\n    field = getDecodedObject(field);\n\n    // 遍历 ziplist ，查找域的位置\n    zl = o->ptr;\n    fptr = ziplistIndex(zl, ZIPLIST_HEAD);\n    if (fptr != NULL) {\n        // 定位包含域的节点\n        fptr = ziplistFind(fptr, field->ptr, sdslen(field->ptr), 1);\n        if (fptr != NULL) {\n            /* Grab pointer to the value (fptr points to the field) */\n            // 域已经找到，取出和它相对应的值的位置\n            vptr = ziplistNext(zl, fptr);\n            redisAssert(vptr != NULL);\n        }\n    }\n\n    decrRefCount(field);\n\n    // 从 ziplist 节点中取出值\n    if (vptr != NULL) {\n        ret = ziplistGet(vptr, vstr, vlen, vll);\n        redisAssert(ret);\n        return 0;\n    }\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. \n *\n * 从 REDIS_ENCODING_HT 编码的 hash 中取出和 field 相对应的值。\n *\n * 成功找到值时返回 0 ，没找到返回 -1 。\n */\nint hashTypeGetFromHashTable(robj *o, robj *field, robj **value) {\n    dictEntry *de;\n\n    // 确保编码正确\n    redisAssert(o->encoding == REDIS_ENCODING_HT);\n\n    // 在字典中查找域（键）\n    de = dictFind(o->ptr, field);\n\n    // 键不存在\n    if (de == NULL) return -1;\n\n    // 取出域（键）的值\n    *value = dictGetVal(de);\n\n    // 成功找到\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. */\n/*\n * 多态 GET 函数，从 hash 中取出域 field 的值，并返回一个值对象。\n *\n * 找到返回值对象，没找到返回 NULL 。\n */\nrobj *hashTypeGetObject(robj *o, robj *field) {\n    robj *value = NULL;\n\n    // 从 ziplist 中取出值\n    if (o->encoding == REDIS_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            // 创建值对象\n            if (vstr) {\n                value = createStringObject((char*)vstr, vlen);\n            } else {\n                value = createStringObjectFromLongLong(vll);\n            }\n        }\n\n    // 从字典中取出值\n    } else if (o->encoding == REDIS_ENCODING_HT) {\n        robj *aux;\n\n        if (hashTypeGetFromHashTable(o, field, &aux) == 0) {\n            incrRefCount(aux);\n            value = aux;\n        }\n\n    } else {\n        redisPanic(\"Unknown hash encoding\");\n    }\n\n    // 返回值对象，或者 NULL\n    return value;\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. \n *\n * 检查给定域 feild 是否存在于 hash 对象 o 中。\n *\n * 存在返回 1 ，不存在返回 0 。\n */\nint hashTypeExists(robj *o, robj *field) {\n\n    // 检查 ziplist\n    if (o->encoding == REDIS_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\n    // 检查字典\n    } else if (o->encoding == REDIS_ENCODING_HT) {\n        robj *aux;\n\n        if (hashTypeGetFromHashTable(o, field, &aux) == 0) return 1;\n\n    // 未知编码\n    } else {\n        redisPanic(\"Unknown hash encoding\");\n    }\n\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 *\n * 将给定的 field-value 对添加到 hash 中，\n * 如果 field 已经存在，那么删除旧的值，并关联新值。\n *\n * This function will take care of incrementing the reference count of the\n * retained fields and value objects. \n *\n * 这个函数负责对 field 和 value 参数进行引用计数自增。\n *\n * 返回 0 表示元素已经存在，这次函数调用执行的是更新操作。\n *\n * 返回 1 则表示函数执行的是新添加操作。\n */\nint hashTypeSet(robj *o, robj *field, robj *value) {\n    int update = 0;\n\n    // 添加到 ziplist\n    if (o->encoding == REDIS_ENCODING_ZIPLIST) {\n        unsigned char *zl, *fptr, *vptr;\n\n        // 解码成字符串或者数字\n        field = getDecodedObject(field);\n        value = getDecodedObject(value);\n\n        // 遍历整个 ziplist ，尝试查找并更新 field （如果它已经存在的话）\n        zl = o->ptr;\n        fptr = ziplistIndex(zl, ZIPLIST_HEAD);\n        if (fptr != NULL) {\n            // 定位到域 field\n            fptr = ziplistFind(fptr, field->ptr, sdslen(field->ptr), 1);\n            if (fptr != NULL) {\n                /* Grab pointer to the value (fptr points to the field) */\n                // 定位到域的值\n                vptr = ziplistNext(zl, fptr);\n                redisAssert(vptr != NULL);\n\n                // 标识这次操作为更新操作\n                update = 1;\n\n                /* Delete value */\n                // 删除旧的键值对\n                zl = ziplistDelete(zl, &vptr);\n\n                /* Insert new value */\n                // 添加新的键值对\n                zl = ziplistInsert(zl, vptr, value->ptr, sdslen(value->ptr));\n            }\n        }\n\n        // 如果这不是更新操作，那么这就是一个添加操作\n        if (!update) {\n            /* Push new field/value pair onto the tail of the ziplist */\n            // 将新的 field-value 对推入到 ziplist 的末尾\n            zl = ziplistPush(zl, field->ptr, sdslen(field->ptr), ZIPLIST_TAIL);\n            zl = ziplistPush(zl, value->ptr, sdslen(value->ptr), ZIPLIST_TAIL);\n        }\n        \n        // 更新对象指针\n        o->ptr = zl;\n\n        // 释放临时对象\n        decrRefCount(field);\n        decrRefCount(value);\n\n        /* Check if the ziplist needs to be converted to a hash table */\n        // 检查在添加操作完成之后，是否需要将 ZIPLIST 编码转换成 HT 编码\n        if (hashTypeLength(o) > server.hash_max_ziplist_entries)\n            hashTypeConvert(o, REDIS_ENCODING_HT);\n\n    // 添加到字典\n    } else if (o->encoding == REDIS_ENCODING_HT) {\n\n        // 添加或替换键值对到字典\n        // 添加返回 1 ，替换返回 0\n        if (dictReplace(o->ptr, field, value)) { /* Insert */\n            incrRefCount(field);\n        } else { /* Update */\n            update = 1;\n        }\n\n        incrRefCount(value);\n    } else {\n        redisPanic(\"Unknown hash encoding\");\n    }\n\n    // 更新/添加指示变量\n    return update;\n}\n\n/* Delete an element from a hash.\n *\n * 将给定 field 及其 value 从哈希表中删除\n *\n * Return 1 on deleted and 0 on not found. \n *\n * 删除成功返回 1 ，因为域不存在而造成的删除失败返回 0 。\n */\nint hashTypeDelete(robj *o, robj *field) {\n    int deleted = 0;\n\n    // 从 ziplist 中删除\n    if (o->encoding == REDIS_ENCODING_ZIPLIST) {\n        unsigned char *zl, *fptr;\n\n        field = getDecodedObject(field);\n\n        zl = o->ptr;\n        fptr = ziplistIndex(zl, ZIPLIST_HEAD);\n        if (fptr != NULL) {\n            // 定位到域\n            fptr = ziplistFind(fptr, field->ptr, sdslen(field->ptr), 1);\n            if (fptr != NULL) {\n                // 删除域和值\n                zl = ziplistDelete(zl,&fptr);\n                zl = ziplistDelete(zl,&fptr);\n                o->ptr = zl;\n                deleted = 1;\n            }\n        }\n\n        decrRefCount(field);\n\n    // 从字典中删除\n    } else if (o->encoding == REDIS_ENCODING_HT) {\n        if (dictDelete((dict*)o->ptr, field) == REDIS_OK) {\n            deleted = 1;\n\n            /* Always check if the dictionary needs a resize after a delete. */\n            // 删除成功时，看字典是否需要收缩\n            if (htNeedsResize(o->ptr)) dictResize(o->ptr);\n        }\n\n    } else {\n        redisPanic(\"Unknown hash encoding\");\n    }\n\n    return deleted;\n}\n\n/* Return the number of elements in a hash. \n *\n * 返回哈希表的 field-value 对数量\n */\nunsigned long hashTypeLength(robj *o) {\n    unsigned long length = ULONG_MAX;\n\n    if (o->encoding == REDIS_ENCODING_ZIPLIST) {\n        // ziplist 中，每个 field-value 对都需要使用两个节点来保存\n        length = ziplistLen(o->ptr) / 2;\n    } else if (o->encoding == REDIS_ENCODING_HT) {\n        length = dictSize((dict*)o->ptr);\n    } else {\n        redisPanic(\"Unknown hash encoding\");\n    }\n\n    return length;\n}\n\n/*\n * 创建一个哈希类型的迭代器\n * hashTypeIterator 类型定义在 redis.h\n *\n * 复杂度：O(1)\n *\n * 返回值：\n *  hashTypeIterator\n */\nhashTypeIterator *hashTypeInitIterator(robj *subject) {\n\n    hashTypeIterator *hi = zmalloc(sizeof(hashTypeIterator));\n\n    // 指向对象\n    hi->subject = subject;\n\n    // 记录编码\n    hi->encoding = subject->encoding;\n\n    // 以 ziplist 的方式初始化迭代器\n    if (hi->encoding == REDIS_ENCODING_ZIPLIST) {\n        hi->fptr = NULL;\n        hi->vptr = NULL;\n\n    // 以字典的方式初始化迭代器\n    } else if (hi->encoding == REDIS_ENCODING_HT) {\n        hi->di = dictGetIterator(subject->ptr);\n\n    } else {\n        redisPanic(\"Unknown hash encoding\");\n    }\n\n    // 返回迭代器\n    return hi;\n}\n\n/*\n * 释放迭代器\n */\nvoid hashTypeReleaseIterator(hashTypeIterator *hi) {\n\n    // 释放字典迭代器\n    if (hi->encoding == REDIS_ENCODING_HT) {\n        dictReleaseIterator(hi->di);\n    }\n\n    // 释放 ziplist 迭代器\n    zfree(hi);\n}\n\n/* Move to the next entry in the hash. \n *\n * 获取哈希中的下一个节点，并将它保存到迭代器。\n *\n * could be found and REDIS_ERR when the iterator reaches the end. \n *\n * 如果获取成功，返回 REDIS_OK ，\n *\n * 如果已经没有元素可获取（为空，或者迭代完毕），那么返回 REDIS_ERR 。\n */\nint hashTypeNext(hashTypeIterator *hi) {\n\n    // 迭代 ziplist\n    if (hi->encoding == REDIS_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        // 第一次执行时，初始化指针\n        if (fptr == NULL) {\n            /* Initialize cursor */\n            redisAssert(vptr == NULL);\n            fptr = ziplistIndex(zl, 0);\n\n        // 获取下一个迭代节点\n        } else {\n            /* Advance cursor */\n            redisAssert(vptr != NULL);\n            fptr = ziplistNext(zl, vptr);\n        }\n\n        // 迭代完毕，或者 ziplist 为空\n        if (fptr == NULL) return REDIS_ERR;\n\n        /* Grab pointer to the value (fptr points to the field) */\n        // 记录值的指针\n        vptr = ziplistNext(zl, fptr);\n        redisAssert(vptr != NULL);\n\n        /* fptr, vptr now point to the first or next pair */\n        // 更新迭代器指针\n        hi->fptr = fptr;\n        hi->vptr = vptr;\n\n    // 迭代字典\n    } else if (hi->encoding == REDIS_ENCODING_HT) {\n        if ((hi->de = dictNext(hi->di)) == NULL) return REDIS_ERR;\n\n    // 未知编码\n    } else {\n        redisPanic(\"Unknown hash encoding\");\n    }\n\n    // 迭代成功\n    return REDIS_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`. \n *\n * 从 ziplist 编码的哈希中，取出迭代器指针当前指向节点的域或值。\n */\nvoid hashTypeCurrentFromZiplist(hashTypeIterator *hi, int what,\n                                unsigned char **vstr,\n                                unsigned int *vlen,\n                                long long *vll)\n{\n    int ret;\n\n    // 确保编码正确\n    redisAssert(hi->encoding == REDIS_ENCODING_ZIPLIST);\n\n    // 取出键\n    if (what & REDIS_HASH_KEY) {\n        ret = ziplistGet(hi->fptr, vstr, vlen, vll);\n        redisAssert(ret);\n\n    // 取出值\n    } else {\n        ret = ziplistGet(hi->vptr, vstr, vlen, vll);\n        redisAssert(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`. \n *\n * 根据迭代器的指针，从字典编码的哈希中取出所指向节点的 field 或者 value 。\n */\nvoid hashTypeCurrentFromHashTable(hashTypeIterator *hi, int what, robj **dst) {\n    redisAssert(hi->encoding == REDIS_ENCODING_HT);\n\n    // 取出键\n    if (what & REDIS_HASH_KEY) {\n        *dst = dictGetKey(hi->de);\n\n    // 取出值\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). \n *\n * 一个非 copy-on-write 友好，但是层次更高的 hashTypeCurrent() 函数，\n * 这个函数返回一个增加了引用计数的对象，或者一个新对象。\n *\n * It is up to the caller to decrRefCount() the object if no reference is\n * retained. \n *\n * 当使用完返回对象之后，调用者需要对对象执行 decrRefCount() 。\n */\nrobj *hashTypeCurrentObject(hashTypeIterator *hi, int what) {\n    robj *dst;\n\n    // ziplist\n    if (hi->encoding == REDIS_ENCODING_ZIPLIST) {\n        unsigned char *vstr = NULL;\n        unsigned int vlen = UINT_MAX;\n        long long vll = LLONG_MAX;\n\n        // 取出键或值\n        hashTypeCurrentFromZiplist(hi, what, &vstr, &vlen, &vll);\n\n        // 创建键或值的对象\n        if (vstr) {\n            dst = createStringObject((char*)vstr, vlen);\n        } else {\n            dst = createStringObjectFromLongLong(vll);\n        }\n\n    // 字典\n    } else if (hi->encoding == REDIS_ENCODING_HT) {\n        // 取出键或者值\n        hashTypeCurrentFromHashTable(hi, what, &dst);\n        // 对对象的引用计数进行自增\n        incrRefCount(dst);\n\n    // 未知编码\n    } else {\n        redisPanic(\"Unknown hash encoding\");\n    }\n\n    // 返回对象\n    return dst;\n}\n\n/*\n * 按 key 在数据库中查找并返回相应的哈希对象，\n * 如果对象不存在，那么创建一个新哈希对象并返回。\n */\nrobj *hashTypeLookupWriteOrCreate(redisClient *c, robj *key) {\n\n    robj *o = lookupKeyWrite(c->db,key);\n\n    // 对象不存在，创建新的\n    if (o == NULL) {\n        o = createHashObject();\n        dbAdd(c->db,key,o);\n\n    // 对象存在，检查类型\n    } else {\n        if (o->type != REDIS_HASH) {\n            addReply(c,shared.wrongtypeerr);\n            return NULL;\n        }\n    }\n\n    // 返回对象\n    return o;\n}\n\n/*\n * 将一个 ziplist 编码的哈希对象 o 转换成其他编码\n */\nvoid hashTypeConvertZiplist(robj *o, int enc) {\n    redisAssert(o->encoding == REDIS_ENCODING_ZIPLIST);\n\n    // 如果输入是 ZIPLIST ，那么不做动作\n    if (enc == REDIS_ENCODING_ZIPLIST) {\n        /* Nothing to do... */\n\n    // 转换成 HT 编码\n    } else if (enc == REDIS_ENCODING_HT) {\n\n        hashTypeIterator *hi;\n        dict *dict;\n        int ret;\n\n        // 创建哈希迭代器\n        hi = hashTypeInitIterator(o);\n\n        // 创建空白的新字典\n        dict = dictCreate(&hashDictType, NULL);\n\n        // 遍历整个 ziplist\n        while (hashTypeNext(hi) != REDIS_ERR) {\n            robj *field, *value;\n\n            // 取出 ziplist 里的键\n            field = hashTypeCurrentObject(hi, REDIS_HASH_KEY);\n            field = tryObjectEncoding(field);\n\n            // 取出 ziplist 里的值\n            value = hashTypeCurrentObject(hi, REDIS_HASH_VALUE);\n            value = tryObjectEncoding(value);\n\n            // 将键值对添加到字典\n            ret = dictAdd(dict, field, value);\n            if (ret != DICT_OK) {\n                redisLogHexDump(REDIS_WARNING,\"ziplist with dup elements dump\",\n                    o->ptr,ziplistBlobLen(o->ptr));\n                redisAssert(ret == DICT_OK);\n            }\n        }\n\n        // 释放 ziplist 的迭代器\n        hashTypeReleaseIterator(hi);\n\n        // 释放对象原来的 ziplist\n        zfree(o->ptr);\n\n        // 更新哈希的编码和值对象\n        o->encoding = REDIS_ENCODING_HT;\n        o->ptr = dict;\n\n    } else {\n        redisPanic(\"Unknown hash encoding\");\n    }\n}\n\n/*\n * 对哈希对象 o 的编码方式进行转换\n *\n * 目前只支持将 ZIPLIST 编码转换成 HT 编码\n */\nvoid hashTypeConvert(robj *o, int enc) {\n\n    if (o->encoding == REDIS_ENCODING_ZIPLIST) {\n        hashTypeConvertZiplist(o, enc);\n\n    } else if (o->encoding == REDIS_ENCODING_HT) {\n        redisPanic(\"Not implemented\");\n\n    } else {\n        redisPanic(\"Unknown hash encoding\");\n    }\n}\n\n/*-----------------------------------------------------------------------------\n * Hash type commands\n *----------------------------------------------------------------------------*/\n\nvoid hsetCommand(redisClient *c) {\n    int update;\n    robj *o;\n\n    // 取出或新创建哈希对象\n    if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;\n\n    // 如果需要的话，转换哈希对象的编码\n    hashTypeTryConversion(o,c->argv,2,3);\n\n    // 编码 field 和 value 对象以节约空间\n    hashTypeTryObjectEncoding(o,&c->argv[2], &c->argv[3]);\n\n    // 设置 field 和 value 到 hash\n    update = hashTypeSet(o,c->argv[2],c->argv[3]);\n\n    // 返回状态：显示 field-value 对是新添加还是更新\n    addReply(c, update ? shared.czero : shared.cone);\n\n    // 发送键修改信号\n    signalModifiedKey(c->db,c->argv[1]);\n\n    // 发送事件通知\n    notifyKeyspaceEvent(REDIS_NOTIFY_HASH,\"hset\",c->argv[1],c->db->id);\n\n    // 将服务器设为脏\n    server.dirty++;\n}\n\nvoid hsetnxCommand(redisClient *c) {\n    robj *o;\n\n    // 取出或新创建哈希对象\n    if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;\n\n    // 如果需要的话，转换哈希对象的编码\n    hashTypeTryConversion(o,c->argv,2,3);\n\n    // 如果 field-value 对已经存在\n    // 那么回复 0 \n    if (hashTypeExists(o, c->argv[2])) {\n        addReply(c, shared.czero);\n\n    // 否则，设置 field-value 对\n    } else {\n        // 对 field 和 value 对象编码，以节省空间\n        hashTypeTryObjectEncoding(o,&c->argv[2], &c->argv[3]);\n        // 设置\n        hashTypeSet(o,c->argv[2],c->argv[3]);\n\n        // 回复 1 ，表示设置成功\n        addReply(c, shared.cone);\n\n        // 发送键修改信号\n        signalModifiedKey(c->db,c->argv[1]);\n\n        // 发送事件通知\n        notifyKeyspaceEvent(REDIS_NOTIFY_HASH,\"hset\",c->argv[1],c->db->id);\n\n        // 将数据库设为脏\n        server.dirty++;\n    }\n}\n\nvoid hmsetCommand(redisClient *c) {\n    int i;\n    robj *o;\n\n    // field-value 参数必须成对出现\n    if ((c->argc % 2) == 1) {\n        addReplyError(c,\"wrong number of arguments for HMSET\");\n        return;\n    }\n\n    // 取出或新创建哈希对象\n    if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;\n\n    // 如果需要的话，转换哈希对象的编码\n    hashTypeTryConversion(o,c->argv,2,c->argc-1);\n\n    // 遍历并设置所有 field-value 对\n    for (i = 2; i < c->argc; i += 2) {\n        // 编码 field-value 对，以节约空间\n        hashTypeTryObjectEncoding(o,&c->argv[i], &c->argv[i+1]);\n        // 设置\n        hashTypeSet(o,c->argv[i],c->argv[i+1]);\n    }\n\n    // 向客户端发送回复\n    addReply(c, shared.ok);\n\n    // 发送键修改信号\n    signalModifiedKey(c->db,c->argv[1]);\n\n    // 发送事件通知\n    notifyKeyspaceEvent(REDIS_NOTIFY_HASH,\"hset\",c->argv[1],c->db->id);\n\n    // 将数据库设为脏\n    server.dirty++;\n}\n\nvoid hincrbyCommand(redisClient *c) {\n    long long value, incr, oldvalue;\n    robj *o, *current, *new;\n\n    // 取出 incr 参数的值，并创建对象\n    if (getLongLongFromObjectOrReply(c,c->argv[3],&incr,NULL) != REDIS_OK) return;\n\n    // 取出或新创建哈希对象\n    if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;\n\n    // 取出 field 的当前值\n    if ((current = hashTypeGetObject(o,c->argv[2])) != NULL) {\n        // 取出值的整数表示\n        if (getLongLongFromObjectOrReply(c,current,&value,\n            \"hash value is not an integer\") != REDIS_OK) {\n            decrRefCount(current);\n            return;\n        }\n        decrRefCount(current);\n    } else {\n        // 如果值当前不存在，那么默认为 0\n        value = 0;\n    }\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        return;\n    }\n\n    // 计算结果\n    value += incr;\n    // 为结果创建新的值对象\n    new = createStringObjectFromLongLong(value);\n    // 编码值对象\n    hashTypeTryObjectEncoding(o,&c->argv[2],NULL);\n    // 关联键和新的值对象，如果已经有对象存在，那么用新对象替换它\n    hashTypeSet(o,c->argv[2],new);\n    decrRefCount(new);\n\n    // 将计算结果用作回复\n    addReplyLongLong(c,value);\n\n    // 发送键修改信号\n    signalModifiedKey(c->db,c->argv[1]);\n\n    // 发送事件通知\n    notifyKeyspaceEvent(REDIS_NOTIFY_HASH,\"hincrby\",c->argv[1],c->db->id);\n\n    // 将数据库设为脏\n    server.dirty++;\n}\n\nvoid hincrbyfloatCommand(redisClient *c) {\n    double long value, incr;\n    robj *o, *current, *new, *aux;\n\n    // 取出 incr 参数\n    if (getLongDoubleFromObjectOrReply(c,c->argv[3],&incr,NULL) != REDIS_OK) return;\n\n    // 取出或新创建哈希对象\n    if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;\n\n    // 取出值对象\n    if ((current = hashTypeGetObject(o,c->argv[2])) != NULL) {\n        // 从值对象中取出浮点值\n        if (getLongDoubleFromObjectOrReply(c,current,&value,\n            \"hash value is not a valid float\") != REDIS_OK) {\n            decrRefCount(current);\n            return;\n        }\n        decrRefCount(current);\n    } else {\n        // 值对象不存在，默认值为 0\n        value = 0;\n    }\n\n    // 计算结果\n    value += incr;\n    // 为计算结果创建值对象\n    new = createStringObjectFromLongDouble(value);\n    // 编码值对象\n    hashTypeTryObjectEncoding(o,&c->argv[2],NULL);\n    // 关联键和新的值对象，如果已经有对象存在，那么用新对象替换它\n    hashTypeSet(o,c->argv[2],new);\n\n    // 返回新的值对象作为回复\n    addReplyBulk(c,new);\n\n    // 发送键修改信号\n    signalModifiedKey(c->db,c->argv[1]);\n\n    // 发送事件通知\n    notifyKeyspaceEvent(REDIS_NOTIFY_HASH,\"hincrbyfloat\",c->argv[1],c->db->id);\n\n    // 将数据库设置脏\n    server.dirty++;\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    // 在传播 INCRBYFLOAT 命令时，总是用 SET 命令来替换 INCRBYFLOAT 命令\n    // 从而防止因为不同的浮点精度和格式化造成 AOF 重启时的数据不一致\n    aux = createStringObject(\"HSET\",4);\n    rewriteClientCommandArgument(c,0,aux);\n    decrRefCount(aux);\n    rewriteClientCommandArgument(c,3,new);\n    decrRefCount(new);\n}\n\n/*\n * 辅助函数：将哈希中域 field 的值添加到回复中\n */\nstatic void addHashFieldToReply(redisClient *c, robj *o, robj *field) {\n    int ret;\n\n    // 对象不存在\n    if (o == NULL) {\n        addReply(c, shared.nullbulk);\n        return;\n    }\n\n    // ziplist 编码\n    if (o->encoding == REDIS_ENCODING_ZIPLIST) {\n        unsigned char *vstr = NULL;\n        unsigned int vlen = UINT_MAX;\n        long long vll = LLONG_MAX;\n\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    // 字典\n    } else if (o->encoding == REDIS_ENCODING_HT) {\n        robj *value;\n\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        redisPanic(\"Unknown hash encoding\");\n    }\n}\n\nvoid hgetCommand(redisClient *c) {\n    robj *o;\n\n    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL ||\n        checkType(c,o,REDIS_HASH)) return;\n\n    // 取出并返回域的值\n    addHashFieldToReply(c, o, c->argv[2]);\n}\n\nvoid hmgetCommand(redisClient *c) {\n    robj *o;\n    int i;\n\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    // 取出哈希对象\n    o = lookupKeyRead(c->db, c->argv[1]);\n\n    // 对象存在，检查类型\n    if (o != NULL && o->type != REDIS_HASH) {\n        addReply(c, shared.wrongtypeerr);\n        return;\n    }\n\n    // 获取多个 field 的值\n    addReplyMultiBulkLen(c, c->argc-2);\n    for (i = 2; i < c->argc; i++) {\n        addHashFieldToReply(c, o, c->argv[i]);\n    }\n}\n\nvoid hdelCommand(redisClient *c) {\n    robj *o;\n    int j, deleted = 0, keyremoved = 0;\n\n    // 取出对象\n    if ((o = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL ||\n        checkType(c,o,REDIS_HASH)) return;\n\n    // 删除指定域值对\n    for (j = 2; j < c->argc; j++) {\n        if (hashTypeDelete(o,c->argv[j])) {\n\n            // 成功删除一个域值对时进行计数\n            deleted++;\n\n            // 如果哈希已经为空，那么删除这个对象\n            if (hashTypeLength(o) == 0) {\n                dbDelete(c->db,c->argv[1]);\n                keyremoved = 1;\n                break;\n            }\n        }\n    }\n\n    // 只要有至少一个域值对被修改了，那么执行以下代码\n    if (deleted) {\n        // 发送键修改信号\n        signalModifiedKey(c->db,c->argv[1]);\n\n        // 发送事件通知\n        notifyKeyspaceEvent(REDIS_NOTIFY_HASH,\"hdel\",c->argv[1],c->db->id);\n\n        // 发送事件通知\n        if (keyremoved)\n            notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,\"del\",c->argv[1],\n                                c->db->id);\n\n        // 将数据库设为脏\n        server.dirty += deleted;\n    }\n\n    // 将成功删除的域值对数量作为结果返回给客户端\n    addReplyLongLong(c,deleted);\n}\n\nvoid hlenCommand(redisClient *c) {\n    robj *o;\n\n    // 取出哈希对象\n    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||\n        checkType(c,o,REDIS_HASH)) return;\n\n    // 回复\n    addReplyLongLong(c,hashTypeLength(o));\n}\n\n/*\n * 从迭代器当前指向的节点中取出哈希的 field 或 value\n */\nstatic void addHashIteratorCursorToReply(redisClient *c, hashTypeIterator *hi, int what) {\n\n    // 处理 ZIPLIST\n    if (hi->encoding == REDIS_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    // 处理 HT\n    } else if (hi->encoding == REDIS_ENCODING_HT) {\n        robj *value;\n\n        hashTypeCurrentFromHashTable(hi, what, &value);\n        addReplyBulk(c, value);\n\n    } else {\n        redisPanic(\"Unknown hash encoding\");\n    }\n}\n\nvoid genericHgetallCommand(redisClient *c, int flags) {\n    robj *o;\n    hashTypeIterator *hi;\n    int multiplier = 0;\n    int length, count = 0;\n\n    // 取出哈希对象\n    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptymultibulk)) == NULL\n        || checkType(c,o,REDIS_HASH)) return;\n\n    // 计算要取出的元素数量\n    if (flags & REDIS_HASH_KEY) multiplier++;\n    if (flags & REDIS_HASH_VALUE) multiplier++;\n\n    length = hashTypeLength(o) * multiplier;\n\n    addReplyMultiBulkLen(c, length);\n\n    // 迭代节点，并取出元素\n    hi = hashTypeInitIterator(o);\n    while (hashTypeNext(hi) != REDIS_ERR) {\n        // 取出键\n        if (flags & REDIS_HASH_KEY) {\n            addHashIteratorCursorToReply(c, hi, REDIS_HASH_KEY);\n            count++;\n        }\n        // 取出值\n        if (flags & REDIS_HASH_VALUE) {\n            addHashIteratorCursorToReply(c, hi, REDIS_HASH_VALUE);\n            count++;\n        }\n    }\n\n    // 释放迭代器\n    hashTypeReleaseIterator(hi);\n    redisAssert(count == length);\n}\n\nvoid hkeysCommand(redisClient *c) {\n    genericHgetallCommand(c,REDIS_HASH_KEY);\n}\n\nvoid hvalsCommand(redisClient *c) {\n    genericHgetallCommand(c,REDIS_HASH_VALUE);\n}\n\nvoid hgetallCommand(redisClient *c) {\n    genericHgetallCommand(c,REDIS_HASH_KEY|REDIS_HASH_VALUE);\n}\n\nvoid hexistsCommand(redisClient *c) {\n    robj *o;\n\n    // 取出哈希对象\n    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||\n        checkType(c,o,REDIS_HASH)) return;\n\n    // 检查给定域是否存在\n    addReply(c, hashTypeExists(o,c->argv[2]) ? shared.cone : shared.czero);\n}\n\nvoid hscanCommand(redisClient *c) {\n    robj *o;\n    unsigned long cursor;\n\n    if (parseScanCursorOrReply(c,c->argv[2],&cursor) == REDIS_ERR) return;\n    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptyscan)) == NULL ||\n        checkType(c,o,REDIS_HASH)) return;\n    scanGenericCommand(c,o,cursor);\n}\n"
  },
  {
    "path": "src/t_list.c",
    "content": "/*\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 \"redis.h\"\n\nvoid signalListAsReady(redisClient *c, robj *key);\n\n/*-----------------------------------------------------------------------------\n * List API\n *----------------------------------------------------------------------------*/\n\n/* Check the argument length to see if it requires us to convert the ziplist\n * to a real list. Only check raw-encoded objects because integer encoded\n * objects are never too long. \n *\n * 对输入值 value 进行检查，看是否需要将 subject 从 ziplist 转换为双端链表，\n * 以便保存值 value 。\n *\n * 函数只对 REDIS_ENCODING_RAW 编码的 value 进行检查，\n * 因为整数编码的值不可能超长。\n */\nvoid listTypeTryConversion(robj *subject, robj *value) {\n\n    // 确保 subject 为 ZIPLIST 编码\n    if (subject->encoding != REDIS_ENCODING_ZIPLIST) return;\n\n    if (sdsEncodedObject(value) &&\n        // 看字符串是否过长\n        sdslen(value->ptr) > server.list_max_ziplist_value)\n            // 将编码转换为双端链表\n            listTypeConvert(subject,REDIS_ENCODING_LINKEDLIST);\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 * 将给定元素添加到列表的表头或表尾。\n *\n * 参数 where 决定了新元素添加的位置：\n *\n *  - REDIS_HEAD 将新元素添加到表头\n *\n *  - REDIS_TAIL 将新元素添加到表尾\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. \n *\n * 调用者无须担心 value 的引用计数，因为这个函数会负责这方面的工作。\n */\nvoid listTypePush(robj *subject, robj *value, int where) {\n\n    /* Check if we need to convert the ziplist */\n    // 是否需要转换编码？\n    listTypeTryConversion(subject,value);\n\n    if (subject->encoding == REDIS_ENCODING_ZIPLIST &&\n        ziplistLen(subject->ptr) >= server.list_max_ziplist_entries)\n            listTypeConvert(subject,REDIS_ENCODING_LINKEDLIST);\n\n    // ZIPLIST\n    if (subject->encoding == REDIS_ENCODING_ZIPLIST) {\n        int pos = (where == REDIS_HEAD) ? ZIPLIST_HEAD : ZIPLIST_TAIL;\n        // 取出对象的值，因为 ZIPLIST 只能保存字符串或整数\n        value = getDecodedObject(value);\n        subject->ptr = ziplistPush(subject->ptr,value->ptr,sdslen(value->ptr),pos);\n        decrRefCount(value);\n\n    // 双端链表\n    } else if (subject->encoding == REDIS_ENCODING_LINKEDLIST) {\n        if (where == REDIS_HEAD) {\n            listAddNodeHead(subject->ptr,value);\n        } else {\n            listAddNodeTail(subject->ptr,value);\n        }\n        incrRefCount(value);\n\n    // 未知编码\n    } else {\n        redisPanic(\"Unknown list encoding\");\n    }\n}\n\n/*\n * 从列表的表头或表尾中弹出一个元素。\n *\n * 参数 where 决定了弹出元素的位置： \n *\n *  - REDIS_HEAD 从表头弹出\n *\n *  - REDIS_TAIL 从表尾弹出\n */\nrobj *listTypePop(robj *subject, int where) {\n\n    robj *value = NULL;\n\n    // ZIPLIST\n    if (subject->encoding == REDIS_ENCODING_ZIPLIST) {\n        unsigned char *p;\n        unsigned char *vstr;\n        unsigned int vlen;\n        long long vlong;\n\n        // 决定弹出元素的位置\n        int pos = (where == REDIS_HEAD) ? 0 : -1;\n\n        p = ziplistIndex(subject->ptr,pos);\n        if (ziplistGet(p,&vstr,&vlen,&vlong)) {\n            // 为被弹出元素创建对象\n            if (vstr) {\n                value = createStringObject((char*)vstr,vlen);\n            } else {\n                value = createStringObjectFromLongLong(vlong);\n            }\n            /* We only need to delete an element when it exists */\n            // 从 ziplist 中删除被弹出元素\n            subject->ptr = ziplistDelete(subject->ptr,&p);\n        }\n\n    // 双端链表\n    } else if (subject->encoding == REDIS_ENCODING_LINKEDLIST) {\n\n        list *list = subject->ptr;\n\n        listNode *ln;\n\n        if (where == REDIS_HEAD) {\n            ln = listFirst(list);\n        } else {\n            ln = listLast(list);\n        }\n\n        // 删除被弹出节点\n        if (ln != NULL) {\n            value = listNodeValue(ln);\n            incrRefCount(value);\n            listDelNode(list,ln);\n        }\n\n    // 未知编码\n    } else {\n        redisPanic(\"Unknown list encoding\");\n    }\n\n    // 返回节点对象\n    return value;\n}\n\n/*\n * 返回列表的节点数量\n */\nunsigned long listTypeLength(robj *subject) {\n\n    // ZIPLIST\n    if (subject->encoding == REDIS_ENCODING_ZIPLIST) {\n        return ziplistLen(subject->ptr);\n\n    // 双端链表\n    } else if (subject->encoding == REDIS_ENCODING_LINKEDLIST) {\n        return listLength((list*)subject->ptr);\n\n    // 未知编码\n    } else {\n        redisPanic(\"Unknown list encoding\");\n    }\n}\n\n/* Initialize an iterator at the specified index.\n *\n * 创建并返回一个列表迭代器。\n *\n * 参数 index 决定开始迭代的列表索引。\n *\n * 参数 direction 则决定了迭代的方向。\n *\n * listTypeIterator 于 redis.h 文件中定义。\n */\nlistTypeIterator *listTypeInitIterator(robj *subject, long index, unsigned char direction) {\n\n    listTypeIterator *li = zmalloc(sizeof(listTypeIterator));\n\n    li->subject = subject;\n\n    li->encoding = subject->encoding;\n\n    li->direction = direction;\n\n    // ZIPLIST\n    if (li->encoding == REDIS_ENCODING_ZIPLIST) {\n        li->zi = ziplistIndex(subject->ptr,index);\n\n    // 双端链表\n    } else if (li->encoding == REDIS_ENCODING_LINKEDLIST) {\n        li->ln = listIndex(subject->ptr,index);\n\n    // 未知编码\n    } else {\n        redisPanic(\"Unknown list encoding\");\n    }\n\n    return li;\n}\n\n/* Clean up the iterator. \n *\n * 释放迭代器\n */\nvoid listTypeReleaseIterator(listTypeIterator *li) {\n    zfree(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. \n *\n * 使用 entry 结构记录迭代器当前指向的节点，并将迭代器的指针移动到下一个元素。\n *\n * 如果列表中还有元素可迭代，那么返回 1 ，否则，返回 0 。\n */\nint listTypeNext(listTypeIterator *li, listTypeEntry *entry) {\n    /* Protect from converting when iterating */\n    redisAssert(li->subject->encoding == li->encoding);\n\n    entry->li = li;\n\n    // 迭代 ZIPLIST\n    if (li->encoding == REDIS_ENCODING_ZIPLIST) {\n\n        // 记录当前节点到 entry\n        entry->zi = li->zi;\n\n        // 移动迭代器的指针\n        if (entry->zi != NULL) {\n            if (li->direction == REDIS_TAIL)\n                li->zi = ziplistNext(li->subject->ptr,li->zi);\n            else\n                li->zi = ziplistPrev(li->subject->ptr,li->zi);\n            return 1;\n        }\n\n    // 迭代双端链表\n    } else if (li->encoding == REDIS_ENCODING_LINKEDLIST) {\n\n        // 记录当前节点到 entry\n        entry->ln = li->ln;\n\n        // 移动迭代器的指针\n        if (entry->ln != NULL) {\n            if (li->direction == REDIS_TAIL)\n                li->ln = li->ln->next;\n            else\n                li->ln = li->ln->prev;\n            return 1;\n        }\n\n    // 未知编码\n    } else {\n        redisPanic(\"Unknown list encoding\");\n    }\n\n    // 列表元素已经全部迭代完\n    return 0;\n}\n\n/* Return entry or NULL at the current position of the iterator. \n *\n * 返回 entry 结构当前所保存的列表节点。\n *\n * 如果 entry 没有记录任何节点，那么返回 NULL 。\n */\nrobj *listTypeGet(listTypeEntry *entry) {\n\n    listTypeIterator *li = entry->li;\n\n    robj *value = NULL;\n\n    // 根据索引，从 ZIPLIST 中取出节点的值\n    if (li->encoding == REDIS_ENCODING_ZIPLIST) {\n        unsigned char *vstr;\n        unsigned int vlen;\n        long long vlong;\n        redisAssert(entry->zi != NULL);\n        if (ziplistGet(entry->zi,&vstr,&vlen,&vlong)) {\n            if (vstr) {\n                value = createStringObject((char*)vstr,vlen);\n            } else {\n                value = createStringObjectFromLongLong(vlong);\n            }\n        }\n\n    // 从双端链表中取出节点的值\n    } else if (li->encoding == REDIS_ENCODING_LINKEDLIST) {\n        redisAssert(entry->ln != NULL);\n        value = listNodeValue(entry->ln);\n        incrRefCount(value);\n\n    } else {\n        redisPanic(\"Unknown list encoding\");\n    }\n\n    return value;\n}\n\n/*\n * 将对象 value 插入到列表节点的之前或之后。\n *\n * where 参数决定了插入的位置：\n *\n *  - REDIS_HEAD 插入到节点之前\n *\n *  - REDIS_TAIL 插入到节点之后\n */\nvoid listTypeInsert(listTypeEntry *entry, robj *value, int where) {\n\n    robj *subject = entry->li->subject;\n\n    // 插入到 ZIPLIST\n    if (entry->li->encoding == REDIS_ENCODING_ZIPLIST) {\n\n        // 返回对象未编码的值\n        value = getDecodedObject(value);\n\n        if (where == REDIS_TAIL) {\n            unsigned char *next = ziplistNext(subject->ptr,entry->zi);\n\n            /* When we insert after the current element, but the current element\n             * is the tail of the list, we need to do a push. */\n            if (next == NULL) {\n                // next 是表尾节点，push 新节点到表尾\n                subject->ptr = ziplistPush(subject->ptr,value->ptr,sdslen(value->ptr),REDIS_TAIL);\n            } else {\n                // 插入到到节点之后\n                subject->ptr = ziplistInsert(subject->ptr,next,value->ptr,sdslen(value->ptr));\n            }\n        } else {\n            subject->ptr = ziplistInsert(subject->ptr,entry->zi,value->ptr,sdslen(value->ptr));\n        }\n        decrRefCount(value);\n\n    // 插入到双端链表\n    } else if (entry->li->encoding == REDIS_ENCODING_LINKEDLIST) {\n\n        if (where == REDIS_TAIL) {\n            listInsertNode(subject->ptr,entry->ln,value,AL_START_TAIL);\n        } else {\n            listInsertNode(subject->ptr,entry->ln,value,AL_START_HEAD);\n        }\n\n        incrRefCount(value);\n\n    } else {\n        redisPanic(\"Unknown list encoding\");\n    }\n}\n\n/* Compare the given object with the entry at the current position. \n *\n * 将当前节点的值和对象 o 进行对比\n *\n * 函数在两值相等时返回 1 ，不相等时返回 0 。\n */\nint listTypeEqual(listTypeEntry *entry, robj *o) {\n\n    listTypeIterator *li = entry->li;\n\n    if (li->encoding == REDIS_ENCODING_ZIPLIST) {\n        redisAssertWithInfo(NULL,o,sdsEncodedObject(o));\n        return ziplistCompare(entry->zi,o->ptr,sdslen(o->ptr));\n\n    } else if (li->encoding == REDIS_ENCODING_LINKEDLIST) {\n        return equalStringObjects(o,listNodeValue(entry->ln));\n\n    } else {\n        redisPanic(\"Unknown list encoding\");\n    }\n}\n\n/* Delete the element pointed to. \n *\n * 删除 entry 所指向的节点\n */\nvoid listTypeDelete(listTypeEntry *entry) {\n\n    listTypeIterator *li = entry->li;\n\n    // ZIPLIST\n    if (li->encoding == REDIS_ENCODING_ZIPLIST) {\n\n        unsigned char *p = entry->zi;\n\n        li->subject->ptr = ziplistDelete(li->subject->ptr,&p);\n\n        /* Update position of the iterator depending on the direction */\n        // 删除节点之后，更新迭代器的指针\n        if (li->direction == REDIS_TAIL)\n            li->zi = p;\n        else\n            li->zi = ziplistPrev(li->subject->ptr,p);\n\n    // 双端链表\n    } else if (entry->li->encoding == REDIS_ENCODING_LINKEDLIST) {\n\n        // 记录后置节点\n        listNode *next;\n\n        if (li->direction == REDIS_TAIL)\n            next = entry->ln->next;\n        else\n            next = entry->ln->prev;\n\n        // 删除当前节点\n        listDelNode(li->subject->ptr,entry->ln);\n\n        // 删除节点之后，更新迭代器的指针\n        li->ln = next;\n\n    } else {\n        redisPanic(\"Unknown list encoding\");\n    }\n}\n\n/*\n * 将列表的底层编码从 ziplist 转换成双端链表\n */\nvoid listTypeConvert(robj *subject, int enc) {\n\n    listTypeIterator *li;\n\n    listTypeEntry entry;\n\n    redisAssertWithInfo(NULL,subject,subject->type == REDIS_LIST);\n\n    // 转换成双端链表\n    if (enc == REDIS_ENCODING_LINKEDLIST) {\n\n        list *l = listCreate();\n\n        listSetFreeMethod(l,decrRefCountVoid);\n\n        /* listTypeGet returns a robj with incremented refcount */\n        // 遍历 ziplist ，并将里面的值全部添加到双端链表中\n        li = listTypeInitIterator(subject,0,REDIS_TAIL);\n        while (listTypeNext(li,&entry)) listAddNodeTail(l,listTypeGet(&entry));\n        listTypeReleaseIterator(li);\n\n        // 更新编码\n        subject->encoding = REDIS_ENCODING_LINKEDLIST;\n\n        // 释放原来的 ziplist\n        zfree(subject->ptr);\n\n        // 更新对象值指针\n        subject->ptr = l;\n\n    } else {\n        redisPanic(\"Unsupported list conversion\");\n    }\n}\n\n/*-----------------------------------------------------------------------------\n * List Commands\n *----------------------------------------------------------------------------*/\n\nvoid pushGenericCommand(redisClient *c, int where) {\n\n    int j, waiting = 0, pushed = 0;\n\n    // 取出列表对象\n    robj *lobj = lookupKeyWrite(c->db,c->argv[1]);\n\n    // 如果列表对象不存在，那么可能有客户端在等待这个键的出现\n    int may_have_waiting_clients = (lobj == NULL);\n\n    if (lobj && lobj->type != REDIS_LIST) {\n        addReply(c,shared.wrongtypeerr);\n        return;\n    }\n\n    // 将列表状态设置为就绪\n    if (may_have_waiting_clients) signalListAsReady(c,c->argv[1]);\n\n    // 遍历所有输入值，并将它们添加到列表中\n    for (j = 2; j < c->argc; j++) {\n\n        // 编码值\n        c->argv[j] = tryObjectEncoding(c->argv[j]);\n\n        // 如果列表对象不存在，那么创建一个，并关联到数据库\n        if (!lobj) {\n            lobj = createZiplistObject();\n            dbAdd(c->db,c->argv[1],lobj);\n        }\n\n        // 将值推入到列表\n        listTypePush(lobj,c->argv[j],where);\n\n        pushed++;\n    }\n\n    // 返回添加的节点数量\n    addReplyLongLong(c, waiting + (lobj ? listTypeLength(lobj) : 0));\n\n    // 如果至少有一个元素被成功推入，那么执行以下代码\n    if (pushed) {\n        char *event = (where == REDIS_HEAD) ? \"lpush\" : \"rpush\";\n\n        // 发送键修改信号\n        signalModifiedKey(c->db,c->argv[1]);\n\n        // 发送事件通知\n        notifyKeyspaceEvent(REDIS_NOTIFY_LIST,event,c->argv[1],c->db->id);\n    }\n\n    server.dirty += pushed;\n}\n\nvoid lpushCommand(redisClient *c) {\n    pushGenericCommand(c,REDIS_HEAD);\n}\n\nvoid rpushCommand(redisClient *c) {\n    pushGenericCommand(c,REDIS_TAIL);\n}\n\nvoid pushxGenericCommand(redisClient *c, robj *refval, robj *val, int where) {\n    robj *subject;\n    listTypeIterator *iter;\n    listTypeEntry entry;\n    int inserted = 0;\n\n    // 取出列表对象\n    if ((subject = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||\n        checkType(c,subject,REDIS_LIST)) return;\n\n    // 执行的是 LINSERT 命令\n    if (refval != NULL) {\n        /* We're not sure if this value can be inserted yet, but we cannot\n         * convert the list inside the iterator. We don't want to loop over\n         * the list twice (once to see if the value can be inserted and once\n         * to do the actual insert), so we assume this value can be inserted\n         * and convert the ziplist to a regular list if necessary. */\n        // 看保存值 value 是否需要将列表编码转换为双端链表\n        listTypeTryConversion(subject,val);\n\n        /* Seek refval from head to tail */\n        // 在列表中查找 refval 对象\n        iter = listTypeInitIterator(subject,0,REDIS_TAIL);\n        while (listTypeNext(iter,&entry)) {\n            if (listTypeEqual(&entry,refval)) {\n                // 找到了，将值插入到节点的前面或后面\n                listTypeInsert(&entry,val,where);\n                inserted = 1;\n                break;\n            }\n        }\n        listTypeReleaseIterator(iter);\n\n        if (inserted) {\n            /* Check if the length exceeds the ziplist length threshold. */\n            // 查看插入之后是否需要将编码转换为双端链表\n            if (subject->encoding == REDIS_ENCODING_ZIPLIST &&\n                ziplistLen(subject->ptr) > server.list_max_ziplist_entries)\n                    listTypeConvert(subject,REDIS_ENCODING_LINKEDLIST);\n\n            signalModifiedKey(c->db,c->argv[1]);\n\n            notifyKeyspaceEvent(REDIS_NOTIFY_LIST,\"linsert\",\n                                c->argv[1],c->db->id);\n            server.dirty++;\n        } else {\n            /* Notify client of a failed insert */\n            // refval 不存在，插入失败\n            addReply(c,shared.cnegone);\n            return;\n        }\n\n    // 执行的是 LPUSHX 或 RPUSHX 命令\n    } else {\n        char *event = (where == REDIS_HEAD) ? \"lpush\" : \"rpush\";\n\n        listTypePush(subject,val,where);\n\n        signalModifiedKey(c->db,c->argv[1]);\n\n        notifyKeyspaceEvent(REDIS_NOTIFY_LIST,event,c->argv[1],c->db->id);\n\n        server.dirty++;\n    }\n\n    addReplyLongLong(c,listTypeLength(subject));\n}\n\nvoid lpushxCommand(redisClient *c) {\n    c->argv[2] = tryObjectEncoding(c->argv[2]);\n    pushxGenericCommand(c,NULL,c->argv[2],REDIS_HEAD);\n}\n\nvoid rpushxCommand(redisClient *c) {\n    c->argv[2] = tryObjectEncoding(c->argv[2]);\n    pushxGenericCommand(c,NULL,c->argv[2],REDIS_TAIL);\n}\n\nvoid linsertCommand(redisClient *c) {\n\n    // 编码 refval 对象\n    c->argv[4] = tryObjectEncoding(c->argv[4]);\n\n    if (strcasecmp(c->argv[2]->ptr,\"after\") == 0) {\n        pushxGenericCommand(c,c->argv[3],c->argv[4],REDIS_TAIL);\n\n    } else if (strcasecmp(c->argv[2]->ptr,\"before\") == 0) {\n        pushxGenericCommand(c,c->argv[3],c->argv[4],REDIS_HEAD);\n\n    } else {\n        addReply(c,shared.syntaxerr);\n    }\n}\n\nvoid llenCommand(redisClient *c) {\n\n    robj *o = lookupKeyReadOrReply(c,c->argv[1],shared.czero);\n\n    if (o == NULL || checkType(c,o,REDIS_LIST)) return;\n\n    addReplyLongLong(c,listTypeLength(o));\n}\n\nvoid lindexCommand(redisClient *c) {\n\n    robj *o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk);\n\n    if (o == NULL || checkType(c,o,REDIS_LIST)) return;\n    long index;\n    robj *value = NULL;\n\n    // 取出整数值对象 index\n    if ((getLongFromObjectOrReply(c, c->argv[2], &index, NULL) != REDIS_OK))\n        return;\n\n    // 根据索引，遍历 ziplist ，直到指定位置\n    if (o->encoding == REDIS_ENCODING_ZIPLIST) {\n        unsigned char *p;\n        unsigned char *vstr;\n        unsigned int vlen;\n        long long vlong;\n\n        p = ziplistIndex(o->ptr,index);\n\n        if (ziplistGet(p,&vstr,&vlen,&vlong)) {\n            if (vstr) {\n                value = createStringObject((char*)vstr,vlen);\n            } else {\n                value = createStringObjectFromLongLong(vlong);\n            }\n            addReplyBulk(c,value);\n            decrRefCount(value);\n        } else {\n            addReply(c,shared.nullbulk);\n        }\n\n    // 根据索引，遍历双端链表，直到指定位置\n    } else if (o->encoding == REDIS_ENCODING_LINKEDLIST) {\n\n        listNode *ln = listIndex(o->ptr,index);\n\n        if (ln != NULL) {\n            value = listNodeValue(ln);\n            addReplyBulk(c,value);\n        } else {\n            addReply(c,shared.nullbulk);\n        }\n    } else {\n        redisPanic(\"Unknown list encoding\");\n    }\n}\n\nvoid lsetCommand(redisClient *c) {\n\n    // 取出列表对象\n    robj *o = lookupKeyWriteOrReply(c,c->argv[1],shared.nokeyerr);\n\n    if (o == NULL || checkType(c,o,REDIS_LIST)) return;\n    long index;\n\n    // 取出值对象 value\n    robj *value = (c->argv[3] = tryObjectEncoding(c->argv[3]));\n\n    // 取出整数值对象 index\n    if ((getLongFromObjectOrReply(c, c->argv[2], &index, NULL) != REDIS_OK))\n        return;\n\n    // 查看保存 value 值是否需要转换列表的底层编码\n    listTypeTryConversion(o,value);\n\n    // 设置到 ziplist\n    if (o->encoding == REDIS_ENCODING_ZIPLIST) {\n        unsigned char *p, *zl = o->ptr;\n        // 查找索引\n        p = ziplistIndex(zl,index);\n        if (p == NULL) {\n            addReply(c,shared.outofrangeerr);\n        } else {\n            // 删除现有的值\n            o->ptr = ziplistDelete(o->ptr,&p);\n            // 插入新值到指定索引\n            value = getDecodedObject(value);\n            o->ptr = ziplistInsert(o->ptr,p,value->ptr,sdslen(value->ptr));\n            decrRefCount(value);\n\n            addReply(c,shared.ok);\n            signalModifiedKey(c->db,c->argv[1]);\n            notifyKeyspaceEvent(REDIS_NOTIFY_LIST,\"lset\",c->argv[1],c->db->id);\n            server.dirty++;\n        }\n\n    // 设置到双端链表\n    } else if (o->encoding == REDIS_ENCODING_LINKEDLIST) {\n\n        listNode *ln = listIndex(o->ptr,index);\n\n        if (ln == NULL) {\n            addReply(c,shared.outofrangeerr);\n        } else {\n            // 删除旧值对象\n            decrRefCount((robj*)listNodeValue(ln));\n            // 指向新对象\n            listNodeValue(ln) = value;\n            incrRefCount(value);\n\n            addReply(c,shared.ok);\n            signalModifiedKey(c->db,c->argv[1]);\n            notifyKeyspaceEvent(REDIS_NOTIFY_LIST,\"lset\",c->argv[1],c->db->id);\n            server.dirty++;\n        }\n    } else {\n        redisPanic(\"Unknown list encoding\");\n    }\n}\n\nvoid popGenericCommand(redisClient *c, int where) {\n\n    // 取出列表对象\n    robj *o = lookupKeyWriteOrReply(c,c->argv[1],shared.nullbulk);\n\n    if (o == NULL || checkType(c,o,REDIS_LIST)) return;\n\n    // 弹出列表元素\n    robj *value = listTypePop(o,where);\n\n    // 根据弹出元素是否为空，决定后续动作\n    if (value == NULL) {\n        addReply(c,shared.nullbulk);\n    } else {\n        char *event = (where == REDIS_HEAD) ? \"lpop\" : \"rpop\";\n\n        addReplyBulk(c,value);\n        decrRefCount(value);\n        notifyKeyspaceEvent(REDIS_NOTIFY_LIST,event,c->argv[1],c->db->id);\n        if (listTypeLength(o) == 0) {\n            notifyKeyspaceEvent(REDIS_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        server.dirty++;\n    }\n}\n\nvoid lpopCommand(redisClient *c) {\n    popGenericCommand(c,REDIS_HEAD);\n}\n\nvoid rpopCommand(redisClient *c) {\n    popGenericCommand(c,REDIS_TAIL);\n}\n\nvoid lrangeCommand(redisClient *c) {\n    robj *o;\n    long start, end, llen, rangelen;\n\n    // 取出索引值 start 和 end\n    if ((getLongFromObjectOrReply(c, c->argv[2], &start, NULL) != REDIS_OK) ||\n        (getLongFromObjectOrReply(c, c->argv[3], &end, NULL) != REDIS_OK)) return;\n\n    // 取出列表对象\n    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptymultibulk)) == NULL\n         || checkType(c,o,REDIS_LIST)) return;\n\n    // 取出列表长度\n    llen = listTypeLength(o);\n\n    /* convert negative indexes */\n    // 将负数索引转换成正数索引\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.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\n    if (o->encoding == REDIS_ENCODING_ZIPLIST) {\n        unsigned char *p = ziplistIndex(o->ptr,start);\n        unsigned char *vstr;\n        unsigned int vlen;\n        long long vlong;\n\n        // 遍历 ziplist ，并将指定索引上的值添加到回复中\n        while(rangelen--) {\n            ziplistGet(p,&vstr,&vlen,&vlong);\n            if (vstr) {\n                addReplyBulkCBuffer(c,vstr,vlen);\n            } else {\n                addReplyBulkLongLong(c,vlong);\n            }\n            p = ziplistNext(o->ptr,p);\n        }\n\n    } else if (o->encoding == REDIS_ENCODING_LINKEDLIST) {\n        listNode *ln;\n\n        /* If we are nearest to the end of the list, reach the element\n         * starting from tail and going backward, as it is faster. */\n        if (start > llen/2) start -= llen;\n        ln = listIndex(o->ptr,start);\n\n        // 遍历双端链表，将指定索引上的值添加到回复\n        while(rangelen--) {\n            addReplyBulk(c,ln->value);\n            ln = ln->next;\n        }\n\n    } else {\n        redisPanic(\"List encoding is not LINKEDLIST nor ZIPLIST!\");\n    }\n}\n\nvoid ltrimCommand(redisClient *c) {\n    robj *o;\n    long start, end, llen, j, ltrim, rtrim;\n    list *list;\n    listNode *ln;\n\n    // 取出索引值 start 和 end\n    if ((getLongFromObjectOrReply(c, c->argv[2], &start, NULL) != REDIS_OK) ||\n        (getLongFromObjectOrReply(c, c->argv[3], &end, NULL) != REDIS_OK)) return;\n\n    // 取出列表对象\n    if ((o = lookupKeyWriteOrReply(c,c->argv[1],shared.ok)) == NULL ||\n        checkType(c,o,REDIS_LIST)) return;\n\n    // 列表长度\n    llen = listTypeLength(o);\n\n    /* convert negative indexes */\n    // 将负数索引转换成正数索引\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    // 删除指定列表两端的元素\n\n    if (o->encoding == REDIS_ENCODING_ZIPLIST) {\n        // 删除左端元素\n        o->ptr = ziplistDeleteRange(o->ptr,0,ltrim);\n        // 删除右端元素\n        o->ptr = ziplistDeleteRange(o->ptr,-rtrim,rtrim);\n\n    } else if (o->encoding == REDIS_ENCODING_LINKEDLIST) {\n        list = o->ptr;\n        // 删除左端元素\n        for (j = 0; j < ltrim; j++) {\n            ln = listFirst(list);\n            listDelNode(list,ln);\n        }\n        // 删除右端元素\n        for (j = 0; j < rtrim; j++) {\n            ln = listLast(list);\n            listDelNode(list,ln);\n        }\n\n    } else {\n        redisPanic(\"Unknown list encoding\");\n    }\n\n    // 发送通知\n    notifyKeyspaceEvent(REDIS_NOTIFY_LIST,\"ltrim\",c->argv[1],c->db->id);\n\n    // 如果列表已经为空，那么删除它\n    if (listTypeLength(o) == 0) {\n        dbDelete(c->db,c->argv[1]);\n        notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,\"del\",c->argv[1],c->db->id);\n    }\n\n    signalModifiedKey(c->db,c->argv[1]);\n\n    server.dirty++;\n\n    addReply(c,shared.ok);\n}\n\nvoid lremCommand(redisClient *c) {\n    robj *subject, *obj;\n\n    // 编码目标对象 elem\n    obj = c->argv[3] = tryObjectEncoding(c->argv[3]);\n    long toremove;\n    long removed = 0;\n    listTypeEntry entry;\n\n    // 取出指定删除模式的 count 参数\n    if ((getLongFromObjectOrReply(c, c->argv[2], &toremove, NULL) != REDIS_OK))\n        return;\n\n    // 取出列表对象\n    subject = lookupKeyWriteOrReply(c,c->argv[1],shared.czero);\n    if (subject == NULL || checkType(c,subject,REDIS_LIST)) return;\n\n    /* Make sure obj is raw when we're dealing with a ziplist */\n    if (subject->encoding == REDIS_ENCODING_ZIPLIST)\n        obj = getDecodedObject(obj);\n\n    listTypeIterator *li;\n\n    // 根据 toremove 参数，决定是从表头还是表尾开始进行删除\n    if (toremove < 0) {\n        toremove = -toremove;\n        li = listTypeInitIterator(subject,-1,REDIS_HEAD);\n    } else {\n        li = listTypeInitIterator(subject,0,REDIS_TAIL);\n    }\n\n    // 查找，比对对象，并进行删除\n    while (listTypeNext(li,&entry)) {\n        if (listTypeEqual(&entry,obj)) {\n            listTypeDelete(&entry);\n            server.dirty++;\n            removed++;\n            // 已经满足删除数量，停止\n            if (toremove && removed == toremove) break;\n        }\n    }\n    listTypeReleaseIterator(li);\n\n    /* Clean up raw encoded object */\n    if (subject->encoding == REDIS_ENCODING_ZIPLIST)\n        decrRefCount(obj);\n\n    // 删除空列表\n    if (listTypeLength(subject) == 0) dbDelete(c->db,c->argv[1]);\n\n    addReplyLongLong(c,removed);\n\n    if (removed) signalModifiedKey(c->db,c->argv[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(redisClient *c, robj *dstkey, robj *dstobj, robj *value) {\n    /* Create the list if the key does not exist */\n    // 如果目标列表不存在，那么创建一个\n    if (!dstobj) {\n        dstobj = createZiplistObject();\n        dbAdd(c->db,dstkey,dstobj);\n        signalListAsReady(c,dstkey);\n    }\n\n    signalModifiedKey(c->db,dstkey);\n\n    // 将值推入目标列表中\n    listTypePush(dstobj,value,REDIS_HEAD);\n\n    notifyKeyspaceEvent(REDIS_NOTIFY_LIST,\"lpush\",dstkey,c->db->id);\n    /* Always send the pushed value to the client. */\n    addReplyBulk(c,value);\n}\n\nvoid rpoplpushCommand(redisClient *c) {\n    robj *sobj, *value;\n    \n    // 来源列表\n    if ((sobj = lookupKeyWriteOrReply(c,c->argv[1],shared.nullbulk)) == NULL ||\n        checkType(c,sobj,REDIS_LIST)) return;\n\n    // 空列表，没有元素可 pop ，直接返回\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\n    // 源列表非空\n    } else {\n\n        // 目标对象\n        robj *dobj = lookupKeyWrite(c->db,c->argv[2]);\n        robj *touchedkey = c->argv[1];\n\n        // 检查目标对象是否列表\n        if (dobj && checkType(c,dobj,REDIS_LIST)) return;\n        // 从源列表中弹出值\n        value = listTypePop(sobj,REDIS_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        // 将值推入目标列表中，如果目标列表不存在，那么创建一个新列表\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(REDIS_NOTIFY_LIST,\"rpop\",touchedkey,c->db->id);\n\n        // 如果源列表已经为空，那么将它删除\n        if (listTypeLength(sobj) == 0) {\n            dbDelete(c->db,touchedkey);\n            notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,\"del\",\n                                touchedkey,c->db->id);\n        }\n\n        signalModifiedKey(c->db,touchedkey);\n\n        decrRefCount(touchedkey);\n\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 *\n * 以下是目前的阻塞 POP 操作的运作方法，以 BLPOP 作为例子：\n *\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 *\n * - 如果用户调用 BLPOP ，并且列表非空，那么程序执行 LPOP 。\n *   因此，当列表非空时，调用 BLPOP 等于调用 LPOP。\n *\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 *\n * - 当 BLPOP 对一个空键执行时，客户端才会被阻塞：\n *   服务器不再对这个客户端发送任何数据，\n *   对这个客户端的状态设为“被阻塞“，直到解除阻塞为止。\n *   并且客户端会被加入到一个以阻塞键为 key ，\n *   以被阻塞客户端为 value 的字典 db->blocking_keys 中。\n *\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 * - 当有 PUSH 命令作用于一个造成客户端阻塞的键时，\n *   程序将这个键标记为“就绪”，并且在执行完这个命令、事务、或脚本之后，\n *   程序会按“先阻塞先服务”的顺序，将列表的元素返回给那些被阻塞的客户端，\n *   被解除阻塞的客户端数量取决于 PUSH 命令推入的元素数量。\n */\n\n/* Set a client in blocking mode for the specified key, with the specified\n * timeout */\n// 根据给定数量的 key ，对给定客户端进行阻塞\n// 参数：\n// keys    任意多个 key\n// numkeys keys 的键数量\n// timeout 阻塞的最长时限\n// target  在解除阻塞时，将结果保存到这个 key 对象，而不是返回给客户端\n//         只用于 BRPOPLPUSH 命令\nvoid blockForKeys(redisClient *c, robj **keys, int numkeys, mstime_t timeout, robj *target) {\n    dictEntry *de;\n    list *l;\n    int j;\n\n    // 设置阻塞状态的超时和目标选项\n    c->bpop.timeout = timeout;\n\n    // target 在执行 RPOPLPUSH 命令时使用\n    c->bpop.target = target;\n\n    if (target != NULL) incrRefCount(target);\n\n    // 关联阻塞客户端和键的相关信息\n    for (j = 0; j < numkeys; j++) {\n\n        /* If the key already exists in the dict ignore it. */\n        // c->bpop.keys 是一个集合（值为 NULL 的字典）\n        // 它记录所有造成客户端阻塞的键\n        // 以下语句在键不存在于集合的时候，将它添加到集合\n        if (dictAdd(c->bpop.keys,keys[j],NULL) != DICT_OK) continue;\n\n        incrRefCount(keys[j]);\n\n        /* And in the other \"side\", to map keys -> clients */\n        // c->db->blocking_keys 字典的键为造成客户端阻塞的键\n        // 而值则是一个链表，链表中包含了所有被阻塞的客户端\n        // 以下程序将阻塞键和被阻塞客户端关联起来\n        de = dictFind(c->db->blocking_keys,keys[j]);\n        if (de == NULL) {\n            // 链表不存在，新创建一个，并将它关联到字典中\n            int retval;\n\n            /* For every key we take a list of clients blocked for it */\n            l = listCreate();\n            retval = dictAdd(c->db->blocking_keys,keys[j],l);\n            incrRefCount(keys[j]);\n            redisAssertWithInfo(c,keys[j],retval == DICT_OK);\n        } else {\n            l = dictGetVal(de);\n        }\n        // 将客户端填接到被阻塞客户端的链表中\n        listAddNodeTail(l,c);\n    }\n    blockClient(c,REDIS_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(redisClient *c) {\n    dictEntry *de;\n    dictIterator *di;\n    list *l;\n\n    redisAssertWithInfo(c,NULL,dictSize(c->bpop.keys) != 0);\n\n    // 遍历所有 key ，将它们从客户端 db->blocking_keys 的链表中移除\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        // 获取所有因为 key 而被阻塞的客户端的链表\n        l = dictFetchValue(c->db->blocking_keys,key);\n\n        redisAssertWithInfo(c,key,l != NULL);\n\n        // 将指定客户端从链表中删除\n        listDelNode(l,listSearchKey(l,c));\n\n        /* If the list is empty we need to remove it to avoid wasting memory */\n        // 如果已经没有其他客户端阻塞在这个 key 上，那么删除这个链表\n        if (listLength(l) == 0)\n            dictDelete(c->db->blocking_keys,key);\n    }\n    dictReleaseIterator(di);\n\n    /* Cleanup the client structure */\n    // 清空 bpop.keys 集合（字典）\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 * 如果有客户端正因为等待给定 key 被 push 而阻塞，\n * 那么将这个 key 的放进 server.ready_keys 列表里面。\n *\n * 注意 db->ready_keys 是一个哈希表，\n * 这可以避免在事务或者脚本中，将同一个 key 一次又一次添加到列表的情况出现。\n *\n * The list will be finally processed by handleClientsBlockedOnLists() \n *\n * 这个列表最终会被 handleClientsBlockedOnLists() 函数处理。\n */\nvoid signalListAsReady(redisClient *c, robj *key) {\n    readyList *rl;\n\n    /* No clients blocking for this key? No need to queue it. */\n    // 没有客户端被这个键阻塞，直接返回\n    if (dictFind(c->db->blocking_keys,key) == NULL) return;\n\n    /* Key was already signaled? No need to queue it again. */\n    // 这个键已经被添加到 ready_keys 中了，直接返回\n    if (dictFind(c->db->ready_keys,key) != NULL) return;\n\n    /* Ok, we need to queue this key into server.ready_keys. */\n    // 创建一个 readyList 结构，保存键和数据库\n    // 然后将 readyList 添加到 server.ready_keys 中\n    rl = zmalloc(sizeof(*rl));\n    rl->key = key;\n    rl->db = c->db;\n    incrRefCount(key);\n    listAddNodeTail(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     *\n     * 将 key 添加到 c->db->ready_keys 集合中，防止重复添加\n     */\n    incrRefCount(key);\n    redisAssert(dictAdd(c->db->ready_keys,key,NULL) == 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 * 函数对被阻塞的客户端 receiver 、造成阻塞的 key 、 key 所在的数据库 db\n * 以及一个值 value 和一个位置值 where 执行以下动作：\n *\n * 1) Provide the client with the 'value' element.\n *\n *    将 value 提供给 receiver\n *\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 *\n *    如果 dstkey 不为空（BRPOPLPUSH的情况），\n *    那么也将 value 推入到 dstkey 指定的列表中。\n *\n * 3) Propagate the resulting BRPOP, BLPOP and additional LPUSH if any into\n *    the AOF and replication channel.\n *\n *    将 BRPOP 、 BLPOP 和可能有的 LPUSH 传播到 AOF 和同步节点\n *\n * The argument 'where' is REDIS_TAIL or REDIS_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 * where 可能是 REDIS_TAIL 或者 REDIS_HEAD ，用于识别该 value 是从那个地方 POP\n * 出来，依靠这个参数，可以同样传播 BLPOP 或者 BRPOP 。\n\n * The function returns REDIS_OK if we are able to serve the client, otherwise\n * REDIS_ERR 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. \n *\n * 如果一切成功，返回 REDIS_OK 。\n * 如果执行失败，那么返回 REDIS_ERR ，让 Redis 撤销对目标节点的 POP 操作。\n * 失败的情况只会出现在 BRPOPLPUSH 命令中，\n * 比如 POP 源列表成功，却发现 PUSH 的目标对象不是列表时，操作就会出现失败。\n */\nint serveClientBlockedOnList(redisClient *receiver, robj *key, robj *dstkey, redisDb *db, robj *value, int where)\n{\n    robj *argv[3];\n\n    // 执行的是 BLPOP 或 BRPOP\n    if (dstkey == NULL) {\n        /* Propagate the [LR]POP operation. */\n        argv[0] = (where == REDIS_HEAD) ? shared.lpop :\n                                          shared.rpop;\n        argv[1] = key;\n        propagate((where == REDIS_HEAD) ?\n            server.lpopCommand : server.rpopCommand,\n            db->id,argv,2,REDIS_PROPAGATE_AOF|REDIS_PROPAGATE_REPL);\n\n        /* BRPOP/BLPOP */\n        addReplyMultiBulkLen(receiver,2);\n        addReplyBulk(receiver,key);\n        addReplyBulk(receiver,value);\n\n    // 执行的是 BRPOPLPUSH \n    } else {\n        /* BRPOPLPUSH */\n\n        // 取出目标对象\n        robj *dstobj =\n            lookupKeyWrite(receiver->db,dstkey);\n        if (!(dstobj &&\n             checkType(receiver,dstobj,REDIS_LIST)))\n        {\n            /* Propagate the RPOP operation. */\n            // 传播 RPOP 命令\n            argv[0] = shared.rpop;\n            argv[1] = key;\n            propagate(server.rpopCommand,\n                db->id,argv,2,\n                REDIS_PROPAGATE_AOF|\n                REDIS_PROPAGATE_REPL);\n\n            // 将值推入到 dstobj 中，如果 dstobj 不存在，\n            // 那么新创建一个\n            rpoplpushHandlePush(receiver,dstkey,dstobj,\n                value);\n\n            /* Propagate the LPUSH operation. */\n            // 传播 LPUSH 命令\n            argv[0] = shared.lpush;\n            argv[1] = dstkey;\n            argv[2] = value;\n            propagate(server.lpushCommand,\n                db->id,argv,3,\n                REDIS_PROPAGATE_AOF|\n                REDIS_PROPAGATE_REPL);\n        } else {\n            /* BRPOPLPUSH failed because of wrong\n             * destination type. */\n            return REDIS_ERR;\n        }\n    }\n    return REDIS_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 * 这个函数会在 Redis 每次执行完单个命令、事务块或 Lua 脚本之后调用。\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. \n *\n * 对所有被阻塞在某个客户端的 key 来说，只要这个 key 被执行了某种 PUSH 操作\n * 那么这个 key 就会被放到 serve.ready_keys 去。\n * \n * 这个函数会遍历整个 serve.ready_keys 链表，\n * 并将里面的 key 的元素弹出给被阻塞客户端，\n * 从而解除客户端的阻塞状态。\n *\n * 函数会一次又一次地进行迭代，\n * 因此它在执行 BRPOPLPUSH 命令的情况下也可以正常获取到正确的新被阻塞客户端。\n */\nvoid handleClientsBlockedOnLists(void) {\n\n    // 遍历整个 ready_keys 链表\n    while(listLength(server.ready_keys) != 0) {\n        list *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        // 备份旧的 ready_keys ，再给服务器端赋值一个新的\n        l = server.ready_keys;\n        server.ready_keys = listCreate();\n\n        while(listLength(l) != 0) {\n\n            // 取出 ready_keys 中的首个链表节点\n            listNode *ln = listFirst(l);\n\n            // 指向 readyList 结构\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            // 从 ready_keys 中移除就绪的 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            // 获取键对象，这个对象应该是非空的，并且是列表\n            robj *o = lookupKeyWrite(rl->db,rl->key);\n            if (o != NULL && o->type == REDIS_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                // 取出所有被这个 key 阻塞的客户端\n                de = dictFind(rl->db->blocking_keys,rl->key);\n                if (de) {\n                    list *clients = dictGetVal(de);\n                    int numclients = listLength(clients);\n\n                    while(numclients--) {\n                        // 取出客户端\n                        listNode *clientnode = listFirst(clients);\n                        redisClient *receiver = clientnode->value;\n\n                        // 设置弹出的目标对象（只在 BRPOPLPUSH 时使用）\n                        robj *dstkey = receiver->bpop.target;\n\n                        // 从列表中弹出元素\n                        // 弹出的位置取决于是执行 BLPOP 还是 BRPOP 或者 BRPOPLPUSH\n                        int where = (receiver->lastcmd &&\n                                     receiver->lastcmd->proc == blpopCommand) ?\n                                    REDIS_HEAD : REDIS_TAIL;\n                        robj *value = listTypePop(o,where);\n\n                        // 还有元素可弹出（非 NULL）\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\n                            // 取消客户端的阻塞状态\n                            unblockClient(receiver);\n\n                            // 将值 value 推入到造成客户度 receiver 阻塞的 key 上\n                            if (serveClientBlockedOnList(receiver,\n                                rl->key,dstkey,rl->db,value,\n                                where) == REDIS_ERR)\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                            // 如果执行到这里，表示还有至少一个客户端被键所阻塞\n                            // 这些客户端要等待对键的下次 PUSH\n                            break;\n                        }\n                    }\n                }\n                \n                // 如果列表元素已经为空，那么从数据库中将它删除\n                if (listTypeLength(o) == 0) dbDelete(rl->db,rl->key);\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            zfree(rl);\n            listDelNode(l,ln);\n        }\n        listRelease(l); /* We have the new list on place at this point. */\n    }\n}\n\n/* Blocking RPOP/LPOP */\nvoid blockingPopGenericCommand(redisClient *c, int where) {\n    robj *o;\n    mstime_t timeout;\n    int j;\n\n    // 取出 timeout 参数\n    if (getTimeoutFromObjectOrReply(c,c->argv[c->argc-1],&timeout,UNIT_SECONDS)\n        != REDIS_OK) return;\n\n    // 遍历所有列表键\n    for (j = 1; j < c->argc-1; j++) {\n\n        // 取出列表键\n        o = lookupKeyWrite(c->db,c->argv[j]);\n\n        // 有非空列表？\n        if (o != NULL) {\n            if (o->type != REDIS_LIST) {\n                addReply(c,shared.wrongtypeerr);\n                return;\n            } else {\n                // 非空列表\n                if (listTypeLength(o) != 0) {\n                    /* Non empty list, this is like a non normal [LR]POP. */\n                    char *event = (where == REDIS_HEAD) ? \"lpop\" : \"rpop\";\n\n                    // 弹出值\n                    robj *value = listTypePop(o,where);\n\n                    redisAssert(value != NULL);\n\n                    // 回复客户端\n                    addReplyMultiBulkLen(c,2);\n                    // 回复弹出元素的列表\n                    addReplyBulk(c,c->argv[j]);\n                    // 回复弹出值\n                    addReplyBulk(c,value);\n\n                    decrRefCount(value);\n\n                    notifyKeyspaceEvent(REDIS_NOTIFY_LIST,event,\n                                        c->argv[j],c->db->id);\n\n                    // 删除空列表\n                    if (listTypeLength(o) == 0) {\n                        dbDelete(c->db,c->argv[j]);\n                        notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,\"del\",\n                                            c->argv[j],c->db->id);\n                    }\n\n                    signalModifiedKey(c->db,c->argv[j]);\n\n                    server.dirty++;\n\n                    /* Replicate it as an [LR]POP instead of B[LR]POP. */\n                    // 传播一个 [LR]POP 而不是 B[LR]POP\n                    rewriteClientCommandVector(c,2,\n                        (where == REDIS_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    // 如果命令在一个事务中执行，那么为了不产生死等待\n    // 服务器只能向客户端发送一个空回复\n    if (c->flags & REDIS_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    // 所有输入列表键都不存在，只能阻塞了\n    blockForKeys(c, c->argv + 1, c->argc - 2, timeout, NULL);\n}\n\nvoid blpopCommand(redisClient *c) {\n    blockingPopGenericCommand(c,REDIS_HEAD);\n}\n\nvoid brpopCommand(redisClient *c) {\n    blockingPopGenericCommand(c,REDIS_TAIL);\n}\n\nvoid brpoplpushCommand(redisClient *c) {\n    mstime_t timeout;\n\n    // 取出 timeout 参数\n    if (getTimeoutFromObjectOrReply(c,c->argv[3],&timeout,UNIT_SECONDS)\n        != REDIS_OK) return;\n\n    // 取出列表键\n    robj *key = lookupKeyWrite(c->db, c->argv[1]);\n\n    // 键为空，阻塞\n    if (key == NULL) {\n        if (c->flags & REDIS_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\n    // 键非空，执行 RPOPLPUSH\n    } else {\n        if (key->type != REDIS_LIST) {\n            addReply(c, shared.wrongtypeerr);\n        } else {\n            /* The list exists and has elements, so\n             * the regular rpoplpushCommand is executed. */\n            redisAssertWithInfo(c,key,listTypeLength(key) > 0);\n\n            rpoplpushCommand(c);\n        }\n    }\n}\n"
  },
  {
    "path": "src/t_set.c",
    "content": "/*\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 \"redis.h\"\n\n/*-----------------------------------------------------------------------------\n * Set Commands\n *----------------------------------------------------------------------------*/\n\nvoid sunionDiffGenericCommand(redisClient *c, robj **setkeys, int setnum, robj *dstkey, int op);\n\n/* Factory method to return a set that *can* hold \"value\". \n *\n * 返回一个可以保存值 value 的集合。\n *\n * When the object has an integer-encodable value, \n * an intset will be returned. Otherwise a regular hash table. \n *\n * 当对象的值可以被编码为整数时，返回 intset ，\n * 否则，返回普通的哈希表。\n */\nrobj *setTypeCreate(robj *value) {\n\n    if (isObjectRepresentableAsLongLong(value,NULL) == REDIS_OK)\n        return createIntsetObject();\n\n    return createSetObject();\n}\n\n/*\n * 多态 add 操作\n *\n * 添加成功返回 1 ，如果元素已经存在，返回 0 。\n */\nint setTypeAdd(robj *subject, robj *value) {\n    long long llval;\n\n    // 字典\n    if (subject->encoding == REDIS_ENCODING_HT) {\n        // 将 value 作为键， NULL 作为值，将元素添加到字典中\n        if (dictAdd(subject->ptr,value,NULL) == DICT_OK) {\n            incrRefCount(value);\n            return 1;\n        }\n\n    // intset\n    } else if (subject->encoding == REDIS_ENCODING_INTSET) {\n        \n        // 如果对象的值可以编码为整数的话，那么将对象的值添加到 intset 中\n        if (isObjectRepresentableAsLongLong(value,&llval) == REDIS_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                // 添加成功\n                // 检查集合在添加新元素之后是否需要转换为字典\n                if (intsetLen(subject->ptr) > server.set_max_intset_entries)\n                    setTypeConvert(subject,REDIS_ENCODING_HT);\n                return 1;\n            }\n\n        // 如果对象的值不能编码为整数，那么将集合从 intset 编码转换为 HT 编码\n        // 然后再执行添加操作\n        } else {\n            /* Failed to get integer from object, convert to regular set. */\n            setTypeConvert(subject,REDIS_ENCODING_HT);\n\n            /* The set *was* an intset and this value is not integer\n             * encodable, so dictAdd should always work. */\n            redisAssertWithInfo(NULL,value,dictAdd(subject->ptr,value,NULL) == DICT_OK);\n            incrRefCount(value);\n            return 1;\n        }\n\n    // 未知编码\n    } else {\n        redisPanic(\"Unknown set encoding\");\n    }\n\n    // 添加失败，元素已经存在\n    return 0;\n}\n\n/*\n * 多态 remove 操作\n *\n * 删除成功返回 1 ，因为元素不存在而导致删除失败返回 0 。\n */\nint setTypeRemove(robj *setobj, robj *value) {\n    long long llval;\n\n    // HT\n    if (setobj->encoding == REDIS_ENCODING_HT) {\n        // 从字典中删除键（集合元素）\n        if (dictDelete(setobj->ptr,value) == DICT_OK) {\n            // 看是否有必要在删除之后缩小字典的大小\n            if (htNeedsResize(setobj->ptr)) dictResize(setobj->ptr);\n            return 1;\n        }\n\n    // INTSET\n    } else if (setobj->encoding == REDIS_ENCODING_INTSET) {\n        // 如果对象的值可以编码为整数的话，那么尝试从 intset 中移除元素\n        if (isObjectRepresentableAsLongLong(value,&llval) == REDIS_OK) {\n            int success;\n            setobj->ptr = intsetRemove(setobj->ptr,llval,&success);\n            if (success) return 1;\n        }\n\n    // 未知编码\n    } else {\n        redisPanic(\"Unknown set encoding\");\n    }\n\n    // 删除失败\n    return 0;\n}\n\n/*\n * 多态 ismember 操作\n */\nint setTypeIsMember(robj *subject, robj *value) {\n    long long llval;\n\n    // HT\n    if (subject->encoding == REDIS_ENCODING_HT) {\n        return dictFind((dict*)subject->ptr,value) != NULL;\n\n    // INTSET\n    } else if (subject->encoding == REDIS_ENCODING_INTSET) {\n        if (isObjectRepresentableAsLongLong(value,&llval) == REDIS_OK) {\n            return intsetFind((intset*)subject->ptr,llval);\n        }\n\n    // 未知编码\n    } else {\n        redisPanic(\"Unknown set encoding\");\n    }\n\n    // 查找失败\n    return 0;\n}\n\n/*\n * 创建并返回一个多态集合迭代器\n *\n * setTypeIterator 定义在 redis.h\n */\nsetTypeIterator *setTypeInitIterator(robj *subject) {\n\n    setTypeIterator *si = zmalloc(sizeof(setTypeIterator));\n    \n    // 指向被迭代的对象\n    si->subject = subject;\n\n    // 记录对象的编码\n    si->encoding = subject->encoding;\n\n    // HT\n    if (si->encoding == REDIS_ENCODING_HT) {\n        // 字典迭代器\n        si->di = dictGetIterator(subject->ptr);\n\n    // INTSET\n    } else if (si->encoding == REDIS_ENCODING_INTSET) {\n        // 索引\n        si->ii = 0;\n\n    // 未知编码\n    } else {\n        redisPanic(\"Unknown set encoding\");\n    }\n\n    // 返回迭代器\n    return si;\n}\n\n/*\n * 释放迭代器\n */\nvoid setTypeReleaseIterator(setTypeIterator *si) {\n\n    if (si->encoding == REDIS_ENCODING_HT)\n        dictReleaseIterator(si->di);\n\n    zfree(si);\n}\n\n/* Move to the next entry in the set. Returns the object at the current\n * position.\n *\n * 取出被迭代器指向的当前集合元素。\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 * (eobj) or (llobj) accordingly.\n *\n * 因为集合即可以编码为 intset ，也可以编码为哈希表，\n * 所以程序会根据集合的编码，选择将值保存到那个参数里：\n *\n *  - 当编码为 intset 时，元素被指向到 llobj 参数\n *\n *  - 当编码为哈希表时，元素被指向到 eobj 参数\n *\n * 并且函数会返回被迭代集合的编码，方便识别。\n *\n * When there are no longer elements -1 is returned.\n *\n * 当集合中的元素全部被迭代完毕时，函数返回 -1 。\n *\n * Returned objects ref count is not incremented, so this function is\n * copy on write friendly. \n *\n * 因为被返回的对象是没有被增加引用计数的，\n * 所以这个函数是对 copy-on-write 友好的。\n */\nint setTypeNext(setTypeIterator *si, robj **objele, int64_t *llele) {\n\n    // 从字典中取出对象\n    if (si->encoding == REDIS_ENCODING_HT) {\n\n        // 更新迭代器\n        dictEntry *de = dictNext(si->di);\n\n        // 字典已迭代完\n        if (de == NULL) return -1;\n\n        // 返回节点的键（集合的元素）\n        *objele = dictGetKey(de);\n\n    // 从 intset 中取出元素\n    } else if (si->encoding == REDIS_ENCODING_INTSET) {\n        if (!intsetGet(si->subject->ptr,si->ii++,llele))\n            return -1;\n    }\n\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 incrementing the ref count of returned objects. So if you don't\n * retain a pointer to this object you should call decrRefCount() against it.\n *\n * setTypeNext 的非 copy-on-write 友好版本，\n * 总是返回一个新的、或者已经增加过引用计数的对象。\n *\n * 调用者在使用完对象之后，应该对对象调用 decrRefCount() 。\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. \n *\n * 这个函数应该在非 copy-on-write 时调用。\n */\nrobj *setTypeNextObject(setTypeIterator *si) {\n    int64_t intele;\n    robj *objele;\n    int encoding;\n\n    // 取出元素\n    encoding = setTypeNext(si,&objele,&intele);\n    // 总是为元素创建对象\n    switch(encoding) {\n        // 已为空\n        case -1:    return NULL;\n        // INTSET 返回一个整数值，需要为这个值创建对象\n        case REDIS_ENCODING_INTSET:\n            return createStringObjectFromLongLong(intele);\n        // HT 本身已经返回对象了，只需执行 incrRefCount()\n        case REDIS_ENCODING_HT:\n            incrRefCount(objele);\n            return objele;\n        default:\n            redisPanic(\"Unsupported encoding\");\n    }\n\n    return NULL; /* just to suppress warnings */\n}\n\n/* Return random element from a non empty set.\n *\n * 从非空集合中随机取出一个元素。\n *\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 * 如果集合的编码为 intset ，那么将元素指向 int64_t 指针 llele 。\n * 如果集合的编码为 HT ，那么将元素对象指向对象指针 objele 。\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 * 函数的返回值为集合的编码方式，通过这个返回值可以知道那个指针保存了元素的值。\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. \n *\n * 因为被返回的对象是没有被增加引用计数的，\n * 所以这个函数是对 copy-on-write 友好的。\n */\nint setTypeRandomElement(robj *setobj, robj **objele, int64_t *llele) {\n\n    if (setobj->encoding == REDIS_ENCODING_HT) {\n        dictEntry *de = dictGetRandomKey(setobj->ptr);\n        *objele = dictGetKey(de);\n\n    } else if (setobj->encoding == REDIS_ENCODING_INTSET) {\n        *llele = intsetRandom(setobj->ptr);\n\n    } else {\n        redisPanic(\"Unknown set encoding\");\n    }\n\n    return setobj->encoding;\n}\n\n/*\n * 集合多态 size 函数\n */\nunsigned long setTypeSize(robj *subject) {\n\n    if (subject->encoding == REDIS_ENCODING_HT) {\n        return dictSize((dict*)subject->ptr);\n\n    } else if (subject->encoding == REDIS_ENCODING_INTSET) {\n        return intsetLen((intset*)subject->ptr);\n\n    } else {\n        redisPanic(\"Unknown set encoding\");\n    }\n}\n\n/* Convert the set to specified encoding. \n *\n * 将集合对象 setobj 的编码转换为 REDIS_ENCODING_HT 。\n *\n * The resulting dict (when converting to a hash table)\n * is presized to hold the number of elements in the original set.\n *\n * 新创建的结果字典会被预先分配为和原来的集合一样大。\n */\nvoid setTypeConvert(robj *setobj, int enc) {\n\n    setTypeIterator *si;\n\n    // 确认类型和编码正确\n    redisAssertWithInfo(NULL,setobj,setobj->type == REDIS_SET &&\n                             setobj->encoding == REDIS_ENCODING_INTSET);\n\n    if (enc == REDIS_ENCODING_HT) {\n        int64_t intele;\n        // 创建新字典\n        dict *d = dictCreate(&setDictType,NULL);\n        robj *element;\n\n        /* Presize the dict to avoid rehashing */\n        // 预先扩展空间\n        dictExpand(d,intsetLen(setobj->ptr));\n\n        /* To add the elements we extract integers and create redis objects */\n        // 遍历集合，并将元素添加到字典中\n        si = setTypeInitIterator(setobj);\n        while (setTypeNext(si,NULL,&intele) != -1) {\n            element = createStringObjectFromLongLong(intele);\n            redisAssertWithInfo(NULL,element,dictAdd(d,element,NULL) == DICT_OK);\n        }\n        setTypeReleaseIterator(si);\n\n        // 更新集合的编码\n        setobj->encoding = REDIS_ENCODING_HT;\n        zfree(setobj->ptr);\n        // 更新集合的值对象\n        setobj->ptr = d;\n    } else {\n        redisPanic(\"Unsupported set conversion\");\n    }\n}\n\nvoid saddCommand(redisClient *c) {\n    robj *set;\n    int j, added = 0;\n\n    // 取出集合对象\n    set = lookupKeyWrite(c->db,c->argv[1]);\n\n    // 对象不存在，创建一个新的，并将它关联到数据库\n    if (set == NULL) {\n        set = setTypeCreate(c->argv[2]);\n        dbAdd(c->db,c->argv[1],set);\n\n    // 对象存在，检查类型\n    } else {\n        if (set->type != REDIS_SET) {\n            addReply(c,shared.wrongtypeerr);\n            return;\n        }\n    }\n\n    // 将所有输入元素添加到集合中\n    for (j = 2; j < c->argc; j++) {\n        c->argv[j] = tryObjectEncoding(c->argv[j]);\n        // 只有元素未存在于集合时，才算一次成功添加\n        if (setTypeAdd(set,c->argv[j])) added++;\n    }\n\n    // 如果有至少一个元素被成功添加，那么执行以下程序\n    if (added) {\n        // 发送键修改信号\n        signalModifiedKey(c->db,c->argv[1]);\n        // 发送事件通知\n        notifyKeyspaceEvent(REDIS_NOTIFY_SET,\"sadd\",c->argv[1],c->db->id);\n    }\n\n    // 将数据库设为脏\n    server.dirty += added;\n\n    // 返回添加元素的数量\n    addReplyLongLong(c,added);\n}\n\nvoid sremCommand(redisClient *c) {\n    robj *set;\n    int j, deleted = 0, keyremoved = 0;\n\n    // 取出集合对象\n    if ((set = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL ||\n        checkType(c,set,REDIS_SET)) return;\n\n    // 删除输入的所有元素\n    for (j = 2; j < c->argc; j++) {\n        \n        // 只有元素在集合中时，才算一次成功删除\n        if (setTypeRemove(set,c->argv[j])) {\n            deleted++;\n            // 如果集合已经为空，那么删除集合对象\n            if (setTypeSize(set) == 0) {\n                dbDelete(c->db,c->argv[1]);\n                keyremoved = 1;\n                break;\n            }\n        }\n    }\n\n    // 如果有至少一个元素被成功删除，那么执行以下程序\n    if (deleted) {\n        // 发送键修改信号\n        signalModifiedKey(c->db,c->argv[1]);\n        // 发送事件通知\n        notifyKeyspaceEvent(REDIS_NOTIFY_SET,\"srem\",c->argv[1],c->db->id);\n        // 发送事件通知\n        if (keyremoved)\n            notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,\"del\",c->argv[1],\n                                c->db->id);\n        // 将数据库设为脏\n        server.dirty += deleted;\n    }\n    \n    // 将被成功删除元素的数量作为回复\n    addReplyLongLong(c,deleted);\n}\n\nvoid smoveCommand(redisClient *c) {\n    robj *srcset, *dstset, *ele;\n\n    // 取出源集合\n    srcset = lookupKeyWrite(c->db,c->argv[1]);\n    // 取出目标集合\n    dstset = lookupKeyWrite(c->db,c->argv[2]);\n\n    // 编码元素\n    ele = c->argv[3] = tryObjectEncoding(c->argv[3]);\n\n    /* If the source key does not exist return 0 */\n    // 源集合不存在，直接返回\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    // 如果源集合的类型错误\n    // 或者目标集合存在、但是类型错误\n    // 那么直接返回\n    if (checkType(c,srcset,REDIS_SET) ||\n        (dstset && checkType(c,dstset,REDIS_SET))) return;\n\n    /* If srcset and dstset are equal, SMOVE is a no-op */\n    // 如果源集合和目标集合相等，那么直接返回\n    if (srcset == dstset) {\n        addReply(c,shared.cone);\n        return;\n    }\n\n    /* If the element cannot be removed from the src set, return 0. */\n    // 从源集合中移除目标元素\n    // 如果目标元素不存在于源集合中，那么直接返回\n    if (!setTypeRemove(srcset,ele)) {\n        addReply(c,shared.czero);\n        return;\n    }\n\n    // 发送事件通知\n    notifyKeyspaceEvent(REDIS_NOTIFY_SET,\"srem\",c->argv[1],c->db->id);\n\n    /* Remove the src set from the database when empty */\n    // 如果源集合已经为空，那么将它从数据库中删除\n    if (setTypeSize(srcset) == 0) {\n        // 删除集合对象\n        dbDelete(c->db,c->argv[1]);\n        // 发送事件通知\n        notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,\"del\",c->argv[1],c->db->id);\n    }\n\n    // 发送键修改信号\n    signalModifiedKey(c->db,c->argv[1]);\n    signalModifiedKey(c->db,c->argv[2]);\n\n    // 将数据库设为脏\n    server.dirty++;\n\n    /* Create the destination set when it doesn't exist */\n    // 如果目标集合不存在，那么创建它\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    // 将元素添加到目标集合\n    if (setTypeAdd(dstset,ele)) {\n        // 将数据库设为脏\n        server.dirty++;\n        // 发送事件通知\n        notifyKeyspaceEvent(REDIS_NOTIFY_SET,\"sadd\",c->argv[2],c->db->id);\n    }\n\n    // 回复 1 \n    addReply(c,shared.cone);\n}\n\nvoid sismemberCommand(redisClient *c) {\n    robj *set;\n\n    // 取出集合对象\n    if ((set = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||\n        checkType(c,set,REDIS_SET)) return;\n\n    // 编码输入元素\n    c->argv[2] = tryObjectEncoding(c->argv[2]);\n\n    // 检查是否存在\n    if (setTypeIsMember(set,c->argv[2]))\n        addReply(c,shared.cone);\n    else\n        addReply(c,shared.czero);\n}\n\nvoid scardCommand(redisClient *c) {\n    robj *o;\n\n    // 取出集合\n    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||\n        checkType(c,o,REDIS_SET)) return;\n\n    // 返回集合的基数\n    addReplyLongLong(c,setTypeSize(o));\n}\n\nvoid spopCommand(redisClient *c) {\n    robj *set, *ele, *aux;\n    int64_t llele;\n    int encoding;\n\n    // 取出集合\n    if ((set = lookupKeyWriteOrReply(c,c->argv[1],shared.nullbulk)) == NULL ||\n        checkType(c,set,REDIS_SET)) return;\n\n    // 从集合中随机取出一个元素\n    encoding = setTypeRandomElement(set,&ele,&llele);\n\n    // 将被取出元素从集合中删除\n    if (encoding == REDIS_ENCODING_INTSET) {\n        ele = createStringObjectFromLongLong(llele);\n        set->ptr = intsetRemove(set->ptr,llele,NULL);\n    } else {\n        incrRefCount(ele);\n        setTypeRemove(set,ele);\n    }\n\n    // 发送事件通知\n    notifyKeyspaceEvent(REDIS_NOTIFY_SET,\"spop\",c->argv[1],c->db->id);\n\n    /* Replicate/AOF this command as an SREM operation */\n    // 将这个命令当作 SREM 来传播，防止产生有害的随机性，导致数据不一致\n    // （不同的服务器随机删除不同的元素）\n    aux = createStringObject(\"SREM\",4);\n    rewriteClientCommandVector(c,3,aux,c->argv[1],ele);\n    decrRefCount(ele);\n    decrRefCount(aux);\n\n    // 返回回复\n    addReplyBulk(c,ele);\n\n    // 如果集合已经为空，那么从数据库中删除它\n    if (setTypeSize(set) == 0) {\n        // 删除集合\n        dbDelete(c->db,c->argv[1]);\n        // 发送事件通知\n        notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,\"del\",c->argv[1],c->db->id);\n    }\n\n    // 发送键修改信号\n    signalModifiedKey(c->db,c->argv[1]);\n\n    // 将数据库设为脏\n    server.dirty++;\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 * 实现 SRANDMEMBER key <count> 变种，\n * 原本的 SRANDMEMBER key 由 srandmemberCommand() 实现\n */\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 *\n * 如果 count 参数乘以这个常量所得的积，比集合的基数要大，\n * 那么程序就不使用“删除元素”的策略。\n *\n * 更多信息请参考后面的函数定义。\n */\n#define SRANDMEMBER_SUB_STRATEGY_MUL 3\n\nvoid srandmemberWithCountCommand(redisClient *c) {\n    long l;\n    unsigned long count, size;\n\n    // 默认在集合中不包含重复元素\n    int uniq = 1;\n\n    robj *set, *ele;\n    int64_t llele;\n    int encoding;\n\n    dict *d;\n\n    // 取出 l 参数\n    if (getLongFromObjectOrReply(c,c->argv[2],&l,NULL) != REDIS_OK) return;\n    if (l >= 0) {\n        // l 为正数，表示返回元素各不相同\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        // 如果 l 为负数，那么表示返回的结果中可以有重复元素\n        count = -l;\n        uniq = 0;\n    }\n\n    // 取出集合对象\n    if ((set = lookupKeyReadOrReply(c,c->argv[1],shared.emptymultibulk))\n        == NULL || checkType(c,set,REDIS_SET)) return;\n    // 取出集合基数\n    size = setTypeSize(set);\n\n    /* If count is zero, serve it ASAP to avoid special cases later. */\n    // count 为 0 ，直接返回\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     *\n     * 情形 1：count 为负数，结果集可以带有重复元素\n     * 直接从集合中取出并返回 N 个随机元素就可以了\n     *\n     * 这种情形不需要额外的结构来保存结果集\n     */\n    if (!uniq) {\n        addReplyMultiBulkLen(c,count);\n\n        while(count--) {\n            // 取出随机元素\n            encoding = setTypeRandomElement(set,&ele,&llele);\n            if (encoding == REDIS_ENCODING_INTSET) {\n                addReplyBulkLongLong(c,llele);\n            } else {\n                addReplyBulk(c,ele);\n            }\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     *\n     * 如果 count 比集合的基数要大，那么直接返回整个集合\n     */\n    if (count >= size) {\n        sunionDiffGenericCommand(c,c->argv+1,1,NULL,REDIS_OP_UNION);\n        return;\n    }\n\n    /* For CASE 3 and CASE 4 we need an auxiliary dictionary. */\n    // 对于情形 3 和情形 4 ，需要一个额外的字典\n    d = dictCreate(&setDictType,NULL);\n\n    /* CASE 3:\n     * \n     * 情形 3：\n     *\n     * The number of elements inside the set is not greater than\n     * SRANDMEMBER_SUB_STRATEGY_MUL times the number of requested elements.\n     *\n     * count 参数乘以 SRANDMEMBER_SUB_STRATEGY_MUL 的积比集合的基数要大。\n     *\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     * 在这种情况下，程序创建一个集合的副本，\n     * 并从集合中删除元素，直到集合的基数等于 count 参数指定的数量为止。\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     *\n     * 使用这种做法的原因是，当 count 的数量接近于集合的基数时，\n     * 从集合中随机取出 count 个参数的方法是非常低效的。\n     */\n    if (count*SRANDMEMBER_SUB_STRATEGY_MUL > size) {\n        setTypeIterator *si;\n\n        /* Add all the elements into the temporary dictionary. */\n        // 遍历集合，将所有元素添加到临时字典中\n        si = setTypeInitIterator(set);\n        while((encoding = setTypeNext(si,&ele,&llele)) != -1) {\n            int retval = DICT_ERR;\n\n            // 为元素创建对象，并添加到字典中\n            if (encoding == REDIS_ENCODING_INTSET) {\n                retval = dictAdd(d,createStringObjectFromLongLong(llele),NULL);\n            } else {\n                retval = dictAdd(d,dupStringObject(ele),NULL);\n            }\n            redisAssert(retval == DICT_OK);\n            /* 上面的代码可以重构为\n            \n            robj *elem_obj;\n            if (encoding == REDIS_ENCODING_INTSET) {\n                elem_obj = createStringObjectFromLongLong(...)\n            } else if () {\n                ...\n            } else if () {\n                ...\n            }\n\n            redisAssert(dictAdd(d, elem_obj) == DICT_OK)\n\n            */\n        }\n        setTypeReleaseIterator(si);\n        redisAssert(dictSize(d) == size);\n\n        /* Remove random elements to reach the right count. */\n        // 随机删除元素，直到集合基数等于 count 参数的值\n        while(size > count) {\n            dictEntry *de;\n\n            // 取出并删除随机元素\n            de = dictGetRandomKey(d);\n            dictDelete(d,dictGetKey(de));\n\n            size--;\n        }\n    }\n    \n    /* CASE 4: We have a big set compared to the requested number of elements.\n     *\n     * 情形 4 ： count 参数要比集合基数小很多。\n     *\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     *\n     * 在这种情况下，我们可以直接从集合中随机地取出元素，\n     * 并将它添加到结果集合中，直到结果集的基数等于 count 为止。\n     */\n    else {\n        unsigned long added = 0;\n\n        while(added < count) {\n\n            // 随机地从目标集合中取出元素\n            encoding = setTypeRandomElement(set,&ele,&llele);\n\n            // 将元素转换为对象\n            if (encoding == REDIS_ENCODING_INTSET) {\n                ele = createStringObjectFromLongLong(llele);\n            } else {\n                ele = dupStringObject(ele);\n            }\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            // 尝试将元素添加到字典中\n            // dictAdd 只有在元素不存在于字典时，才会返回 1\n            // 如果如果结果集已经有同样的元素，那么程序会执行 else 部分\n            // 只有元素不存在于结果集时，添加才会成功\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        // 情形 3 和 4 ：将结果集回复给客户端\n        dictIterator *di;\n        dictEntry *de;\n\n        addReplyMultiBulkLen(c,count);\n        // 遍历结果集元素\n        di = dictGetIterator(d);\n        while((de = dictNext(di)) != NULL)\n            // 回复\n            addReplyBulk(c,dictGetKey(de));\n        dictReleaseIterator(di);\n        dictRelease(d);\n    }\n}\n\nvoid srandmemberCommand(redisClient *c) {\n    robj *set, *ele;\n    int64_t llele;\n    int encoding;\n\n    // 如果带有 count 参数，那么调用 srandmemberWithCountCommand 来处理\n    if (c->argc == 3) {\n        srandmemberWithCountCommand(c);\n        return;\n\n    // 参数错误\n    } else if (c->argc > 3) {\n        addReply(c,shared.syntaxerr);\n        return;\n    }\n\n    // 随机取出单个元素就可以了\n\n    // 取出集合对象\n    if ((set = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL ||\n        checkType(c,set,REDIS_SET)) return;\n\n    // 随机取出一个元素\n    encoding = setTypeRandomElement(set,&ele,&llele);\n    // 回复\n    if (encoding == REDIS_ENCODING_INTSET) {\n        addReplyBulkLongLong(c,llele);\n    } else {\n        addReplyBulk(c,ele);\n    }\n}\n\n/*\n * 计算集合 s1 的基数减去集合 s2 的基数之差\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. \n *\n * 计算集合 s2 的基数减去集合 s1 的基数之差\n */\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(redisClient *c, robj **setkeys, unsigned long setnum, robj *dstkey) {\n\n    // 集合数组\n    robj **sets = zmalloc(sizeof(robj*)*setnum);\n\n    setTypeIterator *si;\n    robj *eleobj, *dstset = NULL;\n    int64_t intobj;\n    void *replylen = NULL;\n    unsigned long j, cardinality = 0;\n    int encoding;\n\n    for (j = 0; j < setnum; j++) {\n\n        // 取出对象\n        // 第一次执行时，取出的是 dest 集合\n        // 之后执行时，取出的都是 source 集合\n        robj *setobj = dstkey ?\n            lookupKeyWrite(c->db,setkeys[j]) :\n            lookupKeyRead(c->db,setkeys[j]);\n\n        // 对象不存在，放弃执行，进行清理\n        if (!setobj) {\n            zfree(sets);\n            if (dstkey) {\n                if (dbDelete(c->db,dstkey)) {\n                    signalModifiedKey(c->db,dstkey);\n                    server.dirty++;\n                }\n                addReply(c,shared.czero);\n            } else {\n                addReply(c,shared.emptymultibulk);\n            }\n            return;\n        }\n        \n        // 检查对象的类型\n        if (checkType(c,setobj,REDIS_SET)) {\n            zfree(sets);\n            return;\n        }\n\n        // 将数组指针指向集合对象\n        sets[j] = setobj;\n    }\n\n    /* Sort sets from the smallest to largest, this will improve our\n     * algorithm's performance */\n    // 按基数对集合进行排序，这样提升算法的效率\n    qsort(sets,setnum,sizeof(robj*),qsortCompareSetsByCardinality);\n\n    /* The first thing we should output is the total number of elements...\n     * since this is a multi-bulk write, but at this stage we don't know\n     * the intersection set size, so we use a trick, append an empty object\n     * to the output list and save the pointer to later modify it with the\n     * right length */\n    // 因为不知道结果集会有多少个元素，所有没有办法直接设置回复的数量\n    // 这里使用了一个小技巧，直接使用一个 BUFF 列表，\n    // 然后将之后的回复都添加到列表中\n    if (!dstkey) {\n        replylen = addDeferredMultiBulkLength(c);\n    } else {\n        /* If we have a target key where to store the resulting set\n         * create this key with an empty set inside */\n        dstset = createIntsetObject();\n    }\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    // 遍历基数最小的第一个集合\n    // 并将它的元素和所有其他集合进行对比\n    // 如果有至少一个集合不包含这个元素，那么这个元素不属于交集\n    si = setTypeInitIterator(sets[0]);\n    while((encoding = setTypeNext(si,&eleobj,&intobj)) != -1) {\n        // 遍历其他集合，检查元素是否在这些集合中存在\n        for (j = 1; j < setnum; j++) {\n\n            // 跳过第一个集合，因为它是结果集的起始值\n            if (sets[j] == sets[0]) continue;\n\n            // 元素的编码为 INTSET \n            // 在其他集合中查找这个对象是否存在\n            if (encoding == REDIS_ENCODING_INTSET) {\n                /* intset with intset is simple... and fast */\n                if (sets[j]->encoding == REDIS_ENCODING_INTSET &&\n                    !intsetFind((intset*)sets[j]->ptr,intobj))\n                {\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 (sets[j]->encoding == REDIS_ENCODING_HT) {\n                    eleobj = createStringObjectFromLongLong(intobj);\n                    if (!setTypeIsMember(sets[j],eleobj)) {\n                        decrRefCount(eleobj);\n                        break;\n                    }\n                    decrRefCount(eleobj);\n                }\n\n            // 元素的编码为 字典\n            // 在其他集合中查找这个对象是否存在\n            } else if (encoding == REDIS_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 == REDIS_ENCODING_INT &&\n                    sets[j]->encoding == REDIS_ENCODING_INTSET &&\n                    !intsetFind((intset*)sets[j]->ptr,(long)eleobj->ptr))\n                {\n                    break;\n                /* else... object to object check is easy as we use the\n                 * type agnostic API here. */\n                } else if (!setTypeIsMember(sets[j],eleobj)) {\n                    break;\n                }\n            }\n        }\n\n        /* Only take action when all sets contain the member */\n        // 如果所有集合都带有目标元素的话，那么执行以下代码\n        if (j == setnum) {\n\n            // SINTER 命令，直接返回结果集元素\n            if (!dstkey) {\n                if (encoding == REDIS_ENCODING_HT)\n                    addReplyBulk(c,eleobj);\n                else\n                    addReplyBulkLongLong(c,intobj);\n                cardinality++;\n\n            // SINTERSTORE 命令，将结果添加到结果集中\n            } else {\n                if (encoding == REDIS_ENCODING_INTSET) {\n                    eleobj = createStringObjectFromLongLong(intobj);\n                    setTypeAdd(dstset,eleobj);\n                    decrRefCount(eleobj);\n                } else {\n                    setTypeAdd(dstset,eleobj);\n                }\n            }\n        }\n    }\n    setTypeReleaseIterator(si);\n\n    // SINTERSTORE 命令，将结果集关联到数据库\n    if (dstkey) {\n        /* Store the resulting set into the target, if the intersection\n         * is not an empty set. */\n        // 删除现在可能有的 dstkey\n        int deleted = dbDelete(c->db,dstkey);\n\n        // 如果结果集非空，那么将它关联到数据库中\n        if (setTypeSize(dstset) > 0) {\n            dbAdd(c->db,dstkey,dstset);\n            addReplyLongLong(c,setTypeSize(dstset));\n            notifyKeyspaceEvent(REDIS_NOTIFY_SET,\"sinterstore\",\n                dstkey,c->db->id);\n        } else {\n            decrRefCount(dstset);\n            addReply(c,shared.czero);\n            if (deleted)\n                notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,\"del\",\n                    dstkey,c->db->id);\n        }\n\n        signalModifiedKey(c->db,dstkey);\n\n        server.dirty++;\n\n    // SINTER 命令，回复结果集的基数\n    } else {\n        setDeferredMultiBulkLength(c,replylen,cardinality);\n    }\n\n    zfree(sets);\n}\n\nvoid sinterCommand(redisClient *c) {\n    sinterGenericCommand(c,c->argv+1,c->argc-1,NULL);\n}\n\nvoid sinterstoreCommand(redisClient *c) {\n    sinterGenericCommand(c,c->argv+2,c->argc-2,c->argv[1]);\n}\n\n/*\n * 命令的类型\n */\n#define REDIS_OP_UNION 0\n#define REDIS_OP_DIFF 1\n#define REDIS_OP_INTER 2\n\nvoid sunionDiffGenericCommand(redisClient *c, robj **setkeys, int setnum, robj *dstkey, int op) {\n\n    // 集合数组\n    robj **sets = zmalloc(sizeof(robj*)*setnum);\n\n    setTypeIterator *si;\n    robj *ele, *dstset = NULL;\n    int j, cardinality = 0;\n    int diff_algo = 1;\n\n    // 取出所有集合对象，并添加到集合数组中\n    for (j = 0; j < setnum; j++) {\n        robj *setobj = dstkey ?\n            lookupKeyWrite(c->db,setkeys[j]) :\n            lookupKeyRead(c->db,setkeys[j]);\n\n        // 不存在的集合当作 NULL 来处理\n        if (!setobj) {\n            sets[j] = NULL;\n            continue;\n        }\n\n        // 有对象不是集合，停止执行，进行清理\n        if (checkType(c,setobj,REDIS_SET)) {\n            zfree(sets);\n            return;\n        }\n\n        // 记录对象\n        sets[j] = setobj;\n    }\n\n    /* Select what DIFF algorithm to use.\n     *\n     * 选择使用那个算法来执行计算\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     * 算法 1 的复杂度为 O(N*M) ，其中 N 为第一个集合的基数，\n     * 而 M 则为其他集合的数量。\n     *\n     * Algorithm 2 is O(N) where N is the total number of elements in all\n     * the sets.\n     *\n     * 算法 2 的复杂度为 O(N) ，其中 N 为所有集合中的元素数量总数。\n     *\n     * We compute what is the best bet with the current input here. \n     *\n     * 程序通过考察输入来决定使用那个算法\n     */\n    if (op == REDIS_OP_DIFF && sets[0]) {\n        long long algo_one_work = 0, algo_two_work = 0;\n\n        // 遍历所有集合\n        for (j = 0; j < setnum; j++) {\n            if (sets[j] == NULL) continue;\n\n            // 计算 setnum 乘以 sets[0] 的基数之积\n            algo_one_work += setTypeSize(sets[0]);\n            // 计算所有集合的基数之和\n            algo_two_work += setTypeSize(sets[j]);\n        }\n\n        /* Algorithm 1 has better constant times and performs less operations\n         * if there are elements in common. Give it some advantage. */\n        // 算法 1 的常数比较低，优先考虑算法 1\n        algo_one_work /= 2;\n        diff_algo = (algo_one_work <= algo_two_work) ? 1 : 2;\n\n        if (diff_algo == 1 && setnum > 1) {\n            /* With algorithm 1 it is better to order the sets to subtract\n             * by decreasing size, so that we are more likely to find\n             * duplicated elements ASAP. */\n            // 如果使用的是算法 1 ，那么最好对 sets[0] 以外的其他集合进行排序\n            // 这样有助于优化算法的性能\n            qsort(sets+1,setnum-1,sizeof(robj*),\n                qsortCompareSetsByRevCardinality);\n        }\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     *\n     * 使用一个临时集合来保存结果集，如果程序执行的是 SUNIONSTORE 命令，\n     * 那么这个结果将会成为将来的集合值对象。\n     */\n    dstset = createIntsetObject();\n\n    // 执行的是并集计算\n    if (op == REDIS_OP_UNION) {\n        /* Union is trivial, just add every element of every set to the\n         * temporary set. */\n        // 遍历所有集合，将元素添加到结果集里就可以了\n        for (j = 0; j < setnum; j++) {\n            if (!sets[j]) continue; /* non existing keys are like empty sets */\n\n            si = setTypeInitIterator(sets[j]);\n            while((ele = setTypeNextObject(si)) != NULL) {\n                // setTypeAdd 只在集合不存在时，才会将元素添加到集合，并返回 1 \n                if (setTypeAdd(dstset,ele)) cardinality++;\n                decrRefCount(ele);\n            }\n            setTypeReleaseIterator(si);\n        }\n\n    // 执行的是差集计算，并且使用算法 1\n    } else if (op == REDIS_OP_DIFF && sets[0] && diff_algo == 1) {\n        /* DIFF Algorithm 1:\n         *\n         * 差集算法 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         * 程序遍历 sets[0] 集合中的所有元素，\n         * 并将这个元素和其他集合的所有元素进行对比，\n         * 只有这个元素不存在于其他所有集合时，\n         * 才将这个元素添加到结果集。\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         *\n         * 这个算法执行最多 N*M 步， N 是第一个集合的基数，\n         * 而 M 是其他集合的数量。\n         */\n        si = setTypeInitIterator(sets[0]);\n        while((ele = setTypeNextObject(si)) != NULL) {\n\n            // 检查元素在其他集合是否存在\n            for (j = 1; j < setnum; j++) {\n                if (!sets[j]) continue; /* no key is an empty set. */\n                if (sets[j] == sets[0]) break; /* same set! */\n                if (setTypeIsMember(sets[j],ele)) break;\n            }\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            decrRefCount(ele);\n        }\n        setTypeReleaseIterator(si);\n\n    // 执行的是差集计算，并且使用算法 2\n    } else if (op == REDIS_OP_DIFF && sets[0] && diff_algo == 2) {\n        /* DIFF Algorithm 2:\n         *\n         * 差集算法 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         * 将 sets[0] 的所有元素都添加到结果集中，\n         * 然后遍历其他所有集合，将相同的元素从结果集中删除。\n         *\n         * This is O(N) where N is the sum of all the elements in every set. \n         *\n         * 算法复杂度为 O(N) ，N 为所有集合的基数之和。\n         */\n        for (j = 0; j < setnum; j++) {\n            if (!sets[j]) continue; /* non existing keys are like empty sets */\n\n            si = setTypeInitIterator(sets[j]);\n            while((ele = setTypeNextObject(si)) != NULL) {\n                // sets[0] 时，将所有元素添加到集合\n                if (j == 0) {\n                    if (setTypeAdd(dstset,ele)) cardinality++;\n                // 不是 sets[0] 时，将所有集合从结果集中移除\n                } else {\n                    if (setTypeRemove(dstset,ele)) cardinality--;\n                }\n                decrRefCount(ele);\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) break;\n        }\n    }\n\n    /* Output the content of the resulting set, if not in STORE mode */\n    // 执行的是 SDIFF 或者 SUNION\n    // 打印结果集中的所有元素\n    if (!dstkey) {\n        addReplyMultiBulkLen(c,cardinality);\n\n        // 遍历并回复结果集中的元素\n        si = setTypeInitIterator(dstset);\n        while((ele = setTypeNextObject(si)) != NULL) {\n            addReplyBulk(c,ele);\n            decrRefCount(ele);\n        }\n        setTypeReleaseIterator(si);\n\n        decrRefCount(dstset);\n\n    // 执行的是 SDIFFSTORE 或者 SUNIONSTORE\n    } else {\n        /* If we have a target key where to store the resulting set\n         * create this key with the result set inside */\n        // 现删除现在可能有的 dstkey\n        int deleted = dbDelete(c->db,dstkey);\n\n        // 如果结果集不为空，将它关联到数据库中\n        if (setTypeSize(dstset) > 0) {\n            dbAdd(c->db,dstkey,dstset);\n            // 返回结果集的基数\n            addReplyLongLong(c,setTypeSize(dstset));\n            notifyKeyspaceEvent(REDIS_NOTIFY_SET,\n                op == REDIS_OP_UNION ? \"sunionstore\" : \"sdiffstore\",\n                dstkey,c->db->id);\n\n        // 结果集为空\n        } else {\n            decrRefCount(dstset);\n            // 返回 0 \n            addReply(c,shared.czero);\n            if (deleted)\n                notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,\"del\",\n                    dstkey,c->db->id);\n        }\n\n        signalModifiedKey(c->db,dstkey);\n\n        server.dirty++;\n    }\n\n    zfree(sets);\n}\n\nvoid sunionCommand(redisClient *c) {\n    sunionDiffGenericCommand(c,c->argv+1,c->argc-1,NULL,REDIS_OP_UNION);\n}\n\nvoid sunionstoreCommand(redisClient *c) {\n    sunionDiffGenericCommand(c,c->argv+2,c->argc-2,c->argv[1],REDIS_OP_UNION);\n}\n\nvoid sdiffCommand(redisClient *c) {\n    sunionDiffGenericCommand(c,c->argv+1,c->argc-1,NULL,REDIS_OP_DIFF);\n}\n\nvoid sdiffstoreCommand(redisClient *c) {\n    sunionDiffGenericCommand(c,c->argv+2,c->argc-2,c->argv[1],REDIS_OP_DIFF);\n}\n\nvoid sscanCommand(redisClient *c) {\n    robj *set;\n    unsigned long cursor;\n\n    if (parseScanCursorOrReply(c,c->argv[2],&cursor) == REDIS_ERR) return;\n    if ((set = lookupKeyReadOrReply(c,c->argv[1],shared.emptyscan)) == NULL ||\n        checkType(c,set,REDIS_SET)) return;\n    scanGenericCommand(c,set,cursor);\n}\n"
  },
  {
    "path": "src/t_string.c",
    "content": "/*\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 \"redis.h\"\n#include <math.h> /* isnan(), isinf() */\n\n/*-----------------------------------------------------------------------------\n * String Commands\n *----------------------------------------------------------------------------*/\n\n/*\n * 检查给定字符串长度 len 是否超过限制值 512 MB\n *\n * 超过返回 REDIS_ERR ，未超过返回 REDIS_OK\n *\n * T = O(1)\n */\nstatic int checkStringLength(redisClient *c, long long size) {\n\n    if (size > 512*1024*1024) {\n        addReplyError(c,\"string exceeds maximum allowed size (512MB)\");\n        return REDIS_ERR;\n    }\n\n    return REDIS_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 * setGenericCommand() 函数实现了 SET 、 SETEX 、 PSETEX 和 SETNX 命令。\n *\n * 'flags' changes the behavior of the command (NX or XX, see belove).\n *\n * flags 参数的值可以是 NX 或 XX ，它们的意义请见下文。\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 * expire 定义了 Redis 对象的过期时间。\n *\n * 而这个过期时间的格式由 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 * ok_reply 和 abort_reply 决定了命令回复的内容，\n * NX 参数和 XX 参数也会改变回复。\n *\n * If ok_reply is NULL \"+OK\" is used.\n * If abort_reply is NULL, \"$-1\" is used. \n *\n * 如果 ok_reply 为 NULL ，那么 \"+OK\" 被返回。\n * 如果 abort_reply 为 NULL ，那么 \"$-1\" 被返回。\n */\n\n#define REDIS_SET_NO_FLAGS 0\n#define REDIS_SET_NX (1<<0)     /* Set if key not exists. */\n#define REDIS_SET_XX (1<<1)     /* Set if key exists. */\n\nvoid setGenericCommand(redisClient *c, int flags, robj *key, robj *val, robj *expire, int unit, robj *ok_reply, robj *abort_reply) {\n\n    long long milliseconds = 0; /* initialized to avoid any harmness warning */\n\n    // 取出过期时间\n    if (expire) {\n\n        // 取出 expire 参数的值\n        // T = O(N)\n        if (getLongLongFromObjectOrReply(c, expire, &milliseconds, NULL) != REDIS_OK)\n            return;\n\n        // expire 参数的值不正确时报错\n        if (milliseconds <= 0) {\n            addReplyError(c,\"invalid expire time in SETEX\");\n            return;\n        }\n\n        // 不论输入的过期时间是秒还是毫秒\n        // Redis 实际都以毫秒的形式保存过期时间\n        // 如果输入的过期时间为秒，那么将它转换为毫秒\n        if (unit == UNIT_SECONDS) milliseconds *= 1000;\n    }\n\n    // 如果设置了 NX 或者 XX 参数，那么检查条件是否不符合这两个设置\n    // 在条件不符合时报错，报错的内容由 abort_reply 参数决定\n    if ((flags & REDIS_SET_NX && lookupKeyWrite(c->db,key) != NULL) ||\n        (flags & REDIS_SET_XX && lookupKeyWrite(c->db,key) == NULL))\n    {\n        addReply(c, abort_reply ? abort_reply : shared.nullbulk);\n        return;\n    }\n\n    // 将键值关联到数据库\n    setKey(c->db,key,val);\n\n    // 将数据库设为脏\n    server.dirty++;\n\n    // 为键设置过期时间\n    if (expire) setExpire(c->db,key,mstime()+milliseconds);\n\n    // 发送事件通知\n    notifyKeyspaceEvent(REDIS_NOTIFY_STRING,\"set\",key,c->db->id);\n\n    // 发送事件通知\n    if (expire) notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,\n        \"expire\",key,c->db->id);\n\n    // 设置成功，向客户端发送回复\n    // 回复的内容由 ok_reply 决定\n    addReply(c, ok_reply ? ok_reply : shared.ok);\n}\n\n/* SET key value [NX] [XX] [EX <seconds>] [PX <milliseconds>] */\nvoid setCommand(redisClient *c) {\n    int j;\n    robj *expire = NULL;\n    int unit = UNIT_SECONDS;\n    int flags = REDIS_SET_NO_FLAGS;\n\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 |= REDIS_SET_NX;\n        } else if ((a[0] == 'x' || a[0] == 'X') &&\n                   (a[1] == 'x' || a[1] == 'X') && a[2] == '\\0') {\n            flags |= REDIS_SET_XX;\n        } else if ((a[0] == 'e' || a[0] == 'E') &&\n                   (a[1] == 'x' || a[1] == 'X') && a[2] == '\\0' && next) {\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' && next) {\n            unit = UNIT_MILLISECONDS;\n            expire = next;\n            j++;\n        } else {\n            addReply(c,shared.syntaxerr);\n            return;\n        }\n    }\n\n    // 尝试对值对象进行编码\n    c->argv[2] = tryObjectEncoding(c->argv[2]);\n\n    setGenericCommand(c,flags,c->argv[1],c->argv[2],expire,unit,NULL,NULL);\n}\n\nvoid setnxCommand(redisClient *c) {\n    c->argv[2] = tryObjectEncoding(c->argv[2]);\n    setGenericCommand(c,REDIS_SET_NX,c->argv[1],c->argv[2],NULL,0,shared.cone,shared.czero);\n}\n\nvoid setexCommand(redisClient *c) {\n    c->argv[3] = tryObjectEncoding(c->argv[3]);\n    setGenericCommand(c,REDIS_SET_NO_FLAGS,c->argv[1],c->argv[3],c->argv[2],UNIT_SECONDS,NULL,NULL);\n}\n\nvoid psetexCommand(redisClient *c) {\n    c->argv[3] = tryObjectEncoding(c->argv[3]);\n    setGenericCommand(c,REDIS_SET_NO_FLAGS,c->argv[1],c->argv[3],c->argv[2],UNIT_MILLISECONDS,NULL,NULL);\n}\n\nint getGenericCommand(redisClient *c) {\n    robj *o;\n\n    // 尝试从数据库中取出键 c->argv[1] 对应的值对象\n    // 如果键不存在时，向客户端发送回复信息，并返回 NULL\n    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL)\n        return REDIS_OK;\n\n    // 值对象存在，检查它的类型\n    if (o->type != REDIS_STRING) {\n        // 类型错误\n        addReply(c,shared.wrongtypeerr);\n        return REDIS_ERR;\n    } else {\n        // 类型正确，向客户端返回对象的值\n        addReplyBulk(c,o);\n        return REDIS_OK;\n    }\n}\n\nvoid getCommand(redisClient *c) {\n    getGenericCommand(c);\n}\n\nvoid getsetCommand(redisClient *c) {\n\n    // 取出并返回键的值对象\n    if (getGenericCommand(c) == REDIS_ERR) return;\n\n    // 编码键的新值 c->argv[2]\n    c->argv[2] = tryObjectEncoding(c->argv[2]);\n\n    // 将数据库中关联键 c->argv[1] 和新值对象 c->argv[2]\n    setKey(c->db,c->argv[1],c->argv[2]);\n\n    // 发送事件通知\n    notifyKeyspaceEvent(REDIS_NOTIFY_STRING,\"set\",c->argv[1],c->db->id);\n\n    // 将服务器设为脏\n    server.dirty++;\n}\n\nvoid setrangeCommand(redisClient *c) {\n    robj *o;\n    long offset;\n\n    sds value = c->argv[3]->ptr;\n\n    // 取出 offset 参数\n    if (getLongFromObjectOrReply(c,c->argv[2],&offset,NULL) != REDIS_OK)\n        return;\n\n    // 检查 offset 参数\n    if (offset < 0) {\n        addReplyError(c,\"offset is out of range\");\n        return;\n    }\n\n    // 取出键现在的值对象\n    o = lookupKeyWrite(c->db,c->argv[1]);\n    if (o == NULL) {\n\n        // 键不存在于数据库中。。。\n\n        /* Return 0 when setting nothing on a non-existing string */\n        // value 为空，没有什么可设置的，向客户端返回 0\n        if (sdslen(value) == 0) {\n            addReply(c,shared.czero);\n            return;\n        }\n\n        /* Return when the resulting string exceeds allowed size */\n        // 如果设置后的长度会超过 Redis 的限制的话\n        // 那么放弃设置，向客户端发送一个出错回复\n        if (checkStringLength(c,offset+sdslen(value)) != REDIS_OK)\n            return;\n\n        // 如果 value 没有问题，可以设置，那么创建一个空字符串值对象\n        // 并在数据库中关联键 c->argv[1] 和这个空字符串对象\n        o = createObject(REDIS_STRING,sdsempty());\n        dbAdd(c->db,c->argv[1],o);\n    } else {\n        size_t olen;\n\n        // 值对象存在。。。\n\n        /* Key exists, check type */\n        // 检查值对象的类型\n        if (checkType(c,o,REDIS_STRING))\n            return;\n\n        /* Return existing string length when setting nothing */\n        // 取出原有字符串的长度\n        olen = stringObjectLen(o);\n\n        // value 为空，没有什么可设置的，向客户端返回 0\n        if (sdslen(value) == 0) {\n            addReplyLongLong(c,olen);\n            return;\n        }\n\n        /* Return when the resulting string exceeds allowed size */\n        // 如果设置后的长度会超过 Redis 的限制的话\n        // 那么放弃设置，向客户端发送一个出错回复\n        if (checkStringLength(c,offset+sdslen(value)) != REDIS_OK)\n            return;\n\n        /* Create a copy when the object is shared or encoded. */\n        o = dbUnshareStringValue(c->db,c->argv[1],o);\n    }\n\n    // 这里的 sdslen(value) > 0 其实可以去掉\n    // 前面已经做了检测了\n    if (sdslen(value) > 0) {\n        // 扩展字符串值对象\n        o->ptr = sdsgrowzero(o->ptr,offset+sdslen(value));\n        // 将 value 复制到字符串中的指定的位置\n        memcpy((char*)o->ptr+offset,value,sdslen(value));\n\n        // 向数据库发送键被修改的信号\n        signalModifiedKey(c->db,c->argv[1]);\n\n        // 发送事件通知\n        notifyKeyspaceEvent(REDIS_NOTIFY_STRING,\n            \"setrange\",c->argv[1],c->db->id);\n\n        // 将服务器设为脏\n        server.dirty++;\n    }\n\n    // 设置成功，返回新的字符串值给客户端\n    addReplyLongLong(c,sdslen(o->ptr));\n}\n\nvoid getrangeCommand(redisClient *c) {\n    robj *o;\n    long start, end;\n    char *str, llbuf[32];\n    size_t strlen;\n\n    // 取出 start 参数\n    if (getLongFromObjectOrReply(c,c->argv[2],&start,NULL) != REDIS_OK)\n        return;\n\n    // 取出 end 参数\n    if (getLongFromObjectOrReply(c,c->argv[3],&end,NULL) != REDIS_OK)\n        return;\n\n    // 从数据库中查找键 c->argv[1] \n    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptybulk)) == NULL ||\n        checkType(c,o,REDIS_STRING)) return;\n\n    // 根据编码，对对象的值进行处理\n    if (o->encoding == REDIS_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    // 将负数索引转换为整数索引\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)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) {\n        // 处理索引范围为空的情况\n        addReply(c,shared.emptybulk);\n    } else {\n        // 向客户端返回给定范围内的字符串内容\n        addReplyBulkCBuffer(c,(char*)str+start,end-start+1);\n    }\n}\n\nvoid mgetCommand(redisClient *c) {\n    int j;\n\n    addReplyMultiBulkLen(c,c->argc-1);\n    // 查找并返回所有输入键的值\n    for (j = 1; j < c->argc; j++) {\n        // 查找键 c->argc[j] 的值\n        robj *o = lookupKeyRead(c->db,c->argv[j]);\n        if (o == NULL) {\n            // 值不存在，向客户端发送空回复\n            addReply(c,shared.nullbulk);\n        } else {\n            if (o->type != REDIS_STRING) {\n                // 值存在，但不是字符串类型\n                addReply(c,shared.nullbulk);\n            } else {\n                // 值存在，并且是字符串\n                addReplyBulk(c,o);\n            }\n        }\n    }\n}\n\nvoid msetGenericCommand(redisClient *c, int nx) {\n    int j, busykeys = 0;\n\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    // 如果 nx 参数为真，那么检查所有输入键在数据库中是否存在\n    // 只要有一个键是存在的，那么就向客户端发送空回复\n    // 并放弃执行接下来的设置操作\n    if (nx) {\n        for (j = 1; j < c->argc; j += 2) {\n            if (lookupKeyWrite(c->db,c->argv[j]) != NULL) {\n                busykeys++;\n            }\n        }\n        // 键存在\n        // 发送空白回复，并放弃执行接下来的设置操作\n        if (busykeys) {\n            addReply(c, shared.czero);\n            return;\n        }\n    }\n\n    // 设置所有键值对\n    for (j = 1; j < c->argc; j += 2) {\n\n        // 对值对象进行解码\n        c->argv[j+1] = tryObjectEncoding(c->argv[j+1]);\n\n        // 将键值对关联到数据库\n        // c->argc[j] 为键\n        // c->argc[j+1] 为值\n        setKey(c->db,c->argv[j],c->argv[j+1]);\n\n        // 发送事件通知\n        notifyKeyspaceEvent(REDIS_NOTIFY_STRING,\"set\",c->argv[j],c->db->id);\n    }\n\n    // 将服务器设为脏\n    server.dirty += (c->argc-1)/2;\n\n    // 设置成功\n    // MSET 返回 OK ，而 MSETNX 返回 1\n    addReply(c, nx ? shared.cone : shared.ok);\n}\n\nvoid msetCommand(redisClient *c) {\n    msetGenericCommand(c,0);\n}\n\nvoid msetnxCommand(redisClient *c) {\n    msetGenericCommand(c,1);\n}\n\nvoid incrDecrCommand(redisClient *c, long long incr) {\n    long long value, oldvalue;\n    robj *o, *new;\n\n    // 取出值对象\n    o = lookupKeyWrite(c->db,c->argv[1]);\n\n    // 检查对象是否存在，以及类型是否正确\n    if (o != NULL && checkType(c,o,REDIS_STRING)) return;\n\n    // 取出对象的整数值，并保存到 value 参数中\n    if (getLongLongFromObjectOrReply(c,o,&value,NULL) != REDIS_OK) return;\n\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        return;\n    }\n\n    // 进行加法计算，并将值保存到新的值对象中\n    // 然后用新的值对象替换原来的值对象\n    value += incr;\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\n    // 发送事件通知\n    notifyKeyspaceEvent(REDIS_NOTIFY_STRING,\"incrby\",c->argv[1],c->db->id);\n\n    // 将服务器设为脏\n    server.dirty++;\n\n    // 返回回复\n    addReply(c,shared.colon);\n    addReply(c,new);\n    addReply(c,shared.crlf);\n}\n\nvoid incrCommand(redisClient *c) {\n    incrDecrCommand(c,1);\n}\n\nvoid decrCommand(redisClient *c) {\n    incrDecrCommand(c,-1);\n}\n\nvoid incrbyCommand(redisClient *c) {\n    long long incr;\n\n    if (getLongLongFromObjectOrReply(c, c->argv[2], &incr, NULL) != REDIS_OK) return;\n    incrDecrCommand(c,incr);\n}\n\nvoid decrbyCommand(redisClient *c) {\n    long long incr;\n\n    if (getLongLongFromObjectOrReply(c, c->argv[2], &incr, NULL) != REDIS_OK) return;\n    incrDecrCommand(c,-incr);\n}\n\nvoid incrbyfloatCommand(redisClient *c) {\n    long double incr, value;\n    robj *o, *new, *aux;\n\n    // 取出值对象\n    o = lookupKeyWrite(c->db,c->argv[1]);\n\n    // 检查对象是否存在，以及类型是否正确\n    if (o != NULL && checkType(c,o,REDIS_STRING)) return;\n\n    // 将对象的整数值保存到 value 参数中\n    // 并取出 incr 参数的值\n    if (getLongDoubleFromObjectOrReply(c,o,&value,NULL) != REDIS_OK ||\n        getLongDoubleFromObjectOrReply(c,c->argv[2],&incr,NULL) != REDIS_OK)\n        return;\n\n    // 进行加法计算，并检查是否溢出\n    value += incr;\n    if (isnan(value) || isinf(value)) {\n        addReplyError(c,\"increment would produce NaN or Infinity\");\n        return;\n    }\n\n    // 用一个包含新值的新对象替换现有的值对象\n    new = createStringObjectFromLongDouble(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\n    // 发送事件通知\n    notifyKeyspaceEvent(REDIS_NOTIFY_STRING,\"incrbyfloat\",c->argv[1],c->db->id);\n\n    // 将服务器设为脏\n    server.dirty++;\n\n    // 回复\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    // 在传播 INCRBYFLOAT 命令时，总是用 SET 命令来替换 INCRBYFLOAT 命令\n    // 从而防止因为不同的浮点精度和格式化造成 AOF 重启时的数据不一致\n    aux = createStringObject(\"SET\",3);\n    rewriteClientCommandArgument(c,0,aux);\n    decrRefCount(aux);\n    rewriteClientCommandArgument(c,2,new);\n}\n\nvoid appendCommand(redisClient *c) {\n    size_t totlen;\n    robj *o, *append;\n\n    // 取出键相应的值对象\n    o = lookupKeyWrite(c->db,c->argv[1]);\n    if (o == NULL) {\n\n        // 键值对不存在。。。\n\n        /* Create the key */\n        // 键值对不存在，创建一个新的\n        c->argv[2] = tryObjectEncoding(c->argv[2]);\n        dbAdd(c->db,c->argv[1],c->argv[2]);\n        incrRefCount(c->argv[2]);\n        totlen = stringObjectLen(c->argv[2]);\n    } else {\n\n        // 键值对存在。。。\n\n        /* Key exists, check type */\n        // 检查类型\n        if (checkType(c,o,REDIS_STRING))\n            return;\n\n        /* \"append\" is an argument, so always an sds */\n        // 检查追加操作之后，字符串的长度是否符合 Redis 的限制\n        append = c->argv[2];\n        totlen = stringObjectLen(o)+sdslen(append->ptr);\n        if (checkStringLength(c,totlen) != REDIS_OK)\n            return;\n\n        /* Append the value */\n        // 执行追加操作\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\n    // 向数据库发送键被修改的信号\n    signalModifiedKey(c->db,c->argv[1]);\n\n    // 发送事件通知\n    notifyKeyspaceEvent(REDIS_NOTIFY_STRING,\"append\",c->argv[1],c->db->id);\n\n    // 将服务器设为脏\n    server.dirty++;\n\n    // 发送回复\n    addReplyLongLong(c,totlen);\n}\n\nvoid strlenCommand(redisClient *c) {\n    robj *o;\n\n    // 取出值对象，并进行类型检查\n    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||\n        checkType(c,o,REDIS_STRING)) return;\n\n    // 返回字符串值的长度\n    addReplyLongLong(c,stringObjectLen(o));\n}\n"
  },
  {
    "path": "src/t_zset.c",
    "content": "/*\n * Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>\n * Copyright (c) 2009-2012, Pieter Noordhuis <pcnoordhuis 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 * Sorted set API\n *----------------------------------------------------------------------------*/\n\n/* ZSETs are ordered sets using two data structures to hold the same elements\n * in order to get O(log(N)) INSERT and REMOVE operations into a sorted\n * data structure.\n *\n * ZSET 同时使用两种数据结构来持有同一个元素，\n * 从而提供 O(log(N)) 复杂度的有序数据结构的插入和移除操作。\n *\n * The elements are added to a hash table mapping Redis objects to scores.\n * At the same time the elements are added to a skip list mapping scores\n * to Redis objects (so objects are sorted by scores in this \"view\"). \n *\n * 哈希表将 Redis 对象映射到分值上。\n * 而跳跃表则将分值映射到 Redis 对象上，\n * 以跳跃表的视角来看，可以说 Redis 对象是根据分值来排序的。\n */\n\n/* This skiplist implementation is almost a C translation of the original\n * algorithm described by William Pugh in \"Skip Lists: A Probabilistic\n * Alternative to Balanced Trees\", modified in three ways:\n *\n * Redis 的跳跃表实现和 William Pugh \n * 在《Skip Lists: A Probabilistic Alternative to Balanced Trees》论文中\n * 描述的跳跃表基本相同，不过在以下三个地方做了修改：\n *\n * a) this implementation allows for repeated scores.\n *    这个实现允许有重复的分值\n *\n * b) the comparison is not just by key (our 'score') but by satellite data.\n *    对元素的比对不仅要比对他们的分值，还要比对他们的对象\n *\n * c) there is a back pointer, so it's a doubly linked list with the back\n * pointers being only at \"level 1\". This allows to traverse the list\n * from tail to head, useful for ZREVRANGE. \n *    每个跳跃表节点都带有一个后退指针，\n *    它允许程序在执行像 ZREVRANGE 这样的命令时，从表尾向表头遍历跳跃表。\n */\n\n#include \"redis.h\"\n#include <math.h>\n\nstatic int zslLexValueGteMin(robj *value, zlexrangespec *spec);\nstatic int zslLexValueLteMax(robj *value, zlexrangespec *spec);\n\n/*\n * 创建一个层数为 level 的跳跃表节点，\n * 并将节点的成员对象设置为 obj ，分值设置为 score 。\n *\n * 返回值为新创建的跳跃表节点\n *\n * T = O(1)\n */\nzskiplistNode *zslCreateNode(int level, double score, robj *obj) {\n    \n    // 分配空间\n    zskiplistNode *zn = zmalloc(sizeof(*zn)+level*sizeof(struct zskiplistLevel));\n\n    // 设置属性\n    zn->score = score;\n    zn->obj = obj;\n\n    return zn;\n}\n\n/*\n * 创建并返回一个新的跳跃表\n *\n * T = O(1)\n */\nzskiplist *zslCreate(void) {\n    int j;\n    zskiplist *zsl;\n\n    // 分配空间\n    zsl = zmalloc(sizeof(*zsl));\n\n    // 设置高度和起始层数\n    zsl->level = 1;\n    zsl->length = 0;\n\n    // 初始化表头节点\n    // T = O(1)\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\n    // 设置表尾\n    zsl->tail = NULL;\n\n    return zsl;\n}\n\n/*\n * 释放给定的跳跃表节点\n *\n * T = O(1)\n */\nvoid zslFreeNode(zskiplistNode *node) {\n\n    decrRefCount(node->obj);\n\n    zfree(node);\n}\n\n/*\n * 释放给定跳跃表，以及表中的所有节点\n *\n * T = O(N)\n */\nvoid zslFree(zskiplist *zsl) {\n\n    zskiplistNode *node = zsl->header->level[0].forward, *next;\n\n    // 释放表头\n    zfree(zsl->header);\n\n    // 释放表中所有节点\n    // T = O(N)\n    while(node) {\n\n        next = node->level[0].forward;\n\n        zslFreeNode(node);\n\n        node = next;\n    }\n    \n    // 释放跳跃表结构\n    zfree(zsl);\n}\n\n/* Returns a random level for the new skiplist node we are going to create.\n *\n * 返回一个随机值，用作新跳跃表节点的层数。\n *\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. \n *\n * 返回值介乎 1 和 ZSKIPLIST_MAXLEVEL 之间（包含 ZSKIPLIST_MAXLEVEL），\n * 根据随机算法所使用的幂次定律，越大的值生成的几率越小。\n *\n * T = O(N)\n */\nint zslRandomLevel(void) {\n    int level = 1;\n\n    while ((random()&0xFFFF) < (ZSKIPLIST_P * 0xFFFF))\n        level += 1;\n\n    return (level<ZSKIPLIST_MAXLEVEL) ? level : ZSKIPLIST_MAXLEVEL;\n}\n\n/*\n * 创建一个成员为 obj ，分值为 score 的新节点，\n * 并将这个新节点插入到跳跃表 zsl 中。\n * \n * 函数的返回值为新节点。\n *\n * T_wrost = O(N^2), T_avg = O(N log 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    redisAssert(!isnan(score));\n\n    // 在各个层查找节点的插入位置\n    // T_wrost = O(N^2), T_avg = O(N log N)\n    x = zsl->header;\n    for (i = zsl->level-1; i >= 0; i--) {\n\n        /* store rank that is crossed to reach the insert position */\n        // 如果 i 不是 zsl->level-1 层\n        // 那么 i 层的起始 rank 值为 i+1 层的 rank 值\n        // 各个层的 rank 值一层层累积\n        // 最终 rank[0] 的值加一就是新节点的前置节点的排位\n        // rank[0] 会在后面成为计算 span 值和 rank 值的基础\n        rank[i] = i == (zsl->level-1) ? 0 : rank[i+1];\n\n        // 沿着前进指针遍历跳跃表\n        // T_wrost = O(N^2), T_avg = O(N log N)\n        while (x->level[i].forward &&\n            (x->level[i].forward->score < score ||\n                // 比对分值\n                (x->level[i].forward->score == score &&\n                // 比对成员， T = O(N)\n                compareStringObjects(x->level[i].forward->obj,obj) < 0))) {\n\n            // 记录沿途跨越了多少个节点\n            rank[i] += x->level[i].span;\n\n            // 移动至下一指针\n            x = x->level[i].forward;\n        }\n        // 记录将要和新节点相连接的节点\n        update[i] = x;\n    }\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     *\n     * zslInsert() 的调用者会确保同分值且同成员的元素不会出现，\n     * 所以这里不需要进一步进行检查，可以直接创建新元素。\n     */\n\n    // 获取一个随机值作为新节点的层数\n    // T = O(N)\n    level = zslRandomLevel();\n\n    // 如果新节点的层数比表中其他节点的层数都要大\n    // 那么初始化表头节点中未使用的层，并将它们记录到 update 数组中\n    // 将来也指向新节点\n    if (level > zsl->level) {\n\n        // 初始化未使用层\n        // T = O(1)\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\n        // 更新表中节点最大层数\n        zsl->level = level;\n    }\n\n    // 创建新节点\n    x = zslCreateNode(level,score,obj);\n\n    // 将前面记录的指针指向新节点，并做相应的设置\n    // T = O(1)\n    for (i = 0; i < level; i++) {\n        \n        // 设置新节点的 forward 指针\n        x->level[i].forward = update[i]->level[i].forward;\n        \n        // 将沿途记录的各个节点的 forward 指针指向新节点\n        update[i]->level[i].forward = x;\n\n        /* update span covered by update[i] as x is inserted here */\n        // 计算新节点跨越的节点数量\n        x->level[i].span = update[i]->level[i].span - (rank[0] - rank[i]);\n\n        // 更新新节点插入之后，沿途节点的 span 值\n        // 其中的 +1 计算的是新节点\n        update[i]->level[i].span = (rank[0] - rank[i]) + 1;\n    }\n\n    /* increment span for untouched levels */\n    // 未接触的节点的 span 值也需要增一，这些节点直接从表头指向新节点\n    // T = O(1)\n    for (i = level; i < zsl->level; i++) {\n        update[i]->level[i].span++;\n    }\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\n    // 跳跃表的节点计数增一\n    zsl->length++;\n\n    return x;\n}\n\n/* Internal function used by zslDelete, zslDeleteByScore and zslDeleteByRank \n * \n * 内部删除函数，\n * 被 zslDelete 、 zslDeleteRangeByScore 和 zslDeleteByRank 等函数调用。\n *\n * T = O(1)\n */\nvoid zslDeleteNode(zskiplist *zsl, zskiplistNode *x, zskiplistNode **update) {\n    int i;\n\n    // 更新所有和被删除节点 x 有关的节点的指针，解除它们之间的关系\n    // T = O(1)\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\n    // 更新被删除节点 x 的前进和后退指针\n    if (x->level[0].forward) {\n        x->level[0].forward->backward = x->backward;\n    } else {\n        zsl->tail = x->backward;\n    }\n\n    // 更新跳跃表最大层数（只在被删除节点是跳跃表中最高的节点时才执行）\n    // T = O(1)\n    while(zsl->level > 1 && zsl->header->level[zsl->level-1].forward == NULL)\n        zsl->level--;\n\n    // 跳跃表节点计数器减一\n    zsl->length--;\n}\n\n/* Delete an element with matching score/object from the skiplist. \n *\n * 从跳跃表 zsl 中删除包含给定节点 score 并且带有指定对象 obj 的节点。\n *\n * T_wrost = O(N^2), T_avg = O(N log N)\n */\nint zslDelete(zskiplist *zsl, double score, robj *obj) {\n    zskiplistNode *update[ZSKIPLIST_MAXLEVEL], *x;\n    int i;\n\n    // 遍历跳跃表，查找目标节点，并记录所有沿途节点\n    // T_wrost = O(N^2), T_avg = O(N log N)\n    x = zsl->header;\n    for (i = zsl->level-1; i >= 0; i--) {\n\n        // 遍历跳跃表的复杂度为 T_wrost = O(N), T_avg = O(log N)\n        while (x->level[i].forward &&\n            (x->level[i].forward->score < score ||\n                // 比对分值\n                (x->level[i].forward->score == score &&\n                // 比对对象，T = O(N)\n                compareStringObjects(x->level[i].forward->obj,obj) < 0)))\n\n            // 沿着前进指针移动\n            x = x->level[i].forward;\n\n        // 记录沿途节点\n        update[i] = x;\n    }\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     *\n     * 检查找到的元素 x ，只有在它的分值和对象都相同时，才将它删除。\n     */\n    x = x->level[0].forward;\n    if (x && score == x->score && equalStringObjects(x->obj,obj)) {\n        // T = O(1)\n        zslDeleteNode(zsl, x, update);\n        // T = O(1)\n        zslFreeNode(x);\n        return 1;\n    } else {\n        return 0; /* not found */\n    }\n\n    return 0; /* not found */\n}\n\n/*\n * 检测给定值 value 是否大于（或大于等于）范围 spec 中的 min 项。\n *\n * 返回 1 表示 value 大于等于 min 项，否则返回 0 。\n *\n * T = O(1)\n */\nstatic int zslValueGteMin(double value, zrangespec *spec) {\n    return spec->minex ? (value > spec->min) : (value >= spec->min);\n}\n\n/*\n * 检测给定值 value 是否小于（或小于等于）范围 spec 中的 max 项。\n *\n * 返回 1 表示 value 小于等于 max 项，否则返回 0 。\n *\n * T = O(1)\n */\nstatic int 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.\n *\n * 如果给定的分值范围包含在跳跃表的分值范围之内，\n * 那么返回 1 ，否则返回 0 。\n *\n * T = O(1)\n */\nint zslIsInRange(zskiplist *zsl, zrangespec *range) {\n    zskiplistNode *x;\n\n    /* Test for ranges that will always be empty. */\n    // 先排除总为空的范围值\n    if (range->min > range->max ||\n            (range->min == range->max && (range->minex || range->maxex)))\n        return 0;\n\n    // 检查最大分值\n    x = zsl->tail;\n    if (x == NULL || !zslValueGteMin(x->score,range))\n        return 0;\n\n    // 检查最小分值\n    x = zsl->header->level[0].forward;\n    if (x == NULL || !zslValueLteMax(x->score,range))\n        return 0;\n\n    return 1;\n}\n\n/* Find the first node that is contained in the specified range.\n *\n * 返回 zsl 中第一个分值符合 range 中指定范围的节点。\n * Returns NULL when no element is contained in the range.\n *\n * 如果 zsl 中没有符合范围的节点，返回 NULL 。\n *\n * T_wrost = O(N), T_avg = O(log N)\n */\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    // 遍历跳跃表，查找符合范围 min 项的节点\n    // T_wrost = O(N), T_avg = O(log 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    redisAssert(x != NULL);\n\n    /* Check if score <= max. */\n    // 检查节点是否符合范围的 max 项\n    // T = O(1)\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.\n *\n * 返回 zsl 中最后一个分值符合 range 中指定范围的节点。\n *\n * 如果 zsl 中没有符合范围的节点，返回 NULL 。\n *\n * T_wrost = O(N), T_avg = O(log N)\n */\nzskiplistNode *zslLastInRange(zskiplist *zsl, zrangespec *range) {\n    zskiplistNode *x;\n    int i;\n\n    /* If everything is out of range, return early. */\n    // 先确保跳跃表中至少有一个节点符合 range 指定的范围，\n    // 否则直接失败\n    // T = O(1)\n    if (!zslIsInRange(zsl,range)) return NULL;\n\n    // 遍历跳跃表，查找符合范围 max 项的节点\n    // T_wrost = O(N), T_avg = O(log 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    redisAssert(x != NULL);\n\n    /* Check if score >= min. */\n    // 检查节点是否符合范围的 min 项\n    // T = O(1)\n    if (!zslValueGteMin(x->score,range)) return NULL;\n\n    // 返回节点\n    return x;\n}\n\n/* Delete all the elements with score between min and max from the skiplist.\n *\n * 删除所有分值在给定范围之内的节点。\n *\n * Min and max are inclusive, so a score >= min || score <= max is deleted.\n * \n * min 和 max 参数都是包含在范围之内的，所以分值 >= min 或 <= max 的节点都会被删除。\n *\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.\n *\n * 节点不仅会从跳跃表中删除，而且会从相应的字典中删除。\n *\n * 返回值为被删除节点的数量\n *\n * T = O(N)\n */\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    // 记录所有和被删除节点（们）有关的节点\n    // T_wrost = O(N) , T_avg = O(log 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    // 定位到给定范围开始的第一个节点\n    x = x->level[0].forward;\n\n    /* Delete nodes while in range. */\n    // 删除范围中的所有节点\n    // T = O(N)\n    while (x &&\n           (range->maxex ? x->score < range->max : x->score <= range->max))\n    {\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\n        // 从跳跃表中删除当前节点\n        zslDeleteNode(zsl,x,update);\n        // 从字典中删除当前节点\n        dictDelete(dict,x->obj);\n        // 释放当前跳跃表节点的结构\n        zslFreeNode(x);\n\n        // 增加删除计数器\n        removed++;\n\n        // 继续处理下个节点\n        x = next;\n    }\n\n    // 返回被删除节点的数量\n    return removed;\n}\n\n/* Delete all the elements with rank between start and end from the skiplist.\n *\n * 从跳跃表中删除所有给定排位内的节点。\n *\n * Start and end are inclusive. Note that start and end need to be 1-based \n *\n * start 和 end 两个位置都是包含在内的。注意它们都是以 1 为起始值。\n *\n * 函数的返回值为被删除节点的数量。\n *\n * T = O(N)\n */\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    // 沿着前进指针移动到指定排位的起始位置，并记录所有沿途指针\n    // T_wrost = O(N) , T_avg = O(log 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    // 移动到排位的起始的第一个节点\n    traversed++;\n    x = x->level[0].forward;\n    // 删除所有在给定排位范围内的节点\n    // T = O(N)\n    while (x && traversed <= end) {\n\n        // 记录下一节点的指针\n        zskiplistNode *next = x->level[0].forward;\n\n        // 从跳跃表中删除节点\n        zslDeleteNode(zsl,x,update);\n        // 从字典中删除节点\n        dictDelete(dict,x->obj);\n        // 释放节点结构\n        zslFreeNode(x);\n\n        // 为删除计数器增一\n        removed++;\n\n        // 为排位计数器增一\n        traversed++;\n\n        // 处理下个节点\n        x = next;\n    }\n\n    // 返回被删除节点的数量\n    return removed;\n}\n\n/* Find the rank for an element by both score and key.\n *\n * 查找包含给定分值和成员对象的节点在跳跃表中的排位。\n *\n * Returns 0 when the element cannot be found, rank otherwise.\n *\n * 如果没有包含给定分值和成员对象的节点，返回 0 ，否则返回排位。\n *\n * Note that the rank is 1-based due to the span of zsl->header to the\n * first element. \n *\n * 注意，因为跳跃表的表头也被计算在内，所以返回的排位以 1 为起始值。\n *\n * T_wrost = O(N), T_avg = O(log N)\n */\nunsigned long zslGetRank(zskiplist *zsl, double score, robj *o) {\n    zskiplistNode *x;\n    unsigned long rank = 0;\n    int i;\n\n    // 遍历整个跳跃表\n    x = zsl->header;\n    for (i = zsl->level-1; i >= 0; i--) {\n\n        // 遍历节点并对比元素\n        while (x->level[i].forward &&\n            (x->level[i].forward->score < score ||\n                // 比对分值\n                (x->level[i].forward->score == score &&\n                // 比对成员对象\n                compareStringObjects(x->level[i].forward->obj,o) <= 0))) {\n\n            // 累积跨越的节点数量\n            rank += x->level[i].span;\n\n            // 沿着前进指针遍历跳跃表\n            x = x->level[i].forward;\n        }\n\n        /* x might be equal to zsl->header, so test if obj is non-NULL */\n        // 必须确保不仅分值相等，而且成员对象也要相等\n        // T = O(N)\n        if (x->obj && equalStringObjects(x->obj,o)) {\n            return rank;\n        }\n    }\n\n    // 没找到\n    return 0;\n}\n\n/* Finds an element by its rank. The rank argument needs to be 1-based. \n * \n * 根据排位在跳跃表中查找元素。排位的起始值为 1 。\n *\n * 成功查找返回相应的跳跃表节点，没找到则返回 NULL 。\n *\n * T_wrost = O(N), T_avg = O(log N)\n */\nzskiplistNode* zslGetElementByRank(zskiplist *zsl, unsigned long rank) {\n    zskiplistNode *x;\n    unsigned long traversed = 0;\n    int i;\n\n    // T_wrost = O(N), T_avg = O(log N)\n    x = zsl->header;\n    for (i = zsl->level-1; i >= 0; i--) {\n\n        // 遍历跳跃表并累积越过的节点数量\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\n        // 如果越过的节点数量已经等于 rank\n        // 那么说明已经到达要找的节点\n        if (traversed == rank) {\n            return x;\n        }\n\n    }\n\n    // 没找到目标节点\n    return NULL;\n}\n\n/* Populate the rangespec according to the objects min and max. \n *\n * 对 min 和 max 进行分析，并将区间的值保存在 spec 中。\n *\n * 分析成功返回 REDIS_OK ，分析出错导致失败返回 REDIS_ERR 。\n *\n * T = O(N)\n */\nstatic int zslParseRange(robj *min, robj *max, zrangespec *spec) {\n    char *eptr;\n\n    // 默认为闭区间\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 == REDIS_ENCODING_INT) {\n        // min 的值为整数，开区间\n        spec->min = (long)min->ptr;\n    } else {\n        // min 对象为字符串，分析 min 的值并决定区间\n        if (((char*)min->ptr)[0] == '(') {\n            // T = O(N)\n            spec->min = strtod((char*)min->ptr+1,&eptr);\n            if (eptr[0] != '\\0' || isnan(spec->min)) return REDIS_ERR;\n            spec->minex = 1;\n        } else {\n            // T = O(N)\n            spec->min = strtod((char*)min->ptr,&eptr);\n            if (eptr[0] != '\\0' || isnan(spec->min)) return REDIS_ERR;\n        }\n    }\n\n    if (max->encoding == REDIS_ENCODING_INT) {\n        // max 的值为整数，开区间\n        spec->max = (long)max->ptr;\n    } else {\n        // max 对象为字符串，分析 max 的值并决定区间\n        if (((char*)max->ptr)[0] == '(') {\n            // T = O(N)\n            spec->max = strtod((char*)max->ptr+1,&eptr);\n            if (eptr[0] != '\\0' || isnan(spec->max)) return REDIS_ERR;\n            spec->maxex = 1;\n        } else {\n            // T = O(N)\n            spec->max = strtod((char*)max->ptr,&eptr);\n            if (eptr[0] != '\\0' || isnan(spec->max)) return REDIS_ERR;\n        }\n    }\n\n    return REDIS_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. REDIS_OK will be\n  * returned.\n  *\n  * If the string is not a valid range REDIS_ERR 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 REDIS_ERR;\n        *ex = 0;\n        *dest = shared.maxstring;\n        incrRefCount(shared.maxstring);\n        return REDIS_OK;\n    case '-':\n        if (c[1] != '\\0') return REDIS_ERR;\n        *ex = 0;\n        *dest = shared.minstring;\n        incrRefCount(shared.minstring);\n        return REDIS_OK;\n    case '(':\n        *ex = 1;\n        *dest = createStringObject(c+1,sdslen(c)-1);\n        return REDIS_OK;\n    case '[':\n        *ex = 0;\n        *dest = createStringObject(c+1,sdslen(c)-1);\n        return REDIS_OK;\n    default:\n        return REDIS_ERR;\n    }\n}\n\n/* Populate the rangespec according to the objects min and max.\n *\n * Return REDIS_OK on success. On error REDIS_ERR 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 == REDIS_ENCODING_INT ||\n        max->encoding == REDIS_ENCODING_INT) return REDIS_ERR;\n\n    spec->min = spec->max = NULL;\n    if (zslParseLexRangeItem(min, &spec->min, &spec->minex) == REDIS_ERR ||\n        zslParseLexRangeItem(max, &spec->max, &spec->maxex) == REDIS_ERR) {\n        if (spec->min) decrRefCount(spec->min);\n        if (spec->max) decrRefCount(spec->max);\n        return REDIS_ERR;\n    } else {\n        return REDIS_OK;\n    }\n}\n\n/* Free a lex range structure, must be called only after zelParseLexRange()\n * populated the structure with success (REDIS_OK returned). */\nvoid zslFreeLexRange(zlexrangespec *spec) {\n    decrRefCount(spec->min);\n    decrRefCount(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    redisAssert(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    redisAssert(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\n/*\n * 取出 sptr 指向节点所保存的有序集合元素的分值\n */\ndouble zzlGetScore(unsigned char *sptr) {\n    unsigned char *vstr;\n    unsigned int vlen;\n    long long vlong;\n    char buf[128];\n    double score;\n\n    redisAssert(sptr != NULL);\n    // 取出节点值\n    redisAssert(ziplistGet(sptr,&vstr,&vlen,&vlong));\n\n    if (vstr) {\n        // 字符串转 double\n        memcpy(buf,vstr,vlen);\n        buf[vlen] = '\\0';\n        score = strtod(buf,NULL);\n    } else {\n        // double 值\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    unsigned char *vstr;\n    unsigned int vlen;\n    long long vlong;\n\n    redisAssert(sptr != NULL);\n    redisAssert(ziplistGet(sptr,&vstr,&vlen,&vlong));\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. \n *\n * 将 eptr 中的元素和 cstr 进行对比。\n *\n * 相等返回 0 ，\n * 不相等并且 eptr 的字符串比 cstr 大时，返回正整数。\n * 不相等并且 eptr 的字符串比 cstr 小时，返回负整数。\n */\nint zzlCompareElements(unsigned char *eptr, unsigned char *cstr, unsigned int clen) {\n    unsigned char *vstr;\n    unsigned int vlen;\n    long long vlong;\n    unsigned char vbuf[32];\n    int minlen, cmp;\n\n    // 取出节点中的字符串值，以及它的长度\n    redisAssert(ziplistGet(eptr,&vstr,&vlen,&vlong));\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    // 对比\n    minlen = (vlen < clen) ? vlen : clen;\n    cmp = memcmp(vstr,cstr,minlen);\n    if (cmp == 0) return vlen-clen;\n    return cmp;\n}\n\n/*\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. \n *\n * 根据 eptr 和 sptr ，移动它们分别指向下个成员和下个分值。\n *\n * 如果后面已经没有元素，那么两个指针都被设为 NULL 。\n */\nvoid zzlNext(unsigned char *zl, unsigned char **eptr, unsigned char **sptr) {\n    unsigned char *_eptr, *_sptr;\n\n    redisAssert(*eptr != NULL && *sptr != NULL);\n\n    // 指向下个成员\n    _eptr = ziplistNext(zl,*sptr);\n    if (_eptr != NULL) {\n        // 指向下个分值\n        _sptr = ziplistNext(zl,_eptr);\n        redisAssert(_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. \n *\n * 根据 eptr 和 sptr 的值，移动指针指向前一个节点。\n *\n * eptr 和 sptr 会保存移动之后的新指针。\n *\n * 如果指针的前面已经没有节点，那么返回 NULL 。\n */\nvoid zzlPrev(unsigned char *zl, unsigned char **eptr, unsigned char **sptr) {\n    unsigned char *_eptr, *_sptr;\n    redisAssert(*eptr != NULL && *sptr != NULL);\n\n    _sptr = ziplistPrev(zl,*eptr);\n    if (_sptr != NULL) {\n        _eptr = ziplistPrev(zl,_sptr);\n        redisAssert(_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. \n *\n * 如果给定的 ziplist 有至少一个节点符合 range 中指定的范围，\n * 那么函数返回 1 ，否则返回 0 。\n */\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    // 取出 ziplist 中的最大分值，并和 range 的最大值对比\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    // 取出 ziplist 中的最小值，并和 range 的最小值进行对比\n    p = ziplistIndex(zl,1); /* First score. */\n    redisAssert(p != NULL);\n    score = zzlGetScore(p);\n    if (!zslValueLteMax(score,range))\n        return 0;\n\n    // ziplist 有至少一个节点符合范围\n    return 1;\n}\n\n/* Find pointer to the first element contained in the specified range.\n *\n * 返回第一个 score 值在给定范围内的节点\n *\n * Returns NULL when no element is contained in the range. \n * Returns NULL when no element is contained in the range.\n *\n * 如果没有节点的 score 值在给定范围，返回 NULL 。\n */\nunsigned char *zzlFirstInRange(unsigned char *zl, zrangespec *range) {\n    // 从表头开始遍历\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    // 分值在 ziplist 中是从小到大排列的\n    // 从表头向表尾遍历\n    while (eptr != NULL) {\n        sptr = ziplistNext(zl,eptr);\n        redisAssert(sptr != NULL);\n\n        score = zzlGetScore(sptr);\n        if (zslValueGteMin(score,range)) {\n            /* Check if score <= max. */\n            // 遇上第一个符合范围的分值，\n            // 返回它的节点指针\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 *\n * 返回 score 值在给定范围内的最后一个节点\n *\n * Returns NULL when no element is contained in the range. \n *\n * 没有元素包含它时，返回 NULL\n */\nunsigned char *zzlLastInRange(unsigned char *zl, zrangespec *range) {\n    // 从表尾开始遍历\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    // 在有序的 ziplist 里从表尾到表头遍历\n    while (eptr != NULL) {\n        sptr = ziplistNext(zl,eptr);\n        redisAssert(sptr != NULL);\n\n        // 获取节点的 score 值\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            redisAssert((eptr = ziplistPrev(zl,sptr)) != NULL);\n        else\n            eptr = NULL;\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    decrRefCount(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    decrRefCount(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    redisAssert(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        redisAssert(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            // 找到最后一个符合范围的值\n            // 返回它的指针\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            redisAssert((eptr = ziplistPrev(zl,sptr)) != NULL);\n        else\n            eptr = NULL;\n    }\n\n    return NULL;\n}\n\n/*\n * 从 ziplist 编码的有序集合中查找 ele 成员，并将它的分值保存到 score 。\n *\n * 寻找成功返回指向成员 ele 的指针，查找失败返回 NULL 。\n */\nunsigned char *zzlFind(unsigned char *zl, robj *ele, double *score) {\n\n    // 定位到首个元素\n    unsigned char *eptr = ziplistIndex(zl,0), *sptr;\n\n    // 解码成员\n    ele = getDecodedObject(ele);\n\n    // 遍历整个 ziplist ，查找元素（确认成员存在，并且取出它的分值）\n    while (eptr != NULL) {\n        // 指向分值\n        sptr = ziplistNext(zl,eptr);\n        redisAssertWithInfo(NULL,ele,sptr != NULL);\n\n        // 比对成员\n        if (ziplistCompare(eptr,ele->ptr,sdslen(ele->ptr))) {\n            /* Matching element, pull out score. */\n            // 成员匹配，取出分值\n            if (score != NULL) *score = zzlGetScore(sptr);\n            decrRefCount(ele);\n            return eptr;\n        }\n\n        /* Move to next element. */\n        eptr = ziplistNext(zl,sptr);\n    }\n\n    decrRefCount(ele);\n    \n    // 没有找到\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. \n *\n * 从 ziplist 中删除 eptr 所指定的有序集合元素（包括成员和分值）\n */\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\n/*\n * 将带有给定成员和分值的新节点插入到 eptr 所指向的节点的前面，\n * 如果 eptr 为 NULL ，那么将新节点插入到 ziplist 的末端。\n *\n * 函数返回插入操作完成之后的 ziplist\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    // 计算分值的字节长度\n    redisAssertWithInfo(NULL,ele,sdsEncodedObject(ele));\n    scorelen = d2string(scorebuf,sizeof(scorebuf),score);\n\n    // 插入到表尾，或者空表\n    if (eptr == NULL) {\n        // | member-1 | score-1 | member-2 | score-2 | ... | member-N | score-N |\n        // 先推入元素\n        zl = ziplistPush(zl,ele->ptr,sdslen(ele->ptr),ZIPLIST_TAIL);\n        // 后推入分值\n        zl = ziplistPush(zl,(unsigned char*)scorebuf,scorelen,ZIPLIST_TAIL);\n\n    // 插入到某个节点的前面\n    } else {\n        /* Keep offset relative to zl, as it might be re-allocated. */\n        // 插入成员\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        // 将分值插入在成员之后\n        redisAssertWithInfo(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. \n *\n * 将 ele 成员和它的分值 score 添加到 ziplist 里面\n *\n * ziplist 里的各个节点按 score 值从小到大排列\n *\n * This function assumes the element is not yet present in the list. \n *\n * 这个函数假设 elem 不存在于有序集\n */\nunsigned char *zzlInsert(unsigned char *zl, robj *ele, double score) {\n\n    // 指向 ziplist 第一个节点（也即是有序集的 member 域）\n    unsigned char *eptr = ziplistIndex(zl,0), *sptr;\n    double s;\n\n    // 解码值\n    ele = getDecodedObject(ele);\n\n    // 遍历整个 ziplist\n    while (eptr != NULL) {\n\n        // 取出分值\n        sptr = ziplistNext(zl,eptr);\n        redisAssertWithInfo(NULL,ele,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            // 遇到第一个 score 值比输入 score 大的节点\n            // 将新节点插入在这个节点的前面，\n            // 让节点在 ziplist 里根据 score 从小到大排列\n            zl = zzlInsertAt(zl,eptr,ele,score);\n            break;\n        } else if (s == score) {\n            /* Ensure lexicographical ordering for elements. */\n            // 如果输入 score 和节点的 score 相同\n            // 那么根据 member 的字符串位置来决定新节点的插入位置\n            if (zzlCompareElements(eptr,ele->ptr,sdslen(ele->ptr)) > 0) {\n                zl = zzlInsertAt(zl,eptr,ele,score);\n                break;\n            }\n        }\n\n        /* Move to next element. */\n        // 输入 score 比节点的 score 值要大\n        // 移动到下一个节点\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,score);\n\n    decrRefCount(ele);\n    return zl;\n}\n\n/*\n * 删除 ziplist 中分值在指定范围内的元素\n *\n * deleted 不为 NULL 时，在删除完毕之后，将被删除元素的数量保存到 *deleted 中。\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    // 指向 ziplist 中第一个符合范围的节点\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    // 一直删除节点，直到遇到不在范围内的值为止\n    // 节点中的值都是有序的\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\n    return zl;\n}\n\n/* Delete all the elements with rank between start and end from the skiplist.\n *\n * 删除 ziplist 中所有在给定排位范围内的元素。\n *\n * Start and end are inclusive. Note that start and end need to be 1-based \n *\n * start 和 end 索引都是包括在内的。并且它们都以 1 为起始值。\n *\n * 如果 deleted 不为 NULL ，那么在删除操作完成之后，将删除元素的数量保存到 *deleted 中\n */\nunsigned char *zzlDeleteRangeByRank(unsigned char *zl, unsigned int start, unsigned int end, unsigned long *deleted) {\n    unsigned int num = (end-start)+1;\n\n    if (deleted) *deleted = num;\n\n    // 每个元素占用两个节点，所以删除的其实位置要乘以 2 \n    // 并且因为 ziplist 的索引以 0 为起始值，而 zzl 的起始值为 1 ，\n    // 所以需要 start - 1 \n    zl = ziplistDeleteRange(zl,2*(start-1),2*num);\n\n    return zl;\n}\n\n/*-----------------------------------------------------------------------------\n * Common sorted set API\n *----------------------------------------------------------------------------*/\n\nunsigned int zsetLength(robj *zobj) {\n\n    int length = -1;\n\n    if (zobj->encoding == REDIS_ENCODING_ZIPLIST) {\n        length = zzlLength(zobj->ptr);\n\n    } else if (zobj->encoding == REDIS_ENCODING_SKIPLIST) {\n        length = ((zset*)zobj->ptr)->zsl->length;\n\n    } else {\n        redisPanic(\"Unknown sorted set encoding\");\n    }\n\n    return length;\n}\n\n/*\n * 将跳跃表对象 zobj 的底层编码转换为 encoding 。\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\n    // 从 ZIPLIST 编码转换为 SKIPLIST 编码\n    if (zobj->encoding == REDIS_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 != REDIS_ENCODING_SKIPLIST)\n            redisPanic(\"Unknown target encoding\");\n\n        // 创建有序集合结构\n        zs = zmalloc(sizeof(*zs));\n        // 字典\n        zs->dict = dictCreate(&zsetDictType,NULL);\n        // 跳跃表\n        zs->zsl = zslCreate();\n\n        // 有序集合在 ziplist 中的排列：\n        //\n        // | member-1 | score-1 | member-2 | score-2 | ... |\n        //\n        // 指向 ziplist 中的首个节点（保存着元素成员）\n        eptr = ziplistIndex(zl,0);\n        redisAssertWithInfo(NULL,zobj,eptr != NULL);\n        // 指向 ziplist 中的第二个节点（保存着元素分值）\n        sptr = ziplistNext(zl,eptr);\n        redisAssertWithInfo(NULL,zobj,sptr != NULL);\n\n        // 遍历所有 ziplist 节点，并将元素的成员和分值添加到有序集合中\n        while (eptr != NULL) {\n            \n            // 取出分值\n            score = zzlGetScore(sptr);\n\n            // 取出成员\n            redisAssertWithInfo(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            // 将成员和分值分别关联到跳跃表和字典中\n            node = zslInsert(zs->zsl,score,ele);\n            redisAssertWithInfo(NULL,zobj,dictAdd(zs->dict,ele,&node->score) == DICT_OK);\n            incrRefCount(ele); /* Added to dictionary. */\n\n            // 移动指针，指向下个元素\n            zzlNext(zl,&eptr,&sptr);\n        }\n\n        // 释放原来的 ziplist\n        zfree(zobj->ptr);\n\n        // 更新对象的值，以及编码方式\n        zobj->ptr = zs;\n        zobj->encoding = REDIS_ENCODING_SKIPLIST;\n\n    // 从 SKIPLIST 转换为 ZIPLIST 编码\n    } else if (zobj->encoding == REDIS_ENCODING_SKIPLIST) {\n\n        // 新的 ziplist\n        unsigned char *zl = ziplistNew();\n\n        if (encoding != REDIS_ENCODING_ZIPLIST)\n            redisPanic(\"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        // 指向跳跃表\n        zs = zobj->ptr;\n\n        // 先释放字典，因为只需要跳跃表就可以遍历整个有序集合了\n        dictRelease(zs->dict);\n\n        // 指向跳跃表首个节点\n        node = zs->zsl->header->level[0].forward;\n\n        // 释放跳跃表表头\n        zfree(zs->zsl->header);\n        zfree(zs->zsl);\n\n        // 遍历跳跃表，取出里面的元素，并将它们添加到 ziplist\n        while (node) {\n\n            // 取出解码后的值对象\n            ele = getDecodedObject(node->obj);\n\n            // 添加元素到 ziplist\n            zl = zzlInsertAt(zl,NULL,ele,node->score);\n            decrRefCount(ele);\n\n            // 沿着跳跃表的第 0 层前进\n            next = node->level[0].forward;\n            zslFreeNode(node);\n            node = next;\n        }\n\n        // 释放跳跃表\n        zfree(zs);\n\n        // 更新对象的值，以及对象的编码方式\n        zobj->ptr = zl;\n        zobj->encoding = REDIS_ENCODING_ZIPLIST;\n    } else {\n        redisPanic(\"Unknown sorted set encoding\");\n    }\n}\n\n/*-----------------------------------------------------------------------------\n * Sorted set commands \n *----------------------------------------------------------------------------*/\n\n/* This generic command implements both ZADD and ZINCRBY. */\nvoid zaddGenericCommand(redisClient *c, int incr) {\n\n    static char *nanerr = \"resulting score is not a number (NaN)\";\n\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 = (c->argc-2)/2;\n    int added = 0, updated = 0;\n\n    // 输入的 score - member 参数必须是成对出现的\n    if (c->argc % 2) {\n        addReply(c,shared.syntaxerr);\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    // 取出所有输入的 score 分值\n    scores = zmalloc(sizeof(double)*elements);\n    for (j = 0; j < elements; j++) {\n        if (getDoubleFromObjectOrReply(c,c->argv[2+j*2],&scores[j],NULL)\n            != REDIS_OK) goto cleanup;\n    }\n\n    /* Lookup the key and create the sorted set if does not exist. */\n    // 取出有序集合对象\n    zobj = lookupKeyWrite(c->db,key);\n    if (zobj == NULL) {\n        // 有序集合不存在，创建新有序集合\n        if (server.zset_max_ziplist_entries == 0 ||\n            server.zset_max_ziplist_value < sdslen(c->argv[3]->ptr))\n        {\n            zobj = createZsetObject();\n        } else {\n            zobj = createZsetZiplistObject();\n        }\n        // 关联对象到数据库\n        dbAdd(c->db,key,zobj);\n    } else {\n        // 对象存在，检查类型\n        if (zobj->type != REDIS_ZSET) {\n            addReply(c,shared.wrongtypeerr);\n            goto cleanup;\n        }\n    }\n\n    // 处理所有元素\n    for (j = 0; j < elements; j++) {\n        score = scores[j];\n\n        // 有序集合为 ziplist 编码\n        if (zobj->encoding == REDIS_ENCODING_ZIPLIST) {\n            unsigned char *eptr;\n\n            /* Prefer non-encoded element when dealing with ziplists. */\n            // 查找成员\n            ele = c->argv[3+j*2];\n            if ((eptr = zzlFind(zobj->ptr,ele,&curscore)) != NULL) {\n\n                // 成员已存在\n\n                // ZINCRYBY 命令时使用\n                if (incr) {\n                    score += curscore;\n                    if (isnan(score)) {\n                        addReplyError(c,nanerr);\n                        goto cleanup;\n                    }\n                }\n\n                /* Remove and re-insert when score changed. */\n                // 执行 ZINCRYBY 命令时，\n                // 或者用户通过 ZADD 修改成员的分值时执行\n                if (score != curscore) {\n                    // 删除已有元素\n                    zobj->ptr = zzlDelete(zobj->ptr,eptr);\n                    // 重新插入元素\n                    zobj->ptr = zzlInsert(zobj->ptr,ele,score);\n                    // 计数器\n                    server.dirty++;\n                    updated++;\n                }\n            } else {\n                /* Optimize: check if the element is too large or the list\n                 * becomes too long *before* executing zzlInsert. */\n                // 元素不存在，直接添加\n                zobj->ptr = zzlInsert(zobj->ptr,ele,score);\n\n                // 查看元素的数量，\n                // 看是否需要将 ZIPLIST 编码转换为有序集合\n                if (zzlLength(zobj->ptr) > server.zset_max_ziplist_entries)\n                    zsetConvert(zobj,REDIS_ENCODING_SKIPLIST);\n\n                // 查看新添加元素的长度\n                // 看是否需要将 ZIPLIST 编码转换为有序集合\n                if (sdslen(ele->ptr) > server.zset_max_ziplist_value)\n                    zsetConvert(zobj,REDIS_ENCODING_SKIPLIST);\n\n                server.dirty++;\n                added++;\n            }\n\n        // 有序集合为 SKIPLIST 编码\n        } else if (zobj->encoding == REDIS_ENCODING_SKIPLIST) {\n            zset *zs = zobj->ptr;\n            zskiplistNode *znode;\n            dictEntry *de;\n\n            // 编码对象\n            ele = c->argv[3+j*2] = tryObjectEncoding(c->argv[3+j*2]);\n\n            // 查看成员是否存在\n            de = dictFind(zs->dict,ele);\n            if (de != NULL) {\n\n                // 成员存在\n\n                // 取出成员\n                curobj = dictGetKey(de);\n                // 取出分值\n                curscore = *(double*)dictGetVal(de);\n\n                // ZINCRYBY 时执行\n                if (incr) {\n                    score += curscore;\n                    if (isnan(score)) {\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                // 执行 ZINCRYBY 命令时，\n                // 或者用户通过 ZADD 修改成员的分值时执行\n                if (score != curscore) {\n                    // 删除原有元素\n                    redisAssertWithInfo(c,curobj,zslDelete(zs->zsl,curscore,curobj));\n\n                    // 重新插入元素\n                    znode = zslInsert(zs->zsl,score,curobj);\n                    incrRefCount(curobj); /* Re-inserted in skiplist. */\n\n                    // 更新字典的分值指针\n                    dictGetVal(de) = &znode->score; /* Update score ptr. */\n\n                    server.dirty++;\n                    updated++;\n                }\n            } else {\n\n                // 元素不存在，直接添加到跳跃表\n                znode = zslInsert(zs->zsl,score,ele);\n                incrRefCount(ele); /* Inserted in skiplist. */\n\n                // 将元素关联到字典\n                redisAssertWithInfo(c,NULL,dictAdd(zs->dict,ele,&znode->score) == DICT_OK);\n                incrRefCount(ele); /* Added to dictionary. */\n\n                server.dirty++;\n                added++;\n            }\n        } else {\n            redisPanic(\"Unknown sorted set encoding\");\n        }\n    }\n\n    if (incr) /* ZINCRBY */\n        addReplyDouble(c,score);\n    else /* ZADD */\n        addReplyLongLong(c,added);\n\ncleanup:\n    zfree(scores);\n    if (added || updated) {\n        signalModifiedKey(c->db,key);\n        notifyKeyspaceEvent(REDIS_NOTIFY_ZSET,\n            incr ? \"zincr\" : \"zadd\", key, c->db->id);\n    }\n}\n\nvoid zaddCommand(redisClient *c) {\n    zaddGenericCommand(c,0);\n}\n\nvoid zincrbyCommand(redisClient *c) {\n    zaddGenericCommand(c,1);\n}\n\nvoid zremCommand(redisClient *c) {\n    robj *key = c->argv[1];\n    robj *zobj;\n    int deleted = 0, keyremoved = 0, j;\n\n    // 取出有序集合对象\n    if ((zobj = lookupKeyWriteOrReply(c,key,shared.czero)) == NULL ||\n        checkType(c,zobj,REDIS_ZSET)) return;\n\n    // 从 ziplist 中删除\n    if (zobj->encoding == REDIS_ENCODING_ZIPLIST) {\n        unsigned char *eptr;\n\n        // 遍历所有输入元素\n        for (j = 2; j < c->argc; j++) {\n            // 如果元素在 ziplist 中存在的话\n            if ((eptr = zzlFind(zobj->ptr,c->argv[j],NULL)) != NULL) {\n                // 元素存在时，删除计算器才增一\n                deleted++;\n                // 那么删除它们\n                zobj->ptr = zzlDelete(zobj->ptr,eptr);\n                \n                // ziplist 已清空，将有序集合从数据库中删除\n                if (zzlLength(zobj->ptr) == 0) {\n                    dbDelete(c->db,key);\n                    break;\n                }\n            }\n        }\n\n    // 从跳跃表和字典中删除\n    } else if (zobj->encoding == REDIS_ENCODING_SKIPLIST) {\n        zset *zs = zobj->ptr;\n        dictEntry *de;\n        double score;\n\n        // 遍历所有输入元素\n        for (j = 2; j < c->argc; j++) {\n\n            // 查找元素\n            de = dictFind(zs->dict,c->argv[j]);\n\n            if (de != NULL) {\n                // 元素存在时，删除计算器才增一\n                deleted++;\n\n                /* Delete from the skiplist */\n                // 将元素从跳跃表中删除\n                score = *(double*)dictGetVal(de);\n                redisAssertWithInfo(c,c->argv[j],zslDelete(zs->zsl,score,c->argv[j]));\n\n                /* Delete from the hash table */\n                // 将元素从字典中删除\n                dictDelete(zs->dict,c->argv[j]);\n\n                // 检查是否需要缩小字典\n                if (htNeedsResize(zs->dict)) dictResize(zs->dict);\n\n                // 字典已被清空，有序集合已经被清空，将它从数据库中删除\n                if (dictSize(zs->dict) == 0) {\n                    dbDelete(c->db,key);\n                    break;\n                }\n            }\n        }\n    } else {\n        redisPanic(\"Unknown sorted set encoding\");\n    }\n\n    // 如果有至少一个元素被删除的话，那么执行以下代码\n    if (deleted) {\n\n        notifyKeyspaceEvent(REDIS_NOTIFY_ZSET,\"zrem\",key,c->db->id);\n\n        if (keyremoved)\n            notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,\"del\",key,c->db->id);\n\n        signalModifiedKey(c->db,key);\n\n        server.dirty += deleted;\n    }\n\n    // 回复被删除元素的数量\n    addReplyLongLong(c,deleted);\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(redisClient *c, int rangetype) {\n    robj *key = c->argv[1];\n    robj *zobj;\n    int keyremoved = 0;\n    unsigned long deleted;\n    zrangespec range;\n    zlexrangespec lexrange;\n    long start, end, llen;\n\n    /* Step 1: Parse the range. */\n    if (rangetype == ZRANGE_RANK) {\n        if ((getLongFromObjectOrReply(c,c->argv[2],&start,NULL) != REDIS_OK) ||\n            (getLongFromObjectOrReply(c,c->argv[3],&end,NULL) != REDIS_OK))\n            return;\n    } else if (rangetype == ZRANGE_SCORE) {\n        if (zslParseRange(c->argv[2],c->argv[3],&range) != REDIS_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) != REDIS_OK) {\n            addReplyError(c,\"min or max not valid string range item\");\n            return;\n        }\n    }\n\n    /* Step 2: Lookup & range sanity checks if needed. */\n    if ((zobj = lookupKeyWriteOrReply(c,key,shared.czero)) == NULL ||\n        checkType(c,zobj,REDIS_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 == REDIS_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\n    // 从跳跃表和字典中删除\n    } else if (zobj->encoding == REDIS_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\n        // 对象已清空，从数据库中删除\n        if (dictSize(zs->dict) == 0) {\n            dbDelete(c->db,key);\n            keyremoved = 1;\n        }\n    } else {\n        redisPanic(\"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(REDIS_NOTIFY_ZSET,event[rangetype],key,c->db->id);\n        if (keyremoved)\n            notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,\"del\",key,c->db->id);\n    }\n\n    server.dirty += deleted;\n\n    // 回复被删除元素的个数\n    addReplyLongLong(c,deleted);\n\ncleanup:\n    if (rangetype == ZRANGE_LEX) zslFreeLexRange(&lexrange);\n}\n\nvoid zremrangebyrankCommand(redisClient *c) {\n    zremrangeGenericCommand(c,ZRANGE_RANK);\n}\n\nvoid zremrangebyscoreCommand(redisClient *c) {\n    zremrangeGenericCommand(c,ZRANGE_SCORE);\n}\n\nvoid zremrangebylexCommand(redisClient *c) {\n    zremrangeGenericCommand(c,ZRANGE_LEX);\n}\n\n/*\n * 多态集合迭代器：可迭代集合或者有序集合\n */\ntypedef struct {\n\n    // 被迭代的对象\n    robj *subject;\n\n    // 对象的类型\n    int type; /* Set, sorted set */\n\n    // 编码\n    int encoding;\n\n    // 权重\n    double weight;\n\n    union {\n        /* Set iterators. */\n        // 集合迭代器\n        union _iterset {\n            // intset 迭代器\n            struct {\n                // 被迭代的 intset\n                intset *is;\n                // 当前节点索引\n                int ii;\n            } is;\n            // 字典迭代器\n            struct {\n                // 被迭代的字典\n                dict *dict;\n                // 字典迭代器\n                dictIterator *di;\n                // 当前字典节点\n                dictEntry *de;\n            } ht;\n        } set;\n\n        /* Sorted set iterators. */\n        // 有序集合迭代器\n        union _iterzset {\n            // ziplist 迭代器\n            struct {\n                // 被迭代的 ziplist\n                unsigned char *zl;\n                // 当前成员指针和当前分值指针\n                unsigned char *eptr, *sptr;\n            } zl;\n            // zset 迭代器\n            struct {\n                // 被迭代的 zset\n                zset *zs;\n                // 当前跳跃表节点\n                zskiplistNode *node;\n            } sl;\n        } zset;\n    } iter;\n} zsetopsrc;\n\n\n/* Use dirty flags for pointers that need to be cleaned up in the next\n * iteration over the zsetopval. \n *\n * DIRTY 常量用于标识在下次迭代之前要进行清理。\n *\n * The dirty flag for the long long value is special,\n * since long long values don't need cleanup. \n *\n * 当 DIRTY 常量作用于 long long 值时，该值不需要被清理。\n *\n * Instead, it means that we already checked that \"ell\" holds a long long,\n * or tried to convert another representation into a long long value.\n *\n * 因为它表示 ell 已经持有一个 long long 值，\n * 或者已经将一个对象转换为 long long 值。\n *\n * When this was successful, OPVAL_VALID_LL is set as well. \n *\n * 当转换成功时， OPVAL_VALID_LL 被设置。\n */\n#define OPVAL_DIRTY_ROBJ 1\n#define OPVAL_DIRTY_LL 2\n#define OPVAL_VALID_LL 4\n\n/* Store value retrieved from the iterator. \n *\n * 用于保存从迭代器里取得的值的结构\n */\ntypedef struct {\n\n    int flags;\n\n    unsigned char _buf[32]; /* Private buffer. */\n\n    // 可以用于保存 member 的几个类型\n    robj *ele;\n    unsigned char *estr;\n    unsigned int elen;\n    long long ell;\n\n    // 分值\n    double score;\n\n} zsetopval;\n\n// 类型别名\ntypedef union _iterset iterset;\ntypedef union _iterzset iterzset;\n\n/*\n * 初始化迭代器\n */\nvoid zuiInitIterator(zsetopsrc *op) {\n\n    // 迭代对象为空，无动作\n    if (op->subject == NULL)\n        return;\n\n    // 迭代集合\n    if (op->type == REDIS_SET) {\n\n        iterset *it = &op->iter.set;\n\n        // 迭代 intset\n        if (op->encoding == REDIS_ENCODING_INTSET) {\n            it->is.is = op->subject->ptr;\n            it->is.ii = 0;\n\n        // 迭代字典\n        } else if (op->encoding == REDIS_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\n        } else {\n            redisPanic(\"Unknown set encoding\");\n        }\n\n    // 迭代有序集合\n    } else if (op->type == REDIS_ZSET) {\n\n        iterzset *it = &op->iter.zset;\n\n        // 迭代 ziplist\n        if (op->encoding == REDIS_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                redisAssert(it->zl.sptr != NULL);\n            }\n\n        // 迭代跳跃表\n        } else if (op->encoding == REDIS_ENCODING_SKIPLIST) {\n            it->sl.zs = op->subject->ptr;\n            it->sl.node = it->sl.zs->zsl->header->level[0].forward;\n\n        } else {\n            redisPanic(\"Unknown sorted set encoding\");\n        }\n\n    // 未知对象类型\n    } else {\n        redisPanic(\"Unsupported type\");\n    }\n}\n\n/*\n * 清空迭代器\n */\nvoid zuiClearIterator(zsetopsrc *op) {\n\n    if (op->subject == NULL)\n        return;\n\n    if (op->type == REDIS_SET) {\n\n        iterset *it = &op->iter.set;\n\n        if (op->encoding == REDIS_ENCODING_INTSET) {\n            REDIS_NOTUSED(it); /* skip */\n\n        } else if (op->encoding == REDIS_ENCODING_HT) {\n            dictReleaseIterator(it->ht.di);\n\n        } else {\n            redisPanic(\"Unknown set encoding\");\n        }\n\n    } else if (op->type == REDIS_ZSET) {\n\n        iterzset *it = &op->iter.zset;\n\n        if (op->encoding == REDIS_ENCODING_ZIPLIST) {\n            REDIS_NOTUSED(it); /* skip */\n\n        } else if (op->encoding == REDIS_ENCODING_SKIPLIST) {\n            REDIS_NOTUSED(it); /* skip */\n\n        } else {\n            redisPanic(\"Unknown sorted set encoding\");\n        }\n    } else {\n        redisPanic(\"Unsupported type\");\n    }\n}\n\n/*\n * 返回正在被迭代的元素的长度\n */\nint zuiLength(zsetopsrc *op) {\n\n    if (op->subject == NULL)\n        return 0;\n\n    if (op->type == REDIS_SET) {\n        if (op->encoding == REDIS_ENCODING_INTSET) {\n            return intsetLen(op->subject->ptr);\n        } else if (op->encoding == REDIS_ENCODING_HT) {\n            dict *ht = op->subject->ptr;\n            return dictSize(ht);\n        } else {\n            redisPanic(\"Unknown set encoding\");\n        }\n\n    } else if (op->type == REDIS_ZSET) {\n\n        if (op->encoding == REDIS_ENCODING_ZIPLIST) {\n            return zzlLength(op->subject->ptr);\n        } else if (op->encoding == REDIS_ENCODING_SKIPLIST) {\n            zset *zs = op->subject->ptr;\n            return zs->zsl->length;\n        } else {\n            redisPanic(\"Unknown sorted set encoding\");\n        }\n\n    } else {\n        redisPanic(\"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. \n *\n * 检查迭代器当前指向的元素是否合法，如果是的话，将它保存到传入的 val 结构中，\n * 然后将迭代器的当前指针指向下一元素，函数返回 1 。\n *\n * If not valid, this means we have reached the\n * end of the structure and can abort. \n *\n * 如果当前指向的元素不合法，那么说明对象已经迭代完毕，函数返回 0 。\n */\nint zuiNext(zsetopsrc *op, zsetopval *val) {\n\n    if (op->subject == NULL)\n        return 0;\n\n    // 对上次的对象进行清理\n    if (val->flags & OPVAL_DIRTY_ROBJ)\n        decrRefCount(val->ele);\n\n    // 清零 val 结构\n    memset(val,0,sizeof(zsetopval));\n\n    // 迭代集合\n    if (op->type == REDIS_SET) {\n\n        iterset *it = &op->iter.set;\n\n        // ziplist 编码的集合\n        if (op->encoding == REDIS_ENCODING_INTSET) {\n            int64_t ell;\n\n            // 取出成员\n            if (!intsetGet(it->is.is,it->is.ii,&ell))\n                return 0;\n            val->ell = ell;\n            // 分值默认为 1.0\n            val->score = 1.0;\n\n            /* Move to next element. */\n            it->is.ii++;\n\n        // 字典编码的集合\n        } else if (op->encoding == REDIS_ENCODING_HT) {\n\n            // 已为空？\n            if (it->ht.de == NULL)\n                return 0;\n\n            // 取出成员\n            val->ele = dictGetKey(it->ht.de);\n            // 分值默认为 1.0\n            val->score = 1.0;\n\n            /* Move to next element. */\n            it->ht.de = dictNext(it->ht.di);\n        } else {\n            redisPanic(\"Unknown set encoding\");\n        }\n\n    // 迭代有序集合\n    } else if (op->type == REDIS_ZSET) {\n\n        iterzset *it = &op->iter.zset;\n\n        // ziplist 编码的有序集合\n        if (op->encoding == REDIS_ENCODING_ZIPLIST) {\n\n            /* No need to check both, but better be explicit. */\n            // 为空？\n            if (it->zl.eptr == NULL || it->zl.sptr == NULL)\n                return 0;\n\n            // 取出成员\n            redisAssert(ziplistGet(it->zl.eptr,&val->estr,&val->elen,&val->ell));\n            // 取出分值\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\n        // SKIPLIST 编码的有序集合\n        } else if (op->encoding == REDIS_ENCODING_SKIPLIST) {\n\n            if (it->sl.node == NULL)\n                return 0;\n\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            redisPanic(\"Unknown sorted set encoding\");\n        }\n    } else {\n        redisPanic(\"Unsupported type\");\n    }\n\n    return 1;\n}\n\n/*\n * 从 val 中取出 long long 值。\n */\nint zuiLongLongFromValue(zsetopval *val) {\n\n    if (!(val->flags & OPVAL_DIRTY_LL)) {\n\n        // 打开标识 DIRTY LL\n        val->flags |= OPVAL_DIRTY_LL;\n\n        // 从对象中取值\n        if (val->ele != NULL) {\n            // 从 INT 编码的字符串中取出整数\n            if (val->ele->encoding == REDIS_ENCODING_INT) {\n                val->ell = (long)val->ele->ptr;\n                val->flags |= OPVAL_VALID_LL;\n            // 从未编码的字符串中转换整数\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\n            } else {\n                redisPanic(\"Unsupported element encoding\");\n            }\n\n        // 从 ziplist 节点中取值\n        } else if (val->estr != NULL) {\n            // 将节点值（一个字符串）转换为整数\n            if (string2ll((char*)val->estr,val->elen,&val->ell))\n                val->flags |= OPVAL_VALID_LL;\n\n        } else {\n            /* The long long was already set, flag as valid. */\n            // 总是打开 VALID LL 标识\n            val->flags |= OPVAL_VALID_LL;\n        }\n    }\n\n    // 检查 VALID LL 标识是否已打开\n    return val->flags & OPVAL_VALID_LL;\n}\n\n/*\n * 根据 val 中的值，创建对象\n */\nrobj *zuiObjectFromValue(zsetopval *val) {\n\n    if (val->ele == NULL) {\n\n        // 从 long long 值中创建对象\n        if (val->estr != NULL) {\n            val->ele = createStringObject((char*)val->estr,val->elen);\n        } else {\n            val->ele = createStringObjectFromLongLong(val->ell);\n        }\n\n        // 打开 ROBJ 标识\n        val->flags |= OPVAL_DIRTY_ROBJ;\n    }\n\n    // 返回值对象\n    return val->ele;\n}\n\n/*\n * 从 val 中取出字符串\n */\nint zuiBufferFromValue(zsetopval *val) {\n\n    if (val->estr == NULL) {\n        if (val->ele != NULL) {\n            if (val->ele->encoding == REDIS_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                redisPanic(\"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\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. \n *\n * 在迭代器指定的对象中查找给定元素\n *\n * 找到返回 1 ，否则返回 0 。\n */\nint zuiFind(zsetopsrc *op, zsetopval *val, double *score) {\n\n    if (op->subject == NULL)\n        return 0;\n\n    // 集合\n    if (op->type == REDIS_SET) {\n        // 成员为整数，分值为 1.0\n        if (op->encoding == REDIS_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\n        // 成为为对象，分值为 1.0\n        } else if (op->encoding == REDIS_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            redisPanic(\"Unknown set encoding\");\n        }\n\n    // 有序集合\n    } else if (op->type == REDIS_ZSET) {\n        // 取出对象\n        zuiObjectFromValue(val);\n\n        // ziplist\n        if (op->encoding == REDIS_ENCODING_ZIPLIST) {\n\n            // 取出成员和分值\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\n        // SKIPLIST 编码\n        } else if (op->encoding == REDIS_ENCODING_SKIPLIST) {\n            zset *zs = op->subject->ptr;\n            dictEntry *de;\n\n            // 从字典中查找成员对象\n            if ((de = dictFind(zs->dict,val->ele)) != NULL) {\n                // 取出分值\n                *score = *(double*)dictGetVal(de);\n                return 1;\n            } else {\n                return 0;\n            }\n        } else {\n            redisPanic(\"Unknown sorted set encoding\");\n        }\n    } else {\n        redisPanic(\"Unsupported type\");\n    }\n}\n\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\n/*\n * 根据 aggregate 参数的值，决定如何对 *target 和 val 进行聚合计算。\n */\ninline static void zunionInterAggregate(double *target, double val, int aggregate) {\n\n    // 求和\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        // 检查是否溢出\n        if (isnan(*target)) *target = 0.0;\n\n    // 求两者小数\n    } else if (aggregate == REDIS_AGGR_MIN) {\n        *target = val < *target ? val : *target;\n\n    // 求两者大数\n    } else if (aggregate == REDIS_AGGR_MAX) {\n        *target = val > *target ? val : *target;\n\n    } else {\n        /* safety net */\n        redisPanic(\"Unknown ZUNION/INTER aggregate type\");\n    }\n}\n\nvoid zunionInterGenericCommand(redisClient *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    // 取出要处理的有序集合的个数 setnum\n    if ((getLongFromObjectOrReply(c, c->argv[2], &setnum, NULL) != REDIS_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    // setnum 参数和传入的 key 数量不相同，出错\n    if (setnum > c->argc-3) {\n        addReply(c,shared.syntaxerr);\n        return;\n    }\n\n    /* read keys to be used for input */\n    // 为每个输入 key 创建一个迭代器\n    src = zcalloc(sizeof(zsetopsrc) * setnum);\n    for (i = 0, j = 3; i < setnum; i++, j++) {\n\n        // 取出 key 对象\n        robj *obj = lookupKeyWrite(c->db,c->argv[j]);\n\n        // 创建迭代器\n        if (obj != NULL) {\n            if (obj->type != REDIS_ZSET && obj->type != REDIS_SET) {\n                zfree(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\n        // 不存在的对象设为 NULL\n        } else {\n            src[i].subject = NULL;\n        }\n\n        /* Default all weights to 1. */\n        // 默认权重为 1.0\n        src[i].weight = 1.0;\n    }\n\n    /* parse optional extra arguments */\n    // 分析并读入可选参数\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                // 权重参数\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\") != REDIS_OK)\n                    {\n                        zfree(src);\n                        return;\n                    }\n                }\n\n            } else if (remaining >= 2 && !strcasecmp(c->argv[j]->ptr,\"aggregate\")) {\n                j++; remaining--;\n                // 聚合方式\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                    zfree(src);\n                    addReply(c,shared.syntaxerr);\n                    return;\n                }\n                j++; remaining--;\n\n            } else {\n                zfree(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    // 对所有集合进行排序，以减少算法的常数项\n    qsort(src,setnum,sizeof(zsetopsrc),zuiCompareByCardinality);\n\n    // 创建结果集对象\n    dstobj = createZsetObject();\n    dstzset = dstobj->ptr;\n    memset(&zval, 0, sizeof(zval));\n\n    // ZINTERSTORE 命令\n    if (op == REDIS_OP_INTER) {\n\n        /* Skip everything if the smallest input is empty. */\n        // 只处理非空集合\n        if (zuiLength(&src[0]) > 0) {\n\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            // 遍历基数最小的 src[0] 集合\n            zuiInitIterator(&src[0]);\n            while (zuiNext(&src[0],&zval)) {\n                double score, value;\n\n                // 计算加权分值\n                score = src[0].weight * zval.score;\n                if (isnan(score)) score = 0;\n\n                // 将 src[0] 集合中的元素和其他集合中的元素做加权聚合计算\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                    // 如果当前迭代到的 src[j] 的对象和 src[0] 的对象一样，\n                    // 那么 src[0] 出现的元素必然也出现在 src[j]\n                    // 那么我们可以直接计算聚合值，\n                    // 不必进行 zuiFind 去确保元素是否出现\n                    // 这种情况在某个 key 输入了两次，\n                    // 并且这个 key 是所有输入集合中基数最小的集合时会出现\n                    if (src[j].subject == src[0].subject) {\n                        value = zval.score*src[j].weight;\n                        zunionInterAggregate(&score,value,aggregate);\n\n                    // 如果能在其他集合找到当前迭代到的元素的话\n                    // 那么进行聚合计算\n                    } else if (zuiFind(&src[j],&zval,&value)) {\n                        value *= src[j].weight;\n                        zunionInterAggregate(&score,value,aggregate);\n\n                    // 如果当前元素没出现在某个集合，那么跳出 for 循环\n                    // 处理下个元素\n                    } else {\n                        break;\n                    }\n                }\n\n                /* Only continue when present in every input. */\n                // 只在交集元素出现时，才执行以下代码\n                if (j == setnum) {\n                    // 取出值对象\n                    tmp = zuiObjectFromValue(&zval);\n                    // 加入到有序集合中\n                    znode = zslInsert(dstzset->zsl,score,tmp);\n                    incrRefCount(tmp); /* added to skiplist */\n                    // 加入到字典中\n                    dictAdd(dstzset->dict,tmp,&znode->score);\n                    incrRefCount(tmp); /* added to dictionary */\n\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\n    // ZUNIONSTORE\n    } else if (op == REDIS_OP_UNION) {\n\n        // 遍历所有输入集合\n        for (i = 0; i < setnum; i++) {\n\n            // 跳过空集合\n            if (zuiLength(&src[i]) == 0)\n                continue;\n\n            // 遍历所有集合元素\n            zuiInitIterator(&src[i]);\n            while (zuiNext(&src[i],&zval)) {\n                double score, value;\n\n                /* Skip an element that when already processed */\n                // 跳过已处理元素\n                if (dictFind(dstzset->dict,zuiObjectFromValue(&zval)) != NULL)\n                    continue;\n\n                /* Initialize score */\n                // 初始化分值\n                score = src[i].weight * zval.score;\n                // 溢出时设为 0\n                if (isnan(score)) score = 0;\n\n                /* We need to check only next sets to see if this element\n                 * exists, since we process every element just one time so\n                 * it can't exist in a previous set (otherwise it would be\n                 * already processed). */\n                for (j = (i+1); j < setnum; j++) {\n                    /* It is not safe to access the zset we are\n                     * iterating, so explicitly check for equal object. */\n                    // 当前元素的集合和被迭代集合一样\n                    // 所以同一个元素必然出现在 src[j] 和 src[i]\n                    // 程序直接计算它们的聚合值\n                    // 而不必使用 zuiFind 来检查元素是否存在\n                    if(src[j].subject == src[i].subject) {\n                        value = zval.score*src[j].weight;\n                        zunionInterAggregate(&score,value,aggregate);\n\n                    // 检查成员是否存在\n                    } else if (zuiFind(&src[j],&zval,&value)) {\n                        value *= src[j].weight;\n                        zunionInterAggregate(&score,value,aggregate);\n                    }\n                }\n\n                // 取出成员\n                tmp = zuiObjectFromValue(&zval);\n                // 插入并集元素到跳跃表\n                znode = zslInsert(dstzset->zsl,score,tmp);\n                incrRefCount(zval.ele); /* added to skiplist */\n                // 添加元素到字典\n                dictAdd(dstzset->dict,tmp,&znode->score);\n                incrRefCount(zval.ele); /* added to dictionary */\n\n                // 更新字符串最大长度\n                if (sdsEncodedObject(tmp)) {\n                    if (sdslen(tmp->ptr) > maxelelen)\n                        maxelelen = sdslen(tmp->ptr);\n                }\n            }\n            zuiClearIterator(&src[i]);\n        }\n    } else {\n        redisPanic(\"Unknown operator\");\n    }\n\n    // 删除已存在的 dstkey ，等待后面用新对象代替它\n    if (dbDelete(c->db,dstkey)) {\n        signalModifiedKey(c->db,dstkey);\n        touched = 1;\n        server.dirty++;\n    }\n\n    // 如果结果集合的长度不为 0 \n    if (dstzset->zsl->length) {\n        /* Convert to ziplist when in limits. */\n        // 看是否需要对结果集合进行编码转换\n        if (dstzset->zsl->length <= server.zset_max_ziplist_entries &&\n            maxelelen <= server.zset_max_ziplist_value)\n                zsetConvert(dstobj,REDIS_ENCODING_ZIPLIST);\n\n        // 将结果集合关联到数据库\n        dbAdd(c->db,dstkey,dstobj);\n\n        // 回复结果集合的长度\n        addReplyLongLong(c,zsetLength(dstobj));\n\n        if (!touched) signalModifiedKey(c->db,dstkey);\n\n        notifyKeyspaceEvent(REDIS_NOTIFY_ZSET,\n            (op == REDIS_OP_UNION) ? \"zunionstore\" : \"zinterstore\",\n            dstkey,c->db->id);\n\n        server.dirty++;\n\n    // 结果集为空\n    } else {\n\n        decrRefCount(dstobj);\n\n        addReply(c,shared.czero);\n\n        if (touched)\n            notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,\"del\",dstkey,c->db->id);\n    }\n\n    zfree(src);\n}\n\nvoid zunionstoreCommand(redisClient *c) {\n    zunionInterGenericCommand(c,c->argv[1], REDIS_OP_UNION);\n}\n\nvoid zinterstoreCommand(redisClient *c) {\n    zunionInterGenericCommand(c,c->argv[1], REDIS_OP_INTER);\n}\n\nvoid zrangeGenericCommand(redisClient *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    // 取出 start 和 end 参数\n    if ((getLongFromObjectOrReply(c, c->argv[2], &start, NULL) != REDIS_OK) ||\n        (getLongFromObjectOrReply(c, c->argv[3], &end, NULL) != REDIS_OK)) return;\n\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    // 取出有序集合对象\n    if ((zobj = lookupKeyReadOrReply(c,key,shared.emptymultibulk)) == NULL\n         || checkType(c,zobj,REDIS_ZSET)) return;\n\n    /* Sanitize indexes. */\n    // 将负数索引转换为正数索引\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    // 过滤/调整索引\n    if (start > end || start >= llen) {\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 == REDIS_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        // 决定迭代的方向\n        if (reverse)\n            eptr = ziplistIndex(zl,-2-(2*start));\n        else\n            eptr = ziplistIndex(zl,2*start);\n\n        redisAssertWithInfo(c,zobj,eptr != NULL);\n        sptr = ziplistNext(zl,eptr);\n\n        // 取出元素\n        while (rangelen--) {\n            redisAssertWithInfo(c,zobj,eptr != NULL && sptr != NULL);\n            redisAssertWithInfo(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 == REDIS_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        // 迭代的方向\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        // 取出元素\n        while(rangelen--) {\n            redisAssertWithInfo(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        redisPanic(\"Unknown sorted set encoding\");\n    }\n}\n\nvoid zrangeCommand(redisClient *c) {\n    zrangeGenericCommand(c,0);\n}\n\nvoid zrevrangeCommand(redisClient *c) {\n    zrangeGenericCommand(c,1);\n}\n\n/* This command implements ZRANGEBYSCORE, ZREVRANGEBYSCORE. */\nvoid genericZrangebyscoreCommand(redisClient *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    // 分析并读入范围\n    if (zslParseRange(c->argv[minidx],c->argv[maxidx],&range) != REDIS_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    // 分析并读入可选参数\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) != REDIS_OK) ||\n                    (getLongFromObjectOrReply(c, c->argv[pos+2], &limit, NULL) != REDIS_OK)) return;\n                pos += 3; remaining -= 3;\n            } else {\n                addReply(c,shared.syntaxerr);\n                return;\n            }\n        }\n    }\n\n    /* Ok, lookup the key and get the range */\n    // 取出有序集合对象\n    if ((zobj = lookupKeyReadOrReply(c,key,shared.emptymultibulk)) == NULL ||\n        checkType(c,zobj,REDIS_ZSET)) return;\n\n    if (zobj->encoding == REDIS_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        // 迭代的方向\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        // 没有元素在指定范围之内\n        if (eptr == NULL) {\n            addReply(c, shared.emptymultibulk);\n            return;\n        }\n\n        /* Get score pointer for the first element. */\n        redisAssertWithInfo(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        // 跳过 offset 指定数量的元素\n        while (eptr && offset--) {\n            if (reverse) {\n                zzlPrev(zl,&eptr,&sptr);\n            } else {\n                zzlNext(zl,&eptr,&sptr);\n            }\n        }\n\n        // 遍历并返回所有在范围内的元素\n        while (eptr && limit--) {\n\n            // 分值\n            score = zzlGetScore(sptr);\n\n            /* Abort when the node is no longer in range. */\n            // 检查分值是否符合范围\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            redisAssertWithInfo(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\n    } else if (zobj->encoding == REDIS_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        // 方向\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        // 没有值在指定范围之内\n        if (ln == NULL) {\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        // 跳过 offset 参数指定的元素数量\n        while (ln && offset--) {\n            if (reverse) {\n                ln = ln->backward;\n            } else {\n                ln = ln->level[0].forward;\n            }\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        redisPanic(\"Unknown sorted set encoding\");\n    }\n\n    if (withscores) {\n        rangelen *= 2;\n    }\n\n    setDeferredMultiBulkLength(c, replylen, rangelen);\n}\n\nvoid zrangebyscoreCommand(redisClient *c) {\n    genericZrangebyscoreCommand(c,0);\n}\n\nvoid zrevrangebyscoreCommand(redisClient *c) {\n    genericZrangebyscoreCommand(c,1);\n}\n\nvoid zcountCommand(redisClient *c) {\n    robj *key = c->argv[1];\n    robj *zobj;\n    zrangespec range;\n    int count = 0;\n\n    /* Parse the range arguments */\n    // 分析并读入范围参数\n    if (zslParseRange(c->argv[2],c->argv[3],&range) != REDIS_OK) {\n        addReplyError(c,\"min or max is not a float\");\n        return;\n    }\n\n    /* Lookup the sorted set */\n    // 取出有序集合\n    if ((zobj = lookupKeyReadOrReply(c, key, shared.czero)) == NULL ||\n        checkType(c, zobj, REDIS_ZSET)) return;\n\n    if (zobj->encoding == REDIS_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        // 指向指定范围内第一个元素的成员\n        eptr = zzlFirstInRange(zl,&range);\n\n        /* No \"first\" element */\n        // 没有任何元素在这个范围内，直接返回\n        if (eptr == NULL) {\n            addReply(c, shared.czero);\n            return;\n        }\n\n        /* First element is in range */\n        // 取出分值\n        sptr = ziplistNext(zl,eptr);\n        score = zzlGetScore(sptr);\n        redisAssertWithInfo(c,zobj,zslValueLteMax(score,&range));\n\n        /* Iterate over elements in range */\n        // 遍历范围内的所有元素\n        while (eptr) {\n\n            score = zzlGetScore(sptr);\n\n            /* Abort when the node is no longer in range. */\n            // 如果分值不符合范围，跳出\n            if (!zslValueLteMax(score,&range)) {\n                break;\n\n            // 分值符合范围，增加 count 计数器\n            // 然后指向下一个元素\n            } else {\n                count++;\n                zzlNext(zl,&eptr,&sptr);\n            }\n        }\n\n    } else if (zobj->encoding == REDIS_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        // 指向指定范围内第一个元素\n        zn = zslFirstInRange(zsl, &range);\n\n        /* Use rank of first element, if any, to determine preliminary count */\n        // 如果有至少一个元素在范围内，那么执行以下代码\n        if (zn != NULL) {\n            // 确定范围内第一个元素的排位\n            rank = zslGetRank(zsl, zn->score, zn->obj);\n\n            count = (zsl->length - (rank - 1));\n\n            /* Find last element in range */\n            // 指向指定范围内的最后一个元素\n            zn = zslLastInRange(zsl, &range);\n\n            /* Use rank of last element, if any, to determine the actual count */\n            // 如果范围内的最后一个元素不为空，那么执行以下代码\n            if (zn != NULL) {\n                // 确定范围内最后一个元素的排位\n                rank = zslGetRank(zsl, zn->score, zn->obj);\n\n                // 这里计算的就是第一个和最后一个两个元素之间的元素数量\n                // （包括这两个元素）\n                count -= (zsl->length - rank);\n            }\n        }\n\n    } else {\n        redisPanic(\"Unknown sorted set encoding\");\n    }\n\n    addReplyLongLong(c, count);\n}\n\nvoid zlexcountCommand(redisClient *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) != REDIS_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, REDIS_ZSET))\n    {\n        zslFreeLexRange(&range);\n        return;\n    }\n\n    if (zobj->encoding == REDIS_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        redisAssertWithInfo(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 == REDIS_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        redisPanic(\"Unknown sorted set encoding\");\n    }\n\n    zslFreeLexRange(&range);\n    addReplyLongLong(c, count);\n}\n\n/* This command implements ZRANGEBYLEX, ZREVRANGEBYLEX. */\nvoid genericZrangebylexCommand(redisClient *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) != REDIS_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) != REDIS_OK) ||\n                    (getLongFromObjectOrReply(c, c->argv[pos+2], &limit, NULL) != REDIS_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,REDIS_ZSET))\n    {\n        zslFreeLexRange(&range);\n        return;\n    }\n\n    if (zobj->encoding == REDIS_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        redisAssertWithInfo(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            redisAssertWithInfo(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 == REDIS_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        redisPanic(\"Unknown sorted set encoding\");\n    }\n\n    zslFreeLexRange(&range);\n    setDeferredMultiBulkLength(c, replylen, rangelen);\n}\n\nvoid zrangebylexCommand(redisClient *c) {\n    genericZrangebylexCommand(c,0);\n}\n\nvoid zrevrangebylexCommand(redisClient *c) {\n    genericZrangebylexCommand(c,1);\n}\n\nvoid zcardCommand(redisClient *c) {\n    robj *key = c->argv[1];\n    robj *zobj;\n\n    // 取出有序集合\n    if ((zobj = lookupKeyReadOrReply(c,key,shared.czero)) == NULL ||\n        checkType(c,zobj,REDIS_ZSET)) return;\n\n    // 返回集合基数\n    addReplyLongLong(c,zsetLength(zobj));\n}\n\nvoid zscoreCommand(redisClient *c) {\n    robj *key = c->argv[1];\n    robj *zobj;\n    double score;\n\n    if ((zobj = lookupKeyReadOrReply(c,key,shared.nullbulk)) == NULL ||\n        checkType(c,zobj,REDIS_ZSET)) return;\n\n    // ziplist\n    if (zobj->encoding == REDIS_ENCODING_ZIPLIST) {\n        // 取出元素\n        if (zzlFind(zobj->ptr,c->argv[2],&score) != NULL)\n            // 回复分值\n            addReplyDouble(c,score);\n        else\n            addReply(c,shared.nullbulk);\n\n    // SKIPLIST\n    } else if (zobj->encoding == REDIS_ENCODING_SKIPLIST) {\n        zset *zs = zobj->ptr;\n        dictEntry *de;\n\n        c->argv[2] = tryObjectEncoding(c->argv[2]);\n        // 直接从字典中取出并返回分值\n        de = dictFind(zs->dict,c->argv[2]);\n        if (de != NULL) {\n            score = *(double*)dictGetVal(de);\n            addReplyDouble(c,score);\n        } else {\n            addReply(c,shared.nullbulk);\n        }\n\n    } else {\n        redisPanic(\"Unknown sorted set encoding\");\n    }\n}\n\nvoid zrankGenericCommand(redisClient *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    // 有序集合\n    if ((zobj = lookupKeyReadOrReply(c,key,shared.nullbulk)) == NULL ||\n        checkType(c,zobj,REDIS_ZSET)) return;\n\n    // 元素数量\n    llen = zsetLength(zobj);\n\n    redisAssertWithInfo(c,ele,sdsEncodedObject(ele));\n\n    if (zobj->encoding == REDIS_ENCODING_ZIPLIST) {\n        unsigned char *zl = zobj->ptr;\n        unsigned char *eptr, *sptr;\n\n        eptr = ziplistIndex(zl,0);\n        redisAssertWithInfo(c,zobj,eptr != NULL);\n        sptr = ziplistNext(zl,eptr);\n        redisAssertWithInfo(c,zobj,sptr != NULL);\n\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            // ZRANK 还是 ZREVRANK ？\n            if (reverse)\n                addReplyLongLong(c,llen-rank);\n            else\n                addReplyLongLong(c,rank-1);\n        } else {\n            addReply(c,shared.nullbulk);\n        }\n\n    } else if (zobj->encoding == REDIS_ENCODING_SKIPLIST) {\n        zset *zs = zobj->ptr;\n        zskiplist *zsl = zs->zsl;\n        dictEntry *de;\n        double score;\n\n        // 从字典中取出元素\n        ele = c->argv[2] = tryObjectEncoding(c->argv[2]);\n        de = dictFind(zs->dict,ele);\n        if (de != NULL) {\n\n            // 取出元素的分值\n            score = *(double*)dictGetVal(de);\n\n            // 在跳跃表中计算该元素的排位\n            rank = zslGetRank(zsl,score,ele);\n            redisAssertWithInfo(c,ele,rank); /* Existing elements always have a rank. */\n\n            // ZRANK 还是 ZREVRANK ？\n            if (reverse)\n                addReplyLongLong(c,llen-rank);\n            else\n                addReplyLongLong(c,rank-1);\n        } else {\n            addReply(c,shared.nullbulk);\n        }\n\n    } else {\n        redisPanic(\"Unknown sorted set encoding\");\n    }\n}\n\nvoid zrankCommand(redisClient *c) {\n    zrankGenericCommand(c, 0);\n}\n\nvoid zrevrankCommand(redisClient *c) {\n    zrankGenericCommand(c, 1);\n}\n\nvoid zscanCommand(redisClient *c) {\n    robj *o;\n    unsigned long cursor;\n\n    if (parseScanCursorOrReply(c,c->argv[2],&cursor) == REDIS_ERR) return;\n    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptyscan)) == NULL ||\n        checkType(c,o,REDIS_ZSET)) return;\n    scanGenericCommand(c,o,cursor);\n}\n"
  },
  {
    "path": "src/testhelp.h",
    "content": "/* This is a really minimal testing framework for C.\n *\n * Example:\n *\n * test_cond(\"Check if 1 == 1\", 1==1)\n * test_cond(\"Check if 5 > 10\", 5 > 10)\n * test_report()\n *\n * ----------------------------------------------------------------------------\n *\n * Copyright (c) 2010-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 __TESTHELP_H\n#define __TESTHELP_H\n\nint __failed_tests = 0;\nint __test_num = 0;\n#define test_cond(descr,_c) do { \\\n    __test_num++; printf(\"%d - %s: \", __test_num, descr); \\\n    if(_c) printf(\"PASSED\\n\"); else {printf(\"FAILED\\n\"); __failed_tests++;} \\\n} while(0);\n#define test_report() do { \\\n    printf(\"%d tests, %d passed, %d failed\\n\", __test_num, \\\n                    __test_num-__failed_tests, __failed_tests); \\\n    if (__failed_tests) { \\\n        printf(\"=== WARNING === We have failed tests here...\\n\"); \\\n        exit(1); \\\n    } \\\n} while(0);\n\n#endif\n"
  },
  {
    "path": "src/util.c",
    "content": "/*\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 \"fmacros.h\"\n#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n#include <ctype.h>\n#include <limits.h>\n#include <math.h>\n#include <unistd.h>\n#include <sys/time.h>\n#include <float.h>\n\n#include \"util.h\"\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/* Convert a string representing an amount of memory into the number of\n * bytes, so for instance memtoll(\"1Gi\") 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 */\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    /* 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        mul = 1;\n    }\n    digits = u-p;\n    if (digits >= sizeof(buf)) {\n        if (err) *err = 1;\n        return LLONG_MAX;\n    }\n    memcpy(buf,p,digits);\n    buf[digits] = '\\0';\n    val = strtoll(buf,NULL,10);\n    return val*mul;\n}\n\n/* Convert a long long into a string. Returns the number of\n * characters needed to represent the number, that can be shorter if passed\n * buffer length is not enough to store the whole number. */\nint ll2string(char *s, size_t len, long long value) {\n    char buf[32], *p;\n    unsigned long long v;\n    size_t l;\n\n    if (len == 0) return 0;\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    l = 32-(p-buf);\n    if (l+1 > len) l = len-1; /* Make sure it fits, including the nul term */\n    memcpy(s,p,l);\n    s[l] = '\\0';\n    return l;\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 stdtod(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\n/* Generate the Redis \"Run ID\", a SHA1-sized random number that identifies a\n * given execution of Redis, 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 getRandomHexChars(char *p, unsigned int len) {\n    FILE *fp = fopen(\"/dev/urandom\",\"r\");\n    char *charset = \"0123456789abcdef\";\n    unsigned int j;\n\n    if (fp == NULL || fread(p,len,1,fp) == 0) {\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. */\n        for (j = 0; j < len; j++)\n            p[j] ^= rand();\n    }\n    /* Turn it into hex digits taking just 4 bits out of 8 for every byte. */\n    for (j = 0; j < len; j++)\n        p[j] = charset[p[j] & 0x0F];\n    if (fp) fclose(fp);\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\n/* Return true if the specified path is just a file basename without any\n * relative or absolute path. This function just checks that no / or \\\n * character exists inside the specified path, that's enough in the\n * environments where Redis runs. */\nint pathIsBaseName(char *path) {\n    return strchr(path,'/') == NULL && strchr(path,'\\\\') == NULL;\n}\n\n#ifdef UTIL_TEST_MAIN\n#include <assert.h>\n\nvoid test_string2ll(void) {\n    char buf[32];\n    long long v;\n\n    /* May not start with +. */\n    strcpy(buf,\"+1\");\n    assert(string2ll(buf,strlen(buf),&v) == 0);\n\n    /* Leading space. */\n    strcpy(buf,\" 1\");\n    assert(string2ll(buf,strlen(buf),&v) == 0);\n\n    /* Trailing space. */\n    strcpy(buf,\"1 \");\n    assert(string2ll(buf,strlen(buf),&v) == 0);\n\n    /* May not start with 0. */\n    strcpy(buf,\"01\");\n    assert(string2ll(buf,strlen(buf),&v) == 0);\n\n    strcpy(buf,\"-1\");\n    assert(string2ll(buf,strlen(buf),&v) == 1);\n    assert(v == -1);\n\n    strcpy(buf,\"0\");\n    assert(string2ll(buf,strlen(buf),&v) == 1);\n    assert(v == 0);\n\n    strcpy(buf,\"1\");\n    assert(string2ll(buf,strlen(buf),&v) == 1);\n    assert(v == 1);\n\n    strcpy(buf,\"99\");\n    assert(string2ll(buf,strlen(buf),&v) == 1);\n    assert(v == 99);\n\n    strcpy(buf,\"-99\");\n    assert(string2ll(buf,strlen(buf),&v) == 1);\n    assert(v == -99);\n\n    strcpy(buf,\"-9223372036854775808\");\n    assert(string2ll(buf,strlen(buf),&v) == 1);\n    assert(v == LLONG_MIN);\n\n    strcpy(buf,\"-9223372036854775809\"); /* overflow */\n    assert(string2ll(buf,strlen(buf),&v) == 0);\n\n    strcpy(buf,\"9223372036854775807\");\n    assert(string2ll(buf,strlen(buf),&v) == 1);\n    assert(v == LLONG_MAX);\n\n    strcpy(buf,\"9223372036854775808\"); /* overflow */\n    assert(string2ll(buf,strlen(buf),&v) == 0);\n}\n\nvoid test_string2l(void) {\n    char buf[32];\n    long v;\n\n    /* May not start with +. */\n    strcpy(buf,\"+1\");\n    assert(string2l(buf,strlen(buf),&v) == 0);\n\n    /* May not start with 0. */\n    strcpy(buf,\"01\");\n    assert(string2l(buf,strlen(buf),&v) == 0);\n\n    strcpy(buf,\"-1\");\n    assert(string2l(buf,strlen(buf),&v) == 1);\n    assert(v == -1);\n\n    strcpy(buf,\"0\");\n    assert(string2l(buf,strlen(buf),&v) == 1);\n    assert(v == 0);\n\n    strcpy(buf,\"1\");\n    assert(string2l(buf,strlen(buf),&v) == 1);\n    assert(v == 1);\n\n    strcpy(buf,\"99\");\n    assert(string2l(buf,strlen(buf),&v) == 1);\n    assert(v == 99);\n\n    strcpy(buf,\"-99\");\n    assert(string2l(buf,strlen(buf),&v) == 1);\n    assert(v == -99);\n\n#if LONG_MAX != LLONG_MAX\n    strcpy(buf,\"-2147483648\");\n    assert(string2l(buf,strlen(buf),&v) == 1);\n    assert(v == LONG_MIN);\n\n    strcpy(buf,\"-2147483649\"); /* overflow */\n    assert(string2l(buf,strlen(buf),&v) == 0);\n\n    strcpy(buf,\"2147483647\");\n    assert(string2l(buf,strlen(buf),&v) == 1);\n    assert(v == LONG_MAX);\n\n    strcpy(buf,\"2147483648\"); /* overflow */\n    assert(string2l(buf,strlen(buf),&v) == 0);\n#endif\n}\n\nint main(int argc, char **argv) {\n    test_string2ll();\n    test_string2l();\n    return 0;\n}\n#endif\n"
  },
  {
    "path": "src/util.h",
    "content": "/*\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#ifndef __REDIS_UTIL_H\n#define __REDIS_UTIL_H\n\n#include \"sds.h\"\n\nint stringmatchlen(const char *p, int plen, const char *s, int slen, int nocase);\nint stringmatch(const char *p, const char *s, int nocase);\nlong long memtoll(const char *p, int *err);\nint ll2string(char *s, size_t len, long long value);\nint string2ll(const char *s, size_t slen, long long *value);\nint string2l(const char *s, size_t slen, long *value);\nint d2string(char *buf, size_t len, double value);\nsds getAbsolutePath(char *filename);\nint pathIsBaseName(char *path);\n\n#endif\n"
  },
  {
    "path": "src/valgrind.sup",
    "content": "{\n   <lzf_unitialized_hash_table>\n   Memcheck:Cond\n   fun:lzf_compress\n}\n\n{\n   <lzf_unitialized_hash_table>\n   Memcheck:Value4\n   fun:lzf_compress\n}\n\n{\n   <lzf_unitialized_hash_table>\n   Memcheck:Value8\n   fun:lzf_compress\n}\n"
  },
  {
    "path": "src/version.h",
    "content": "#define REDIS_VERSION \"2.9.11\"\n"
  },
  {
    "path": "src/ziplist.c",
    "content": "/* The ziplist is a specially encoded dually linked list that is designed\n * to be very memory efficient. \n *\n * Ziplist 是为了尽可能地节约内存而设计的特殊编码双端链表。\n *\n * It stores both strings and integer values,\n * where integers are encoded as actual integers instead of a series of\n * characters. \n *\n * Ziplist 可以储存字符串值和整数值，\n * 其中，整数值被保存为实际的整数，而不是字符数组。\n *\n * 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 * Ziplist 允许在列表的两端进行 O(1) 复杂度的 push 和 pop 操作。\n * 但是，因为这些操作都需要对整个 ziplist 进行内存重分配，\n * 所以实际的复杂度和 ziplist 占用的内存大小有关。\n *\n * ----------------------------------------------------------------------------\n *\n * ZIPLIST OVERALL LAYOUT:\n * Ziplist 的整体布局：\n *\n * The general layout of the ziplist is as follows:\n * 以下是 ziplist 的一般布局：\n *\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 * <zlbytes> 是一个无符号整数，保存着 ziplist 使用的内存数量。\n *\n * 通过这个值，程序可以直接对 ziplist 的内存大小进行调整，\n * 而无须为了计算 ziplist 的内存大小而遍历整个列表。\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 * <zltail> 保存着到达列表中最后一个节点的偏移量。\n *\n * 这个偏移量使得对表尾的 pop 操作可以在无须遍历整个列表的情况下进行。\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 * <zllen> 保存着列表中的节点数量。\n * \n * 当 zllen 保存的值大于 2**16-2 时，\n * 程序需要遍历整个列表才能知道列表实际包含了多少个节点。\n *\n * <zlend> is a single byte special value, equal to 255, which indicates the\n * end of the list.\n *\n * <zlend> 的长度为 1 字节，值为 255 ，标识列表的末尾。\n *\n * ZIPLIST ENTRIES:\n * ZIPLIST 节点：\n *\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 * 每个 ziplist 节点的前面都带有一个 header ，这个 header 包含两部分信息：\n *\n * 1)前置节点的长度，在程序从后向前遍历时使用。\n *\n * 2)当前节点所保存的值的类型和长度。\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 * 编码前置节点的长度的方法如下：\n *\n * 1) 如果前置节点的长度小于 254 字节，那么程序将使用 1 个字节来保存这个长度值。\n *\n * 2) 如果前置节点的长度大于等于 254 字节，那么程序将使用 5 个字节来保存这个长度值：\n *    a) 第 1 个字节的值将被设为 254 ，用于标识这是一个 5 字节长的长度值。\n *    b) 之后的 4 个字节则用于保存前置节点的实际长度。\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 * header 另一部分的内容和节点所保存的值有关。\n *\n * 1) 如果节点保存的是字符串值，\n *    那么这部分 header 的头 2 个位将保存编码字符串长度所使用的类型，\n *    而之后跟着的内容则是字符串的实际长度。\n *\n * |00pppppp| - 1 byte\n *      String value with length less than or equal to 63 bytes (6 bits).\n *      字符串的长度小于或等于 63 字节。\n * |01pppppp|qqqqqqqq| - 2 bytes\n *      String value with length less than or equal to 16383 bytes (14 bits).\n *      字符串的长度小于或等于 16383 字节。\n * |10______|qqqqqqqq|rrrrrrrr|ssssssss|tttttttt| - 5 bytes\n *      String value with length greater than or equal to 16384 bytes.\n *      字符串的长度大于或等于 16384 字节。\n *\n * 2) 如果节点保存的是整数值，\n *    那么这部分 header 的头 2 位都将被设置为 1 ，\n *    而之后跟着的 2 位则用于标识节点所保存的整数的类型。\n *\n * |11000000| - 1 byte\n *      Integer encoded as int16_t (2 bytes).\n *      节点的值为 int16_t 类型的整数，长度为 2 字节。\n * |11010000| - 1 byte\n *      Integer encoded as int32_t (4 bytes).\n *      节点的值为 int32_t 类型的整数，长度为 4 字节。\n * |11100000| - 1 byte\n *      Integer encoded as int64_t (8 bytes).\n *      节点的值为 int64_t 类型的整数，长度为 8 字节。\n * |11110000| - 1 byte\n *      Integer encoded as 24 bit signed (3 bytes).\n *      节点的值为 24 位（3 字节）长的整数。\n * |11111110| - 1 byte\n *      Integer encoded as 8 bit signed (1 byte).\n *      节点的值为 8 位（1 字节）长的整数。\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 *      节点的值为介于 0 至 12 之间的无符号整数。\n *      因为 0000 和 1111 都不能使用，所以位的实际值将是 1 至 13 。\n *      程序在取得这 4 个位的值之后，还需要减去 1 ，才能计算出正确的值。\n *      比如说，如果位的值为 0001 = 1 ，那么程序返回的值将是 1 - 1 = 0 。\n * |11111111| - End of ziplist.\n *      ziplist 的结尾标识\n *\n * All the integers are represented in little endian byte order.\n *\n * 所有整数都表示为小端字节序。\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#include \"zmalloc.h\"\n#include \"util.h\"\n#include \"ziplist.h\"\n#include \"endianconv.h\"\n#include \"redisassert.h\"\n\n/*\n * ziplist 末端标识符，以及 5 字节长长度标识符\n */\n#define ZIP_END 255\n#define ZIP_BIGLEN 254\n\n/* Different encoding/length possibilities */\n/*\n * 字符串编码和整数编码的掩码\n */\n#define ZIP_STR_MASK 0xc0\n#define ZIP_INT_MASK 0x30\n\n/*\n * 字符串编码类型\n */\n#define ZIP_STR_06B (0 << 6)\n#define ZIP_STR_14B (1 << 6)\n#define ZIP_STR_32B (2 << 6)\n\n/*\n * 整数编码类型\n */\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\n/* 4 bit integer immediate encoding \n *\n * 4 位整数编码的掩码和类型\n */\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/*\n * 24 位整数的最大值和最小值\n */\n#define INT24_MAX 0x7fffff\n#define INT24_MIN (-INT24_MAX - 1)\n\n/* Macro to determine type \n *\n * 查看给定编码 enc 是否字符串编码\n */\n#define ZIP_IS_STR(enc) (((enc) & ZIP_STR_MASK) < ZIP_STR_MASK)\n\n/* Utility macros */\n/*\n * ziplist 属性宏\n */\n// 定位到 ziplist 的 bytes 属性，该属性记录了整个 ziplist 所占用的内存字节数\n// 用于取出 bytes 属性的现有值，或者为 bytes 属性赋予新值\n#define ZIPLIST_BYTES(zl)       (*((uint32_t*)(zl)))\n// 定位到 ziplist 的 offset 属性，该属性记录了到达表尾节点的偏移量\n// 用于取出 offset 属性的现有值，或者为 offset 属性赋予新值\n#define ZIPLIST_TAIL_OFFSET(zl) (*((uint32_t*)((zl)+sizeof(uint32_t))))\n// 定位到 ziplist 的 length 属性，该属性记录了 ziplist 包含的节点数量\n// 用于取出 length 属性的现有值，或者为 length 属性赋予新值\n#define ZIPLIST_LENGTH(zl)      (*((uint16_t*)((zl)+sizeof(uint32_t)*2)))\n// 返回 ziplist 表头的大小\n#define ZIPLIST_HEADER_SIZE     (sizeof(uint32_t)*2+sizeof(uint16_t))\n// 返回指向 ziplist 第一个节点（的起始位置）的指针\n#define ZIPLIST_ENTRY_HEAD(zl)  ((zl)+ZIPLIST_HEADER_SIZE)\n// 返回指向 ziplist 最后一个节点（的起始位置）的指针\n#define ZIPLIST_ENTRY_TAIL(zl)  ((zl)+intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl)))\n// 返回指向 ziplist 末端 ZIP_END （的起始位置）的指针\n#define ZIPLIST_ENTRY_END(zl)   ((zl)+intrev32ifbe(ZIPLIST_BYTES(zl))-1)\n\n/* \n空白 ziplist 示例图\n\narea        |<---- ziplist header ---->|<-- end -->|\n\nsize          4 bytes   4 bytes 2 bytes  1 byte\n            +---------+--------+-------+-----------+\ncomponent   | zlbytes | zltail | zllen | zlend     |\n            |         |        |       |           |\nvalue       |  1011   |  1010  |   0   | 1111 1111 |\n            +---------+--------+-------+-----------+\n                                       ^\n                                       |\n                               ZIPLIST_ENTRY_HEAD\n                                       &\naddress                        ZIPLIST_ENTRY_TAIL\n                                       &\n                               ZIPLIST_ENTRY_END\n\n非空 ziplist 示例图\n\narea        |<---- ziplist header ---->|<----------- entries ------------->|<-end->|\n\nsize          4 bytes  4 bytes  2 bytes    ?        ?        ?        ?     1 byte\n            +---------+--------+-------+--------+--------+--------+--------+-------+\ncomponent   | zlbytes | zltail | zllen | entry1 | entry2 |  ...   | entryN | zlend |\n            +---------+--------+-------+--------+--------+--------+--------+-------+\n                                       ^                          ^        ^\naddress                                |                          |        |\n                                ZIPLIST_ENTRY_HEAD                |   ZIPLIST_ENTRY_END\n                                                                  |\n                                                        ZIPLIST_ENTRY_TAIL\n*/\n\n/* We know a positive increment can only be 1 because entries can only be\n * pushed one at a time. */\n/*\n * 增加 ziplist 的节点数\n *\n * T = O(1)\n */\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\n/*\n * 保存 ziplist 节点信息的结构\n */\ntypedef struct zlentry {\n\n    // prevrawlen ：前置节点的长度\n    // prevrawlensize ：编码 prevrawlen 所需的字节大小\n    unsigned int prevrawlensize, prevrawlen;\n\n    // len ：当前节点值的长度\n    // lensize ：编码 len 所需的字节大小\n    unsigned int lensize, len;\n\n    // 当前节点 header 的大小\n    // 等于 prevrawlensize + lensize\n    unsigned int headersize;\n\n    // 当前节点值所使用的编码类型\n    unsigned char encoding;\n\n    // 指向当前节点的指针\n    unsigned char *p;\n\n} zlentry;\n\n/* Extract the encoding from the byte pointed by 'ptr' and set it into\n * 'encoding'. \n *\n * 从 ptr 中取出节点值的编码类型，并将它保存到 encoding 变量中。\n *\n * T = O(1)\n */\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\n/* Return bytes needed to store integer encoded by 'encoding' \n *\n * 返回保存 encoding 编码的值所需的字节数量\n *\n * T = O(1)\n */\nstatic unsigned int zipIntSize(unsigned char encoding) {\n\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\n    assert(NULL);\n    return 0;\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. \n *\n * 编码节点长度值 l ，并将它写入到 p 中，然后返回编码 l 所需的字节数量。\n *\n * 如果 p 为 NULL ，那么仅返回编码 l 所需的字节数量，不进行写入。\n *\n * T = O(1)\n */\nstatic unsigned int zipEncodeLength(unsigned char *p, unsigned char encoding, unsigned int rawlen) {\n    unsigned char len = 1, buf[5];\n\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\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    // 将编码后的长度写入 p \n    memcpy(p,buf,len);\n\n    // 返回编码所需的字节数\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 *\n * 解码 ptr 指针，取出列表节点的相关信息，并将它们保存在以下变量中：\n *\n * - encoding 保存节点值的编码类型。\n *\n * - lensize 保存编码节点长度所需的字节数。\n *\n * - len 保存节点的长度。\n *\n * T = O(1)\n */\n#define ZIP_DECODE_LENGTH(ptr, encoding, lensize, len) do {                    \\\n                                                                               \\\n    /* 取出值的编码类型 */                                                     \\\n    ZIP_ENTRY_ENCODING((ptr), (encoding));                                     \\\n                                                                               \\\n    /* 字符串编码 */                                                           \\\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                                                                               \\\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. \n *\n * 对前置节点的长度 len 进行编码，并将它写入到 p 中，\n * 然后返回编码 len 所需的字节数量。\n *\n * 如果 p 为 NULL ，那么不进行写入，仅返回编码 len 所需的字节数量。\n *\n * T = O(1)\n */\nstatic unsigned int zipPrevEncodeLength(unsigned char *p, unsigned int len) {\n\n    // 仅返回编码 len 所需的字节数量\n    if (p == NULL) {\n        return (len < ZIP_BIGLEN) ? 1 : sizeof(len)+1;\n\n    // 写入并返回编码 len 所需的字节数量\n    } else {\n\n        // 1 字节\n        if (len < ZIP_BIGLEN) {\n            p[0] = len;\n            return 1;\n\n        // 5 字节\n        } else {\n            // 添加 5 字节长度标识\n            p[0] = ZIP_BIGLEN;\n            // 写入编码\n            memcpy(p+1,&len,sizeof(len));\n            // 如果有必要的话，进行大小端转换\n            memrev32ifbe(p+1);\n            // 返回编码长度\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). \n *\n * 将原本只需要 1 个字节来保存的前置节点长度 len 编码至一个 5 字节长的 header 中。\n *\n * T = O(1)\n */\nstatic void zipPrevEncodeLengthForceLarge(unsigned char *p, unsigned int len) {\n\n    if (p == NULL) return;\n\n    // 设置 5 字节长度标识\n    p[0] = ZIP_BIGLEN;\n\n    // 写入 len\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 *\n * 解码 ptr 指针，\n * 取出编码前置节点长度所需的字节数，并将它保存到 prevlensize 变量中。\n *\n * T = O(1)\n */\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 *\n * 解码 ptr 指针，\n * 取出编码前置节点长度所需的字节数，\n * 并将这个字节数保存到 prevlensize 中。\n *\n * 然后根据 prevlensize ，从 ptr 中取出前置节点的长度值，\n * 并将这个长度值保存到 prevlen 变量中。\n *\n * T = O(1)\n */\n#define ZIP_DECODE_PREVLEN(ptr, prevlensize, prevlen) do {                     \\\n                                                                               \\\n    /* 先计算被编码长度值的字节数 */                                           \\\n    ZIP_DECODE_PREVLENSIZE(ptr, prevlensize);                                  \\\n                                                                               \\\n    /* 再根据编码字节数来取出长度值 */                                         \\\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'. \n *\n * 计算编码新的前置节点长度 len 所需的字节数，\n * 减去编码 p 原来的前置节点长度所需的字节数之差。\n *\n * T = O(1)\n */\nstatic int zipPrevLenByteDiff(unsigned char *p, unsigned int len) {\n    unsigned int prevlensize;\n\n    // 取出编码原来的前置节点长度所需的字节数\n    // T = O(1)\n    ZIP_DECODE_PREVLENSIZE(p, prevlensize);\n\n    // 计算编码 len 所需的字节数，然后进行减法运算\n    // T = O(1)\n    return zipPrevEncodeLength(NULL, len) - prevlensize;\n}\n\n/* Return the total number of bytes used by the entry pointed to by 'p'. \n *\n * 返回指针 p 所指向的节点占用的字节数总和。\n *\n * T = O(1)\n */\nstatic unsigned int zipRawEntryLength(unsigned char *p) {\n    unsigned int prevlensize, encoding, lensize, len;\n\n    // 取出编码前置节点的长度所需的字节数\n    // T = O(1)\n    ZIP_DECODE_PREVLENSIZE(p, prevlensize);\n\n    // 取出当前节点值的编码类型，编码节点值长度所需的字节数，以及节点值的长度\n    // T = O(1)\n    ZIP_DECODE_LENGTH(p + prevlensize, encoding, lensize, len);\n\n    // 计算节点占用的字节数总和\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'. \n *\n * 检查 entry 中指向的字符串能否被编码为整数。\n *\n * 如果可以的话，\n * 将编码后的整数保存在指针 v 的值中，并将编码的方式保存在指针 encoding 的值中。\n *\n * 注意，这里的 entry 和前面代表节点的 entry 不是一个意思。\n *\n * T = O(N)\n */\nstatic int zipTryEncoding(unsigned char *entry, unsigned int entrylen, long long *v, unsigned char *encoding) {\n    long long value;\n\n    // 忽略太长或太短的字符串\n    if (entrylen >= 32 || entrylen == 0) return 0;\n\n    // 尝试转换\n    // T = O(N)\n    if (string2ll((char*)entry,entrylen,&value)) {\n\n        /* Great, the string can be encoded. Check what's the smallest\n         * of our encoding types that can hold this value. */\n        // 转换成功，以从小到大的顺序检查适合值 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\n        // 记录值到指针\n        *v = value;\n\n        // 返回转换成功标识\n        return 1;\n    }\n\n    // 转换失败\n    return 0;\n}\n\n/* Store integer 'value' at 'p', encoded as 'encoding' \n * \n * 以 encoding 指定的编码方式，将整数值 value 写入到 p 。\n *\n * T = O(1)\n */\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\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'\n * \n * 以 encoding 指定的编码方式，读取并返回指针 p 中的整数值。\n *\n * T = O(1)\n */\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\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\n    return ret;\n}\n\n/* Return a struct with all information about an entry. \n *\n * 将 p 所指向的列表节点的信息全部保存到 zlentry 中，并返回该 zlentry 。\n *\n * T = O(1)\n */\nstatic zlentry zipEntry(unsigned char *p) {\n    zlentry e;\n\n    // e.prevrawlensize 保存着编码前一个节点的长度所需的字节数\n    // e.prevrawlen 保存着前一个节点的长度\n    // T = O(1)\n    ZIP_DECODE_PREVLEN(p, e.prevrawlensize, e.prevrawlen);\n\n    // p + e.prevrawlensize 将指针移动到列表节点本身\n    // e.encoding 保存着节点值的编码类型\n    // e.lensize 保存着编码节点值长度所需的字节数\n    // e.len 保存着节点值的长度\n    // T = O(1)\n    ZIP_DECODE_LENGTH(p + e.prevrawlensize, e.encoding, e.lensize, e.len);\n\n    // 计算头结点的字节数\n    e.headersize = e.prevrawlensize + e.lensize;\n\n    // 记录指针\n    e.p = p;\n\n    return e;\n}\n\n/* Create a new empty ziplist. \n *\n * 创建并返回一个新的 ziplist \n *\n * T = O(1)\n */\nunsigned char *ziplistNew(void) {\n\n    // ZIPLIST_HEADER_SIZE 是 ziplist 表头的大小\n    // 1 字节是表末端 ZIP_END 的大小\n    unsigned int bytes = ZIPLIST_HEADER_SIZE+1;\n\n    // 为表头和表末端分配空间\n    unsigned char *zl = zmalloc(bytes);\n\n    // 初始化表属性\n    ZIPLIST_BYTES(zl) = intrev32ifbe(bytes);\n    ZIPLIST_TAIL_OFFSET(zl) = intrev32ifbe(ZIPLIST_HEADER_SIZE);\n    ZIPLIST_LENGTH(zl) = 0;\n\n    // 设置表末端\n    zl[bytes-1] = ZIP_END;\n\n    return zl;\n}\n\n/* Resize the ziplist. \n *\n * 调整 ziplist 的大小为 len 字节。\n *\n * 当 ziplist 原有的大小小于 len 时，扩展 ziplist 不会改变 ziplist 原有的元素。\n *\n * T = O(N)\n */\nstatic unsigned char *ziplistResize(unsigned char *zl, unsigned int len) {\n\n    // 用 zrealloc ，扩展时不改变现有元素\n    zl = zrealloc(zl,len);\n\n    // 更新 bytes 属性\n    ZIPLIST_BYTES(zl) = intrev32ifbe(len);\n\n    // 重新设置表末端\n    zl[len-1] = ZIP_END;\n\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 * 当将一个新节点添加到某个节点之前的时候，\n * 如果原节点的 header 空间不足以保存新节点的长度，\n * 那么就需要对原节点的 header 空间进行扩展（从 1 字节扩展到 5 字节）。\n *\n * 但是，当对原节点进行扩展之后，原节点的下一个节点的 prevlen 可能出现空间不足，\n * 这种情况在多个连续节点的长度都接近 ZIP_BIGLEN 时可能发生。\n *\n * 这个函数就用于检查并修复后续节点的空间问题。\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 * 反过来说，\n * 因为节点的长度变小而引起的连续缩小也是可能出现的，\n * 不过，为了避免扩展-缩小-扩展-缩小这样的情况反复出现（flapping，抖动），\n * 我们不处理这种情况，而是任由 prevlen 比所需的长度更长。\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. \n *\n * 注意，程序的检查是针对 p 的后续节点，而不是 p 所指向的节点。\n * 因为节点 p 在传入之前已经完成了所需的空间扩展工作。\n *\n * T = O(N^2)\n */\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    // T = O(N^2)\n    while (p[0] != ZIP_END) {\n\n        // 将 p 所指向的节点的信息保存到 cur 结构中\n        cur = zipEntry(p);\n        // 当前节点的长度\n        rawlen = cur.headersize + cur.len;\n        // 计算编码当前节点的长度所需的字节数\n        // T = O(1)\n        rawlensize = zipPrevEncodeLength(NULL,rawlen);\n\n        /* Abort if there is no next entry. */\n        // 如果已经没有后续空间需要更新了，跳出\n        if (p[rawlen] == ZIP_END) break;\n\n        // 取出后续节点的信息，保存到 next 结构中\n        // T = O(1)\n        next = zipEntry(p+rawlen);\n\n        /* Abort when \"prevlen\" has not changed. */\n        // 后续节点编码当前节点的空间已经足够，无须再进行任何处理，跳出\n        // 可以证明，只要遇到一个空间足够的节点，\n        // 那么这个节点之后的所有节点的空间都是足够的\n        if (next.prevrawlen == rawlen) break;\n\n        if (next.prevrawlensize < rawlensize) {\n\n            /* The \"prevlen\" field of \"next\" needs more bytes to hold\n             * the raw length of \"cur\". */\n            // 执行到这里，表示 next 空间的大小不足以编码 cur 的长度\n            // 所以程序需要对 next 节点的（header 部分）空间进行扩展\n\n            // 记录 p 的偏移量\n            offset = p-zl;\n            // 计算需要增加的节点数量\n            extra = rawlensize-next.prevrawlensize;\n            // 扩展 zl 的大小\n            // T = O(N)\n            zl = ziplistResize(zl,curlen+extra);\n            // 还原指针 p\n            p = zl+offset;\n\n            /* Current pointer and offset for next element. */\n            // 记录下一节点的偏移量\n            np = p+rawlen;\n            noffset = np-zl;\n\n            /* Update tail offset when next element is not the tail element. */\n            // 当 next 节点不是表尾节点时，更新列表到表尾节点的偏移量\n            // \n            // 不用更新的情况（next 为表尾节点）：\n            //\n            // |     | next |      ==>    |     | new next          |\n            //       ^                          ^\n            //       |                          |\n            //     tail                        tail\n            //\n            // 需要更新的情况（next 不是表尾节点）：\n            //\n            // | next |     |   ==>     | new next          |     |\n            //        ^                        ^\n            //        |                        |\n            //    old tail                 old tail\n            // \n            // 更新之后：\n            //\n            // | new next          |     |\n            //                     ^\n            //                     |\n            //                  new tail\n            // T = O(1)\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            // 向后移动 cur 节点之后的数据，为 cur 的新 header 腾出空间\n            //\n            // 示例：\n            //\n            // | header | value |  ==>  | header |    | value |  ==>  | header      | value |\n            //                                   |<-->|\n            //                            为新 header 腾出的空间\n            // T = O(N)\n            memmove(np+rawlensize,\n                np+next.prevrawlensize,\n                curlen-noffset-next.prevrawlensize-1);\n            // 将新的前一节点长度值编码进新的 next 节点的 header\n            // T = O(1)\n            zipPrevEncodeLength(np,rawlen);\n\n            /* Advance the cursor */\n            // 移动指针，继续处理下个节点\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                // 执行到这里，说明 next 节点编码前置节点的 header 空间有 5 字节\n                // 而编码 rawlen 只需要 1 字节\n                // 但是程序不会对 next 进行缩小，\n                // 所以这里只将 rawlen 写入 5 字节的 header 中就算了。\n                // T = O(1)\n                zipPrevEncodeLengthForceLarge(p+rawlen,rawlen);\n            } else {\n                // 运行到这里，\n                // 说明 cur 节点的长度正好可以编码到 next 节点的 header 中\n                // T = O(1)\n                zipPrevEncodeLength(p+rawlen,rawlen);\n            }\n\n            /* Stop here, as the raw length of \"next\" has not changed. */\n            break;\n        }\n    }\n\n    return zl;\n}\n\n/* Delete \"num\" entries, starting at \"p\". Returns pointer to the ziplist. \n *\n * 从位置 p 开始，连续删除 num 个节点。\n *\n * 函数的返回值为处理删除操作之后的 ziplist 。\n *\n * T = O(N^2)\n */\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    // 计算被删除节点总共占用的内存字节数\n    // 以及被删除节点的总个数\n    // T = O(N)\n    first = zipEntry(p);\n    for (i = 0; p[0] != ZIP_END && i < num; i++) {\n        p += zipRawEntryLength(p);\n        deleted++;\n    }\n\n    // totlen 是所有被删除节点总共占用的内存字节数\n    totlen = p-first.p;\n    if (totlen > 0) {\n        if (p[0] != ZIP_END) {\n\n            // 执行这里，表示被删除节点之后仍然有节点存在\n\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            // 因为位于被删除范围之后的第一个节点的 header 部分的大小\n            // 可能容纳不了新的前置节点，所以需要计算新旧前置节点之间的字节数差\n            // T = O(1)\n            nextdiff = zipPrevLenByteDiff(p,first.prevrawlen);\n            // 如果有需要的话，将指针 p 后退 nextdiff 字节，为新 header 空出空间\n            p -= nextdiff;\n            // 将 first 的前置节点的长度编码至 p 中\n            // T = O(1)\n            zipPrevEncodeLength(p,first.prevrawlen);\n\n            /* Update offset for tail */\n            // 更新到达表尾的偏移量\n            // T = O(1)\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            // 如果被删除节点之后，有多于一个节点\n            // 那么程序需要将 nextdiff 记录的字节数也计算到表尾偏移量中\n            // 这样才能让表尾偏移量正确对齐表尾节点\n            // T = O(1)\n            tail = zipEntry(p);\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            // 从表尾向表头移动数据，覆盖被删除节点的数据\n            // T = O(N)\n            memmove(first.p,p,\n                intrev32ifbe(ZIPLIST_BYTES(zl))-(p-zl)-1);\n        } else {\n\n            // 执行这里，表示被删除节点之后已经没有其他节点了\n\n            /* The entire tail was deleted. No need to move memory. */\n            // T = O(1)\n            ZIPLIST_TAIL_OFFSET(zl) =\n                intrev32ifbe((first.p-zl)-first.prevrawlen);\n        }\n\n        /* Resize and update length */\n        // 缩小并更新 ziplist 的长度\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        // 如果 p 所指向的节点的大小已经变更，那么进行级联更新\n        // 检查 p 之后的所有节点是否符合 ziplist 的编码要求\n        // T = O(N^2)\n        if (nextdiff != 0)\n            zl = __ziplistCascadeUpdate(zl,p);\n    }\n\n    return zl;\n}\n\n/* Insert item at \"p\". */\n/*\n * 根据指针 p 所指定的位置，将长度为 slen 的字符串 s 插入到 zl 中。\n *\n * 函数的返回值为完成插入操作之后的 ziplist\n *\n * T = O(N^2)\n */\nstatic unsigned char *__ziplistInsert(unsigned char *zl, unsigned char *p, unsigned char *s, unsigned int slen) {\n    // 记录当前 ziplist 的长度\n    size_t curlen = intrev32ifbe(ZIPLIST_BYTES(zl)), reqlen, 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 entry, tail;\n\n    /* Find out prevlen for the entry that is inserted. */\n    if (p[0] != ZIP_END) {\n        // 如果 p[0] 不指向列表末端，说明列表非空，并且 p 正指向列表的其中一个节点\n        // 那么取出 p 所指向节点的信息，并将它保存到 entry 结构中\n        // 然后用 prevlen 变量记录前置节点的长度\n        // （当插入新节点之后 p 所指向的节点就成了新节点的前置节点）\n        // T = O(1)\n        entry = zipEntry(p);\n        prevlen = entry.prevrawlen;\n    } else {\n        // 如果 p 指向表尾末端，那么程序需要检查列表是否为：\n        // 1)如果 ptail 也指向 ZIP_END ，那么列表为空；\n        // 2)如果列表不为空，那么 ptail 将指向列表的最后一个节点。\n        unsigned char *ptail = ZIPLIST_ENTRY_TAIL(zl);\n        if (ptail[0] != ZIP_END) {\n            // 表尾节点为新节点的前置节点\n\n            // 取出表尾节点的长度\n            // T = O(1)\n            prevlen = zipRawEntryLength(ptail);\n        }\n    }\n\n    /* See if the entry can be encoded */\n    // 尝试看能否将输入字符串转换为整数，如果成功的话：\n    // 1)value 将保存转换后的整数值\n    // 2)encoding 则保存适用于 value 的编码方式\n    // 无论使用什么编码， reqlen 都保存节点值的长度\n    // T = O(N)\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    // 计算编码前置节点的长度所需的大小\n    // T = O(1)\n    reqlen += zipPrevEncodeLength(NULL,prevlen);\n    // 计算编码当前节点值所需的大小\n    // T = O(1)\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    // 只要新节点不是被添加到列表末端，\n    // 那么程序就需要检查看 p 所指向的节点（的 header）能否编码新节点的长度。\n    // nextdiff 保存了新旧编码之间的字节大小差，如果这个值大于 0 \n    // 那么说明需要对 p 所指向的节点（的 header ）进行扩展\n    // T = O(1)\n    nextdiff = (p[0] != ZIP_END) ? zipPrevLenByteDiff(p,reqlen) : 0;\n\n    /* Store offset because a realloc may change the address of zl. */\n    // 因为重分配空间可能会改变 zl 的地址\n    // 所以在分配之前，需要记录 zl 到 p 的偏移量，然后在分配之后依靠偏移量还原 p \n    offset = p-zl;\n    // curlen 是 ziplist 原来的长度\n    // reqlen 是整个新节点的长度\n    // nextdiff 是新节点的后继节点扩展 header 的长度（要么 0 字节，要么 4 个字节）\n    // T = O(N)\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        // 新元素之后还有节点，因为新元素的加入，需要对这些原有节点进行调整\n\n        /* Subtract one because of the ZIP_END bytes */\n        // 移动现有元素，为新元素的插入空间腾出位置\n        // T = O(N)\n        memmove(p+reqlen,p-nextdiff,curlen-offset-1+nextdiff);\n\n        /* Encode this entry's raw length in the next entry. */\n        // 将新节点的长度编码至后置节点\n        // p+reqlen 定位到后置节点\n        // reqlen 是新节点的长度\n        // T = O(1)\n        zipPrevEncodeLength(p+reqlen,reqlen);\n\n        /* Update offset for tail */\n        // 更新到达表尾的偏移量，将新节点的长度也算上\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        // 如果新节点的后面有多于一个节点\n        // 那么程序需要将 nextdiff 记录的字节数也计算到表尾偏移量中\n        // 这样才能让表尾偏移量正确对齐表尾节点\n        // T = O(1)\n        tail = zipEntry(p+reqlen);\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        // 新元素是新的表尾节点\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    // 当 nextdiff != 0 时，新节点的后继节点的（header 部分）长度已经被改变，\n    // 所以需要级联地更新后续的节点\n    if (nextdiff != 0) {\n        offset = p-zl;\n        // T  = O(N^2)\n        zl = __ziplistCascadeUpdate(zl,p+reqlen);\n        p = zl+offset;\n    }\n\n    /* Write the entry */\n    // 一切搞定，将前置节点的长度写入新节点的 header\n    p += zipPrevEncodeLength(p,prevlen);\n    // 将节点值的长度写入新节点的 header\n    p += zipEncodeLength(p,encoding,slen);\n    // 写入节点值\n    if (ZIP_IS_STR(encoding)) {\n        // T = O(N)\n        memcpy(p,s,slen);\n    } else {\n        // T = O(1)\n        zipSaveInteger(p,value,encoding);\n    }\n\n    // 更新列表的节点数量计数器\n    // T = O(1)\n    ZIPLIST_INCR_LENGTH(zl,1);\n\n    return zl;\n}\n\n/*\n * 将长度为 slen 的字符串 s 推入到 zl 中。\n *\n * where 参数的值决定了推入的方向：\n * - 值为 ZIPLIST_HEAD 时，将新值推入到表头。\n * - 否则，将新值推入到表末端。\n *\n * 函数的返回值为添加新值后的 ziplist 。\n *\n * T = O(N^2)\n */\nunsigned char *ziplistPush(unsigned char *zl, unsigned char *s, unsigned int slen, int where) {\n\n    // 根据 where 参数的值，决定将值推入到表头还是表尾\n    unsigned char *p;\n    p = (where == ZIPLIST_HEAD) ? ZIPLIST_ENTRY_HEAD(zl) : ZIPLIST_ENTRY_END(zl);\n\n    // 返回添加新值后的 ziplist\n    // T = O(N^2)\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. */\n/*\n * 根据给定索引，遍历列表，并返回索引指定节点的指针。\n *\n * 如果索引为正，那么从表头向表尾遍历。\n * 如果索引为负，那么从表尾向表头遍历。\n * 正数索引从 0 开始，负数索引从 -1 开始。\n *\n * 如果索引超过列表的节点数量，或者列表为空，那么返回 NULL 。\n *\n * T = O(N)\n */\nunsigned char *ziplistIndex(unsigned char *zl, int index) {\n\n    unsigned char *p;\n\n    zlentry entry;\n\n    // 处理负数索引\n    if (index < 0) {\n\n        // 将索引转换为正数\n        index = (-index)-1;\n        \n        // 定位到表尾节点\n        p = ZIPLIST_ENTRY_TAIL(zl);\n\n        // 如果列表不为空，那么。。。\n        if (p[0] != ZIP_END) {\n\n            // 从表尾向表头遍历\n            entry = zipEntry(p);\n            // T = O(N)\n            while (entry.prevrawlen > 0 && index--) {\n                // 前移指针\n                p -= entry.prevrawlen;\n                // T = O(1)\n                entry = zipEntry(p);\n            }\n        }\n\n    // 处理正数索引\n    } else {\n\n        // 定位到表头节点\n        p = ZIPLIST_ENTRY_HEAD(zl);\n\n        // T = O(N)\n        while (p[0] != ZIP_END && index--) {\n            // 后移指针\n            // T = O(1)\n            p += zipRawEntryLength(p);\n        }\n    }\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. */\n/*\n * 返回 p 所指向节点的后置节点。\n *\n * 如果 p 为表末端，或者 p 已经是表尾节点，那么返回 NULL 。\n *\n * T = O(1)\n */\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    // p 已经指向列表末端\n    if (p[0] == ZIP_END) {\n        return NULL;\n    }\n\n    // 指向后一节点\n    p += zipRawEntryLength(p);\n    if (p[0] == ZIP_END) {\n        // p 已经是表尾节点，没有后置节点\n        return NULL;\n    }\n\n    return p;\n}\n\n/* Return pointer to previous entry in ziplist. */\n/*\n * 返回 p 所指向节点的前置节点。\n *\n * 如果 p 所指向为空列表，或者 p 已经指向表头节点，那么返回 NULL 。\n *\n * T = O(1)\n */\nunsigned char *ziplistPrev(unsigned char *zl, unsigned char *p) {\n    zlentry entry;\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    \n    // 如果 p 指向列表末端（列表为空，或者刚开始从表尾向表头迭代）\n    // 那么尝试取出列表尾端节点\n    if (p[0] == ZIP_END) {\n        p = ZIPLIST_ENTRY_TAIL(zl);\n        // 尾端节点也指向列表末端，那么列表为空\n        return (p[0] == ZIP_END) ? NULL : p;\n    \n    // 如果 p 指向列表头，那么说明迭代已经完成\n    } else if (p == ZIPLIST_ENTRY_HEAD(zl)) {\n        return NULL;\n\n    // 既不是表头也不是表尾，从表尾向表头移动指针\n    } else {\n        // 计算前一个节点的节点数\n        entry = zipEntry(p);\n        assert(entry.prevrawlen > 0);\n        // 移动指针，指向前一个节点\n        return p-entry.prevrawlen;\n    }\n}\n\n/* Get entry pointed to by 'p' and store in either 'e' or 'v' depending\n * on the encoding of the entry. 'e' 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. */\n/*\n * 取出 p 所指向节点的值：\n *\n * - 如果节点保存的是字符串，那么将字符串值指针保存到 *sstr 中，字符串长度保存到 *slen\n *\n * - 如果节点保存的是整数，那么将整数保存到 *sval\n *\n * 程序可以通过检查 *sstr 是否为 NULL 来检查值是字符串还是整数。\n *\n * 提取值成功返回 1 ，\n * 如果 p 为空，或者 p 指向的是列表末端，那么返回 0 ，提取值失败。\n *\n * T = O(1)\n */\nunsigned int ziplistGet(unsigned char *p, unsigned char **sstr, unsigned int *slen, long long *sval) {\n\n    zlentry entry;\n    if (p == NULL || p[0] == ZIP_END) return 0;\n    if (sstr) *sstr = NULL;\n\n    // 取出 p 所指向的节点的各项信息，并保存到结构 entry 中\n    // T = O(1)\n    entry = zipEntry(p);\n\n    // 节点的值为字符串，将字符串长度保存到 *slen ，字符串保存到 *sstr\n    // T = O(1)\n    if (ZIP_IS_STR(entry.encoding)) {\n        if (sstr) {\n            *slen = entry.len;\n            *sstr = p+entry.headersize;\n        }\n    \n    // 节点的值为整数，解码值，并将值保存到 *sval\n    // T = O(1)\n    } else {\n        if (sval) {\n            *sval = zipLoadInteger(p+entry.headersize,entry.encoding);\n        }\n    }\n\n    return 1;\n}\n\n/* Insert an entry at \"p\". \n *\n * 将包含给定值 s 的新节点插入到给定的位置 p 中。\n *\n * 如果 p 指向一个节点，那么新节点将放在原有节点的前面。\n *\n * T = O(N^2)\n */\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. \n *\n * 从 zl 中删除 *p 所指向的节点，\n * 并且原地更新 *p 所指向的位置，使得可以在迭代列表的过程中对节点进行删除。\n *\n * T = O(N^2)\n */\nunsigned char *ziplistDelete(unsigned char *zl, unsigned char **p) {\n\n    // 因为 __ziplistDelete 时会对 zl 进行内存重分配\n    // 而内存充分配可能会改变 zl 的内存地址\n    // 所以这里需要记录到达 *p 的偏移量\n    // 这样在删除节点之后就可以通过偏移量来将 *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\n    return zl;\n}\n\n/* Delete a range of entries from the ziplist. \n *\n * 从 index 索引指定的节点开始，连续地从 zl 中删除 num 个节点。\n *\n * T = O(N^2)\n */\nunsigned char *ziplistDeleteRange(unsigned char *zl, unsigned int index, unsigned int num) {\n\n    // 根据索引定位到节点\n    // T = O(N)\n    unsigned char *p = ziplistIndex(zl,index);\n\n    // 连续删除 num 个节点\n    // T = O(N^2)\n    return (p == NULL) ? zl : __ziplistDelete(zl,p,num);\n}\n\n/* Compare entry pointer to by 'p' with 'entry'. Return 1 if equal. \n *\n * 将 p 所指向的节点的值和 sstr 进行对比。\n *\n * 如果节点值和 sstr 的值相等，返回 1 ，不相等则返回 0 。\n *\n * T = O(N)\n */\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    // 取出节点\n    entry = zipEntry(p);\n    if (ZIP_IS_STR(entry.encoding)) {\n\n        // 节点值为字符串，进行字符串对比\n\n        /* Raw compare */\n        if (entry.len == slen) {\n            // T = O(N)\n            return memcmp(p+entry.headersize,sstr,slen) == 0;\n        } else {\n            return 0;\n        }\n    } else {\n        \n        // 节点值为整数，进行整数对比\n\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          // T = O(1)\n          zval = zipLoadInteger(p+entry.headersize,entry.encoding);\n          return zval == sval;\n        }\n    }\n\n    return 0;\n}\n\n/* Find pointer to the entry equal to the specified entry. \n * \n * 寻找节点值和 vstr 相等的列表节点，并返回该节点的指针。\n * \n * Skip 'skip' entries between every comparison. \n *\n * 每次比对之前都跳过 skip 个节点。\n *\n * Returns NULL when the field could not be found. \n *\n * 如果找不到相应的节点，则返回 NULL 。\n *\n * T = O(N^2)\n */\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    // 只要未到达列表末端，就一直迭代\n    // T = O(N^2)\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\n            /* Compare current entry with specified entry */\n            // 对比字符串值\n            // T = O(N)\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                // 因为传入值有可能被编码了，\n                // 所以当第一次进行值对比时，程序会对传入值进行解码\n                // 这个解码操作只会进行一次\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                // 对比整数值\n                if (vencoding != UCHAR_MAX) {\n                    // T = O(1)\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        // 后移指针，指向后置节点\n        p = q + len;\n    }\n\n    // 没有找到指定的节点\n    return NULL;\n}\n\n/* Return length of ziplist. \n * \n * 返回 ziplist 中的节点个数\n *\n * T = O(N)\n */\nunsigned int ziplistLen(unsigned char *zl) {\n\n    unsigned int len = 0;\n\n    // 节点数小于 UINT16_MAX\n    // T = O(1)\n    if (intrev16ifbe(ZIPLIST_LENGTH(zl)) < UINT16_MAX) {\n        len = intrev16ifbe(ZIPLIST_LENGTH(zl));\n\n    // 节点数大于 UINT16_MAX 时，需要遍历整个列表才能计算出节点数\n    // T = O(N)\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\n    return len;\n}\n\n/* Return ziplist blob size in bytes. \n *\n * 返回整个 ziplist 占用的内存字节数\n *\n * T = O(1)\n */\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        entry = zipEntry(p);\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 ZIPLIST_TEST_MAIN\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\nunsigned 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\nunsigned 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\nlong long usec(void) {\n    struct timeval tv;\n    gettimeofday(&tv,NULL);\n    return (((long long)tv.tv_sec)*1000000)+tv.tv_usec;\n}\n\nvoid 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        zfree(zl);\n    }\n}\n\nvoid 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        else\n            printf(\"%lld\", vlong);\n\n        printf(\"\\n\");\n        ziplistDeleteRange(zl,-1,1);\n    } else {\n        printf(\"ERROR: Could not pop\\n\");\n        exit(1);\n    }\n}\n\nint 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\nvoid verify(unsigned char *zl, zlentry *e) {\n    int i;\n    int len = ziplistLen(zl);\n    zlentry _e;\n\n    for (i = 0; i < len; i++) {\n        memset(&e[i], 0, sizeof(zlentry));\n        e[i] = zipEntry(ziplistIndex(zl, i));\n\n        memset(&_e, 0, sizeof(zlentry));\n        _e = zipEntry(ziplistIndex(zl, -len+i));\n\n        assert(memcmp(&e[i], &_e, sizeof(zlentry)) == 0);\n    }\n}\n\nint main(int argc, char **argv) {\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    zl = createList();\n    ziplistRepr(zl);\n\n    pop(zl,ZIPLIST_TAIL);\n    ziplistRepr(zl);\n\n    pop(zl,ZIPLIST_HEAD);\n    ziplistRepr(zl);\n\n    pop(zl,ZIPLIST_TAIL);\n    ziplistRepr(zl);\n\n    pop(zl,ZIPLIST_TAIL);\n    ziplistRepr(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    }\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    }\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    }\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    }\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    }\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    }\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    }\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    }\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    }\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    }\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    }\n\n    printf(\"Delete inclusive range 0,0:\\n\");\n    {\n        zl = createList();\n        zl = ziplistDeleteRange(zl, 0, 1);\n        ziplistRepr(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    }\n\n    printf(\"Delete inclusive range 1,2:\\n\");\n    {\n        zl = createList();\n        zl = ziplistDeleteRange(zl, 1, 2);\n        ziplistRepr(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    }\n\n    printf(\"Delete with num overflow:\\n\");\n    {\n        zl = createList();\n        zl = ziplistDeleteRange(zl, 1, 5);\n        ziplistRepr(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    }\n\n    printf(\"Regression test for >255 byte strings:\\n\");\n    {\n        char v1[257],v2[257];\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        assert(ziplistGet(p,&entry,&elen,&value));\n        assert(strncmp(v1,(char*)entry,elen) == 0);\n        p = ziplistIndex(zl,1);\n        assert(ziplistGet(p,&entry,&elen,&value));\n        assert(strncmp(v2,(char*)entry,elen) == 0);\n        printf(\"SUCCESS\\n\\n\");\n    }\n\n    printf(\"Regression test deleting next to last entries:\\n\");\n    {\n        char v[3][257];\n        zlentry e[3];\n        int 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    }\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            assert(ziplistGet(p,NULL,NULL,&value));\n            assert(i == value);\n\n            p = ziplistIndex(zl,-i-1);\n            assert(ziplistGet(p,NULL,NULL,&value));\n            assert(999-i == value);\n        }\n        printf(\"SUCCESS\\n\\n\");\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    }\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        list *ref;\n        listNode *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 = listCreate();\n            listSetFreeMethod(ref,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                    listAddNodeHead(ref,sdsnewlen(buf, buflen));\n                } else if (where == ZIPLIST_TAIL) {\n                    listAddNodeTail(ref,sdsnewlen(buf, buflen));\n                } else {\n                    assert(NULL);\n                }\n            }\n\n            assert(listLength(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 = listIndex(ref,j);\n\n                assert(ziplistGet(p,&sstr,&slen,&sval));\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,listNodeValue(refnode),buflen) == 0);\n            }\n            zfree(zl);\n            listRelease(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\n#endif\n"
  },
  {
    "path": "src/ziplist.h",
    "content": "/*\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#define ZIPLIST_HEAD 0\n#define ZIPLIST_TAIL 1\n\nunsigned char *ziplistNew(void);\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, unsigned 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"
  },
  {
    "path": "src/zipmap.c",
    "content": "/* String -> String Map data structure optimized for size.\n *\n * 为节约空间而实现的字符串到字符串映射结构\n *\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 * 本文件实现了一个将字符串映射到另一个字符串的数据结构，\n * 这个数据结构非常节约内存，并且支持复杂度为 O(N) 的查找操作。\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 * Redis 使用这个数据结构来储存键值对数量不多的 Hash ，\n * 一旦键值对的数量超过某个给定值，Hash 的底层表示就会自动转换成哈希表。\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 * 因为很多时候，一个 Hash 都只保存少数几个 key-value 对，\n * 所以使用 zipmap 比起直接使用真正的哈希表要节约不少内存。\n *\n * --------------------------------------------------------------------------\n *\n * 注意，从 2.6 版本开始， Redis 使用 ziplist 来表示小 Hash ，\n * 而不再使用 zipmap ，\n * 具体信息请见：https://github.com/antirez/redis/issues/188\n * -- huangz\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 * 以下是带有 \"foo\" => \"bar\" 和 \"hello\" => \"world\" 两个映射的 zipmap 的内存结构：\n *\n * <zmlen><len>\"foo\"<len><free>\"bar\"<len>\"hello\"<len><free>\"world\"<ZIPMAP_END>\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 * <zmlen> 的长度为 1 字节，它记录了 zipmap 保存的键值对数量：\n *\n *  1) 只有在 zipmap 的键值对数量 < 254 时，这个值才被使用。\n *\n *  2) 当 zipmap 的键值对数量 >= 254 ，程序需要遍历整个 zipmap 才能知道它的确切大小。\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 * 252, it's a single-byte length. If it is 253 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. The special value 254 is used to mark\n * empty space that can be used to add new key/value pairs.\n *\n * <len> 表示跟在它后面的字符串(键或值)的长度。\n *\n * <len> 可以用 1 字节或者 5 字节来编码：\n *\n *   * 如果 <len> 的第一字节(无符号 8 bit)是介于 0 至 252 之间的值，\n *     那么这个字节就是字符串的长度。\n *\n *   * 如果第一字节的值为 253 ，那么这个字节之后的 4 字节无符号整数\n *     (大/小端由所宿主机器决定)就是字符串的长度。\n *\n *   * 值 254 用于标识未被使用的、可以添加新 key-value 对的空间。\n *\n *   * 值 255 用于表示 zipmap 的末尾。\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> 是字符串之后，未被使用的字节数量。\n *\n * <free> 的值用于记录那些因为值被修改，而被节约下来的空间数量。\n *\n * 举个例子：\n * zimap 里原本有一个 \"foo\" -> \"bar\" 的映射，\n * 但是后来它被修改为 \"foo\" -> \"hi\" ，\n * 现在，在字符串 \"hi\" 之后就有一个字节的未使用空间。\n *\n * 似乎情况，未使用的空间可以用于将来再次对值做修改\n * （比如，再次将 \"foo\" 的值修改为 \"yoo\" ，等等）\n * 如果未使用空间足够大，那么在它里面添加一个新的 key-value 对也是可能的。\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 * <free> 总是一个无符号 8 位数字。\n * 在执行更新操作之后，如果剩余字节数大于等于 ZIPMAP_VALUE_MAX_FREE ，\n * 那么 zipmap 就会进行重分配，并对自身空间进行紧缩，\n * 因此， <free> 的值不会很大，8 位的长度对于保存 <free> 来说已经足够。\n *\n * The most compact representation of the above two elements hash is actually:\n *\n * 一个包含 \"foo\" -> \"bar\" 和 \"hello\" -> \"world\" 映射的 zipmap 的最紧凑的表示如下：\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 * 因此 zipmap 的查找操作的复杂度为 O(N) ，\n * 其中 N 是键值对的数量，而不是 zipmap 的字节数量（前者的常数更小一些）。\n */\n\n#include <stdio.h>\n#include <string.h>\n#include \"zmalloc.h\"\n#include \"endianconv.h\"\n\n// 一个字节所能保存的 zipmap 元素数量不能等于或超过这个值\n#define ZIPMAP_BIGLEN 254\n\n// zipmap 的结束标识\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// <free> 域允许的最大值\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// 返回编码给定长度 _l 所需的字节数\n#define ZIPMAP_LEN_BYTES(_l) (((_l) < ZIPMAP_BIGLEN) ? 1 : sizeof(unsigned int)+1)\n\n/* Create a new empty zipmap. \n *\n * 创建一个新的 zipmap\n *\n * T = O(1)\n */\nunsigned char *zipmapNew(void) {\n\n    unsigned char *zm = zmalloc(2);\n\n    zm[0] = 0; /* Length */\n    zm[1] = ZIPMAP_END;\n\n    return zm;\n}\n\n/* Decode the encoded length pointed by 'p' \n *\n * 解码并返回 p 所指向的已编码长度\n *\n * T = O(1)\n */\nstatic unsigned int zipmapDecodeLength(unsigned char *p) {\n\n    unsigned int len = *p;\n\n    // 单字节长度\n    if (len < ZIPMAP_BIGLEN) return len;\n\n    // 5 字节长度\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. \n *\n * 编码长度 l ，将它写入到 p 中，然后返回编码 l 所需的字节数。\n * 如果 p 是 NULL ，那么函数只返回编码 l 所需的字节数，不进行任何写入。\n *\n * T = O(1)\n */\nstatic unsigned int zipmapEncodeLength(unsigned char *p, unsigned int len) {\n\n    // 只返回编码所需的字节数\n    if (p == NULL) {\n        return ZIPMAP_LEN_BYTES(len);\n\n    // 编码，并写入，然后返回编码所需的字节数\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 * 在 zipmap 中查找和给定 key 匹配的节点：\n *\n *  1)找到的话就返回节点的指针。\n *\n *  2)没找到则返回 NULL 。\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. \n *\n * 如果没有找到相应的节点（函数返回 NULL），并且 totlen 不为 NULL ，\n * 那么 *totlen 的值将被设为整个 zipmap 的大小，\n * 这样调用者就可以根据 *totlen 的值，对 zipmap 进行内存重分配，\n * 从而让 zipmap 容纳更多节点。\n *\n * T = O(N^2)\n */\nstatic unsigned char *zipmapLookupRaw(unsigned char *zm, unsigned char *key, unsigned int klen, unsigned int *totlen) {\n\n    // zm+1 略过 <zmlen> 属性，将 p 指向 zipmap 的首个节点\n    unsigned char *p = zm+1, *k = NULL;\n    unsigned int l,llen;\n\n    // 遍历整个 zipmap 来寻找\n    // T = O(N^2)\n    while(*p != ZIPMAP_END) {\n        unsigned char free;\n\n        /* Match or skip the key */\n        // 计算键的长度\n        l = zipmapDecodeLength(p);\n        // 计算编码键的长度所需的字节数\n        llen = zipmapEncodeLength(NULL,l);\n        // 对比 key\n        // T = O(N)\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                // 如果调用者需要知道整个 zipmap 的长度，那么记录找到的指针到变量 k\n                // 之后遍历时，程序只计算 zipmap 剩余节点的长度，不再用 memcmp 进行对比\n                // 因为 k 已经不为 NULL 了\n                k = p;\n            } else {\n                // 如果调用者不需要知道整个 zipmap 的长度，那么直接返回 p \n                return p;\n            }\n        }\n\n        // 越过键节点，指向值节点\n        p += llen+l;\n\n        /* Skip the value as well */\n        // 计算值的长度\n        l = zipmapDecodeLength(p);\n        // 计算编码值的长度所需的字节数，\n        // 并移动指针 p ，越过该 <len> 属性，指向 <free> 属性\n        p += zipmapEncodeLength(NULL,l);\n        // 取出 <free> 属性的值\n        free = p[0];\n        // 略过值节点，指向下一节点\n        p += l+1+free; /* +1 to skip the free byte */\n    }\n\n    // 计算并记录 zipmap 的空间长度\n    // + 1 是将 ZIPMAP_END 也计算在内\n    if (totlen != NULL) *totlen = (unsigned int)(p-zm)+1;\n\n    // 返回找到 key 的指针\n    return k;\n}\n\n/*\n * 返回键长度为 klen 而值长度为 vlen 的新节点所需的字节数\n *\n * T = O(1)\n */\nstatic unsigned long zipmapRequiredLength(unsigned int klen, unsigned int vlen) {\n    unsigned int l;\n\n    // 节点的基本长度要求：\n    // 1) klen : 键的长度\n    // 2) vlen : 值的长度\n    // 3) 两个字符串都需要至少 1 字节来保存长度，而 <free> 也需要 1 个字节，共 3 字节\n    l = klen+vlen+3;\n\n    // 如果 1 字节不足以编码键的长度，那么需要多 4 个字节\n    if (klen >= ZIPMAP_BIGLEN) l += 4;\n\n    // 如果 1 字节不足以编码值的长度，那么需要多 4 个字节\n    if (vlen >= ZIPMAP_BIGLEN) l += 4;\n\n    return l;\n}\n\n/* Return the total amount used by a key (encoded length + payload) \n *\n * 返回键所占用的字节数\n *\n * 包括编码长度值所需的字节数，以及值的长度本身\n *\n * T = O(1)\n */\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) \n *\n * 返回值所占用的字节总数\n *\n * 包括编码长度值所需的字节数，单个字节的 <free> 属性，以及值的长度本身\n *\n * T = O(1)\n */\nstatic unsigned int zipmapRawValueLength(unsigned char *p) {\n\n    // 取出值的长度\n    unsigned int l = zipmapDecodeLength(p);\n    unsigned int used;\n    \n    // 编码长度所需的字节数\n    used = zipmapEncodeLength(NULL,l);\n    // 计算总和\n    used += p[used] + 1 + l;\n\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). \n *\n * 如果 p 指向一个键，那么这个函数返回保存这个节点所需的字节数总量\n *\n * 节点的总量 = 键占用的空间数量 + 值占用的空间数量 + free 属性占用的空间数量\n *\n * T = O(1)\n */\nstatic unsigned int zipmapRawEntryLength(unsigned char *p) {\n    unsigned int l = zipmapRawKeyLength(p);\n    return l + zipmapRawValueLength(p+l);\n}\n\n/*\n * 将 zipmap 的大小调整为 len 。\n *\n * T = O(N)\n */\nstatic inline unsigned char *zipmapResize(unsigned char *zm, unsigned int len) {\n\n    zm = zrealloc(zm, len);\n\n    zm[len-1] = ZIPMAP_END;\n\n    return zm;\n}\n\n/* Set key to value, creating the key if it does not already exist.\n *\n * 将 key 的值设置为 value ，如果 key 不存在于 zipmap 中，那么新创建一个。\n *\n * If 'update' is not NULL, *update is set to 1 if the key was\n * already preset, otherwise to 0. \n *\n * 如果 update 不为 NULL ：\n *\n *  1) 那么在 key 已经存在时，将 *update 设为 1 。\n *\n *  2) 如果 key 未存在，将 *update 设为 0 。\n *\n * T = O(N^2)\n */\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    // 计算节点所需的长度\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    // 按 key 在 zipmap 中查找节点\n    // T = O(N^2)\n    p = zipmapLookupRaw(zm,key,klen,&zmlen);\n    if (p == NULL) {\n\n        /* Key not found: enlarge */\n        // key 不存在，扩展 zipmap\n\n        // T = O(N)\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\n        /* Key found. Is there enough space for the new value? */\n        /* Compute the total length: */\n        // 键已经存在，检查旧的值空间大小能否满足新值\n        // 如果不满足的话，扩展 zipmap 并移动数据\n\n        if (update) *update = 1;\n        // T = O(1)\n        freelen = zipmapRawEntryLength(p);\n        if (freelen < reqlen) {\n\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            // 如果已有空间不满足新值所需空间，那么对 zipmap 进行扩展\n            // T = O(N)\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            // 向后移动数据，为节点空出足以存放新值的空间\n            // T = O(N)\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    // 计算节点空余空间的长度，如果空余空间太大了，就进行缩短\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        // 前移数据，覆盖空余空间\n        // T = O(N)\n        memmove(p+reqlen, p+freelen, zmlen-(offset+freelen+1));\n        zmlen -= empty;\n        // 缩小 zipmap ，移除多余的空间\n        // T = O(N)\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\n    /* Key: */\n    // 写入键\n    // T = O(N)\n    p += zipmapEncodeLength(p,klen);\n    memcpy(p,key,klen);\n    p += klen;\n\n    /* Value: */\n    // 写入值\n    // T = O(N)\n    p += zipmapEncodeLength(p,vlen);\n    *p++ = vempty;\n    memcpy(p,val,vlen);\n\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. \n *\n * 从 zipmap 中删除包含给定 key 的节点。\n *\n * 如果 deleted 参数不为 NULL ，那么：\n *\n *  1) 如果因为 key 没找到而导致删除失败，那么将 *deleted 设为 0 。\n *\n *  2) 如果 key 找到了，并且成功将它删除了，那么将 *deleted 设为 1 。\n *\n * T = O(N^2)\n */\nunsigned char *zipmapDel(unsigned char *zm, unsigned char *key, unsigned int klen, int *deleted) {\n\n    unsigned int zmlen, freelen;\n\n    // T = O(N^2)\n    unsigned char *p = zipmapLookupRaw(zm,key,klen,&zmlen);\n    if (p) {\n        // 找到，进行删除\n\n        // 计算节点的总长\n        freelen = zipmapRawEntryLength(p);\n        // 移动内存，覆盖被删除的数据\n        // T = O(N)\n        memmove(p, p+freelen, zmlen-((p-zm)+freelen+1));\n        // 缩小 zipmap \n        // T = O(N)\n        zm = zipmapResize(zm, zmlen-freelen);\n\n        /* Decrease zipmap length */\n        // 减少 zipmap 的节点数量\n        // 注意，如果节点数量已经大于等于 ZIPMAP_BIGLEN \n        // 那么这里不会进行减少，只有在调用 zipmapLen 的时候\n        // 如果有需要的话，正确的节点数量才会被设置\n        // 具体请看 zipmapLen 的源码\n        if (zm[0] < ZIPMAP_BIGLEN) zm[0]--;\n\n        if (deleted) *deleted = 1;\n    } else {\n        if (deleted) *deleted = 0;\n    }\n\n    return zm;\n}\n\n/* Call before iterating through elements via zipmapNext() \n *\n * 在通过 zipmapNext 遍历 zipmap 之前调用\n *\n * 返回指向 zipmap 首个节点的指针。\n *\n * T = O(1)\n */\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 *\n * 这个函数用于遍历 zipmap 的所有元素。\n *\n * In the first call the first argument is the pointer to the zipmap + 1.\n *\n * 在第一次调用这个函数时， zm 参数的值为 zipmap + 1 \n * （也即是，指向 zipmap 的第一个节点）\n *\n * In the next calls what zipmapNext returns is used as first argument.\n *\n * 而在之后的调用中， zm 参数的值为之前调用 zipmapNext 时所返回的值。\n *\n * Example:\n * \n * 示例：\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 *\n * T = O(1)\n */\nunsigned char *zipmapNext(unsigned char *zm, unsigned char **key, unsigned int *klen, unsigned char **value, unsigned int *vlen) {\n\n    // 已到达列表末尾，停止迭代\n    if (zm[0] == ZIPMAP_END) return NULL;\n\n    // 取出键，并保存到 key 参数中\n    if (key) {\n        *key = zm;\n        *klen = zipmapDecodeLength(zm);\n        *key += ZIPMAP_LEN_BYTES(*klen);\n    }\n    // 越过键\n    zm += zipmapRawKeyLength(zm);\n\n    // 取出值，并保存到 value 参数中\n    if (value) {\n        *value = zm+1;\n        *vlen = zipmapDecodeLength(zm);\n        *value += ZIPMAP_LEN_BYTES(*vlen);\n    }\n    // 越过值\n    zm += zipmapRawValueLength(zm);\n\n    // 返回指向下一节点的指针\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. \n *\n * 在 zipmap 中按 key 进行查找，\n * 将值的指针保存到 *value 中，并将值的长度保存到 *vlen 中。\n *\n * 成功找到值时函数返回 1 ，没找到则返回 0 。\n *\n * T = O(N^2)\n */\nint zipmapGet(unsigned char *zm, unsigned char *key, unsigned int klen, unsigned char **value, unsigned int *vlen) {\n    unsigned char *p;\n\n    // 在 zipmap 中按 key 查找\n    // 没找到直接返回 0 \n    // T = O(N^2)\n    if ((p = zipmapLookupRaw(zm,key,klen,NULL)) == NULL) return 0;\n\n    // 越过键，指向值\n    p += zipmapRawKeyLength(p);\n    // 取出值的长度\n    *vlen = zipmapDecodeLength(p);\n    // 将 *value 指向值， +1 为越过 <free> 属性\n    *value = p + ZIPMAP_LEN_BYTES(*vlen) + 1;\n\n    // 找到，返回 1 \n    return 1;\n}\n\n/* Return 1 if the key exists, otherwise 0 is returned. \n *\n * 如果给定 key 存在于 zipmap 中，那么返回 1 ，不存在则返回 0 。\n *\n * T = O(N^2)\n */\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 \n *\n * 返回 zipmap 中包含的节点数\n *\n * T = O(N)\n */\nunsigned int zipmapLen(unsigned char *zm) {\n\n    unsigned int len = 0;\n\n    if (zm[0] < ZIPMAP_BIGLEN) {\n        // 长度可以用 1 字节保存\n        // T = O(1)\n        len = zm[0];\n    } else {\n        // 长度不能用 1 字节保存，需要遍历整个 zipmap\n        // T = O(N)\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        // 如果字节数量已经少于 ZIPMAP_BIGLEN ，那么重新将值保存到 len 中\n        // 这种情况在节点数超过 ZIPMAP_BIGLEN 之后，有节点被删除时会出现\n        if (len < ZIPMAP_BIGLEN) zm[0] = len;\n    }\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. \n *\n * 返回整个 zipmap 占用的字节大小\n *\n * T = O(N)\n */\nsize_t zipmapBlobLen(unsigned char *zm) {\n\n    unsigned int totlen;\n\n    // 虽然 zipmapLookupRaw 一般情况下的复杂度为 O(N^2)\n    // 但是当 key 参数为 NULL 时，无须使用 memcmp 来进行字符串对比\n    // zipmapLookupRaw 退化成一个单纯的计算长度的函数来使用\n    // 这种情况下， zipmapLookupRaw 的复杂度为 O(N)\n    zipmapLookupRaw(zm,NULL,0,&totlen);\n\n    return totlen;\n}\n\n#ifdef ZIPMAP_TEST_MAIN\nvoid zipmapRepr(unsigned char *p) {\n    unsigned int l;\n\n    printf(\"{status %u}\",*p++);\n    while(1) {\n        if (p[0] == ZIPMAP_END) {\n            printf(\"{end}\");\n            break;\n        } else {\n            unsigned char e;\n\n            l = zipmapDecodeLength(p);\n            printf(\"{key %u}\",l);\n            p += zipmapEncodeLength(NULL,l);\n            if (l != 0 && fwrite(p,l,1,stdout) == 0) perror(\"fwrite\");\n            p += l;\n\n            l = zipmapDecodeLength(p);\n            printf(\"{value %u}\",l);\n            p += zipmapEncodeLength(NULL,l);\n            e = *p++;\n            if (l != 0 && fwrite(p,l,1,stdout) == 0) perror(\"fwrite\");\n            p += l+e;\n            if (e) {\n                printf(\"[\");\n                while(e--) printf(\".\");\n                printf(\"]\");\n            }\n        }\n    }\n    printf(\"\\n\");\n}\n\nint main(void) {\n    unsigned char *zm;\n\n    zm = zipmapNew();\n\n    zm = zipmapSet(zm,(unsigned char*) \"name\",4, (unsigned char*) \"foo\",3,NULL);\n    zm = zipmapSet(zm,(unsigned char*) \"surname\",7, (unsigned char*) \"foo\",3,NULL);\n    zm = zipmapSet(zm,(unsigned char*) \"age\",3, (unsigned char*) \"foo\",3,NULL);\n    zipmapRepr(zm);\n\n    zm = zipmapSet(zm,(unsigned char*) \"hello\",5, (unsigned char*) \"world!\",6,NULL);\n    zm = zipmapSet(zm,(unsigned char*) \"foo\",3, (unsigned char*) \"bar\",3,NULL);\n    zm = zipmapSet(zm,(unsigned char*) \"foo\",3, (unsigned char*) \"!\",1,NULL);\n    zipmapRepr(zm);\n    zm = zipmapSet(zm,(unsigned char*) \"foo\",3, (unsigned char*) \"12345\",5,NULL);\n    zipmapRepr(zm);\n    zm = zipmapSet(zm,(unsigned char*) \"new\",3, (unsigned char*) \"xx\",2,NULL);\n    zm = zipmapSet(zm,(unsigned char*) \"noval\",5, (unsigned char*) \"\",0,NULL);\n    zipmapRepr(zm);\n    zm = zipmapDel(zm,(unsigned char*) \"new\",3,NULL);\n    zipmapRepr(zm);\n\n    printf(\"\\nLook up large key:\\n\");\n    {\n        unsigned char buf[512];\n        unsigned char *value;\n        unsigned int vlen, i;\n        for (i = 0; i < 512; i++) buf[i] = 'a';\n\n        zm = zipmapSet(zm,buf,512,(unsigned char*) \"long\",4,NULL);\n        if (zipmapGet(zm,buf,512,&value,&vlen)) {\n            printf(\"  <long key> is associated to the %d bytes value: %.*s\\n\",\n                vlen, vlen, value);\n        }\n    }\n\n    printf(\"\\nPerform a direct lookup:\\n\");\n    {\n        unsigned char *value;\n        unsigned int vlen;\n\n        if (zipmapGet(zm,(unsigned char*) \"foo\",3,&value,&vlen)) {\n            printf(\"  foo is associated to the %d bytes value: %.*s\\n\",\n                vlen, vlen, value);\n        }\n    }\n    printf(\"\\nIterate through elements:\\n\");\n    {\n        unsigned char *i = zipmapRewind(zm);\n        unsigned char *key, *value;\n        unsigned int klen, vlen;\n\n        while((i = zipmapNext(i,&key,&klen,&value,&vlen)) != NULL) {\n            printf(\"  %d:%.*s => %d:%.*s\\n\", klen, klen, key, vlen, vlen, value);\n        }\n    }\n    return 0;\n}\n#endif\n"
  },
  {
    "path": "src/zipmap.h",
    "content": "/* String -> String Map data structure optimized for size.\n *\n * See zipmap.c for more info.\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#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": "src/zmalloc.c",
    "content": "/* zmalloc - total amount of allocated memory aware version of malloc()\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#include <stdio.h>\n#include <stdlib.h>\n\n/* This function provide us access to the original libc free(). This is useful\n * for instance to free results obtained by backtrace_symbols(). We need\n * to define this function before including zmalloc.h that may shadow the\n * free implementation if we use jemalloc or another non standard allocator. */\nvoid zlibc_free(void *ptr) {\n    free(ptr);\n}\n\n#include <string.h>\n#include <pthread.h>\n#include \"config.h\"\n#include \"zmalloc.h\"\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/* Explicitly override malloc/free etc when using tcmalloc. */\n#if defined(USE_TCMALLOC)\n#define malloc(size) tc_malloc(size)\n#define calloc(count,size) tc_calloc(count,size)\n#define realloc(ptr,size) tc_realloc(ptr,size)\n#define free(ptr) tc_free(ptr)\n#elif defined(USE_JEMALLOC)\n#define malloc(size) je_malloc(size)\n#define calloc(count,size) je_calloc(count,size)\n#define realloc(ptr,size) je_realloc(ptr,size)\n#define free(ptr) je_free(ptr)\n#endif\n\n#ifdef HAVE_ATOMIC\n#define update_zmalloc_stat_add(__n) __sync_add_and_fetch(&used_memory, (__n))\n#define update_zmalloc_stat_sub(__n) __sync_sub_and_fetch(&used_memory, (__n))\n#else\n#define update_zmalloc_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_zmalloc_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\n#endif\n\n#define update_zmalloc_stat_alloc(__n) do { \\\n    size_t _n = (__n); \\\n    if (_n&(sizeof(long)-1)) _n += sizeof(long)-(_n&(sizeof(long)-1)); \\\n    if (zmalloc_thread_safe) { \\\n        update_zmalloc_stat_add(_n); \\\n    } else { \\\n        used_memory += _n; \\\n    } \\\n} while(0)\n\n#define update_zmalloc_stat_free(__n) do { \\\n    size_t _n = (__n); \\\n    if (_n&(sizeof(long)-1)) _n += sizeof(long)-(_n&(sizeof(long)-1)); \\\n    if (zmalloc_thread_safe) { \\\n        update_zmalloc_stat_sub(_n); \\\n    } else { \\\n        used_memory -= _n; \\\n    } \\\n} while(0)\n\nstatic size_t used_memory = 0;\nstatic int zmalloc_thread_safe = 0;\npthread_mutex_t used_memory_mutex = PTHREAD_MUTEX_INITIALIZER;\n\nstatic void zmalloc_default_oom(size_t size) {\n    fprintf(stderr, \"zmalloc: Out of memory trying to allocate %zu bytes\\n\",\n        size);\n    fflush(stderr);\n    abort();\n}\n\nstatic void (*zmalloc_oom_handler)(size_t) = zmalloc_default_oom;\n\nvoid *zmalloc(size_t size) {\n    void *ptr = malloc(size+PREFIX_SIZE);\n\n    if (!ptr) zmalloc_oom_handler(size);\n#ifdef HAVE_MALLOC_SIZE\n    update_zmalloc_stat_alloc(zmalloc_size(ptr));\n    return ptr;\n#else\n    *((size_t*)ptr) = size;\n    update_zmalloc_stat_alloc(size+PREFIX_SIZE);\n    return (char*)ptr+PREFIX_SIZE;\n#endif\n}\n\nvoid *zcalloc(size_t size) {\n    void *ptr = calloc(1, size+PREFIX_SIZE);\n\n    if (!ptr) zmalloc_oom_handler(size);\n#ifdef HAVE_MALLOC_SIZE\n    update_zmalloc_stat_alloc(zmalloc_size(ptr));\n    return ptr;\n#else\n    *((size_t*)ptr) = size;\n    update_zmalloc_stat_alloc(size+PREFIX_SIZE);\n    return (char*)ptr+PREFIX_SIZE;\n#endif\n}\n\nvoid *zrealloc(void *ptr, size_t size) {\n#ifndef HAVE_MALLOC_SIZE\n    void *realptr;\n#endif\n    size_t oldsize;\n    void *newptr;\n\n    if (ptr == NULL) return zmalloc(size);\n#ifdef HAVE_MALLOC_SIZE\n    oldsize = zmalloc_size(ptr);\n    newptr = realloc(ptr,size);\n    if (!newptr) zmalloc_oom_handler(size);\n\n    update_zmalloc_stat_free(oldsize);\n    update_zmalloc_stat_alloc(zmalloc_size(newptr));\n    return newptr;\n#else\n    realptr = (char*)ptr-PREFIX_SIZE;\n    oldsize = *((size_t*)realptr);\n    newptr = realloc(realptr,size+PREFIX_SIZE);\n    if (!newptr) zmalloc_oom_handler(size);\n\n    *((size_t*)newptr) = size;\n    update_zmalloc_stat_free(oldsize);\n    update_zmalloc_stat_alloc(size);\n    return (char*)newptr+PREFIX_SIZE;\n#endif\n}\n\n/* Provide zmalloc_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 zmalloc_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 zfree(void *ptr) {\n#ifndef HAVE_MALLOC_SIZE\n    void *realptr;\n    size_t oldsize;\n#endif\n\n    if (ptr == NULL) return;\n#ifdef HAVE_MALLOC_SIZE\n    update_zmalloc_stat_free(zmalloc_size(ptr));\n    free(ptr);\n#else\n    realptr = (char*)ptr-PREFIX_SIZE;\n    oldsize = *((size_t*)realptr);\n    update_zmalloc_stat_free(oldsize+PREFIX_SIZE);\n    free(realptr);\n#endif\n}\n\nchar *zstrdup(const char *s) {\n    size_t l = strlen(s)+1;\n    char *p = zmalloc(l);\n\n    memcpy(p,s,l);\n    return p;\n}\n\nsize_t zmalloc_used_memory(void) {\n    size_t um;\n\n    if (zmalloc_thread_safe) {\n#ifdef HAVE_ATOMIC\n        um = __sync_add_and_fetch(&used_memory, 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    else {\n        um = used_memory;\n    }\n\n    return um;\n}\n\nvoid zmalloc_enable_thread_safeness(void) {\n    zmalloc_thread_safe = 1;\n}\n\nvoid zmalloc_set_oom_handler(void (*oom_handler)(size_t)) {\n    zmalloc_oom_handler = oom_handler;\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 zmalloc_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 zmalloc_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 zmalloc_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 zmalloc()..\n     *\n     * Fragmentation will appear to be always 1 (no fragmentation)\n     * of course... */\n    return zmalloc_used_memory();\n}\n#endif\n\n/* Fragmentation = RSS / allocated-bytes */\nfloat zmalloc_get_fragmentation_ratio(size_t rss) {\n    return (float)rss/zmalloc_used_memory();\n}\n\n#if defined(HAVE_PROC_SMAPS)\nsize_t zmalloc_get_private_dirty(void) {\n    char line[1024];\n    size_t pd = 0;\n    FILE *fp = fopen(\"/proc/self/smaps\",\"r\");\n\n    if (!fp) return 0;\n    while(fgets(line,sizeof(line),fp) != NULL) {\n        if (strncmp(line,\"Private_Dirty:\",14) == 0) {\n            char *p = strchr(line,'k');\n            if (p) {\n                *p = '\\0';\n                pd += strtol(line+14,NULL,10) * 1024;\n            }\n        }\n    }\n    fclose(fp);\n    return pd;\n}\n#else\nsize_t zmalloc_get_private_dirty(void) {\n    return 0;\n}\n#endif\n"
  },
  {
    "path": "src/zmalloc.h",
    "content": "/* zmalloc - total amount of allocated memory aware version of malloc()\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#ifndef __ZMALLOC_H\n#define __ZMALLOC_H\n\n/* Double expansion needed for stringification of macro values. */\n#define __xstr(s) __str(s)\n#define __str(s) #s\n\n#if defined(USE_TCMALLOC)\n#define ZMALLOC_LIB (\"tcmalloc-\" __xstr(TC_VERSION_MAJOR) \".\" __xstr(TC_VERSION_MINOR))\n#include <google/tcmalloc.h>\n#if (TC_VERSION_MAJOR == 1 && TC_VERSION_MINOR >= 6) || (TC_VERSION_MAJOR > 1)\n#define HAVE_MALLOC_SIZE 1\n#define zmalloc_size(p) tc_malloc_size(p)\n#else\n#error \"Newer version of tcmalloc required\"\n#endif\n\n#elif defined(USE_JEMALLOC)\n#define ZMALLOC_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 zmalloc_size(p) je_malloc_usable_size(p)\n#else\n#error \"Newer version of jemalloc required\"\n#endif\n\n#elif defined(__APPLE__)\n#include <malloc/malloc.h>\n#define HAVE_MALLOC_SIZE 1\n#define zmalloc_size(p) malloc_size(p)\n#endif\n\n#ifndef ZMALLOC_LIB\n#define ZMALLOC_LIB \"libc\"\n#endif\n\nvoid *zmalloc(size_t size);\nvoid *zcalloc(size_t size);\nvoid *zrealloc(void *ptr, size_t size);\nvoid zfree(void *ptr);\nchar *zstrdup(const char *s);\nsize_t zmalloc_used_memory(void);\nvoid zmalloc_enable_thread_safeness(void);\nvoid zmalloc_set_oom_handler(void (*oom_handler)(size_t));\nfloat zmalloc_get_fragmentation_ratio(size_t rss);\nsize_t zmalloc_get_rss(void);\nsize_t zmalloc_get_private_dirty(void);\nvoid zlibc_free(void *ptr);\n\n#ifndef HAVE_MALLOC_SIZE\nsize_t zmalloc_size(void *ptr);\n#endif\n\n#endif /* __ZMALLOC_H */\n"
  },
  {
    "path": "tests/assets/default.conf",
    "content": "# Redis configuration for testing.\n\nnotify-keyspace-events KEA\ndaemonize no\npidfile /var/run/redis.pid\nport 6379\ntimeout 0\nbind 127.0.0.1\nloglevel verbose\nlogfile ''\ndatabases 16\n\nsave 900 1\nsave 300 10\nsave 60 10000\n\nrdbcompression yes\ndbfilename dump.rdb\ndir ./\n\nslave-serve-stale-data yes\nappendonly no\nappendfsync everysec\nno-appendfsync-on-rewrite no\nactiverehashing yes\n"
  },
  {
    "path": "tests/cluster/cluster.tcl",
    "content": "# Cluster-specific test functions.\n#\n# Copyright (C) 2014 Salvatore Sanfilippo antirez@gmail.com\n# This softare is released under the BSD License. See the COPYING file for\n# more information.\n\n# Returns a parsed CLUSTER NODES output as a list of dictionaries.\nproc get_cluster_nodes id {\n    set lines [split [R $id cluster nodes] \"\\r\\n\"]\n    set nodes {}\n    foreach l $lines {\n        set l [string trim $l]\n        if {$l eq {}} continue\n        set args [split $l]\n        set node [dict create \\\n            id [lindex $args 0] \\\n            addr [lindex $args 1] \\\n            flags [split [lindex $args 2] ,] \\\n            slaveof [lindex $args 3] \\\n            ping_sent [lindex $args 4] \\\n            pong_recv [lindex $args 5] \\\n            config_epoch [lindex $args 6] \\\n            linkstate [lindex $args 7] \\\n            slots [lrange $args 8 -1] \\\n        ]\n        lappend nodes $node\n    }\n    return $nodes\n}\n\n# Test node for flag.\nproc has_flag {node flag} {\n    expr {[lsearch -exact [dict get $node flags] $flag] != -1}\n}\n\n# Returns the parsed myself node entry as a dictionary.\nproc get_myself id {\n    set nodes [get_cluster_nodes $id]\n    foreach n $nodes {\n        if {[has_flag $n myself]} {return $n}\n    }\n    return {}\n}\n\n# Return the value of the specified CLUSTER INFO field.\nproc CI {n field} {\n    get_info_field [R $n cluster info] $field\n}\n\n# Assuming nodes are reest, this function performs slots allocation.\n# Only the first 'n' nodes are used.\nproc cluster_allocate_slots {n} {\n    set slot 16383\n    while {$slot >= 0} {\n        # Allocate successive slots to random nodes.\n        set node [randomInt $n]\n        lappend slots_$node $slot\n        incr slot -1\n    }\n    for {set j 0} {$j < $n} {incr j} {\n        R $j cluster addslots {*}[set slots_${j}]\n    }\n}\n\n# Check that cluster nodes agree about \"state\", or raise an error.\nproc assert_cluster_state {state} {\n    foreach_redis_id id {\n        if {[instance_is_killed redis $id]} continue\n        wait_for_condition 1000 50 {\n            [CI $id cluster_state] eq $state\n        } else {\n            fail \"Cluster node $id cluster_state:[CI $id cluster_state]\"\n        }\n    }\n}\n"
  },
  {
    "path": "tests/cluster/run.tcl",
    "content": "# Cluster test suite. Copyright (C) 2014 Salvatore Sanfilippo antirez@gmail.com\n# This softare is released under the BSD License. See the COPYING file for\n# more information.\n\ncd tests/cluster\nsource cluster.tcl\nsource ../instances.tcl\nsource ../../support/cluster.tcl ; # Redis Cluster client.\n\nset ::instances_count 20 ; # How many instances we use at max.\n\nproc main {} {\n    parse_options\n    spawn_instance redis $::redis_base_port $::instances_count {\n        \"cluster-enabled yes\"\n        \"appendonly yes\"\n    }\n    run_tests\n    cleanup\n}\n\nif {[catch main e]} {\n    puts $::errorInfo\n    cleanup\n}\n"
  },
  {
    "path": "tests/cluster/tests/00-base.tcl",
    "content": "# Check the basic monitoring and failover capabilities.\n\nsource \"../tests/includes/init-tests.tcl\"\n\nif {$::simulate_error} {\n    test \"This test will fail\" {\n        fail \"Simulated error\"\n    }\n}\n\ntest \"Cluster nodes are reachable\" {\n    foreach_redis_id id {\n        # Every node should just know itself.\n        assert {[R $id ping] eq {PONG}}\n    }\n}\n\ntest \"Different nodes have different IDs\" {\n    set ids {}\n    set numnodes 0\n    foreach_redis_id id {\n        incr numnodes\n        # Every node should just know itself.\n        set nodeid [dict get [get_myself $id] id]\n        assert {$nodeid ne {}}\n        lappend ids $nodeid\n    }\n    set numids [llength [lsort -unique $ids]]\n    assert {$numids == $numnodes}\n}\n\ntest \"Check if nodes auto-discovery works\" {\n    # Join node 0 with 1, 1 with 2, ... and so forth.\n    # If auto-discovery works all nodes will know every other node\n    # eventually.\n    set ids {}\n    foreach_redis_id id {lappend ids $id}\n    for {set j 0} {$j < [expr [llength $ids]-1]} {incr j} {\n        set a [lindex $ids $j]\n        set b [lindex $ids [expr $j+1]]\n        set b_port [get_instance_attrib redis $b port]\n        R $a cluster meet 127.0.0.1 $b_port\n    }\n\n    foreach_redis_id id {\n        wait_for_condition 1000 50 {\n            [llength [get_cluster_nodes $id]] == [llength $ids]\n        } else {\n            fail \"Cluster failed to join into a full mesh.\"\n        }\n    }\n}\n\ntest \"Before slots allocation, all nodes report cluster failure\" {\n    assert_cluster_state fail\n}\n\ntest \"It is possible to perform slot allocation\" {\n    cluster_allocate_slots 5\n}\n\ntest \"After the join, every node gets a different config epoch\" {\n    set trynum 60\n    while {[incr trynum -1] != 0} {\n        # We check that this condition is true for *all* the nodes.\n        set ok 1 ; # Will be set to 0 every time a node is not ok.\n        foreach_redis_id id {\n            set epochs {}\n            foreach n [get_cluster_nodes $id] {\n                lappend epochs [dict get $n config_epoch]\n            }\n            if {[lsort $epochs] != [lsort -unique $epochs]} {\n                set ok 0 ; # At least one collision!\n            }\n        }\n        if {$ok} break\n        after 1000\n        puts -nonewline .\n        flush stdout\n    }\n    if {$trynum == 0} {\n        fail \"Config epoch conflict resolution is not working.\"\n    }\n}\n\ntest \"Nodes should report cluster_state is ok now\" {\n    assert_cluster_state ok\n}\n\ntest \"It is possible to write and read from the cluster\" {\n    set port [get_instance_attrib redis 0 port]\n    set cluster [redis_cluster 127.0.0.1:$port]\n    for {set j 0} {$j < 100} {incr j} {\n        $cluster set key.$j $j\n    }\n    for {set j 0} {$j < 100} {incr j} {\n        assert {[$cluster get key.$j] eq $j}\n    }\n    $cluster close\n}\n"
  },
  {
    "path": "tests/cluster/tests/includes/init-tests.tcl",
    "content": "# Initialization tests -- most units will start including this.\n\ntest \"(init) Restart killed instances\" {\n    foreach type {redis} {\n        foreach_${type}_id id {\n            if {[get_instance_attrib $type $id pid] == -1} {\n                puts -nonewline \"$type/$id \"\n                flush stdout\n                restart_instance $type $id\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "tests/helpers/bg_complex_data.tcl",
    "content": "source tests/support/redis.tcl\nsource tests/support/util.tcl\n\nproc bg_complex_data {host port db ops} {\n    set r [redis $host $port]\n    $r select $db\n    createComplexDataset $r $ops\n}\n\nbg_complex_data [lindex $argv 0] [lindex $argv 1] [lindex $argv 2] [lindex $argv 3]\n"
  },
  {
    "path": "tests/helpers/gen_write_load.tcl",
    "content": "source tests/support/redis.tcl\n\nproc gen_write_load {host port seconds} {\n    set start_time [clock seconds]\n    set r [redis $host $port 1]\n    $r select 9\n    while 1 {\n        $r set [expr rand()] [expr rand()]\n        if {[clock seconds]-$start_time > $seconds} {\n            exit 0\n        }\n    }\n}\n\ngen_write_load [lindex $argv 0] [lindex $argv 1] [lindex $argv 2]\n"
  },
  {
    "path": "tests/instances.tcl",
    "content": "# Multi-instance test framework.\n# This is used in order to test Sentinel and Redis Cluster, and provides\n# basic capabilities for spawning and handling N parallel Redis / Sentinel\n# instances.\n#\n# Copyright (C) 2014 Salvatore Sanfilippo antirez@gmail.com\n# This softare is released under the BSD License. See the COPYING file for\n# more information.\n\npackage require Tcl 8.5\n\nset tcl_precision 17\nsource ../support/redis.tcl\nsource ../support/util.tcl\nsource ../support/server.tcl\nsource ../support/test.tcl\n\nset ::verbose 0\nset ::pause_on_error 0\nset ::simulate_error 0\nset ::sentinel_instances {}\nset ::redis_instances {}\nset ::sentinel_base_port 20000\nset ::redis_base_port 30000\nset ::pids {} ; # We kill everything at exit\nset ::dirs {} ; # We remove all the temp dirs at exit\nset ::run_matching {} ; # If non empty, only tests matching pattern are run.\n\nif {[catch {cd tmp}]} {\n    puts \"tmp directory not found.\"\n    puts \"Please run this test from the Redis source root.\"\n    exit 1\n}\n\n# Spawn a redis or sentinel instance, depending on 'type'.\nproc spawn_instance {type base_port count {conf {}}} {\n    for {set j 0} {$j < $count} {incr j} {\n        set port [find_available_port $base_port]\n        incr base_port\n        puts \"Starting $type #$j at port $port\"\n\n        # Create a directory for this instance.\n        set dirname \"${type}_${j}\"\n        lappend ::dirs $dirname\n        catch {exec rm -rf $dirname}\n        file mkdir $dirname\n\n        # Write the instance config file.\n        set cfgfile [file join $dirname $type.conf]\n        set cfg [open $cfgfile w]\n        puts $cfg \"port $port\"\n        puts $cfg \"dir ./$dirname\"\n        puts $cfg \"logfile log.txt\"\n        # Add additional config files\n        foreach directive $conf {\n            puts $cfg $directive\n        }\n        close $cfg\n\n        # Finally exec it and remember the pid for later cleanup.\n        if {$type eq \"redis\"} {\n            set prgname redis-server\n        } elseif {$type eq \"sentinel\"} {\n            set prgname redis-sentinel\n        } else {\n            error \"Unknown instance type.\"\n        }\n        set pid [exec ../../../src/${prgname} $cfgfile &]\n        lappend ::pids $pid\n\n        # Check availability\n        if {[server_is_up 127.0.0.1 $port 100] == 0} {\n            abort_sentinel_test \"Problems starting $type #$j: ping timeout\"\n        }\n\n        # Push the instance into the right list\n        lappend ::${type}_instances [list \\\n            pid $pid \\\n            host 127.0.0.1 \\\n            port $port \\\n            link [redis 127.0.0.1 $port] \\\n        ]\n    }\n}\n\nproc cleanup {} {\n    puts \"Cleaning up...\"\n    foreach pid $::pids {\n        catch {exec kill -9 $pid}\n    }\n    foreach dir $::dirs {\n        catch {exec rm -rf $dir}\n    }\n}\n\nproc abort_sentinel_test msg {\n    puts \"WARNING: Aborting the test.\"\n    puts \">>>>>>>> $msg\"\n    cleanup\n    exit 1\n}\n\nproc parse_options {} {\n    for {set j 0} {$j < [llength $::argv]} {incr j} {\n        set opt [lindex $::argv $j]\n        set val [lindex $::argv [expr $j+1]]\n        if {$opt eq \"--single\"} {\n            incr j\n            set ::run_matching \"*${val}*\"\n        } elseif {$opt eq \"--pause-on-error\"} {\n            set ::pause_on_error 1\n        } elseif {$opt eq \"--fail\"} {\n            set ::simulate_error 1\n        } elseif {$opt eq \"--help\"} {\n            puts \"Hello, I'm sentinel.tcl and I run Sentinel unit tests.\"\n            puts \"\\nOptions:\"\n            puts \"--single <pattern>      Only runs tests specified by pattern.\"\n            puts \"--pause-on-error        Pause for manual inspection on error.\"\n            puts \"--fail                  Simulate a test failure.\"\n            puts \"--help                  Shows this help.\"\n            exit 0\n        } else {\n            puts \"Unknown option $opt\"\n            exit 1\n        }\n    }\n}\n\n# If --pause-on-error option was passed at startup this function is called\n# on error in order to give the developer a chance to understand more about\n# the error condition while the instances are still running.\nproc pause_on_error {} {\n    puts \"\"\n    puts [colorstr yellow \"*** Please inspect the error now ***\"]\n    puts \"\\nType \\\"continue\\\" to resume the test, \\\"help\\\" for help screen.\\n\"\n    while 1 {\n        puts -nonewline \"> \"\n        flush stdout\n        set line [gets stdin]\n        set argv [split $line \" \"]\n        set cmd [lindex $argv 0]\n        if {$cmd eq {continue}} {\n            break\n        } elseif {$cmd eq {show-sentinel-logs}} {\n            set count 10\n            if {[lindex $argv 1] ne {}} {set count [lindex $argv 1]}\n            foreach_sentinel_id id {\n                puts \"=== SENTINEL $id ====\"\n                puts [exec tail -$count sentinel_$id/log.txt]\n                puts \"---------------------\\n\"\n            }\n        } elseif {$cmd eq {ls}} {\n            foreach_redis_id id {\n                puts -nonewline \"Redis $id\"\n                set errcode [catch {\n                    set str {}\n                    append str \"@[RI $id tcp_port]: \"\n                    append str \"[RI $id role] \"\n                    if {[RI $id role] eq {slave}} {\n                        append str \"[RI $id master_host]:[RI $id master_port]\"\n                    }\n                    set str\n                } retval]\n                if {$errcode} {\n                    puts \" -- $retval\"\n                } else {\n                    puts $retval\n                }\n            }\n            foreach_sentinel_id id {\n                puts -nonewline \"Sentinel $id\"\n                set errcode [catch {\n                    set str {}\n                    append str \"@[SI $id tcp_port]: \"\n                    append str \"[join [S $id sentinel get-master-addr-by-name mymaster]]\"\n                    set str\n                } retval]\n                if {$errcode} {\n                    puts \" -- $retval\"\n                } else {\n                    puts $retval\n                }\n            }\n        } elseif {$cmd eq {help}} {\n            puts \"ls                     List Sentinel and Redis instances.\"\n            puts \"show-sentinel-logs \\[N\\] Show latest N lines of logs.\"\n            puts \"S <id> cmd ... arg     Call command in Sentinel <id>.\"\n            puts \"R <id> cmd ... arg     Call command in Redis <id>.\"\n            puts \"SI <id> <field>        Show Sentinel <id> INFO <field>.\"\n            puts \"RI <id> <field>        Show Sentinel <id> INFO <field>.\"\n            puts \"continue               Resume test.\"\n        } else {\n            set errcode [catch {eval $line} retval]\n            if {$retval ne {}} {puts \"$retval\"}\n        }\n    }\n}\n\n# We redefine 'test' as for Sentinel we don't use the server-client\n# architecture for the test, everything is sequential.\nproc test {descr code} {\n    set ts [clock format [clock seconds] -format %H:%M:%S]\n    puts -nonewline \"$ts> $descr: \"\n    flush stdout\n\n    if {[catch {set retval [uplevel 1 $code]} error]} {\n        if {[string match \"assertion:*\" $error]} {\n            set msg [string range $error 10 end]\n            puts [colorstr red $msg]\n            if {$::pause_on_error} pause_on_error\n            puts \"(Jumping to next unit after error)\"\n            return -code continue\n        } else {\n            # Re-raise, let handler up the stack take care of this.\n            error $error $::errorInfo\n        }\n    } else {\n        puts [colorstr green OK]\n    }\n}\n\nproc run_tests {} {\n    set tests [lsort [glob ../tests/*]]\n    foreach test $tests {\n        if {$::run_matching ne {} && [string match $::run_matching $test] == 0} {\n            continue\n        }\n        if {[file isdirectory $test]} continue\n        puts [colorstr yellow \"Testing unit: [lindex [file split $test] end]\"]\n        source $test\n    }\n}\n\n# The \"S\" command is used to interact with the N-th Sentinel.\n# The general form is:\n#\n# S <sentinel-id> command arg arg arg ...\n#\n# Example to ping the Sentinel 0 (first instance): S 0 PING\nproc S {n args} {\n    set s [lindex $::sentinel_instances $n]\n    [dict get $s link] {*}$args\n}\n\n# Like R but to chat with Redis instances.\nproc R {n args} {\n    set r [lindex $::redis_instances $n]\n    [dict get $r link] {*}$args\n}\n\nproc get_info_field {info field} {\n    set fl [string length $field]\n    append field :\n    foreach line [split $info \"\\n\"] {\n        set line [string trim $line \"\\r\\n \"]\n        if {[string range $line 0 $fl] eq $field} {\n            return [string range $line [expr {$fl+1}] end]\n        }\n    }\n    return {}\n}\n\nproc SI {n field} {\n    get_info_field [S $n info] $field\n}\n\nproc RI {n field} {\n    get_info_field [R $n info] $field\n}\n\n# Iterate over IDs of sentinel or redis instances.\nproc foreach_instance_id {instances idvar code} {\n    upvar 1 $idvar id\n    for {set id 0} {$id < [llength $instances]} {incr id} {\n        set errcode [catch {uplevel 1 $code} result]\n        if {$errcode == 1} {\n            error $result $::errorInfo $::errorCode\n        } elseif {$errcode == 4} {\n            continue\n        } elseif {$errcode == 3} {\n            break\n        } elseif {$errcode != 0} {\n            return -code $errcode $result\n        }\n    }\n}\n\nproc foreach_sentinel_id {idvar code} {\n    set errcode [catch {uplevel 1 [list foreach_instance_id $::sentinel_instances $idvar $code]} result]\n    return -code $errcode $result\n}\n\nproc foreach_redis_id {idvar code} {\n    set errcode [catch {uplevel 1 [list foreach_instance_id $::redis_instances $idvar $code]} result]\n    return -code $errcode $result\n}\n\n# Get the specific attribute of the specified instance type, id.\nproc get_instance_attrib {type id attrib} {\n    dict get [lindex [set ::${type}_instances] $id] $attrib\n}\n\n# Set the specific attribute of the specified instance type, id.\nproc set_instance_attrib {type id attrib newval} {\n    set d [lindex [set ::${type}_instances] $id]\n    dict set d $attrib $newval\n    lset ::${type}_instances $id $d\n}\n\n# Create a master-slave cluster of the given number of total instances.\n# The first instance \"0\" is the master, all others are configured as\n# slaves.\nproc create_redis_master_slave_cluster n {\n    foreach_redis_id id {\n        if {$id == 0} {\n            # Our master.\n            R $id slaveof no one\n            R $id flushall\n        } elseif {$id < $n} {\n            R $id slaveof [get_instance_attrib redis 0 host] \\\n                          [get_instance_attrib redis 0 port]\n        } else {\n            # Instances not part of the cluster.\n            R $id slaveof no one\n        }\n    }\n    # Wait for all the slaves to sync.\n    wait_for_condition 1000 50 {\n        [RI 0 connected_slaves] == ($n-1)\n    } else {\n        fail \"Unable to create a master-slaves cluster.\"\n    }\n}\n\nproc get_instance_id_by_port {type port} {\n    foreach_${type}_id id {\n        if {[get_instance_attrib $type $id port] == $port} {\n            return $id\n        }\n    }\n    fail \"Instance $type port $port not found.\"\n}\n\n# Kill an instance of the specified type/id with SIGKILL.\n# This function will mark the instance PID as -1 to remember that this instance\n# is no longer running and will remove its PID from the list of pids that\n# we kill at cleanup.\n#\n# The instance can be restarted with restart-instance.\nproc kill_instance {type id} {\n    set pid [get_instance_attrib $type $id pid]\n    if {$pid == -1} {\n        error \"You tried to kill $type $id twice.\"\n    }\n    exec kill -9 $pid\n    set_instance_attrib $type $id pid -1\n    set_instance_attrib $type $id link you_tried_to_talk_with_killed_instance\n\n    # Remove the PID from the list of pids to kill at exit.\n    set ::pids [lsearch -all -inline -not -exact $::pids $pid]\n}\n\n# Return true of the instance of the specified type/id is killed.\nproc instance_is_killed {type id} {\n    set pid [get_instance_attrib $type $id pid]\n    return $pid == -1\n}\n\n# Restart an instance previously killed by kill_instance\nproc restart_instance {type id} {\n    set dirname \"${type}_${id}\"\n    set cfgfile [file join $dirname $type.conf]\n    set port [get_instance_attrib $type $id port]\n\n    # Execute the instance with its old setup and append the new pid\n    # file for cleanup.\n    if {$type eq \"redis\"} {\n        set prgname redis-server\n    } else {\n        set prgname redis-sentinel\n    }\n    set pid [exec ../../../src/${prgname} $cfgfile &]\n    set_instance_attrib $type $id pid $pid\n    lappend ::pids $pid\n\n    # Check that the instance is running\n    if {[server_is_up 127.0.0.1 $port 100] == 0} {\n        abort_sentinel_test \"Problems starting $type #$j: ping timeout\"\n    }\n\n    # Connect with it with a fresh link\n    set_instance_attrib $type $id link [redis 127.0.0.1 $port]\n}\n\n"
  },
  {
    "path": "tests/integration/aof-race.tcl",
    "content": "set defaults { appendonly {yes} appendfilename {appendonly.aof} }\nset server_path [tmpdir server.aof]\nset aof_path \"$server_path/appendonly.aof\"\n\nproc start_server_aof {overrides code} {\n    upvar defaults defaults srv srv server_path server_path\n    set config [concat $defaults $overrides]\n    start_server [list overrides $config] $code\n}\n\ntags {\"aof\"} {\n    # Specific test for a regression where internal buffers were not properly\n    # cleaned after a child responsible for an AOF rewrite exited. This buffer\n    # was subsequently appended to the new AOF, resulting in duplicate commands.\n    start_server_aof [list dir $server_path] {\n        set client [redis [srv host] [srv port]]\n        set bench [open \"|src/redis-benchmark -q -p [srv port] -c 20 -n 20000 incr foo\" \"r+\"]\n        after 100\n\n        # Benchmark should be running by now: start background rewrite\n        $client bgrewriteaof\n\n        # Read until benchmark pipe reaches EOF\n        while {[string length [read $bench]] > 0} {}\n\n        # Check contents of foo\n        assert_equal 20000 [$client get foo]\n    }\n\n    # Restart server to replay AOF\n    start_server_aof [list dir $server_path] {\n        set client [redis [srv host] [srv port]]\n        assert_equal 20000 [$client get foo]\n    }\n}\n"
  },
  {
    "path": "tests/integration/aof.tcl",
    "content": "set defaults { appendonly {yes} appendfilename {appendonly.aof} }\nset server_path [tmpdir server.aof]\nset aof_path \"$server_path/appendonly.aof\"\n\nproc append_to_aof {str} {\n    upvar fp fp\n    puts -nonewline $fp $str\n}\n\nproc create_aof {code} {\n    upvar fp fp aof_path aof_path\n    set fp [open $aof_path w+]\n    uplevel 1 $code\n    close $fp\n}\n\nproc start_server_aof {overrides code} {\n    upvar defaults defaults srv srv server_path server_path\n    set config [concat $defaults $overrides]\n    set srv [start_server [list overrides $config]]\n    uplevel 1 $code\n    kill_server $srv\n}\n\ntags {\"aof\"} {\n    ## Test the server doesn't start when the AOF contains an unfinished MULTI\n    create_aof {\n        append_to_aof [formatCommand set foo hello]\n        append_to_aof [formatCommand multi]\n        append_to_aof [formatCommand set bar world]\n    }\n\n    start_server_aof [list dir $server_path] {\n        test \"Unfinished MULTI: Server should have logged an error\" {\n            set pattern \"*Unexpected end of file reading the append only file*\"\n            set retry 10\n            while {$retry} {\n                set result [exec tail -n1 < [dict get $srv stdout]]\n                if {[string match $pattern $result]} {\n                    break\n                }\n                incr retry -1\n                after 1000\n            }\n            if {$retry == 0} {\n                error \"assertion:expected error not found on config file\"\n            }\n        }\n    }\n\n    ## Test that the server exits when the AOF contains a short read\n    create_aof {\n        append_to_aof [formatCommand set foo hello]\n        append_to_aof [string range [formatCommand set bar world] 0 end-1]\n    }\n\n    start_server_aof [list dir $server_path] {\n        test \"Short read: Server should have logged an error\" {\n            set pattern \"*Bad file format reading the append only file*\"\n            set retry 10\n            while {$retry} {\n                set result [exec tail -n1 < [dict get $srv stdout]]\n                if {[string match $pattern $result]} {\n                    break\n                }\n                incr retry -1\n                after 1000\n            }\n            if {$retry == 0} {\n                error \"assertion:expected error not found on config file\"\n            }\n        }\n    }\n\n    ## Test that redis-check-aof indeed sees this AOF is not valid\n    test \"Short read: Utility should confirm the AOF is not valid\" {\n        catch {\n            exec src/redis-check-aof $aof_path\n        } result\n        assert_match \"*not valid*\" $result\n    }\n\n    test \"Short read: Utility should be able to fix the AOF\" {\n        set result [exec src/redis-check-aof --fix $aof_path << \"y\\n\"]\n        assert_match \"*Successfully truncated AOF*\" $result\n    }\n\n    ## Test that the server can be started using the truncated AOF\n    start_server_aof [list dir $server_path] {\n        test \"Fixed AOF: Server should have been started\" {\n            assert_equal 1 [is_alive $srv]\n        }\n\n        test \"Fixed AOF: Keyspace should contain values that were parsable\" {\n            set client [redis [dict get $srv host] [dict get $srv port]]\n            assert_equal \"hello\" [$client get foo]\n            assert_equal \"\" [$client get bar]\n        }\n    }\n\n    ## Test that SPOP (that modifies the client's argc/argv) is correctly free'd\n    create_aof {\n        append_to_aof [formatCommand sadd set foo]\n        append_to_aof [formatCommand sadd set bar]\n        append_to_aof [formatCommand spop set]\n    }\n\n    start_server_aof [list dir $server_path] {\n        test \"AOF+SPOP: Server should have been started\" {\n            assert_equal 1 [is_alive $srv]\n        }\n\n        test \"AOF+SPOP: Set should have 1 member\" {\n            set client [redis [dict get $srv host] [dict get $srv port]]\n            assert_equal 1 [$client scard set]\n        }\n    }\n\n    ## Test that EXPIREAT is loaded correctly\n    create_aof {\n        append_to_aof [formatCommand rpush list foo]\n        append_to_aof [formatCommand expireat list 1000]\n        append_to_aof [formatCommand rpush list bar]\n    }\n\n    start_server_aof [list dir $server_path] {\n        test \"AOF+EXPIRE: Server should have been started\" {\n            assert_equal 1 [is_alive $srv]\n        }\n\n        test \"AOF+EXPIRE: List should be empty\" {\n            set client [redis [dict get $srv host] [dict get $srv port]]\n            assert_equal 0 [$client llen list]\n        }\n    }\n\n    start_server {overrides {appendonly {yes} appendfilename {appendonly.aof}}} {\n        test {Redis should not try to convert DEL into EXPIREAT for EXPIRE -1} {\n            r set x 10\n            r expire x -1\n        }\n    }\n}\n"
  },
  {
    "path": "tests/integration/convert-zipmap-hash-on-load.tcl",
    "content": "# Copy RDB with zipmap encoded hash to server path\nset server_path [tmpdir \"server.convert-zipmap-hash-on-load\"]\n\nexec cp -f tests/assets/hash-zipmap.rdb $server_path\nstart_server [list overrides [list \"dir\" $server_path \"dbfilename\" \"hash-zipmap.rdb\"]] {\n  test \"RDB load zipmap hash: converts to ziplist\" {\n    r select 0\n\n    assert_match \"*ziplist*\" [r debug object hash]\n    assert_equal 2 [r hlen hash]\n    assert_match {v1 v2} [r hmget hash f1 f2]\n  }\n}\n\nexec cp -f tests/assets/hash-zipmap.rdb $server_path\nstart_server [list overrides [list \"dir\" $server_path \"dbfilename\" \"hash-zipmap.rdb\" \"hash-max-ziplist-entries\" 1]] {\n  test \"RDB load zipmap hash: converts to hash table when hash-max-ziplist-entries is exceeded\" {\n    r select 0\n\n    assert_match \"*hashtable*\" [r debug object hash]\n    assert_equal 2 [r hlen hash]\n    assert_match {v1 v2} [r hmget hash f1 f2]\n  }\n}\n\nexec cp -f tests/assets/hash-zipmap.rdb $server_path\nstart_server [list overrides [list \"dir\" $server_path \"dbfilename\" \"hash-zipmap.rdb\" \"hash-max-ziplist-value\" 1]] {\n  test \"RDB load zipmap hash: converts to hash table when hash-max-ziplist-value is exceeded\" {\n    r select 0\n\n    assert_match \"*hashtable*\" [r debug object hash]\n    assert_equal 2 [r hlen hash]\n    assert_match {v1 v2} [r hmget hash f1 f2]\n  }\n}\n"
  },
  {
    "path": "tests/integration/rdb.tcl",
    "content": "set server_path [tmpdir \"server.rdb-encoding-test\"]\n\n# Copy RDB with different encodings in server path\nexec cp tests/assets/encodings.rdb $server_path\n\nstart_server [list overrides [list \"dir\" $server_path \"dbfilename\" \"encodings.rdb\"]] {\n  test \"RDB encoding loading test\" {\n    r select 0\n    csvdump r\n  } {\"compressible\",\"string\",\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"\n\"hash\",\"hash\",\"a\",\"1\",\"aa\",\"10\",\"aaa\",\"100\",\"b\",\"2\",\"bb\",\"20\",\"bbb\",\"200\",\"c\",\"3\",\"cc\",\"30\",\"ccc\",\"300\",\"ddd\",\"400\",\"eee\",\"5000000000\",\n\"hash_zipped\",\"hash\",\"a\",\"1\",\"b\",\"2\",\"c\",\"3\",\n\"list\",\"list\",\"1\",\"2\",\"3\",\"a\",\"b\",\"c\",\"100000\",\"6000000000\",\"1\",\"2\",\"3\",\"a\",\"b\",\"c\",\"100000\",\"6000000000\",\"1\",\"2\",\"3\",\"a\",\"b\",\"c\",\"100000\",\"6000000000\",\n\"list_zipped\",\"list\",\"1\",\"2\",\"3\",\"a\",\"b\",\"c\",\"100000\",\"6000000000\",\n\"number\",\"string\",\"10\"\n\"set\",\"set\",\"1\",\"100000\",\"2\",\"3\",\"6000000000\",\"a\",\"b\",\"c\",\n\"set_zipped_1\",\"set\",\"1\",\"2\",\"3\",\"4\",\n\"set_zipped_2\",\"set\",\"100000\",\"200000\",\"300000\",\"400000\",\n\"set_zipped_3\",\"set\",\"1000000000\",\"2000000000\",\"3000000000\",\"4000000000\",\"5000000000\",\"6000000000\",\n\"string\",\"string\",\"Hello World\"\n\"zset\",\"zset\",\"a\",\"1\",\"b\",\"2\",\"c\",\"3\",\"aa\",\"10\",\"bb\",\"20\",\"cc\",\"30\",\"aaa\",\"100\",\"bbb\",\"200\",\"ccc\",\"300\",\"aaaa\",\"1000\",\"cccc\",\"123456789\",\"bbbb\",\"5000000000\",\n\"zset_zipped\",\"zset\",\"a\",\"1\",\"b\",\"2\",\"c\",\"3\",\n}\n}\n\nset server_path [tmpdir \"server.rdb-startup-test\"]\n\nstart_server [list overrides [list \"dir\" $server_path]] {\n    test {Server started empty with non-existing RDB file} {\n        r debug digest\n    } {0000000000000000000000000000000000000000}\n    # Save an RDB file, needed for the next test.\n    r save\n}\n\nstart_server [list overrides [list \"dir\" $server_path]] {\n    test {Server started empty with empty RDB file} {\n        r debug digest\n    } {0000000000000000000000000000000000000000}\n}\n\n# Helper function to start a server and kill it, just to check the error\n# logged.\nset defaults {}\nproc start_server_and_kill_it {overrides code} {\n    upvar defaults defaults srv srv server_path server_path\n    set config [concat $defaults $overrides]\n    set srv [start_server [list overrides $config]]\n    uplevel 1 $code\n    kill_server $srv\n}\n\n# Make the RDB file unreadable\nfile attributes [file join $server_path dump.rdb] -permissions 0222\n\n# Detect root account (it is able to read the file even with 002 perm)\nset isroot 0\ncatch {\n    open [file join $server_path dump.rdb]\n    set isroot 1\n}\n\n# Now make sure the server aborted with an error\nif {!$isroot} {\n    start_server_and_kill_it [list \"dir\" $server_path] {\n        test {Server should not start if RDB file can't be open} {\n            wait_for_condition 50 100 {\n                [string match {*Fatal error loading*} \\\n                    [exec tail -n1 < [dict get $srv stdout]]]\n            } else {\n                fail \"Server started even if RDB was unreadable!\"\n            }\n        }\n    }\n}\n\n# Fix permissions of the RDB file.\nfile attributes [file join $server_path dump.rdb] -permissions 0666\n\n# Corrupt its CRC64 checksum.\nset filesize [file size [file join $server_path dump.rdb]]\nset fd [open [file join $server_path dump.rdb] r+]\nfconfigure $fd -translation binary\nseek $fd -8 end\nputs -nonewline $fd \"foobar00\"; # Corrupt the checksum\nclose $fd\n\n# Now make sure the server aborted with an error\nstart_server_and_kill_it [list \"dir\" $server_path] {\n    test {Server should not start if RDB is corrupted} {\n        wait_for_condition 50 100 {\n            [string match {*RDB checksum*} \\\n                [exec tail -n1 < [dict get $srv stdout]]]\n        } else {\n            fail \"Server started even if RDB was corrupted!\"\n        }\n    }\n}\n"
  },
  {
    "path": "tests/integration/redis-cli.tcl",
    "content": "start_server {tags {\"cli\"}} {\n    proc open_cli {} {\n        set ::env(TERM) dumb\n        set fd [open [format \"|src/redis-cli -p %d -n 9\" [srv port]] \"r+\"]\n        fconfigure $fd -buffering none\n        fconfigure $fd -blocking false\n        fconfigure $fd -translation binary\n        assert_equal \"redis> \" [read_cli $fd]\n        set _ $fd\n    }\n\n    proc close_cli {fd} {\n        close $fd\n    }\n\n    proc read_cli {fd} {\n        set buf [read $fd]\n        while {[string length $buf] == 0} {\n            # wait some time and try again\n            after 10\n            set buf [read $fd]\n        }\n        set _ $buf\n    }\n\n    proc write_cli {fd buf} {\n        puts $fd $buf\n        flush $fd\n    }\n\n    # Helpers to run tests in interactive mode\n    proc run_command {fd cmd} {\n        write_cli $fd $cmd\n        set lines [split [read_cli $fd] \"\\n\"]\n        assert_equal \"redis> \" [lindex $lines end]\n        join [lrange $lines 0 end-1] \"\\n\"\n    }\n\n    proc test_interactive_cli {name code} {\n        set ::env(FAKETTY) 1\n        set fd [open_cli]\n        test \"Interactive CLI: $name\" $code\n        close_cli $fd\n        unset ::env(FAKETTY)\n    }\n\n    # Helpers to run tests where stdout is not a tty\n    proc write_tmpfile {contents} {\n        set tmp [tmpfile \"cli\"]\n        set tmpfd [open $tmp \"w\"]\n        puts -nonewline $tmpfd $contents\n        close $tmpfd\n        set _ $tmp\n    }\n\n    proc _run_cli {opts args} {\n        set cmd [format \"src/redis-cli -p %d -n 9 $args\" [srv port]]\n        foreach {key value} $opts {\n            if {$key eq \"pipe\"} {\n                set cmd \"sh -c \\\"$value | $cmd\\\"\"\n            }\n            if {$key eq \"path\"} {\n                set cmd \"$cmd < $value\"\n            }\n        }\n\n        set fd [open \"|$cmd\" \"r\"]\n        fconfigure $fd -buffering none\n        fconfigure $fd -translation binary\n        set resp [read $fd 1048576]\n        close $fd\n        set _ $resp\n    }\n\n    proc run_cli {args} {\n        _run_cli {} {*}$args\n    }\n\n    proc run_cli_with_input_pipe {cmd args} {\n        _run_cli [list pipe $cmd] {*}$args\n    }\n\n    proc run_cli_with_input_file {path args} {\n        _run_cli [list path $path] {*}$args\n    }\n\n    proc test_nontty_cli {name code} {\n        test \"Non-interactive non-TTY CLI: $name\" $code\n    }\n\n    # Helpers to run tests where stdout is a tty (fake it)\n    proc test_tty_cli {name code} {\n        set ::env(FAKETTY) 1\n        test \"Non-interactive TTY CLI: $name\" $code\n        unset ::env(FAKETTY)\n    }\n\n    test_interactive_cli \"INFO response should be printed raw\" {\n        set lines [split [run_command $fd info] \"\\n\"]\n        foreach line $lines {\n            assert [regexp {^[a-z0-9_]+:[a-z0-9_]+} $line]\n        }\n    }\n\n    test_interactive_cli \"Status reply\" {\n        assert_equal \"OK\" [run_command $fd \"set key foo\"]\n    }\n\n    test_interactive_cli \"Integer reply\" {\n        assert_equal \"(integer) 1\" [run_command $fd \"incr counter\"]\n    }\n\n    test_interactive_cli \"Bulk reply\" {\n        r set key foo\n        assert_equal \"\\\"foo\\\"\" [run_command $fd \"get key\"]\n    }\n\n    test_interactive_cli \"Multi-bulk reply\" {\n        r rpush list foo\n        r rpush list bar\n        assert_equal \"1. \\\"foo\\\"\\n2. \\\"bar\\\"\" [run_command $fd \"lrange list 0 -1\"]\n    }\n\n    test_interactive_cli \"Parsing quotes\" {\n        assert_equal \"OK\" [run_command $fd \"set key \\\"bar\\\"\"]\n        assert_equal \"bar\" [r get key]\n        assert_equal \"OK\" [run_command $fd \"set key \\\" bar \\\"\"]\n        assert_equal \" bar \" [r get key]\n        assert_equal \"OK\" [run_command $fd \"set key \\\"\\\\\\\"bar\\\\\\\"\\\"\"]\n        assert_equal \"\\\"bar\\\"\" [r get key]\n        assert_equal \"OK\" [run_command $fd \"set key \\\"\\tbar\\t\\\"\"]\n        assert_equal \"\\tbar\\t\" [r get key]\n\n        # invalid quotation\n        assert_equal \"Invalid argument(s)\" [run_command $fd \"get \\\"\\\"key\"]\n        assert_equal \"Invalid argument(s)\" [run_command $fd \"get \\\"key\\\"x\"]\n\n        # quotes after the argument are weird, but should be allowed\n        assert_equal \"OK\" [run_command $fd \"set key\\\"\\\" bar\"]\n        assert_equal \"bar\" [r get key]\n    }\n\n    test_tty_cli \"Status reply\" {\n        assert_equal \"OK\\n\" [run_cli set key bar]\n        assert_equal \"bar\" [r get key]\n    }\n\n    test_tty_cli \"Integer reply\" {\n        r del counter\n        assert_equal \"(integer) 1\\n\" [run_cli incr counter]\n    }\n\n    test_tty_cli \"Bulk reply\" {\n        r set key \"tab\\tnewline\\n\"\n        assert_equal \"\\\"tab\\\\tnewline\\\\n\\\"\\n\" [run_cli get key]\n    }\n\n    test_tty_cli \"Multi-bulk reply\" {\n        r del list\n        r rpush list foo\n        r rpush list bar\n        assert_equal \"1. \\\"foo\\\"\\n2. \\\"bar\\\"\\n\" [run_cli lrange list 0 -1]\n    }\n\n    test_tty_cli \"Read last argument from pipe\" {\n        assert_equal \"OK\\n\" [run_cli_with_input_pipe \"echo foo\" set key]\n        assert_equal \"foo\\n\" [r get key]\n    }\n\n    test_tty_cli \"Read last argument from file\" {\n        set tmpfile [write_tmpfile \"from file\"]\n        assert_equal \"OK\\n\" [run_cli_with_input_file $tmpfile set key]\n        assert_equal \"from file\" [r get key]\n    }\n\n    test_nontty_cli \"Status reply\" {\n        assert_equal \"OK\" [run_cli set key bar]\n        assert_equal \"bar\" [r get key]\n    }\n\n    test_nontty_cli \"Integer reply\" {\n        r del counter\n        assert_equal \"1\" [run_cli incr counter]\n    }\n\n    test_nontty_cli \"Bulk reply\" {\n        r set key \"tab\\tnewline\\n\"\n        assert_equal \"tab\\tnewline\\n\" [run_cli get key]\n    }\n\n    test_nontty_cli \"Multi-bulk reply\" {\n        r del list\n        r rpush list foo\n        r rpush list bar\n        assert_equal \"foo\\nbar\" [run_cli lrange list 0 -1]\n    }\n\n    test_nontty_cli \"Read last argument from pipe\" {\n        assert_equal \"OK\" [run_cli_with_input_pipe \"echo foo\" set key]\n        assert_equal \"foo\\n\" [r get key]\n    }\n\n    test_nontty_cli \"Read last argument from file\" {\n        set tmpfile [write_tmpfile \"from file\"]\n        assert_equal \"OK\" [run_cli_with_input_file $tmpfile set key]\n        assert_equal \"from file\" [r get key]\n    }\n}\n"
  },
  {
    "path": "tests/integration/replication-2.tcl",
    "content": "start_server {tags {\"repl\"}} {\n    start_server {} {\n        test {First server should have role slave after SLAVEOF} {\n            r -1 slaveof [srv 0 host] [srv 0 port]\n            after 1000\n            s -1 role\n        } {slave}\n\n        test {MASTER and SLAVE dataset should be identical after complex ops} {\n            createComplexDataset r 10000\n            after 500\n            if {[r debug digest] ne [r -1 debug digest]} {\n                set csv1 [csvdump r]\n                set csv2 [csvdump {r -1}]\n                set fd [open /tmp/repldump1.txt w]\n                puts -nonewline $fd $csv1\n                close $fd\n                set fd [open /tmp/repldump2.txt w]\n                puts -nonewline $fd $csv2\n                close $fd\n                puts \"Master - Slave inconsistency\"\n                puts \"Run diff -u against /tmp/repldump*.txt for more info\"\n            }\n            assert_equal [r debug digest] [r -1 debug digest]\n        }\n    }\n}\n"
  },
  {
    "path": "tests/integration/replication-3.tcl",
    "content": "start_server {tags {\"repl\"}} {\n    start_server {} {\n        test {First server should have role slave after SLAVEOF} {\n            r -1 slaveof [srv 0 host] [srv 0 port]\n            wait_for_condition 50 100 {\n                [s -1 master_link_status] eq {up}\n            } else {\n                fail \"Replication not started.\"\n            }\n        }\n\n        if {$::accurate} {set numops 50000} else {set numops 5000}\n\n        test {MASTER and SLAVE consistency with expire} {\n            createComplexDataset r $numops useexpire\n            after 4000 ;# Make sure everything expired before taking the digest\n            r keys *   ;# Force DEL syntesizing to slave\n            after 1000 ;# Wait another second. Now everything should be fine.\n            if {[r debug digest] ne [r -1 debug digest]} {\n                set csv1 [csvdump r]\n                set csv2 [csvdump {r -1}]\n                set fd [open /tmp/repldump1.txt w]\n                puts -nonewline $fd $csv1\n                close $fd\n                set fd [open /tmp/repldump2.txt w]\n                puts -nonewline $fd $csv2\n                close $fd\n                puts \"Master - Slave inconsistency\"\n                puts \"Run diff -u against /tmp/repldump*.txt for more info\"\n            }\n            assert_equal [r debug digest] [r -1 debug digest]\n        }\n    }\n}\n\nstart_server {tags {\"repl\"}} {\n    start_server {} {\n        test {First server should have role slave after SLAVEOF} {\n            r -1 slaveof [srv 0 host] [srv 0 port]\n            wait_for_condition 50 100 {\n                [s -1 master_link_status] eq {up}\n            } else {\n                fail \"Replication not started.\"\n            }\n        }\n\n        set numops 20000 ;# Enough to trigger the Script Cache LRU eviction.\n\n        # While we are at it, enable AOF to test it will be consistent as well\n        # after the test.\n        r config set appendonly yes\n\n        test {MASTER and SLAVE consistency with EVALSHA replication} {\n            array set oldsha {}\n            for {set j 0} {$j < $numops} {incr j} {\n                set key \"key:$j\"\n                # Make sure to create scripts that have different SHA1s\n                set script \"return redis.call('incr','$key')\"\n                set sha1 [r eval \"return redis.sha1hex(\\\"$script\\\")\" 0]\n                set oldsha($j) $sha1\n                r eval $script 0\n                set res [r evalsha $sha1 0]\n                assert {$res == 2}\n                # Additionally call one of the old scripts as well, at random.\n                set res [r evalsha $oldsha([randomInt $j]) 0]\n                assert {$res > 2}\n\n                # Trigger an AOF rewrite while we are half-way, this also\n                # forces the flush of the script cache, and we will cover\n                # more code as a result.\n                if {$j == $numops / 2} {\n                    catch {r bgrewriteaof}\n                }\n            }\n\n            wait_for_condition 50 100 {\n                [r dbsize] == $numops &&\n                [r -1 dbsize] == $numops &&\n                [r debug digest] eq [r -1 debug digest]\n            } else {\n                set csv1 [csvdump r]\n                set csv2 [csvdump {r -1}]\n                set fd [open /tmp/repldump1.txt w]\n                puts -nonewline $fd $csv1\n                close $fd\n                set fd [open /tmp/repldump2.txt w]\n                puts -nonewline $fd $csv2\n                close $fd\n                puts \"Master - Slave inconsistency\"\n                puts \"Run diff -u against /tmp/repldump*.txt for more info\"\n\n            }\n\n            set old_digest [r debug digest]\n            r config set appendonly no\n            r debug loadaof\n            set new_digest [r debug digest]\n            assert {$old_digest eq $new_digest}\n        }\n    }\n}\n"
  },
  {
    "path": "tests/integration/replication-4.tcl",
    "content": "proc start_bg_complex_data {host port db ops} {\n    set tclsh [info nameofexecutable]\n    exec $tclsh tests/helpers/bg_complex_data.tcl $host $port $db $ops &\n}\n\nproc stop_bg_complex_data {handle} {\n    catch {exec /bin/kill -9 $handle}\n}\n\nstart_server {tags {\"repl\"}} {\n    start_server {} {\n\n        set master [srv -1 client]\n        set master_host [srv -1 host]\n        set master_port [srv -1 port]\n        set slave [srv 0 client]\n\n        set load_handle0 [start_bg_complex_data $master_host $master_port 9 100000]\n        set load_handle1 [start_bg_complex_data $master_host $master_port 11 100000]\n        set load_handle2 [start_bg_complex_data $master_host $master_port 12 100000]\n\n        test {First server should have role slave after SLAVEOF} {\n            $slave slaveof $master_host $master_port\n            after 1000\n            s 0 role\n        } {slave}\n\n        test {Test replication with parallel clients writing in differnet DBs} {\n            after 5000\n            stop_bg_complex_data $load_handle0\n            stop_bg_complex_data $load_handle1\n            stop_bg_complex_data $load_handle2\n            set retry 10\n            while {$retry && ([$master debug digest] ne [$slave debug digest])}\\\n            {\n                after 1000\n                incr retry -1\n            }\n            assert {[$master dbsize] > 0}\n\n            if {[$master debug digest] ne [$slave debug digest]} {\n                set csv1 [csvdump r]\n                set csv2 [csvdump {r -1}]\n                set fd [open /tmp/repldump1.txt w]\n                puts -nonewline $fd $csv1\n                close $fd\n                set fd [open /tmp/repldump2.txt w]\n                puts -nonewline $fd $csv2\n                close $fd\n                puts \"Master - Slave inconsistency\"\n                puts \"Run diff -u against /tmp/repldump*.txt for more info\"\n            }\n            assert_equal [r debug digest] [r -1 debug digest]\n        }\n    }\n}\n\nstart_server {tags {\"repl\"}} {\n    start_server {} {\n        set master [srv -1 client]\n        set master_host [srv -1 host]\n        set master_port [srv -1 port]\n        set slave [srv 0 client]\n\n        test {First server should have role slave after SLAVEOF} {\n            $slave slaveof $master_host $master_port\n            wait_for_condition 50 100 {\n                [s 0 master_link_status] eq {up}\n            } else {\n                fail \"Replication not started.\"\n            }\n        }\n\n        test {With min-slaves-to-write (1,3): master should be writable} {\n            $master config set min-slaves-max-lag 3\n            $master config set min-slaves-to-write 1\n            $master set foo bar\n        } {OK}\n\n        test {With min-slaves-to-write (2,3): master should not be writable} {\n            $master config set min-slaves-max-lag 3\n            $master config set min-slaves-to-write 2\n            catch {$master set foo bar} e\n            set e\n        } {NOREPLICAS*}\n\n        test {With min-slaves-to-write: master not writable with lagged slave} {\n            $master config set min-slaves-max-lag 2\n            $master config set min-slaves-to-write 1\n            assert {[$master set foo bar] eq {OK}}\n            $slave deferred 1\n            $slave debug sleep 6\n            after 4000\n            catch {$master set foo bar} e\n            set e\n        } {NOREPLICAS*}\n    }\n}\n\nstart_server {tags {\"repl\"}} {\n    start_server {} {\n        set master [srv -1 client]\n        set master_host [srv -1 host]\n        set master_port [srv -1 port]\n        set slave [srv 0 client]\n\n        test {First server should have role slave after SLAVEOF} {\n            $slave slaveof $master_host $master_port\n            wait_for_condition 50 100 {\n                [s 0 role] eq {slave}\n            } else {\n                fail \"Replication not started.\"\n            }\n        }\n\n        test {Replication: commands with many arguments (issue #1221)} {\n            # We now issue large MSET commands, that may trigger a specific\n            # class of bugs, see issue #1221.\n            for {set j 0} {$j < 100} {incr j} {\n                set cmd [list mset]\n                for {set x 0} {$x < 1000} {incr x} {\n                    lappend cmd [randomKey] [randomValue]\n                }\n                $master {*}$cmd\n            }\n\n            set retry 10\n            while {$retry && ([$master debug digest] ne [$slave debug digest])}\\\n            {\n                after 1000\n                incr retry -1\n            }\n            assert {[$master dbsize] > 0}\n        }\n    }\n}\n"
  },
  {
    "path": "tests/integration/replication-psync.tcl",
    "content": "proc start_bg_complex_data {host port db ops} {\n    set tclsh [info nameofexecutable]\n    exec $tclsh tests/helpers/bg_complex_data.tcl $host $port $db $ops &\n}\n\nproc stop_bg_complex_data {handle} {\n    catch {exec /bin/kill -9 $handle}\n}\n\n# Creates a master-slave pair and breaks the link continuously to force\n# partial resyncs attempts, all this while flooding the master with\n# write queries.\n#\n# You can specifiy backlog size, ttl, delay before reconnection, test duration\n# in seconds, and an additional condition to verify at the end.\nproc test_psync {descr duration backlog_size backlog_ttl delay cond} {\n    start_server {tags {\"repl\"}} {\n        start_server {} {\n\n            set master [srv -1 client]\n            set master_host [srv -1 host]\n            set master_port [srv -1 port]\n            set slave [srv 0 client]\n\n            $master config set repl-backlog-size $backlog_size\n            $master config set repl-backlog-ttl $backlog_ttl\n\n            set load_handle0 [start_bg_complex_data $master_host $master_port 9 100000]\n            set load_handle1 [start_bg_complex_data $master_host $master_port 11 100000]\n            set load_handle2 [start_bg_complex_data $master_host $master_port 12 100000]\n\n            test {First server should have role slave after SLAVEOF} {\n                $slave slaveof $master_host $master_port\n                wait_for_condition 50 100 {\n                    [s 0 role] eq {slave}\n                } else {\n                    fail \"Replication not started.\"\n                }\n            }\n\n            test \"Test replication partial resync: $descr\" {\n                # Now while the clients are writing data, break the maste-slave\n                # link multiple times.\n                for {set j 0} {$j < $duration*10} {incr j} {\n                    after 100\n                    # catch {puts \"MASTER [$master dbsize] keys, SLAVE [$slave dbsize] keys\"}\n\n                    if {($j % 20) == 0} {\n                        catch {\n                            if {$delay} {\n                                $slave multi\n                                $slave client kill $master_host:$master_port\n                                $slave debug sleep $delay\n                                $slave exec\n                            } else {\n                                $slave client kill $master_host:$master_port\n                            }\n                        }\n                    }\n                }\n                stop_bg_complex_data $load_handle0\n                stop_bg_complex_data $load_handle1\n                stop_bg_complex_data $load_handle2\n                set retry 10\n                while {$retry && ([$master debug digest] ne [$slave debug digest])}\\\n                {\n                    after 1000\n                    incr retry -1\n                }\n                assert {[$master dbsize] > 0}\n\n                if {[$master debug digest] ne [$slave debug digest]} {\n                    set csv1 [csvdump r]\n                    set csv2 [csvdump {r -1}]\n                    set fd [open /tmp/repldump1.txt w]\n                    puts -nonewline $fd $csv1\n                    close $fd\n                    set fd [open /tmp/repldump2.txt w]\n                    puts -nonewline $fd $csv2\n                    close $fd\n                    puts \"Master - Slave inconsistency\"\n                    puts \"Run diff -u against /tmp/repldump*.txt for more info\"\n                }\n                assert_equal [r debug digest] [r -1 debug digest]\n                eval $cond\n            }\n        }\n    }\n}\n\ntest_psync {ok psync} 6 1000000 3600 0 {\n    assert {[s -1 sync_partial_ok] > 0}\n}\n\ntest_psync {no backlog} 6 100 3600 0 {\n    assert {[s -1 sync_partial_err] > 0}\n}\n\ntest_psync {ok after delay} 3 100000000 3600 3 {\n    assert {[s -1 sync_partial_ok] > 0}\n}\n\ntest_psync {backlog expired} 3 100000000 1 3 {\n    assert {[s -1 sync_partial_err] > 0}\n}\n"
  },
  {
    "path": "tests/integration/replication.tcl",
    "content": "start_server {tags {\"repl\"}} {\n    start_server {} {\n        test {First server should have role slave after SLAVEOF} {\n            r -1 slaveof [srv 0 host] [srv 0 port]\n            wait_for_condition 50 100 {\n                [s -1 role] eq {slave} &&\n                [string match {*master_link_status:up*} [r -1 info replication]]\n            } else {\n                fail \"Can't turn the instance into a slave\"\n            }\n        }\n\n        test {BRPOPLPUSH replication, when blocking against empty list} {\n            set rd [redis_deferring_client]\n            $rd brpoplpush a b 5\n            r lpush a foo\n            wait_for_condition 50 100 {\n                [r debug digest] eq [r -1 debug digest]\n            } else {\n                fail \"Master and slave have different digest: [r debug digest] VS [r -1 debug digest]\"\n            }\n        }\n\n        test {BRPOPLPUSH replication, list exists} {\n            set rd [redis_deferring_client]\n            r lpush c 1\n            r lpush c 2\n            r lpush c 3\n            $rd brpoplpush c d 5\n            after 1000\n            assert_equal [r debug digest] [r -1 debug digest]\n        }\n    }\n}\n\nstart_server {tags {\"repl\"}} {\n    r set mykey foo\n    \n    start_server {} {\n        test {Second server should have role master at first} {\n            s role\n        } {master}\n        \n        test {SLAVEOF should start with link status \"down\"} {\n            r slaveof [srv -1 host] [srv -1 port]\n            s master_link_status\n        } {down}\n        \n        test {The role should immediately be changed to \"slave\"} {\n            s role\n        } {slave}\n\n        wait_for_sync r\n        test {Sync should have transferred keys from master} {\n            r get mykey\n        } {foo}\n        \n        test {The link status should be up} {\n            s master_link_status\n        } {up}\n        \n        test {SET on the master should immediately propagate} {\n            r -1 set mykey bar\n\n            wait_for_condition 500 100 {\n                [r  0 get mykey] eq {bar}\n            } else {\n                fail \"SET on master did not propagated on slave\"\n            }\n        }\n\n        test {FLUSHALL should replicate} {\n            r -1 flushall\n            if {$::valgrind} {after 2000}\n            list [r -1 dbsize] [r 0 dbsize]\n        } {0 0}\n    }\n}\n\nproc start_write_load {host port seconds} {\n    set tclsh [info nameofexecutable]\n    exec $tclsh tests/helpers/gen_write_load.tcl $host $port $seconds &\n}\n\nproc stop_write_load {handle} {\n    catch {exec /bin/kill -9 $handle}\n}\n\nstart_server {tags {\"repl\"}} {\n    set master [srv 0 client]\n    set master_host [srv 0 host]\n    set master_port [srv 0 port]\n    set slaves {}\n    set load_handle0 [start_write_load $master_host $master_port 3]\n    set load_handle1 [start_write_load $master_host $master_port 5]\n    set load_handle2 [start_write_load $master_host $master_port 20]\n    set load_handle3 [start_write_load $master_host $master_port 8]\n    set load_handle4 [start_write_load $master_host $master_port 4]\n    start_server {} {\n        lappend slaves [srv 0 client]\n        start_server {} {\n            lappend slaves [srv 0 client]\n            start_server {} {\n                lappend slaves [srv 0 client]\n                test \"Connect multiple slaves at the same time (issue #141)\" {\n                    # Send SALVEOF commands to slaves\n                    [lindex $slaves 0] slaveof $master_host $master_port\n                    [lindex $slaves 1] slaveof $master_host $master_port\n                    [lindex $slaves 2] slaveof $master_host $master_port\n\n                    # Wait for all the three slaves to reach the \"online\" state\n                    set retry 500\n                    while {$retry} {\n                        set info [r -3 info]\n                        if {[string match {*slave0:*state=online*slave1:*state=online*slave2:*state=online*} $info]} {\n                            break\n                        } else {\n                            incr retry -1\n                            after 100\n                        }\n                    }\n                    if {$retry == 0} {\n                        error \"assertion:Slaves not correctly synchronized\"\n                    }\n\n                    # Stop the write load\n                    stop_write_load $load_handle0\n                    stop_write_load $load_handle1\n                    stop_write_load $load_handle2\n                    stop_write_load $load_handle3\n                    stop_write_load $load_handle4\n\n                    # Wait that slaves exit the \"loading\" state\n                    wait_for_condition 500 100 {\n                        ![string match {*loading:1*} [[lindex $slaves 0] info]] &&\n                        ![string match {*loading:1*} [[lindex $slaves 1] info]] &&\n                        ![string match {*loading:1*} [[lindex $slaves 2] info]]\n                    } else {\n                        fail \"Slaves still loading data after too much time\"\n                    }\n\n                    # Make sure that slaves and master have same number of keys\n                    wait_for_condition 500 100 {\n                        [$master dbsize] == [[lindex $slaves 0] dbsize] &&\n                        [$master dbsize] == [[lindex $slaves 1] dbsize] &&\n                        [$master dbsize] == [[lindex $slaves 2] dbsize]\n                    } else {\n                        fail \"Different number of keys between masted and slave after too long time.\"\n                    }\n\n                    # Check digests\n                    set digest [$master debug digest]\n                    set digest0 [[lindex $slaves 0] debug digest]\n                    set digest1 [[lindex $slaves 1] debug digest]\n                    set digest2 [[lindex $slaves 2] debug digest]\n                    assert {$digest ne 0000000000000000000000000000000000000000}\n                    assert {$digest eq $digest0}\n                    assert {$digest eq $digest1}\n                    assert {$digest eq $digest2}\n                }\n           }\n        }\n    }\n}\n"
  },
  {
    "path": "tests/sentinel/run.tcl",
    "content": "# Sentinel test suite. Copyright (C) 2014 Salvatore Sanfilippo antirez@gmail.com\n# This softare is released under the BSD License. See the COPYING file for\n# more information.\n\ncd tests/sentinel\nsource ../instances.tcl\n\nset ::instances_count 5 ; # How many instances we use at max.\n\nproc main {} {\n    parse_options\n    spawn_instance sentinel $::sentinel_base_port $::instances_count\n    spawn_instance redis $::redis_base_port $::instances_count\n    run_tests\n    cleanup\n}\n\nif {[catch main e]} {\n    puts $::errorInfo\n    cleanup\n}\n"
  },
  {
    "path": "tests/sentinel/tests/00-base.tcl",
    "content": "# Check the basic monitoring and failover capabilities.\n\nsource \"../tests/includes/init-tests.tcl\"\n\nif {$::simulate_error} {\n    test \"This test will fail\" {\n        fail \"Simulated error\"\n    }\n}\n\ntest \"Basic failover works if the master is down\" {\n    set old_port [RI $master_id tcp_port]\n    set addr [S 0 SENTINEL GET-MASTER-ADDR-BY-NAME mymaster]\n    assert {[lindex $addr 1] == $old_port}\n    kill_instance redis $master_id\n    foreach_sentinel_id id {\n        wait_for_condition 1000 50 {\n            [lindex [S $id SENTINEL GET-MASTER-ADDR-BY-NAME mymaster] 1] != $old_port\n        } else {\n            fail \"At least one Sentinel did not received failover info\"\n        }\n    }\n    restart_instance redis $master_id\n    set addr [S 0 SENTINEL GET-MASTER-ADDR-BY-NAME mymaster]\n    set master_id [get_instance_id_by_port redis [lindex $addr 1]]\n}\n\ntest \"New master [join $addr {:}] role matches\" {\n    assert {[RI $master_id role] eq {master}}\n}\n\ntest \"All the other slaves now point to the new master\" {\n    foreach_redis_id id {\n        if {$id != $master_id && $id != 0} {\n            wait_for_condition 1000 50 {\n                [RI $id master_port] == [lindex $addr 1]\n            } else {\n                fail \"Redis ID $id not configured to replicate with new master\"\n            }\n        }\n    }\n}\n\ntest \"The old master eventually gets reconfigured as a slave\" {\n    wait_for_condition 1000 50 {\n        [RI 0 master_port] == [lindex $addr 1]\n    } else {\n        fail \"Old master not reconfigured as slave of new master\"\n    }\n}\n\ntest \"ODOWN is not possible without N (quorum) Sentinels reports\" {\n    foreach_sentinel_id id {\n        S $id SENTINEL SET mymaster quorum [expr $sentinels+1]\n    }\n    set old_port [RI $master_id tcp_port]\n    set addr [S 0 SENTINEL GET-MASTER-ADDR-BY-NAME mymaster]\n    assert {[lindex $addr 1] == $old_port}\n    kill_instance redis $master_id\n\n    # Make sure failover did not happened.\n    set addr [S 0 SENTINEL GET-MASTER-ADDR-BY-NAME mymaster]\n    assert {[lindex $addr 1] == $old_port}\n    restart_instance redis $master_id\n}\n\ntest \"Failover is not possible without majority agreement\" {\n    foreach_sentinel_id id {\n        S $id SENTINEL SET mymaster quorum $quorum\n    }\n\n    # Crash majority of sentinels\n    for {set id 0} {$id < $quorum} {incr id} {\n        kill_instance sentinel $id\n    }\n\n    # Kill the current master\n    kill_instance redis $master_id\n\n    # Make sure failover did not happened.\n    set addr [S $quorum SENTINEL GET-MASTER-ADDR-BY-NAME mymaster]\n    assert {[lindex $addr 1] == $old_port}\n    restart_instance redis $master_id\n\n    # Cleanup: restart Sentinels to monitor the master.\n    for {set id 0} {$id < $quorum} {incr id} {\n        restart_instance sentinel $id\n    }\n}\n\ntest \"Failover works if we configure for absolute agreement\" {\n    foreach_sentinel_id id {\n        S $id SENTINEL SET mymaster quorum $sentinels\n    }\n\n    # Wait for Sentinels to monitor the master again\n    foreach_sentinel_id id {\n        wait_for_condition 1000 50 {\n            [dict get [S $id SENTINEL MASTER mymaster] info-refresh] < 100000\n        } else {\n            fail \"At least one Sentinel is not monitoring the master\"\n        }\n    }\n\n    kill_instance redis $master_id\n\n    foreach_sentinel_id id {\n        wait_for_condition 1000 50 {\n            [lindex [S $id SENTINEL GET-MASTER-ADDR-BY-NAME mymaster] 1] != $old_port\n        } else {\n            fail \"At least one Sentinel did not received failover info\"\n        }\n    }\n    restart_instance redis $master_id\n    set addr [S 0 SENTINEL GET-MASTER-ADDR-BY-NAME mymaster]\n    set master_id [get_instance_id_by_port redis [lindex $addr 1]]\n\n    # Set the min ODOWN agreement back to strict majority.\n    foreach_sentinel_id id {\n        S $id SENTINEL SET mymaster quorum $quorum\n    }\n}\n\ntest \"New master [join $addr {:}] role matches\" {\n    assert {[RI $master_id role] eq {master}}\n}\n"
  },
  {
    "path": "tests/sentinel/tests/01-conf-update.tcl",
    "content": "# Test Sentinel configuration consistency after partitions heal.\n\nsource \"../tests/includes/init-tests.tcl\"\n\ntest \"We can failover with Sentinel 1 crashed\" {\n    set old_port [RI $master_id tcp_port]\n    set addr [S 0 SENTINEL GET-MASTER-ADDR-BY-NAME mymaster]\n    assert {[lindex $addr 1] == $old_port}\n\n    # Crash Sentinel 1\n    kill_instance sentinel 1\n\n    kill_instance redis $master_id\n    foreach_sentinel_id id {\n        if {$id != 1} {\n            wait_for_condition 1000 50 {\n                [lindex [S $id SENTINEL GET-MASTER-ADDR-BY-NAME mymaster] 1] != $old_port\n            } else {\n                fail \"Sentinel $id did not received failover info\"\n            }\n        }\n    }\n    restart_instance redis $master_id\n    set addr [S 0 SENTINEL GET-MASTER-ADDR-BY-NAME mymaster]\n    set master_id [get_instance_id_by_port redis [lindex $addr 1]]\n}\n\ntest \"After Sentinel 1 is restarted, its config gets updated\" {\n    restart_instance sentinel 1\n    wait_for_condition 1000 50 {\n        [lindex [S 1 SENTINEL GET-MASTER-ADDR-BY-NAME mymaster] 1] != $old_port\n    } else {\n        fail \"Restarted Sentinel did not received failover info\"\n    }\n}\n\ntest \"New master [join $addr {:}] role matches\" {\n    assert {[RI $master_id role] eq {master}}\n}\n"
  },
  {
    "path": "tests/sentinel/tests/02-slaves-reconf.tcl",
    "content": "# Check that slaves are reconfigured at a latter time if they are partitioned.\n#\n# Here we should test:\n# 1) That slaves point to the new master after failover.\n# 2) That partitioned slaves point to new master when they are partitioned\n#    away during failover and return at a latter time.\n\nsource \"../tests/includes/init-tests.tcl\"\n\nproc 03_test_slaves_replication {} {\n    uplevel 1 {\n        test \"Check that slaves replicate from current master\" {\n            set master_port [RI $master_id tcp_port]\n            foreach_redis_id id {\n                if {$id == $master_id} continue\n                if {[instance_is_killed redis $id]} continue\n                wait_for_condition 1000 50 {\n                    [RI $id master_port] == $master_port\n                } else {\n                    fail \"Redis slave $id is replicating from wrong master\"\n                }\n            }\n        }\n    }\n}\n\nproc 03_crash_and_failover {} {\n    uplevel 1 {\n        test \"Crash the master and force a failover\" {\n            set old_port [RI $master_id tcp_port]\n            set addr [S 0 SENTINEL GET-MASTER-ADDR-BY-NAME mymaster]\n            assert {[lindex $addr 1] == $old_port}\n            kill_instance redis $master_id\n            foreach_sentinel_id id {\n                wait_for_condition 1000 50 {\n                    [lindex [S $id SENTINEL GET-MASTER-ADDR-BY-NAME mymaster] 1] != $old_port\n                } else {\n                    fail \"At least one Sentinel did not received failover info\"\n                }\n            }\n            restart_instance redis $master_id\n            set addr [S 0 SENTINEL GET-MASTER-ADDR-BY-NAME mymaster]\n            set master_id [get_instance_id_by_port redis [lindex $addr 1]]\n        }\n    }\n}\n\n03_test_slaves_replication\n03_crash_and_failover\n03_test_slaves_replication\n\ntest \"Kill a slave instance\" {\n    foreach_redis_id id {\n        if {$id == $master_id} continue\n        set killed_slave_id $id\n        kill_instance redis $id\n        break\n    }\n}\n\n03_crash_and_failover\n03_test_slaves_replication\n\ntest \"Wait for failover to end\" {\n    set inprogress 1\n    while {$inprogress} {\n        set inprogress 0\n        foreach_sentinel_id id {\n            if {[dict exists [S $id SENTINEL MASTER mymaster] failover-state]} {\n                incr inprogress\n            }\n        }\n        if {$inprogress} {after 100}\n    }\n}\n\ntest \"Restart killed slave and test replication of slaves again...\" {\n    restart_instance redis $killed_slave_id\n}\n\n# Now we check if the slave rejoining the partition is reconfigured even\n# if the failover finished.\n03_test_slaves_replication\n"
  },
  {
    "path": "tests/sentinel/tests/03-runtime-reconf.tcl",
    "content": "# Test runtime reconfiguration command SENTINEL SET.\n"
  },
  {
    "path": "tests/sentinel/tests/04-slave-selection.tcl",
    "content": "# Test slave selection algorithm.\n#\n# This unit should test:\n# 1) That when there are no suitable slaves no failover is performed.\n# 2) That among the available slaves, the one with better offset is picked.\n"
  },
  {
    "path": "tests/sentinel/tests/includes/init-tests.tcl",
    "content": "# Initialization tests -- most units will start including this.\n\ntest \"(init) Restart killed instances\" {\n    foreach type {redis sentinel} {\n        foreach_${type}_id id {\n            if {[get_instance_attrib $type $id pid] == -1} {\n                puts -nonewline \"$type/$id \"\n                flush stdout\n                restart_instance $type $id\n            }\n        }\n    }\n}\n\nset redis_slaves 4\ntest \"(init) Create a master-slaves cluster of [expr $redis_slaves+1] instances\" {\n    create_redis_master_slave_cluster [expr {$redis_slaves+1}]\n}\nset master_id 0\n\ntest \"(init) Sentinels can start monitoring a master\" {\n    set sentinels [llength $::sentinel_instances]\n    set quorum [expr {$sentinels/2+1}]\n    foreach_sentinel_id id {\n        catch {S $id SENTINEL REMOVE mymaster}\n        S $id SENTINEL MONITOR mymaster \\\n              [get_instance_attrib redis $master_id host] \\\n              [get_instance_attrib redis $master_id port] $quorum\n    }\n    foreach_sentinel_id id {\n        assert {[S $id sentinel master mymaster] ne {}}\n        S $id SENTINEL SET mymaster down-after-milliseconds 2000\n        S $id SENTINEL SET mymaster failover-timeout 20000\n        S $id SENTINEL SET mymaster parallel-syncs 10\n    }\n}\n\ntest \"(init) Sentinels can talk with the master\" {\n    foreach_sentinel_id id {\n        wait_for_condition 100 50 {\n            [catch {S $id SENTINEL GET-MASTER-ADDR-BY-NAME mymaster}] == 0\n        } else {\n            fail \"Sentinel $id can't talk with the master.\"\n        }\n    }\n}\n\ntest \"(init) Sentinels are able to auto-discover other sentinels\" {\n    set sentinels [llength $::sentinel_instances]\n    foreach_sentinel_id id {\n        wait_for_condition 100 50 {\n            [dict get [S $id SENTINEL MASTER mymaster] num-other-sentinels] == ($sentinels-1)\n        } else {\n            fail \"At least some sentinel can't detect some other sentinel\"\n        }\n    }\n}\n\ntest \"(init) Sentinels are able to auto-discover slaves\" {\n    foreach_sentinel_id id {\n        wait_for_condition 100 50 {\n            [dict get [S $id SENTINEL MASTER mymaster] num-slaves] == $redis_slaves\n        } else {\n            fail \"At least some sentinel can't detect some slave\"\n        }\n    }\n}\n"
  },
  {
    "path": "tests/sentinel/tmp/.gitignore",
    "content": "redis_*\nsentinel_*\n"
  },
  {
    "path": "tests/support/cluster.tcl",
    "content": "# Tcl redis cluster client as a wrapper of redis.rb.\n# Copyright (C) 2014 Salvatore Sanfilippo\n# Released under the BSD license like Redis itself\n#\n# Example usage:\n#\n# set c [redis_cluster 127.0.0.1 6379 127.0.0.1 6380]\n# $c set foo\n# $c get foo\n# $c close\n\npackage require Tcl 8.5\npackage provide redis_cluster 0.1\n\nnamespace eval redis_cluster {}\nset ::redis_cluster::id 0\narray set ::redis_cluster::startup_nodes {}\narray set ::redis_cluster::nodes {}\narray set ::redis_cluster::slots {}\n\n# List of \"plain\" commands, which are commands where the sole key is always\n# the first argument.\nset ::redis_cluster::plain_commands {\n    get set setnx setex psetex append strlen exists setbit getbit\n    setrange getrange substr incr decr rpush lpush rpushx lpushx\n    linsert rpop lpop brpop llen lindex lset lrange ltrim lrem\n    sadd srem sismember scard spop srandmember smembers sscan zadd\n    zincrby zrem zremrangebyscore zremrangebyrank zremrangebylex zrange\n    zrangebyscore zrevrangebyscore zrangebylex zrevrangebylex zcount\n    zlexcount zrevrange zcard zscore zrank zrevrank zscan hset hsetnx\n    hget hmset hmget hincrby hincrbyfloat hdel hlen hkeys hvals\n    hgetall hexists hscan incrby decrby incrbyfloat getset move\n    expire expireat pexpire pexpireat type ttl pttl persist restore\n    dump bitcount bitpos pfadd pfcount\n}\n\nproc redis_cluster {nodes} {\n    set id [incr ::redis_cluster::id]\n    set ::redis_cluster::startup_nodes($id) $nodes\n    set ::redis_cluster::nodes($id) {}\n    set ::redis_cluster::slots($id) {}\n    set handle [interp alias {} ::redis_cluster::instance$id {} ::redis_cluster::__dispatch__ $id]\n    $handle refresh_nodes_map\n    return $handle\n}\n\n# Totally reset the slots / nodes state for the client, calls\n# CLUSTER NODES in the first startup node available, populates the\n# list of nodes ::redis_cluster::nodes($id) with an hash mapping node\n# ip:port to a representation of the node (another hash), and finally\n# maps ::redis_cluster::slots($id) with an hash mapping slot numbers\n# to node IDs.\n#\n# This function is called when a new Redis Cluster client is initialized\n# and every time we get a -MOVED redirection error.\nproc ::redis_cluster::__method__refresh_nodes_map {id} {\n    # Contact the first responding startup node.\n    set idx 0; # Index of the node that will respond.\n    set errmsg {}\n    foreach start_node $::redis_cluster::startup_nodes($id) {\n        lassign [split $start_node :] start_host start_port\n        if {[catch {\n            set r {}\n            set r [redis $start_host $start_port]\n            set nodes_descr [$r cluster nodes]\n            $r close\n        } e]} {\n            if {$r ne {}} {catch {$r close}}\n            incr idx\n            if {[string length $errmsg] < 200} {\n                append errmsg \" $start_node: $e\"\n            }\n            continue ; # Try next.\n        } else {\n            break; # Good node found.\n        }\n    }\n\n    if {$idx == [llength $::redis_cluster::startup_nodes($id)]} {\n        error \"No good startup node found. $errmsg\"\n    }\n\n    # Put the node that responded as first in the list if it is not\n    # already the first.\n    if {$idx != 0} {\n        set l $::redis_cluster::startup_nodes($id)\n        set left [lrange $l 0 [expr {$idx-1}]]\n        set right [lrange $l [expr {$idx+1}] end]\n        set l [concat [lindex $l $idx] $left $right]\n        set ::redis_cluster::startup_nodes($id) $l\n    }\n\n    # Parse CLUSTER NODES output to populate the nodes description.\n    set nodes {} ; # addr -> node description hash.\n    foreach line [split $nodes_descr \"\\n\"] {\n        set line [string trim $line]\n        if {$line eq {}} continue\n        set args [split $line \" \"]\n        lassign $args nodeid addr flags slaveof pingsent pongrecv configepoch linkstate\n        set slots [lrange $args 8 end]\n        if {$addr eq {:0}} {\n            set addr $start_host:$start_port\n        }\n        lassign [split $addr :] host port\n\n        # Connect to the node\n        set link {}\n        catch {set link [redis $host $port]}\n\n        # Build this node description as an hash.\n        set node [dict create \\\n            id $nodeid \\\n            addr $addr \\\n            host $host \\\n            port $port \\\n            flags $flags \\\n            slaveof $slaveof \\\n            slots $slots \\\n            link $link \\\n        ]\n        dict set nodes $addr $node\n        lappend ::redis_cluster::startup_nodes($id) $addr\n    }\n\n    # Close all the existing links in the old nodes map, and set the new\n    # map as current.\n    foreach n $::redis_cluster::nodes($id) {\n        catch {\n            [dict get $n link] close\n        }\n    }\n    set ::redis_cluster::nodes($id) $nodes\n\n    # Populates the slots -> nodes map.\n    dict for {addr node} $nodes {\n        foreach slotrange [dict get $node slots] {\n            lassign [split $slotrange -] start end\n            if {$end == {}} {set end $start}\n            for {set j $start} {$j <= $end} {incr j} {\n                dict set ::redis_cluster::slots($id) $j $addr\n            }\n        }\n    }\n\n    # Only retain unique entries in the startup nodes list\n    set ::redis_cluster::startup_nodes($id) [lsort -unique $::redis_cluster::startup_nodes($id)]\n}\n\n# Free a redis_cluster handle.\nproc ::redis_cluster::__method__close {id} {\n    catch {\n        set nodes $::redis_cluster::nodes($id)\n        dict for {addr node} $nodes {\n            catch {\n                [dict get $node link] close\n            }\n        }\n    }\n    catch {unset ::redis_cluster::startup_nodes($id)}\n    catch {unset ::redis_cluster::nodes($id)}\n    catch {unset ::redis_cluster::slots($id)}\n    catch {interp alias {} ::redis_cluster::instance$id {}}\n}\n\nproc ::redis_cluster::__dispatch__ {id method args} {\n    if {[info command ::redis_cluster::__method__$method] eq {}} {\n        # Get the keys from the command.\n        set keys [::redis_cluster::get_keys_from_command $method $args]\n        if {$keys eq {}} {\n            error \"Redis command '$method' is not supported by redis_cluster.\"\n        }\n\n        # Resolve the keys in the corresponding hash slot they hash to.\n        set slot [::redis_cluster::get_slot_from_keys $keys]\n        if {$slot eq {}} {\n            error \"Invalid command: multiple keys not hashing to the same slot.\"\n        }\n\n        # Get the node mapped to this slot.\n        set node_addr [dict get $::redis_cluster::slots($id) $slot]\n        if {$node_addr eq {}} {\n            error \"No mapped node for slot $slot.\"\n        }\n\n        # Execute the command in the node we think is the slot owner.\n        set retry 100\n        while {[incr retry -1]} {\n            if {$retry < 5} {after 100}\n            set node [dict get $::redis_cluster::nodes($id) $node_addr]\n            set link [dict get $node link]\n            if {[catch {$link $method {*}$args} e]} {\n                if {$link eq {} || \\\n                    [string range $e 0 4] eq {MOVED} || \\\n                    [string range $e 0 2] eq {I/O} \\\n                } {\n                    # MOVED redirection.\n                    ::redis_cluster::__method__refresh_nodes_map $id\n                    set node_addr [dict get $::redis_cluster::slots($id) $slot]\n                    continue\n                } elseif {[string range $e 0 2] eq {ASK}} {\n                    # ASK redirection.\n                    set node_addr [lindex $e 2]\n                    continue\n                } else {\n                    # Non redirecting error.\n                    error $e $::errorInfo $::errorCode\n                }\n            } else {\n                # OK query went fine\n                return $e\n            }\n        }\n        error \"Too many redirections or failures contacting Redis Cluster.\"\n    } else {\n        uplevel 1 [list ::redis_cluster::__method__$method $id] $args\n    }\n}\n\nproc ::redis_cluster::get_keys_from_command {cmd argv} {\n    set cmd [string tolower $cmd]\n    # Most Redis commands get just one key as first argument.\n    if {[lsearch -exact $::redis_cluster::plain_commands $cmd] != -1} {\n        return [list [lindex $argv 0]]\n    }\n\n    # Special handling for other commands\n    switch -exact $cmd {\n        mget {return $argv}\n    }\n\n    # All the remaining commands are not handled.\n    return {}\n}\n\n# Returns the CRC16 of the specified string.\n# The CRC parameters are described in the Redis Cluster specification.\nset ::redis_cluster::XMODEMCRC16Lookup {\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\nproc ::redis_cluster::crc16 {s} {\n    set s [encoding convertto ascii $s]\n    set crc 0\n    foreach char [split $s {}] {\n        scan $char %c byte\n        set crc [expr {(($crc<<8)&0xffff) ^ [lindex $::redis_cluster::XMODEMCRC16Lookup [expr {(($crc>>8)^$byte) & 0xff}]]}]\n    }\n    return $crc\n}\n\n# Hash a single key returning the slot it belongs to, Implemented hash\n# tags as described in the Redis Cluster specification.\nproc ::redis_cluster::hash {key} {\n    # TODO: Handle hash slots.\n    expr {[::redis_cluster::crc16 $key] & 16383}\n}\n\n# Return the slot the specified keys hash to.\n# If the keys hash to multiple slots, an empty string is returned to\n# signal that the command can't be run in Redis Cluster.\nproc ::redis_cluster::get_slot_from_keys {keys} {\n    set slot {}\n    foreach k $keys {\n        set s [::redis_cluster::hash $k]\n        if {$slot eq {}} {\n            set slot $s\n        } elseif {$slot != $s} {\n            return {} ; # Error\n        }\n    }\n    return $slot\n}\n"
  },
  {
    "path": "tests/support/redis.tcl",
    "content": "# Tcl client library - used by the Redis test\n# Copyright (C) 2009-2014 Salvatore Sanfilippo\n# Released under the BSD license like Redis itself\n#\n# Example usage:\n#\n# set r [redis 127.0.0.1 6379]\n# $r lpush mylist foo\n# $r lpush mylist bar\n# $r lrange mylist 0 -1\n# $r close\n#\n# Non blocking usage example:\n#\n# proc handlePong {r type reply} {\n#     puts \"PONG $type '$reply'\"\n#     if {$reply ne \"PONG\"} {\n#         $r ping [list handlePong]\n#     }\n# }\n# \n# set r [redis]\n# $r blocking 0\n# $r get fo [list handlePong]\n#\n# vwait forever\n\npackage require Tcl 8.5\npackage provide redis 0.1\n\nnamespace eval redis {}\nset ::redis::id 0\narray set ::redis::fd {}\narray set ::redis::blocking {}\narray set ::redis::deferred {}\narray set ::redis::callback {}\narray set ::redis::state {} ;# State in non-blocking reply reading\narray set ::redis::statestack {} ;# Stack of states, for nested mbulks\n\nproc redis {{server 127.0.0.1} {port 6379} {defer 0}} {\n    set fd [socket $server $port]\n    fconfigure $fd -translation binary\n    set id [incr ::redis::id]\n    set ::redis::fd($id) $fd\n    set ::redis::blocking($id) 1\n    set ::redis::deferred($id) $defer\n    ::redis::redis_reset_state $id\n    interp alias {} ::redis::redisHandle$id {} ::redis::__dispatch__ $id\n}\n\nproc ::redis::__dispatch__ {id method args} {\n    set fd $::redis::fd($id)\n    set blocking $::redis::blocking($id)\n    set deferred $::redis::deferred($id)\n    if {$blocking == 0} {\n        if {[llength $args] == 0} {\n            error \"Please provide a callback in non-blocking mode\"\n        }\n        set callback [lindex $args end]\n        set args [lrange $args 0 end-1]\n    }\n    if {[info command ::redis::__method__$method] eq {}} {\n        set cmd \"*[expr {[llength $args]+1}]\\r\\n\"\n        append cmd \"$[string length $method]\\r\\n$method\\r\\n\"\n        foreach a $args {\n            append cmd \"$[string length $a]\\r\\n$a\\r\\n\"\n        }\n        ::redis::redis_write $fd $cmd\n        flush $fd\n\n        if {!$deferred} {\n            if {$blocking} {\n                ::redis::redis_read_reply $fd\n            } else {\n                # Every well formed reply read will pop an element from this\n                # list and use it as a callback. So pipelining is supported\n                # in non blocking mode.\n                lappend ::redis::callback($id) $callback\n                fileevent $fd readable [list ::redis::redis_readable $fd $id]\n            }\n        }\n    } else {\n        uplevel 1 [list ::redis::__method__$method $id $fd] $args\n    }\n}\n\nproc ::redis::__method__blocking {id fd val} {\n    set ::redis::blocking($id) $val\n    fconfigure $fd -blocking $val\n}\n\nproc ::redis::__method__read {id fd} {\n    ::redis::redis_read_reply $fd\n}\n\nproc ::redis::__method__write {id fd buf} {\n    ::redis::redis_write $fd $buf\n}\n\nproc ::redis::__method__flush {id fd} {\n    flush $fd\n}\n\nproc ::redis::__method__close {id fd} {\n    catch {close $fd}\n    catch {unset ::redis::fd($id)}\n    catch {unset ::redis::blocking($id)}\n    catch {unset ::redis::state($id)}\n    catch {unset ::redis::statestack($id)}\n    catch {unset ::redis::callback($id)}\n    catch {interp alias {} ::redis::redisHandle$id {}}\n}\n\nproc ::redis::__method__channel {id fd} {\n    return $fd\n}\n\nproc ::redis::__method__deferred {id fd val} {\n    set ::redis::deferred($id) $val\n}\n\nproc ::redis::redis_write {fd buf} {\n    puts -nonewline $fd $buf\n}\n\nproc ::redis::redis_writenl {fd buf} {\n    redis_write $fd $buf\n    redis_write $fd \"\\r\\n\"\n    flush $fd\n}\n\nproc ::redis::redis_readnl {fd len} {\n    set buf [read $fd $len]\n    read $fd 2 ; # discard CR LF\n    return $buf\n}\n\nproc ::redis::redis_bulk_read {fd} {\n    set count [redis_read_line $fd]\n    if {$count == -1} return {}\n    set buf [redis_readnl $fd $count]\n    return $buf\n}\n\nproc ::redis::redis_multi_bulk_read fd {\n    set count [redis_read_line $fd]\n    if {$count == -1} return {}\n    set l {}\n    set err {}\n    for {set i 0} {$i < $count} {incr i} {\n        if {[catch {\n            lappend l [redis_read_reply $fd]\n        } e] && $err eq {}} {\n            set err $e\n        }\n    }\n    if {$err ne {}} {return -code error $err}\n    return $l\n}\n\nproc ::redis::redis_read_line fd {\n    string trim [gets $fd]\n}\n\nproc ::redis::redis_read_reply fd {\n    set type [read $fd 1]\n    switch -exact -- $type {\n        : -\n        + {redis_read_line $fd}\n        - {return -code error [redis_read_line $fd]}\n        $ {redis_bulk_read $fd}\n        * {redis_multi_bulk_read $fd}\n        default {\n            if {$type eq {}} {return -code error \"I/O error reading reply\"}\n            return -code error \"Bad protocol, '$type' as reply type byte\"\n        }\n    }\n}\n\nproc ::redis::redis_reset_state id {\n    set ::redis::state($id) [dict create buf {} mbulk -1 bulk -1 reply {}]\n    set ::redis::statestack($id) {}\n}\n\nproc ::redis::redis_call_callback {id type reply} {\n    set cb [lindex $::redis::callback($id) 0]\n    set ::redis::callback($id) [lrange $::redis::callback($id) 1 end]\n    uplevel #0 $cb [list ::redis::redisHandle$id $type $reply]\n    ::redis::redis_reset_state $id\n}\n\n# Read a reply in non-blocking mode.\nproc ::redis::redis_readable {fd id} {\n    if {[eof $fd]} {\n        redis_call_callback $id eof {}\n        ::redis::__method__close $id $fd\n        return\n    }\n    if {[dict get $::redis::state($id) bulk] == -1} {\n        set line [gets $fd]\n        if {$line eq {}} return ;# No complete line available, return\n        switch -exact -- [string index $line 0] {\n            : -\n            + {redis_call_callback $id reply [string range $line 1 end-1]}\n            - {redis_call_callback $id err [string range $line 1 end-1]}\n            $ {\n                dict set ::redis::state($id) bulk \\\n                    [expr [string range $line 1 end-1]+2]\n                if {[dict get $::redis::state($id) bulk] == 1} {\n                    # We got a $-1, hack the state to play well with this.\n                    dict set ::redis::state($id) bulk 2\n                    dict set ::redis::state($id) buf \"\\r\\n\"\n                    ::redis::redis_readable $fd $id\n                }\n            }\n            * {\n                dict set ::redis::state($id) mbulk [string range $line 1 end-1]\n                # Handle *-1\n                if {[dict get $::redis::state($id) mbulk] == -1} {\n                    redis_call_callback $id reply {}\n                }\n            }\n            default {\n                redis_call_callback $id err \\\n                    \"Bad protocol, $type as reply type byte\"\n            }\n        }\n    } else {\n        set totlen [dict get $::redis::state($id) bulk]\n        set buflen [string length [dict get $::redis::state($id) buf]]\n        set toread [expr {$totlen-$buflen}]\n        set data [read $fd $toread]\n        set nread [string length $data]\n        dict append ::redis::state($id) buf $data\n        # Check if we read a complete bulk reply\n        if {[string length [dict get $::redis::state($id) buf]] ==\n            [dict get $::redis::state($id) bulk]} {\n            if {[dict get $::redis::state($id) mbulk] == -1} {\n                redis_call_callback $id reply \\\n                    [string range [dict get $::redis::state($id) buf] 0 end-2]\n            } else {\n                dict with ::redis::state($id) {\n                    lappend reply [string range $buf 0 end-2]\n                    incr mbulk -1\n                    set bulk -1\n                }\n                if {[dict get $::redis::state($id) mbulk] == 0} {\n                    redis_call_callback $id reply \\\n                        [dict get $::redis::state($id) reply]\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "tests/support/server.tcl",
    "content": "set ::global_overrides {}\nset ::tags {}\nset ::valgrind_errors {}\n\nproc start_server_error {config_file error} {\n    set err {}\n    append err \"Cant' start the Redis server\\n\"\n    append err \"CONFIGURATION:\"\n    append err [exec cat $config_file]\n    append err \"\\nERROR:\"\n    append err [string trim $error]\n    send_data_packet $::test_server_fd err $err\n}\n\nproc check_valgrind_errors stderr {\n    set fd [open $stderr]\n    set buf [read $fd]\n    close $fd\n\n    if {[regexp -- { at 0x} $buf] ||\n        (![regexp -- {definitely lost: 0 bytes} $buf] &&\n         ![regexp -- {no leaks are possible} $buf])} {\n        send_data_packet $::test_server_fd err \"Valgrind error: $buf\\n\"\n    }\n}\n\nproc kill_server config {\n    # nothing to kill when running against external server\n    if {$::external} return\n\n    # nevermind if its already dead\n    if {![is_alive $config]} { return }\n    set pid [dict get $config pid]\n\n    # check for leaks\n    if {![dict exists $config \"skipleaks\"]} {\n        catch {\n            if {[string match {*Darwin*} [exec uname -a]]} {\n                tags {\"leaks\"} {\n                    test \"Check for memory leaks (pid $pid)\" {\n                        set output {0 leaks}\n                        catch {exec leaks $pid} output\n                        if {[string match {*process does not exist*} $output] ||\n                            [string match {*cannot examine*} $output]} {\n                            # In a few tests we kill the server process.\n                            set output \"0 leaks\"\n                        }\n                        set output\n                    } {*0 leaks*}\n                }\n            }\n        }\n    }\n\n    # kill server and wait for the process to be totally exited\n    catch {exec kill $pid}\n    while {[is_alive $config]} {\n        incr wait 10\n\n        if {$wait >= 5000} {\n            puts \"Forcing process $pid to exit...\"\n            catch {exec kill -KILL $pid}\n        } elseif {$wait % 1000 == 0} {\n            puts \"Waiting for process $pid to exit...\"\n        }\n        after 10\n    }\n\n    # Check valgrind errors if needed\n    if {$::valgrind} {\n        check_valgrind_errors [dict get $config stderr]\n    }\n}\n\nproc is_alive config {\n    set pid [dict get $config pid]\n    if {[catch {exec ps -p $pid} err]} {\n        return 0\n    } else {\n        return 1\n    }\n}\n\nproc ping_server {host port} {\n    set retval 0\n    if {[catch {\n        set fd [socket $host $port]\n        fconfigure $fd -translation binary\n        puts $fd \"PING\\r\\n\"\n        flush $fd\n        set reply [gets $fd]\n        if {[string range $reply 0 0] eq {+} ||\n            [string range $reply 0 0] eq {-}} {\n            set retval 1\n        }\n        close $fd\n    } e]} {\n        if {$::verbose} {\n            puts -nonewline \".\"\n        }\n    } else {\n        if {$::verbose} {\n            puts -nonewline \"ok\"\n        }\n    }\n    return $retval\n}\n\n# Return 1 if the server at the specified addr is reachable by PING, otherwise\n# returns 0. Performs a try every 50 milliseconds for the specified number\n# of retries.\nproc server_is_up {host port retrynum} {\n    after 10 ;# Use a small delay to make likely a first-try success.\n    set retval 0\n    while {[incr retrynum -1]} {\n        if {[catch {ping_server $host $port} ping]} {\n            set ping 0\n        }\n        if {$ping} {return 1}\n        after 50\n    }\n    return 0\n}\n\n# doesn't really belong here, but highly coupled to code in start_server\nproc tags {tags code} {\n    set ::tags [concat $::tags $tags]\n    uplevel 1 $code\n    set ::tags [lrange $::tags 0 end-[llength $tags]]\n}\n\nproc start_server {options {code undefined}} {\n    # If we are running against an external server, we just push the\n    # host/port pair in the stack the first time\n    if {$::external} {\n        if {[llength $::servers] == 0} {\n            set srv {}\n            dict set srv \"host\" $::host\n            dict set srv \"port\" $::port\n            set client [redis $::host $::port]\n            dict set srv \"client\" $client\n            $client select 9\n\n            # append the server to the stack\n            lappend ::servers $srv\n        }\n        uplevel 1 $code\n        return\n    }\n\n    # setup defaults\n    set baseconfig \"default.conf\"\n    set overrides {}\n    set tags {}\n\n    # parse options\n    foreach {option value} $options {\n        switch $option {\n            \"config\" {\n                set baseconfig $value }\n            \"overrides\" {\n                set overrides $value }\n            \"tags\" {\n                set tags $value\n                set ::tags [concat $::tags $value] }\n            default {\n                error \"Unknown option $option\" }\n        }\n    }\n\n    set data [split [exec cat \"tests/assets/$baseconfig\"] \"\\n\"]\n    set config {}\n    foreach line $data {\n        if {[string length $line] > 0 && [string index $line 0] ne \"#\"} {\n            set elements [split $line \" \"]\n            set directive [lrange $elements 0 0]\n            set arguments [lrange $elements 1 end]\n            dict set config $directive $arguments\n        }\n    }\n    \n    # use a different directory every time a server is started\n    dict set config dir [tmpdir server]\n    \n    # start every server on a different port\n    set ::port [find_available_port [expr {$::port+1}]]\n    dict set config port $::port\n\n    # apply overrides from global space and arguments\n    foreach {directive arguments} [concat $::global_overrides $overrides] {\n        dict set config $directive $arguments\n    }\n    \n    # write new configuration to temporary file\n    set config_file [tmpfile redis.conf]\n    set fp [open $config_file w+]\n    foreach directive [dict keys $config] {\n        puts -nonewline $fp \"$directive \"\n        puts $fp [dict get $config $directive]\n    }\n    close $fp\n\n    set stdout [format \"%s/%s\" [dict get $config \"dir\"] \"stdout\"]\n    set stderr [format \"%s/%s\" [dict get $config \"dir\"] \"stderr\"]\n\n    if {$::valgrind} {\n        exec valgrind --suppressions=src/valgrind.sup --show-reachable=no --show-possibly-lost=no --leak-check=full src/redis-server $config_file > $stdout 2> $stderr &\n    } else {\n        exec src/redis-server $config_file > $stdout 2> $stderr &\n    }\n    \n    # check that the server actually started\n    # ugly but tries to be as fast as possible...\n    if {$::valgrind} {set retrynum 1000} else {set retrynum 100}\n\n    if {$::verbose} {\n        puts -nonewline \"=== ($tags) Starting server ${::host}:${::port} \"\n    }\n\n    if {$code ne \"undefined\"} {\n        set serverisup [server_is_up $::host $::port $retrynum]\n    } else {\n        set serverisup 1\n    }\n\n    if {$::verbose} {\n        puts \"\"\n    }\n\n    if {!$serverisup} {\n        set err {}\n        append err [exec cat $stdout] \"\\n\" [exec cat $stderr]\n        start_server_error $config_file $err\n        return\n    }\n    \n    # find out the pid\n    while {![info exists pid]} {\n        regexp {\\[(\\d+)\\]} [exec cat $stdout] _ pid\n        after 100\n    }\n\n    # setup properties to be able to initialize a client object\n    set host $::host\n    set port $::port\n    if {[dict exists $config bind]} { set host [dict get $config bind] }\n    if {[dict exists $config port]} { set port [dict get $config port] }\n\n    # setup config dict\n    dict set srv \"config_file\" $config_file\n    dict set srv \"config\" $config\n    dict set srv \"pid\" $pid\n    dict set srv \"host\" $host\n    dict set srv \"port\" $port\n    dict set srv \"stdout\" $stdout\n    dict set srv \"stderr\" $stderr\n\n    # if a block of code is supplied, we wait for the server to become\n    # available, create a client object and kill the server afterwards\n    if {$code ne \"undefined\"} {\n        set line [exec head -n1 $stdout]\n        if {[string match {*already in use*} $line]} {\n            error_and_quit $config_file $line\n        }\n\n        while 1 {\n            # check that the server actually started and is ready for connections\n            if {[exec grep \"ready to accept\" | wc -l < $stdout] > 0} {\n                break\n            }\n            after 10\n        }\n\n        # append the server to the stack\n        lappend ::servers $srv\n\n        # connect client (after server dict is put on the stack)\n        reconnect\n\n        # execute provided block\n        set num_tests $::num_tests\n        if {[catch { uplevel 1 $code } error]} {\n            set backtrace $::errorInfo\n\n            # Kill the server without checking for leaks\n            dict set srv \"skipleaks\" 1\n            kill_server $srv\n\n            # Print warnings from log\n            puts [format \"\\nLogged warnings (pid %d):\" [dict get $srv \"pid\"]]\n            set warnings [warnings_from_file [dict get $srv \"stdout\"]]\n            if {[string length $warnings] > 0} {\n                puts \"$warnings\"\n            } else {\n                puts \"(none)\"\n            }\n            puts \"\"\n\n            error $error $backtrace\n        }\n\n        # Don't do the leak check when no tests were run\n        if {$num_tests == $::num_tests} {\n            dict set srv \"skipleaks\" 1\n        }\n\n        # pop the server object\n        set ::servers [lrange $::servers 0 end-1]\n\n        set ::tags [lrange $::tags 0 end-[llength $tags]]\n        kill_server $srv\n    } else {\n        set ::tags [lrange $::tags 0 end-[llength $tags]]\n        set _ $srv\n    }\n}\n"
  },
  {
    "path": "tests/support/test.tcl",
    "content": "set ::num_tests 0\nset ::num_passed 0\nset ::num_failed 0\nset ::tests_failed {}\n\nproc fail {msg} {\n    error \"assertion:$msg\"\n}\n\nproc assert {condition} {\n    if {![uplevel 1 [list expr $condition]]} {\n        error \"assertion:Expected condition '$condition' to be true ([uplevel 1 [list subst -nocommands $condition]])\"\n    }\n}\n\nproc assert_match {pattern value} {\n    if {![string match $pattern $value]} {\n        error \"assertion:Expected '$value' to match '$pattern'\"\n    }\n}\n\nproc assert_equal {expected value} {\n    if {$expected ne $value} {\n        error \"assertion:Expected '$value' to be equal to '$expected'\"\n    }\n}\n\nproc assert_error {pattern code} {\n    if {[catch {uplevel 1 $code} error]} {\n        assert_match $pattern $error\n    } else {\n        error \"assertion:Expected an error but nothing was catched\"\n    }\n}\n\nproc assert_encoding {enc key} {\n    # Swapped out values don't have an encoding, so make sure that\n    # the value is swapped in before checking the encoding.\n    set dbg [r debug object $key]\n    while {[string match \"* swapped at:*\" $dbg]} {\n        r debug swapin $key\n        set dbg [r debug object $key]\n    }\n    assert_match \"* encoding:$enc *\" $dbg\n}\n\nproc assert_type {type key} {\n    assert_equal $type [r type $key]\n}\n\n# Wait for the specified condition to be true, with the specified number of\n# max retries and delay between retries. Otherwise the 'elsescript' is\n# executed.\nproc wait_for_condition {maxtries delay e _else_ elsescript} {\n    while {[incr maxtries -1] >= 0} {\n        set errcode [catch {uplevel 1 [list expr $e]} result]\n        if {$errcode == 0} {\n            if {$result} break\n        } else {\n            return -code $errcode $result\n        }\n        after $delay\n    }\n    if {$maxtries == -1} {\n        set errcode [catch [uplevel 1 $elsescript] result]\n        return -code $errcode $result\n    }\n}\n\nproc test {name code {okpattern undefined}} {\n    # abort if tagged with a tag to deny\n    foreach tag $::denytags {\n        if {[lsearch $::tags $tag] >= 0} {\n            return\n        }\n    }\n\n    # check if tagged with at least 1 tag to allow when there *is* a list\n    # of tags to allow, because default policy is to run everything\n    if {[llength $::allowtags] > 0} {\n        set matched 0\n        foreach tag $::allowtags {\n            if {[lsearch $::tags $tag] >= 0} {\n                incr matched\n            }\n        }\n        if {$matched < 1} {\n            return\n        }\n    }\n\n    incr ::num_tests\n    set details {}\n    lappend details \"$name in $::curfile\"\n\n    send_data_packet $::test_server_fd testing $name\n\n    if {[catch {set retval [uplevel 1 $code]} error]} {\n        if {[string match \"assertion:*\" $error]} {\n            set msg [string range $error 10 end]\n            lappend details $msg\n            lappend ::tests_failed $details\n\n            incr ::num_failed\n            send_data_packet $::test_server_fd err [join $details \"\\n\"]\n        } else {\n            # Re-raise, let handler up the stack take care of this.\n            error $error $::errorInfo\n        }\n    } else {\n        if {$okpattern eq \"undefined\" || $okpattern eq $retval || [string match $okpattern $retval]} {\n            incr ::num_passed\n            send_data_packet $::test_server_fd ok $name\n        } else {\n            set msg \"Expected '$okpattern' to equal or match '$retval'\"\n            lappend details $msg\n            lappend ::tests_failed $details\n\n            incr ::num_failed\n            send_data_packet $::test_server_fd err [join $details \"\\n\"]\n        }\n    }\n\n    if {$::traceleaks} {\n        set output [exec leaks redis-server]\n        if {![string match {*0 leaks*} $output]} {\n            send_data_packet $::test_server_fd err \"Detected a memory leak in test '$name': $output\"\n        }\n    }\n}\n"
  },
  {
    "path": "tests/support/tmpfile.tcl",
    "content": "set ::tmpcounter 0\nset ::tmproot \"./tests/tmp\"\nfile mkdir $::tmproot\n\n# returns a dirname unique to this process to write to\nproc tmpdir {basename} {\n    set dir [file join $::tmproot $basename.[pid].[incr ::tmpcounter]]\n    file mkdir $dir\n    set _ $dir\n}\n\n# return a filename unique to this process to write to\nproc tmpfile {basename} {\n    file join $::tmproot $basename.[pid].[incr ::tmpcounter]\n}\n"
  },
  {
    "path": "tests/support/util.tcl",
    "content": "proc randstring {min max {type binary}} {\n    set len [expr {$min+int(rand()*($max-$min+1))}]\n    set output {}\n    if {$type eq {binary}} {\n        set minval 0\n        set maxval 255\n    } elseif {$type eq {alpha}} {\n        set minval 48\n        set maxval 122\n    } elseif {$type eq {compr}} {\n        set minval 48\n        set maxval 52\n    }\n    while {$len} {\n        append output [format \"%c\" [expr {$minval+int(rand()*($maxval-$minval+1))}]]\n        incr len -1\n    }\n    return $output\n}\n\n# Useful for some test\nproc zlistAlikeSort {a b} {\n    if {[lindex $a 0] > [lindex $b 0]} {return 1}\n    if {[lindex $a 0] < [lindex $b 0]} {return -1}\n    string compare [lindex $a 1] [lindex $b 1]\n}\n\n# Return all log lines starting with the first line that contains a warning.\n# Generally, this will be an assertion error with a stack trace.\nproc warnings_from_file {filename} {\n    set lines [split [exec cat $filename] \"\\n\"]\n    set matched 0\n    set logall 0\n    set result {}\n    foreach line $lines {\n        if {[string match {*REDIS BUG REPORT START*} $line]} {\n            set logall 1\n        }\n        if {[regexp {^\\[\\d+\\]\\s+\\d+\\s+\\w+\\s+\\d{2}:\\d{2}:\\d{2} \\#} $line]} {\n            set matched 1\n        }\n        if {$logall || $matched} {\n            lappend result $line\n        }\n    }\n    join $result \"\\n\"\n}\n\n# Return value for INFO property\nproc status {r property} {\n    if {[regexp \"\\r\\n$property:(.*?)\\r\\n\" [{*}$r info] _ value]} {\n        set _ $value\n    }\n}\n\nproc waitForBgsave r {\n    while 1 {\n        if {[status r rdb_bgsave_in_progress] eq 1} {\n            if {$::verbose} {\n                puts -nonewline \"\\nWaiting for background save to finish... \"\n                flush stdout\n            }\n            after 1000\n        } else {\n            break\n        }\n    }\n}\n\nproc waitForBgrewriteaof r {\n    while 1 {\n        if {[status r aof_rewrite_in_progress] eq 1} {\n            if {$::verbose} {\n                puts -nonewline \"\\nWaiting for background AOF rewrite to finish... \"\n                flush stdout\n            }\n            after 1000\n        } else {\n            break\n        }\n    }\n}\n\nproc wait_for_sync r {\n    while 1 {\n        if {[status $r master_link_status] eq \"down\"} {\n            after 10\n        } else {\n            break\n        }\n    }\n}\n\n# Random integer between 0 and max (excluded).\nproc randomInt {max} {\n    expr {int(rand()*$max)}\n}\n\n# Random signed integer between -max and max (both extremes excluded).\nproc randomSignedInt {max} {\n    set i [randomInt $max]\n    if {rand() > 0.5} {\n        set i -$i\n    }\n    return $i\n}\n\nproc randpath args {\n    set path [expr {int(rand()*[llength $args])}]\n    uplevel 1 [lindex $args $path]\n}\n\nproc randomValue {} {\n    randpath {\n        # Small enough to likely collide\n        randomSignedInt 1000\n    } {\n        # 32 bit compressible signed/unsigned\n        randpath {randomSignedInt 2000000000} {randomSignedInt 4000000000}\n    } {\n        # 64 bit\n        randpath {randomSignedInt 1000000000000}\n    } {\n        # Random string\n        randpath {randstring 0 256 alpha} \\\n                {randstring 0 256 compr} \\\n                {randstring 0 256 binary}\n    }\n}\n\nproc randomKey {} {\n    randpath {\n        # Small enough to likely collide\n        randomInt 1000\n    } {\n        # 32 bit compressible signed/unsigned\n        randpath {randomInt 2000000000} {randomInt 4000000000}\n    } {\n        # 64 bit\n        randpath {randomInt 1000000000000}\n    } {\n        # Random string\n        randpath {randstring 1 256 alpha} \\\n                {randstring 1 256 compr}\n    }\n}\n\nproc findKeyWithType {r type} {\n    for {set j 0} {$j < 20} {incr j} {\n        set k [{*}$r randomkey]\n        if {$k eq {}} {\n            return {}\n        }\n        if {[{*}$r type $k] eq $type} {\n            return $k\n        }\n    }\n    return {}\n}\n\nproc createComplexDataset {r ops {opt {}}} {\n    for {set j 0} {$j < $ops} {incr j} {\n        set k [randomKey]\n        set k2 [randomKey]\n        set f [randomValue]\n        set v [randomValue]\n\n        if {[lsearch -exact $opt useexpire] != -1} {\n            if {rand() < 0.1} {\n                {*}$r expire [randomKey] [randomInt 2]\n            }\n        }\n\n        randpath {\n            set d [expr {rand()}]\n        } {\n            set d [expr {rand()}]\n        } {\n            set d [expr {rand()}]\n        } {\n            set d [expr {rand()}]\n        } {\n            set d [expr {rand()}]\n        } {\n            randpath {set d +inf} {set d -inf}\n        }\n        set t [{*}$r type $k]\n\n        if {$t eq {none}} {\n            randpath {\n                {*}$r set $k $v\n            } {\n                {*}$r lpush $k $v\n            } {\n                {*}$r sadd $k $v\n            } {\n                {*}$r zadd $k $d $v\n            } {\n                {*}$r hset $k $f $v\n            } {\n                {*}$r del $k\n            }\n            set t [{*}$r type $k]\n        }\n\n        switch $t {\n            {string} {\n                # Nothing to do\n            }\n            {list} {\n                randpath {{*}$r lpush $k $v} \\\n                        {{*}$r rpush $k $v} \\\n                        {{*}$r lrem $k 0 $v} \\\n                        {{*}$r rpop $k} \\\n                        {{*}$r lpop $k}\n            }\n            {set} {\n                randpath {{*}$r sadd $k $v} \\\n                        {{*}$r srem $k $v} \\\n                        {\n                            set otherset [findKeyWithType {*}$r set]\n                            if {$otherset ne {}} {\n                                randpath {\n                                    {*}$r sunionstore $k2 $k $otherset\n                                } {\n                                    {*}$r sinterstore $k2 $k $otherset\n                                } {\n                                    {*}$r sdiffstore $k2 $k $otherset\n                                }\n                            }\n                        }\n            }\n            {zset} {\n                randpath {{*}$r zadd $k $d $v} \\\n                        {{*}$r zrem $k $v} \\\n                        {\n                            set otherzset [findKeyWithType {*}$r zset]\n                            if {$otherzset ne {}} {\n                                randpath {\n                                    {*}$r zunionstore $k2 2 $k $otherzset\n                                } {\n                                    {*}$r zinterstore $k2 2 $k $otherzset\n                                }\n                            }\n                        }\n            }\n            {hash} {\n                randpath {{*}$r hset $k $f $v} \\\n                        {{*}$r hdel $k $f}\n            }\n        }\n    }\n}\n\nproc formatCommand {args} {\n    set cmd \"*[llength $args]\\r\\n\"\n    foreach a $args {\n        append cmd \"$[string length $a]\\r\\n$a\\r\\n\"\n    }\n    set _ $cmd\n}\n\nproc csvdump r {\n    set o {}\n    foreach k [lsort [{*}$r keys *]] {\n        set type [{*}$r type $k]\n        append o [csvstring $k] , [csvstring $type] ,\n        switch $type {\n            string {\n                append o [csvstring [{*}$r get $k]] \"\\n\"\n            }\n            list {\n                foreach e [{*}$r lrange $k 0 -1] {\n                    append o [csvstring $e] ,\n                }\n                append o \"\\n\"\n            }\n            set {\n                foreach e [lsort [{*}$r smembers $k]] {\n                    append o [csvstring $e] ,\n                }\n                append o \"\\n\"\n            }\n            zset {\n                foreach e [{*}$r zrange $k 0 -1 withscores] {\n                    append o [csvstring $e] ,\n                }\n                append o \"\\n\"\n            }\n            hash {\n                set fields [{*}$r hgetall $k]\n                set newfields {}\n                foreach {k v} $fields {\n                    lappend newfields [list $k $v]\n                }\n                set fields [lsort -index 0 $newfields]\n                foreach kv $fields {\n                    append o [csvstring [lindex $kv 0]] ,\n                    append o [csvstring [lindex $kv 1]] ,\n                }\n                append o \"\\n\"\n            }\n        }\n    }\n    return $o\n}\n\nproc csvstring s {\n    return \"\\\"$s\\\"\"\n}\n\nproc roundFloat f {\n    format \"%.10g\" $f\n}\n\nproc find_available_port start {\n    for {set j $start} {$j < $start+1024} {incr j} {\n        if {[catch {\n            set fd [socket 127.0.0.1 $j]\n        }]} {\n            return $j\n        } else {\n            close $fd\n        }\n    }\n    if {$j == $start+1024} {\n        error \"Can't find a non busy port in the $start-[expr {$start+1023}] range.\"\n    }\n}\n\n# Test if TERM looks like to support colors\nproc color_term {} {\n    expr {[info exists ::env(TERM)] && [string match *xterm* $::env(TERM)]}\n}\n\nproc colorstr {color str} {\n    if {[color_term]} {\n        set b 0\n        if {[string range $color 0 4] eq {bold-}} {\n            set b 1\n            set color [string range $color 5 end]\n        }\n        switch $color {\n            red {set colorcode {31}}\n            green {set colorcode {32}}\n            yellow {set colorcode {33}}\n            blue {set colorcode {34}}\n            magenta {set colorcode {35}}\n            cyan {set colorcode {36}}\n            white {set colorcode {37}}\n            default {set colorcode {37}}\n        }\n        if {$colorcode ne {}} {\n            return \"\\033\\[$b;${colorcode};40m$str\\033\\[0m\"\n        }\n    } else {\n        return $str\n    }\n}\n"
  },
  {
    "path": "tests/test_helper.tcl",
    "content": "# Redis test suite. Copyright (C) 2009 Salvatore Sanfilippo antirez@gmail.com\n# This softare is released under the BSD License. See the COPYING file for\n# more information.\n\npackage require Tcl 8.5\n\nset tcl_precision 17\nsource tests/support/redis.tcl\nsource tests/support/server.tcl\nsource tests/support/tmpfile.tcl\nsource tests/support/test.tcl\nsource tests/support/util.tcl\n\nset ::all_tests {\n    unit/printver\n    unit/dump\n    unit/auth\n    unit/protocol\n    unit/basic\n    unit/scan\n    unit/type/list\n    unit/type/list-2\n    unit/type/list-3\n    unit/type/set\n    unit/type/zset\n    unit/type/hash\n    unit/sort\n    unit/expire\n    unit/other\n    unit/multi\n    unit/quit\n    unit/aofrw\n    integration/replication\n    integration/replication-2\n    integration/replication-3\n    integration/replication-4\n    integration/replication-psync\n    integration/aof\n    integration/rdb\n    integration/convert-zipmap-hash-on-load\n    unit/pubsub\n    unit/slowlog\n    unit/scripting\n    unit/maxmemory\n    unit/introspection\n    unit/limits\n    unit/obuf-limits\n    unit/bitops\n    unit/memefficiency\n    unit/hyperloglog\n}\n# Index to the next test to run in the ::all_tests list.\nset ::next_test 0\n\nset ::host 127.0.0.1\nset ::port 21111\nset ::traceleaks 0\nset ::valgrind 0\nset ::verbose 0\nset ::quiet 0\nset ::denytags {}\nset ::allowtags {}\nset ::external 0; # If \"1\" this means, we are running against external instance\nset ::file \"\"; # If set, runs only the tests in this comma separated list\nset ::curfile \"\"; # Hold the filename of the current suite\nset ::accurate 0; # If true runs fuzz tests with more iterations\nset ::force_failure 0\n\n# Set to 1 when we are running in client mode. The Redis test uses a\n# server-client model to run tests simultaneously. The server instance\n# runs the specified number of client instances that will actually run tests.\n# The server is responsible of showing the result to the user, and exit with\n# the appropriate exit code depending on the test outcome.\nset ::client 0\nset ::numclients 16\n\nproc execute_tests name {\n    set path \"tests/$name.tcl\"\n    set ::curfile $path\n    source $path\n    send_data_packet $::test_server_fd done \"$name\"\n}\n\n# Setup a list to hold a stack of server configs. When calls to start_server\n# are nested, use \"srv 0 pid\" to get the pid of the inner server. To access\n# outer servers, use \"srv -1 pid\" etcetera.\nset ::servers {}\nproc srv {args} {\n    set level 0\n    if {[string is integer [lindex $args 0]]} {\n        set level [lindex $args 0]\n        set property [lindex $args 1]\n    } else {\n        set property [lindex $args 0]\n    }\n    set srv [lindex $::servers end+$level]\n    dict get $srv $property\n}\n\n# Provide easy access to the client for the inner server. It's possible to\n# prepend the argument list with a negative level to access clients for\n# servers running in outer blocks.\nproc r {args} {\n    set level 0\n    if {[string is integer [lindex $args 0]]} {\n        set level [lindex $args 0]\n        set args [lrange $args 1 end]\n    }\n    [srv $level \"client\"] {*}$args\n}\n\nproc reconnect {args} {\n    set level [lindex $args 0]\n    if {[string length $level] == 0 || ![string is integer $level]} {\n        set level 0\n    }\n\n    set srv [lindex $::servers end+$level]\n    set host [dict get $srv \"host\"]\n    set port [dict get $srv \"port\"]\n    set config [dict get $srv \"config\"]\n    set client [redis $host $port]\n    dict set srv \"client\" $client\n\n    # select the right db when we don't have to authenticate\n    if {![dict exists $config \"requirepass\"]} {\n        $client select 9\n    }\n\n    # re-set $srv in the servers list\n    lset ::servers end+$level $srv\n}\n\nproc redis_deferring_client {args} {\n    set level 0\n    if {[llength $args] > 0 && [string is integer [lindex $args 0]]} {\n        set level [lindex $args 0]\n        set args [lrange $args 1 end]\n    }\n\n    # create client that defers reading reply\n    set client [redis [srv $level \"host\"] [srv $level \"port\"] 1]\n\n    # select the right db and read the response (OK)\n    $client select 9\n    $client read\n    return $client\n}\n\n# Provide easy access to INFO properties. Same semantic as \"proc r\".\nproc s {args} {\n    set level 0\n    if {[string is integer [lindex $args 0]]} {\n        set level [lindex $args 0]\n        set args [lrange $args 1 end]\n    }\n    status [srv $level \"client\"] [lindex $args 0]\n}\n\nproc cleanup {} {\n    if {!$::quiet} {puts -nonewline \"Cleanup: may take some time... \"}\n    flush stdout\n    catch {exec rm -rf {*}[glob tests/tmp/redis.conf.*]}\n    catch {exec rm -rf {*}[glob tests/tmp/server.*]}\n    if {!$::quiet} {puts \"OK\"}\n}\n\nproc test_server_main {} {\n    cleanup\n    set tclsh [info nameofexecutable]\n    # Open a listening socket, trying different ports in order to find a\n    # non busy one.\n    set port [find_available_port 11111]\n    if {!$::quiet} {\n        puts \"Starting test server at port $port\"\n    }\n    socket -server accept_test_clients -myaddr 127.0.0.1 $port\n\n    # Start the client instances\n    set ::clients_pids {}\n    set start_port [expr {$::port+100}]\n    for {set j 0} {$j < $::numclients} {incr j} {\n        set start_port [find_available_port $start_port]\n        set p [exec $tclsh [info script] {*}$::argv \\\n            --client $port --port $start_port &]\n        lappend ::clients_pids $p\n        incr start_port 10\n    }\n\n    # Setup global state for the test server\n    set ::idle_clients {}\n    set ::active_clients {}\n    array set ::active_clients_task {}\n    array set ::clients_start_time {}\n    set ::clients_time_history {}\n    set ::failed_tests {}\n\n    # Enter the event loop to handle clients I/O\n    after 100 test_server_cron\n    vwait forever\n}\n\n# This function gets called 10 times per second, for now does nothing but\n# may be used in the future in order to detect test clients taking too much\n# time to execute the task.\nproc test_server_cron {} {\n    # Do some work here.\n    after 100 test_server_cron\n}\n\nproc accept_test_clients {fd addr port} {\n    fconfigure $fd -encoding binary\n    fileevent $fd readable [list read_from_test_client $fd]\n}\n\n# This is the readable handler of our test server. Clients send us messages\n# in the form of a status code such and additional data. Supported\n# status types are:\n#\n# ready: the client is ready to execute the command. Only sent at client\n#        startup. The server will queue the client FD in the list of idle\n#        clients.\n# testing: just used to signal that a given test started.\n# ok: a test was executed with success.\n# err: a test was executed with an error.\n# exception: there was a runtime exception while executing the test.\n# done: all the specified test file was processed, this test client is\n#       ready to accept a new task.\nproc read_from_test_client fd {\n    set bytes [gets $fd]\n    set payload [read $fd $bytes]\n    foreach {status data} $payload break\n    if {$status eq {ready}} {\n        if {!$::quiet} {\n            puts \"\\[$status\\]: $data\"\n        }\n        signal_idle_client $fd\n    } elseif {$status eq {done}} {\n        set elapsed [expr {[clock seconds]-$::clients_start_time($fd)}]\n        set all_tests_count [llength $::all_tests]\n        set running_tests_count [expr {[llength $::active_clients]-1}]\n        set completed_tests_count [expr {$::next_test-$running_tests_count}]\n        puts \"\\[$completed_tests_count/$all_tests_count [colorstr yellow $status]\\]: $data ($elapsed seconds)\"\n        lappend ::clients_time_history $elapsed $data\n        signal_idle_client $fd\n        set ::active_clients_task($fd) DONE\n    } elseif {$status eq {ok}} {\n        if {!$::quiet} {\n            puts \"\\[[colorstr green $status]\\]: $data\"\n        }\n        set ::active_clients_task($fd) \"(OK) $data\"\n    } elseif {$status eq {err}} {\n        set err \"\\[[colorstr red $status]\\]: $data\"\n        puts $err\n        lappend ::failed_tests $err\n        set ::active_clients_task($fd) \"(ERR) $data\"\n    } elseif {$status eq {exception}} {\n        puts \"\\[[colorstr red $status]\\]: $data\"\n        foreach p $::clients_pids {\n            catch {exec kill -9 $p}\n        }\n        exit 1\n    } elseif {$status eq {testing}} {\n        set ::active_clients_task($fd) \"(IN PROGRESS) $data\"\n    } else {\n        if {!$::quiet} {\n            puts \"\\[$status\\]: $data\"\n        }\n    }\n}\n\n# A new client is idle. Remove it from the list of active clients and\n# if there are still test units to run, launch them.\nproc signal_idle_client fd {\n    # Remove this fd from the list of active clients.\n    set ::active_clients \\\n        [lsearch -all -inline -not -exact $::active_clients $fd]\n\n    if 0 {\n        # The following loop is only useful for debugging tests that may\n        # enter an infinite loop. Commented out normally.\n        foreach x $::active_clients {\n            if {[info exist ::active_clients_task($x)]} {\n                puts \"$x => $::active_clients_task($x)\"\n            } else {\n                puts \"$x => ???\"\n            }\n        }\n    }\n\n    # New unit to process?\n    if {$::next_test != [llength $::all_tests]} {\n        if {!$::quiet} {\n            puts [colorstr bold-white \"Testing [lindex $::all_tests $::next_test]\"]\n            set ::active_clients_task($fd) \"ASSIGNED: $fd ([lindex $::all_tests $::next_test])\"\n        }\n        set ::clients_start_time($fd) [clock seconds]\n        send_data_packet $fd run [lindex $::all_tests $::next_test]\n        lappend ::active_clients $fd\n        incr ::next_test\n    } else {\n        lappend ::idle_clients $fd\n        if {[llength $::active_clients] == 0} {\n            the_end\n        }\n    }\n}\n\n# The the_end funciton gets called when all the test units were already\n# executed, so the test finished.\nproc the_end {} {\n    # TODO: print the status, exit with the rigth exit code.\n    puts \"\\n                   The End\\n\"\n    puts \"Execution time of different units:\"\n    foreach {time name} $::clients_time_history {\n        puts \"  $time seconds - $name\"\n    }\n    if {[llength $::failed_tests]} {\n        puts \"\\n[colorstr bold-red {!!! WARNING}] The following tests failed:\\n\"\n        foreach failed $::failed_tests {\n            puts \"*** $failed\"\n        }\n        cleanup\n        exit 1\n    } else {\n        puts \"\\n[colorstr bold-white {\\o/}] [colorstr bold-green {All tests passed without errors!}]\\n\"\n        cleanup\n        exit 0\n    }\n}\n\n# The client is not even driven (the test server is instead) as we just need\n# to read the command, execute, reply... all this in a loop.\nproc test_client_main server_port {\n    set ::test_server_fd [socket localhost $server_port]\n    fconfigure $::test_server_fd -encoding binary\n    send_data_packet $::test_server_fd ready [pid]\n    while 1 {\n        set bytes [gets $::test_server_fd]\n        set payload [read $::test_server_fd $bytes]\n        foreach {cmd data} $payload break\n        if {$cmd eq {run}} {\n            execute_tests $data\n        } else {\n            error \"Unknown test client command: $cmd\"\n        }\n    }\n}\n\nproc send_data_packet {fd status data} {\n    set payload [list $status $data]\n    puts $fd [string length $payload]\n    puts -nonewline $fd $payload\n    flush $fd\n}\n\nproc print_help_screen {} {\n    puts [join {\n        \"--valgrind         Run the test over valgrind.\"\n        \"--accurate         Run slow randomized tests for more iterations.\"\n        \"--quiet            Don't show individual tests.\"\n        \"--single <unit>    Just execute the specified unit (see next option).\"\n        \"--list-tests       List all the available test units.\"\n        \"--clients <num>    Number of test clients (16).\"\n        \"--force-failure    Force the execution of a test that always fails.\"\n        \"--help             Print this help screen.\"\n    } \"\\n\"]\n}\n\n# parse arguments\nfor {set j 0} {$j < [llength $argv]} {incr j} {\n    set opt [lindex $argv $j]\n    set arg [lindex $argv [expr $j+1]]\n    if {$opt eq {--tags}} {\n        foreach tag $arg {\n            if {[string index $tag 0] eq \"-\"} {\n                lappend ::denytags [string range $tag 1 end]\n            } else {\n                lappend ::allowtags $tag\n            }\n        }\n        incr j\n    } elseif {$opt eq {--valgrind}} {\n        set ::valgrind 1\n    } elseif {$opt eq {--quiet}} {\n        set ::quiet 1\n    } elseif {$opt eq {--host}} {\n        set ::external 1\n        set ::host $arg\n        incr j\n    } elseif {$opt eq {--port}} {\n        set ::port $arg\n        incr j\n    } elseif {$opt eq {--accurate}} {\n        set ::accurate 1\n    } elseif {$opt eq {--force-failure}} {\n        set ::force_failure 1\n    } elseif {$opt eq {--single}} {\n        set ::all_tests $arg\n        incr j\n    } elseif {$opt eq {--list-tests}} {\n        foreach t $::all_tests {\n            puts $t\n        }\n        exit 0\n    } elseif {$opt eq {--client}} {\n        set ::client 1\n        set ::test_server_port $arg\n        incr j\n    } elseif {$opt eq {--clients}} {\n        set ::numclients $arg\n        incr j\n    } elseif {$opt eq {--help}} {\n        print_help_screen\n        exit 0\n    } else {\n        puts \"Wrong argument: $opt\"\n        exit 1\n    }\n}\n\nproc attach_to_replication_stream {} {\n    set s [socket [srv 0 \"host\"] [srv 0 \"port\"]]\n    fconfigure $s -translation binary\n    puts -nonewline $s \"SYNC\\r\\n\"\n    flush $s\n\n    # Get the count\n    set count [gets $s]\n    set prefix [string range $count 0 0]\n    if {$prefix ne {$}} {\n        error \"attach_to_replication_stream error. Received '$count' as count.\"\n    }\n    set count [string range $count 1 end]\n\n    # Consume the bulk payload\n    while {$count} {\n        set buf [read $s $count]\n        set count [expr {$count-[string length $buf]}]\n    }\n    return $s\n}\n\nproc read_from_replication_stream {s} {\n    fconfigure $s -blocking 0\n    set attempt 0\n    while {[gets $s count] == -1} {\n        if {[incr attempt] == 10} return \"\"\n        after 100\n    }\n    fconfigure $s -blocking 1\n    set count [string range $count 1 end]\n\n    # Return a list of arguments for the command.\n    set res {}\n    for {set j 0} {$j < $count} {incr j} {\n        read $s 1\n        set arg [::redis::redis_bulk_read $s]\n        if {$j == 0} {set arg [string tolower $arg]}\n        lappend res $arg\n    }\n    return $res\n}\n\nproc assert_replication_stream {s patterns} {\n    for {set j 0} {$j < [llength $patterns]} {incr j} {\n        assert_match [lindex $patterns $j] [read_from_replication_stream $s]\n    }\n}\n\nproc close_replication_stream {s} {\n    close $s\n}\n\n# With the parallel test running multiple Redis instances at the same time\n# we need a fast enough computer, otherwise a lot of tests may generate\n# false positives.\n# If the computer is too slow we revert the sequential test without any\n# parallelism, that is, clients == 1.\nproc is_a_slow_computer {} {\n    set start [clock milliseconds]\n    for {set j 0} {$j < 1000000} {incr j} {}\n    set elapsed [expr [clock milliseconds]-$start]\n    expr {$elapsed > 200}\n}\n\nif {$::client} {\n    if {[catch { test_client_main $::test_server_port } err]} {\n        set estr \"Executing test client: $err.\\n$::errorInfo\"\n        if {[catch {send_data_packet $::test_server_fd exception $estr}]} {\n            puts $estr\n        }\n        exit 1\n    }\n} else {\n    if {[is_a_slow_computer]} {\n        puts \"** SLOW COMPUTER ** Using a single client to avoid false positives.\"\n        set ::numclients 1\n    }\n\n    if {[catch { test_server_main } err]} {\n        if {[string length $err] > 0} {\n            # only display error when not generated by the test suite\n            if {$err ne \"exception\"} {\n                puts $::errorInfo\n            }\n            exit 1\n        }\n    }\n}\n"
  },
  {
    "path": "tests/unit/aofrw.tcl",
    "content": "start_server {tags {\"aofrw\"}} {\n\n    test {Turning off AOF kills the background writing child if any} {\n        r config set appendonly yes\n        waitForBgrewriteaof r\n        r multi\n        r bgrewriteaof\n        r config set appendonly no\n        r exec\n        wait_for_condition 50 100 {\n            [string match {*Killing*AOF*child*} [exec tail -n5 < [srv 0 stdout]]]\n        } else {\n            fail \"Can't find 'Killing AOF child' into recent logs\"\n        }\n    }\n\n    foreach d {string int} {\n        foreach e {ziplist linkedlist} {\n            test \"AOF rewrite of list with $e encoding, $d data\" {\n                r flushall\n                if {$e eq {ziplist}} {set len 10} else {set len 1000}\n                for {set j 0} {$j < $len} {incr j} {\n                    if {$d eq {string}} {\n                        set data [randstring 0 16 alpha]\n                    } else {\n                        set data [randomInt 4000000000]\n                    }\n                    r lpush key $data\n                }\n                assert_equal [r object encoding key] $e\n                set d1 [r debug digest]\n                r bgrewriteaof\n                waitForBgrewriteaof r\n                r debug loadaof\n                set d2 [r debug digest]\n                if {$d1 ne $d2} {\n                    error \"assertion:$d1 is not equal to $d2\"\n                }\n            }\n        }\n    }\n\n    foreach d {string int} {\n        foreach e {intset hashtable} {\n            test \"AOF rewrite of set with $e encoding, $d data\" {\n                r flushall\n                if {$e eq {intset}} {set len 10} else {set len 1000}\n                for {set j 0} {$j < $len} {incr j} {\n                    if {$d eq {string}} {\n                        set data [randstring 0 16 alpha]\n                    } else {\n                        set data [randomInt 4000000000]\n                    }\n                    r sadd key $data\n                }\n                if {$d ne {string}} {\n                    assert_equal [r object encoding key] $e\n                }\n                set d1 [r debug digest]\n                r bgrewriteaof\n                waitForBgrewriteaof r\n                r debug loadaof\n                set d2 [r debug digest]\n                if {$d1 ne $d2} {\n                    error \"assertion:$d1 is not equal to $d2\"\n                }\n            }\n        }\n    }\n\n    foreach d {string int} {\n        foreach e {ziplist hashtable} {\n            test \"AOF rewrite of hash with $e encoding, $d data\" {\n                r flushall\n                if {$e eq {ziplist}} {set len 10} else {set len 1000}\n                for {set j 0} {$j < $len} {incr j} {\n                    if {$d eq {string}} {\n                        set data [randstring 0 16 alpha]\n                    } else {\n                        set data [randomInt 4000000000]\n                    }\n                    r hset key $data $data\n                }\n                assert_equal [r object encoding key] $e\n                set d1 [r debug digest]\n                r bgrewriteaof\n                waitForBgrewriteaof r\n                r debug loadaof\n                set d2 [r debug digest]\n                if {$d1 ne $d2} {\n                    error \"assertion:$d1 is not equal to $d2\"\n                }\n            }\n        }\n    }\n\n    foreach d {string int} {\n        foreach e {ziplist skiplist} {\n            test \"AOF rewrite of zset with $e encoding, $d data\" {\n                r flushall\n                if {$e eq {ziplist}} {set len 10} else {set len 1000}\n                for {set j 0} {$j < $len} {incr j} {\n                    if {$d eq {string}} {\n                        set data [randstring 0 16 alpha]\n                    } else {\n                        set data [randomInt 4000000000]\n                    }\n                    r zadd key [expr rand()] $data\n                }\n                assert_equal [r object encoding key] $e\n                set d1 [r debug digest]\n                r bgrewriteaof\n                waitForBgrewriteaof r\n                r debug loadaof\n                set d2 [r debug digest]\n                if {$d1 ne $d2} {\n                    error \"assertion:$d1 is not equal to $d2\"\n                }\n            }\n        }\n    }\n\n    test {BGREWRITEAOF is delayed if BGSAVE is in progress} {\n        r multi\n        r bgsave\n        r bgrewriteaof\n        r info persistence\n        set res [r exec]\n        assert_match {*scheduled*} [lindex $res 1]\n        assert_match {*aof_rewrite_scheduled:1*} [lindex $res 2]\n        while {[string match {*aof_rewrite_scheduled:1*} [r info persistence]]} {\n            after 100\n        }\n    }\n\n    test {BGREWRITEAOF is refused if already in progress} {\n        catch {\n            r multi\n            r bgrewriteaof\n            r bgrewriteaof\n            r exec\n        } e\n        assert_match {*ERR*already*} $e\n        while {[string match {*aof_rewrite_scheduled:1*} [r info persistence]]} {\n            after 100\n        }\n    }\n}\n"
  },
  {
    "path": "tests/unit/auth.tcl",
    "content": "start_server {tags {\"auth\"}} {\n    test {AUTH fails if there is no password configured server side} {\n        catch {r auth foo} err\n        set _ $err\n    } {ERR*no password*}\n}\n\nstart_server {tags {\"auth\"} overrides {requirepass foobar}} {\n    test {AUTH fails when a wrong password is given} {\n        catch {r auth wrong!} err\n        set _ $err\n    } {ERR*invalid password}\n    \n    test {Arbitrary command gives an error when AUTH is required} {\n        catch {r set foo bar} err\n        set _ $err\n    } {NOAUTH*}\n\n    test {AUTH succeeds when the right password is given} {\n        r auth foobar\n    } {OK}\n\n    test {Once AUTH succeeded we can actually send commands to the server} {\n        r set foo 100\n        r incr foo\n    } {101}\n}\n"
  },
  {
    "path": "tests/unit/basic.tcl",
    "content": "start_server {tags {\"basic\"}} {\n    test {DEL all keys to start with a clean DB} {\n        foreach key [r keys *] {r del $key}\n        r dbsize\n    } {0}\n\n    test {SET and GET an item} {\n        r set x foobar\n        r get x\n    } {foobar}\n\n    test {SET and GET an empty item} {\n        r set x {}\n        r get x\n    } {}\n\n    test {DEL against a single item} {\n        r del x\n        r get x\n    } {}\n\n    test {Vararg DEL} {\n        r set foo1 a\n        r set foo2 b\n        r set foo3 c\n        list [r del foo1 foo2 foo3 foo4] [r mget foo1 foo2 foo3]\n    } {3 {{} {} {}}}\n\n    test {KEYS with pattern} {\n        foreach key {key_x key_y key_z foo_a foo_b foo_c} {\n            r set $key hello\n        }\n        lsort [r keys foo*]\n    } {foo_a foo_b foo_c}\n\n    test {KEYS to get all keys} {\n        lsort [r keys *]\n    } {foo_a foo_b foo_c key_x key_y key_z}\n\n    test {DBSIZE} {\n        r dbsize\n    } {6}\n\n    test {DEL all keys} {\n        foreach key [r keys *] {r del $key}\n        r dbsize\n    } {0}\n\n    test {Very big payload in GET/SET} {\n        set buf [string repeat \"abcd\" 1000000]\n        r set foo $buf\n        r get foo\n    } [string repeat \"abcd\" 1000000]\n\n    tags {\"slow\"} {\n        test {Very big payload random access} {\n            set err {}\n            array set payload {}\n            for {set j 0} {$j < 100} {incr j} {\n                set size [expr 1+[randomInt 100000]]\n                set buf [string repeat \"pl-$j\" $size]\n                set payload($j) $buf\n                r set bigpayload_$j $buf\n            }\n            for {set j 0} {$j < 1000} {incr j} {\n                set index [randomInt 100]\n                set buf [r get bigpayload_$index]\n                if {$buf != $payload($index)} {\n                    set err \"Values differ: I set '$payload($index)' but I read back '$buf'\"\n                    break\n                }\n            }\n            unset payload\n            set _ $err\n        } {}\n\n        test {SET 10000 numeric keys and access all them in reverse order} {\n            set err {}\n            for {set x 0} {$x < 10000} {incr x} {\n                r set $x $x\n            }\n            set sum 0\n            for {set x 9999} {$x >= 0} {incr x -1} {\n                set val [r get $x]\n                if {$val ne $x} {\n                    set err \"Eleemnt at position $x is $val instead of $x\"\n                    break\n                }\n            }\n            set _ $err\n        } {}\n\n        test {DBSIZE should be 10101 now} {\n            r dbsize\n        } {10101}\n    }\n\n    test {INCR against non existing key} {\n        set res {}\n        append res [r incr novar]\n        append res [r get novar]\n    } {11}\n\n    test {INCR against key created by incr itself} {\n        r incr novar\n    } {2}\n\n    test {INCR against key originally set with SET} {\n        r set novar 100\n        r incr novar\n    } {101}\n\n    test {INCR over 32bit value} {\n        r set novar 17179869184\n        r incr novar\n    } {17179869185}\n\n    test {INCRBY over 32bit value with over 32bit increment} {\n        r set novar 17179869184\n        r incrby novar 17179869184\n    } {34359738368}\n\n    test {INCR fails against key with spaces (left)} {\n        r set novar \"    11\"\n        catch {r incr novar} err\n        format $err\n    } {ERR*}\n\n    test {INCR fails against key with spaces (right)} {\n        r set novar \"11    \"\n        catch {r incr novar} err\n        format $err\n    } {ERR*}\n\n    test {INCR fails against key with spaces (both)} {\n        r set novar \"    11    \"\n        catch {r incr novar} err\n        format $err\n    } {ERR*}\n\n    test {INCR fails against a key holding a list} {\n        r rpush mylist 1\n        catch {r incr mylist} err\n        r rpop mylist\n        format $err\n    } {WRONGTYPE*}\n\n    test {DECRBY over 32bit value with over 32bit increment, negative res} {\n        r set novar 17179869184\n        r decrby novar 17179869185\n    } {-1}\n\n    test {INCRBYFLOAT against non existing key} {\n        r del novar\n        list    [roundFloat [r incrbyfloat novar 1]] \\\n                [roundFloat [r get novar]] \\\n                [roundFloat [r incrbyfloat novar 0.25]] \\\n                [roundFloat [r get novar]]\n    } {1 1 1.25 1.25}\n\n    test {INCRBYFLOAT against key originally set with SET} {\n        r set novar 1.5\n        roundFloat [r incrbyfloat novar 1.5]\n    } {3}\n\n    test {INCRBYFLOAT over 32bit value} {\n        r set novar 17179869184\n        r incrbyfloat novar 1.5\n    } {17179869185.5}\n\n    test {INCRBYFLOAT over 32bit value with over 32bit increment} {\n        r set novar 17179869184\n        r incrbyfloat novar 17179869184\n    } {34359738368}\n\n    test {INCRBYFLOAT fails against key with spaces (left)} {\n        set err {}\n        r set novar \"    11\"\n        catch {r incrbyfloat novar 1.0} err\n        format $err\n    } {ERR*valid*}\n\n    test {INCRBYFLOAT fails against key with spaces (right)} {\n        set err {}\n        r set novar \"11    \"\n        catch {r incrbyfloat novar 1.0} err\n        format $err\n    } {ERR*valid*}\n\n    test {INCRBYFLOAT fails against key with spaces (both)} {\n        set err {}\n        r set novar \" 11 \"\n        catch {r incrbyfloat novar 1.0} err\n        format $err\n    } {ERR*valid*}\n\n    test {INCRBYFLOAT fails against a key holding a list} {\n        r del mylist\n        set err {}\n        r rpush mylist 1\n        catch {r incrbyfloat mylist 1.0} err\n        r del mylist\n        format $err\n    } {WRONGTYPE*}\n\n    test {INCRBYFLOAT does not allow NaN or Infinity} {\n        r set foo 0\n        set err {}\n        catch {r incrbyfloat foo +inf} err\n        set err\n        # p.s. no way I can force NaN to test it from the API because\n        # there is no way to increment / decrement by infinity nor to\n        # perform divisions.\n    } {ERR*would produce*}\n\n    test {INCRBYFLOAT decrement} {\n        r set foo 1\n        roundFloat [r incrbyfloat foo -1.1]\n    } {-0.1}\n\n    test \"SETNX target key missing\" {\n        r del novar\n        assert_equal 1 [r setnx novar foobared]\n        assert_equal \"foobared\" [r get novar]\n    }\n\n    test \"SETNX target key exists\" {\n        r set novar foobared\n        assert_equal 0 [r setnx novar blabla]\n        assert_equal \"foobared\" [r get novar]\n    }\n\n    test \"SETNX against not-expired volatile key\" {\n        r set x 10\n        r expire x 10000\n        assert_equal 0 [r setnx x 20]\n        assert_equal 10 [r get x]\n    }\n\n    test \"SETNX against expired volatile key\" {\n        # Make it very unlikely for the key this test uses to be expired by the\n        # active expiry cycle. This is tightly coupled to the implementation of\n        # active expiry and dbAdd() but currently the only way to test that\n        # SETNX expires a key when it should have been.\n        for {set x 0} {$x < 9999} {incr x} {\n            r setex key-$x 3600 value\n        }\n\n        # This will be one of 10000 expiring keys. A cycle is executed every\n        # 100ms, sampling 10 keys for being expired or not.  This key will be\n        # expired for at most 1s when we wait 2s, resulting in a total sample\n        # of 100 keys. The probability of the success of this test being a\n        # false positive is therefore approx. 1%.\n        r set x 10\n        r expire x 1\n\n        # Wait for the key to expire\n        after 2000\n\n        assert_equal 1 [r setnx x 20]\n        assert_equal 20 [r get x]\n    }\n\n    test \"DEL against expired key\" {\n        r debug set-active-expire 0\n        r setex keyExpire 1 valExpire\n        after 1100\n        assert_equal 0 [r del keyExpire]\n        r debug set-active-expire 1\n    }\n\n    test {EXISTS} {\n        set res {}\n        r set newkey test\n        append res [r exists newkey]\n        r del newkey\n        append res [r exists newkey]\n    } {10}\n\n    test {Zero length value in key. SET/GET/EXISTS} {\n        r set emptykey {}\n        set res [r get emptykey]\n        append res [r exists emptykey]\n        r del emptykey\n        append res [r exists emptykey]\n    } {10}\n\n    test {Commands pipelining} {\n        set fd [r channel]\n        puts -nonewline $fd \"SET k1 xyzk\\r\\nGET k1\\r\\nPING\\r\\n\"\n        flush $fd\n        set res {}\n        append res [string match OK* [::redis::redis_read_reply $fd]]\n        append res [::redis::redis_read_reply $fd]\n        append res [string match PONG* [::redis::redis_read_reply $fd]]\n        format $res\n    } {1xyzk1}\n\n    test {Non existing command} {\n        catch {r foobaredcommand} err\n        string match ERR* $err\n    } {1}\n    \n    test {RENAME basic usage} {\n        r set mykey hello\n        r rename mykey mykey1\n        r rename mykey1 mykey2\n        r get mykey2\n    } {hello}\n\n    test {RENAME source key should no longer exist} {\n        r exists mykey\n    } {0}\n\n    test {RENAME against already existing key} {\n        r set mykey a\n        r set mykey2 b\n        r rename mykey2 mykey\n        set res [r get mykey]\n        append res [r exists mykey2]\n    } {b0}\n\n    test {RENAMENX basic usage} {\n        r del mykey\n        r del mykey2\n        r set mykey foobar\n        r renamenx mykey mykey2\n        set res [r get mykey2]\n        append res [r exists mykey]\n    } {foobar0}\n\n    test {RENAMENX against already existing key} {\n        r set mykey foo\n        r set mykey2 bar\n        r renamenx mykey mykey2\n    } {0}\n\n    test {RENAMENX against already existing key (2)} {\n        set res [r get mykey]\n        append res [r get mykey2]\n    } {foobar}\n\n    test {RENAME against non existing source key} {\n        catch {r rename nokey foobar} err\n        format $err\n    } {ERR*}\n\n    test {RENAME where source and dest key is the same} {\n        catch {r rename mykey mykey} err\n        format $err\n    } {ERR*}\n\n    test {RENAME with volatile key, should move the TTL as well} {\n        r del mykey mykey2\n        r set mykey foo\n        r expire mykey 100\n        assert {[r ttl mykey] > 95 && [r ttl mykey] <= 100}\n        r rename mykey mykey2\n        assert {[r ttl mykey2] > 95 && [r ttl mykey2] <= 100}\n    }\n\n    test {RENAME with volatile key, should not inherit TTL of target key} {\n        r del mykey mykey2\n        r set mykey foo\n        r set mykey2 bar\n        r expire mykey2 100\n        assert {[r ttl mykey] == -1 && [r ttl mykey2] > 0}\n        r rename mykey mykey2\n        r ttl mykey2\n    } {-1}\n\n    test {DEL all keys again (DB 0)} {\n        foreach key [r keys *] {\n            r del $key\n        }\n        r dbsize\n    } {0}\n\n    test {DEL all keys again (DB 1)} {\n        r select 10\n        foreach key [r keys *] {\n            r del $key\n        }\n        set res [r dbsize]\n        r select 9\n        format $res\n    } {0}\n\n    test {MOVE basic usage} {\n        r set mykey foobar\n        r move mykey 10\n        set res {}\n        lappend res [r exists mykey]\n        lappend res [r dbsize]\n        r select 10\n        lappend res [r get mykey]\n        lappend res [r dbsize]\n        r select 9\n        format $res\n    } [list 0 0 foobar 1]\n\n    test {MOVE against key existing in the target DB} {\n        r set mykey hello\n        r move mykey 10\n    } {0}\n\n    test {SET/GET keys in different DBs} {\n        r set a hello\n        r set b world\n        r select 10\n        r set a foo\n        r set b bared\n        r select 9\n        set res {}\n        lappend res [r get a]\n        lappend res [r get b]\n        r select 10\n        lappend res [r get a]\n        lappend res [r get b]\n        r select 9\n        format $res\n    } {hello world foo bared}\n    \n    test {MGET} {\n        r flushdb\n        r set foo BAR\n        r set bar FOO\n        r mget foo bar\n    } {BAR FOO}\n\n    test {MGET against non existing key} {\n        r mget foo baazz bar\n    } {BAR {} FOO}\n\n    test {MGET against non-string key} {\n        r sadd myset ciao\n        r sadd myset bau\n        r mget foo baazz bar myset\n    } {BAR {} FOO {}}\n\n    test {RANDOMKEY} {\n        r flushdb\n        r set foo x\n        r set bar y\n        set foo_seen 0\n        set bar_seen 0\n        for {set i 0} {$i < 100} {incr i} {\n            set rkey [r randomkey]\n            if {$rkey eq {foo}} {\n                set foo_seen 1\n            }\n            if {$rkey eq {bar}} {\n                set bar_seen 1\n            }\n        }\n        list $foo_seen $bar_seen\n    } {1 1}\n\n    test {RANDOMKEY against empty DB} {\n        r flushdb\n        r randomkey\n    } {}\n\n    test {RANDOMKEY regression 1} {\n        r flushdb\n        r set x 10\n        r del x\n        r randomkey\n    } {}\n\n    test {GETSET (set new value)} {\n        list [r getset foo xyz] [r get foo]\n    } {{} xyz}\n\n    test {GETSET (replace old value)} {\n        r set foo bar\n        list [r getset foo xyz] [r get foo]\n    } {bar xyz}\n    \n    test {MSET base case} {\n        r mset x 10 y \"foo bar\" z \"x x x x x x x\\n\\n\\r\\n\"\n        r mget x y z\n    } [list 10 {foo bar} \"x x x x x x x\\n\\n\\r\\n\"]\n\n    test {MSET wrong number of args} {\n        catch {r mset x 10 y \"foo bar\" z} err\n        format $err\n    } {*wrong number*}\n\n    test {MSETNX with already existent key} {\n        list [r msetnx x1 xxx y2 yyy x 20] [r exists x1] [r exists y2]\n    } {0 0 0}\n\n    test {MSETNX with not existing keys} {\n        list [r msetnx x1 xxx y2 yyy] [r get x1] [r get y2]\n    } {1 xxx yyy}\n\n    test \"STRLEN against non-existing key\" {\n        assert_equal 0 [r strlen notakey]\n    }\n\n    test \"STRLEN against integer-encoded value\" {\n        r set myinteger -555\n        assert_equal 4 [r strlen myinteger]\n    }\n\n    test \"STRLEN against plain string\" {\n        r set mystring \"foozzz0123456789 baz\"\n        assert_equal 20 [r strlen mystring]\n    }\n\n    test \"SETBIT against non-existing key\" {\n        r del mykey\n        assert_equal 0 [r setbit mykey 1 1]\n        assert_equal [binary format B* 01000000] [r get mykey]\n    }\n\n    test \"SETBIT against string-encoded key\" {\n        # Ascii \"@\" is integer 64 = 01 00 00 00\n        r set mykey \"@\"\n\n        assert_equal 0 [r setbit mykey 2 1]\n        assert_equal [binary format B* 01100000] [r get mykey]\n        assert_equal 1 [r setbit mykey 1 0]\n        assert_equal [binary format B* 00100000] [r get mykey]\n    }\n\n    test \"SETBIT against integer-encoded key\" {\n        # Ascii \"1\" is integer 49 = 00 11 00 01\n        r set mykey 1\n        assert_encoding int mykey\n\n        assert_equal 0 [r setbit mykey 6 1]\n        assert_equal [binary format B* 00110011] [r get mykey]\n        assert_equal 1 [r setbit mykey 2 0]\n        assert_equal [binary format B* 00010011] [r get mykey]\n    }\n\n    test \"SETBIT against key with wrong type\" {\n        r del mykey\n        r lpush mykey \"foo\"\n        assert_error \"WRONGTYPE*\" {r setbit mykey 0 1}\n    }\n\n    test \"SETBIT with out of range bit offset\" {\n        r del mykey\n        assert_error \"*out of range*\" {r setbit mykey [expr 4*1024*1024*1024] 1}\n        assert_error \"*out of range*\" {r setbit mykey -1 1}\n    }\n\n    test \"SETBIT with non-bit argument\" {\n        r del mykey\n        assert_error \"*out of range*\" {r setbit mykey 0 -1}\n        assert_error \"*out of range*\" {r setbit mykey 0  2}\n        assert_error \"*out of range*\" {r setbit mykey 0 10}\n        assert_error \"*out of range*\" {r setbit mykey 0 20}\n    }\n\n    test \"SETBIT fuzzing\" {\n        set str \"\"\n        set len [expr 256*8]\n        r del mykey\n\n        for {set i 0} {$i < 2000} {incr i} {\n            set bitnum [randomInt $len]\n            set bitval [randomInt 2]\n            set fmt [format \"%%-%ds%%d%%-s\" $bitnum]\n            set head [string range $str 0 $bitnum-1]\n            set tail [string range $str $bitnum+1 end]\n            set str [string map {\" \" 0} [format $fmt $head $bitval $tail]]\n\n            r setbit mykey $bitnum $bitval\n            assert_equal [binary format B* $str] [r get mykey]\n        }\n    }\n\n    test \"GETBIT against non-existing key\" {\n        r del mykey\n        assert_equal 0 [r getbit mykey 0]\n    }\n\n    test \"GETBIT against string-encoded key\" {\n        # Single byte with 2nd and 3rd bit set\n        r set mykey \"`\"\n\n        # In-range\n        assert_equal 0 [r getbit mykey 0]\n        assert_equal 1 [r getbit mykey 1]\n        assert_equal 1 [r getbit mykey 2]\n        assert_equal 0 [r getbit mykey 3]\n\n        # Out-range\n        assert_equal 0 [r getbit mykey 8]\n        assert_equal 0 [r getbit mykey 100]\n        assert_equal 0 [r getbit mykey 10000]\n    }\n\n    test \"GETBIT against integer-encoded key\" {\n        r set mykey 1\n        assert_encoding int mykey\n\n        # Ascii \"1\" is integer 49 = 00 11 00 01\n        assert_equal 0 [r getbit mykey 0]\n        assert_equal 0 [r getbit mykey 1]\n        assert_equal 1 [r getbit mykey 2]\n        assert_equal 1 [r getbit mykey 3]\n\n        # Out-range\n        assert_equal 0 [r getbit mykey 8]\n        assert_equal 0 [r getbit mykey 100]\n        assert_equal 0 [r getbit mykey 10000]\n    }\n\n    test \"SETRANGE against non-existing key\" {\n        r del mykey\n        assert_equal 3 [r setrange mykey 0 foo]\n        assert_equal \"foo\" [r get mykey]\n\n        r del mykey\n        assert_equal 0 [r setrange mykey 0 \"\"]\n        assert_equal 0 [r exists mykey]\n\n        r del mykey\n        assert_equal 4 [r setrange mykey 1 foo]\n        assert_equal \"\\000foo\" [r get mykey]\n    }\n\n    test \"SETRANGE against string-encoded key\" {\n        r set mykey \"foo\"\n        assert_equal 3 [r setrange mykey 0 b]\n        assert_equal \"boo\" [r get mykey]\n\n        r set mykey \"foo\"\n        assert_equal 3 [r setrange mykey 0 \"\"]\n        assert_equal \"foo\" [r get mykey]\n\n        r set mykey \"foo\"\n        assert_equal 3 [r setrange mykey 1 b]\n        assert_equal \"fbo\" [r get mykey]\n\n        r set mykey \"foo\"\n        assert_equal 7 [r setrange mykey 4 bar]\n        assert_equal \"foo\\000bar\" [r get mykey]\n    }\n\n    test \"SETRANGE against integer-encoded key\" {\n        r set mykey 1234\n        assert_encoding int mykey\n        assert_equal 4 [r setrange mykey 0 2]\n        assert_encoding raw mykey\n        assert_equal 2234 [r get mykey]\n\n        # Shouldn't change encoding when nothing is set\n        r set mykey 1234\n        assert_encoding int mykey\n        assert_equal 4 [r setrange mykey 0 \"\"]\n        assert_encoding int mykey\n        assert_equal 1234 [r get mykey]\n\n        r set mykey 1234\n        assert_encoding int mykey\n        assert_equal 4 [r setrange mykey 1 3]\n        assert_encoding raw mykey\n        assert_equal 1334 [r get mykey]\n\n        r set mykey 1234\n        assert_encoding int mykey\n        assert_equal 6 [r setrange mykey 5 2]\n        assert_encoding raw mykey\n        assert_equal \"1234\\0002\" [r get mykey]\n    }\n\n    test \"SETRANGE against key with wrong type\" {\n        r del mykey\n        r lpush mykey \"foo\"\n        assert_error \"WRONGTYPE*\" {r setrange mykey 0 bar}\n    }\n\n    test \"SETRANGE with out of range offset\" {\n        r del mykey\n        assert_error \"*maximum allowed size*\" {r setrange mykey [expr 512*1024*1024-4] world}\n\n        r set mykey \"hello\"\n        assert_error \"*out of range*\" {r setrange mykey -1 world}\n        assert_error \"*maximum allowed size*\" {r setrange mykey [expr 512*1024*1024-4] world}\n    }\n\n    test \"GETRANGE against non-existing key\" {\n        r del mykey\n        assert_equal \"\" [r getrange mykey 0 -1]\n    }\n\n    test \"GETRANGE against string value\" {\n        r set mykey \"Hello World\"\n        assert_equal \"Hell\" [r getrange mykey 0 3]\n        assert_equal \"Hello World\" [r getrange mykey 0 -1]\n        assert_equal \"orld\" [r getrange mykey -4 -1]\n        assert_equal \"\" [r getrange mykey 5 3]\n        assert_equal \" World\" [r getrange mykey 5 5000]\n        assert_equal \"Hello World\" [r getrange mykey -5000 10000]\n    }\n\n    test \"GETRANGE against integer-encoded value\" {\n        r set mykey 1234\n        assert_equal \"123\" [r getrange mykey 0 2]\n        assert_equal \"1234\" [r getrange mykey 0 -1]\n        assert_equal \"234\" [r getrange mykey -3 -1]\n        assert_equal \"\" [r getrange mykey 5 3]\n        assert_equal \"4\" [r getrange mykey 3 5000]\n        assert_equal \"1234\" [r getrange mykey -5000 10000]\n    }\n\n    test \"GETRANGE fuzzing\" {\n        for {set i 0} {$i < 1000} {incr i} {\n            r set bin [set bin [randstring 0 1024 binary]]\n            set _start [set start [randomInt 1500]]\n            set _end [set end [randomInt 1500]]\n            if {$_start < 0} {set _start \"end-[abs($_start)-1]\"}\n            if {$_end < 0} {set _end \"end-[abs($_end)-1]\"}\n            assert_equal [string range $bin $_start $_end] [r getrange bin $start $end]\n        }\n    }\n\n    test {Extended SET can detect syntax errors} {\n        set e {}\n        catch {r set foo bar non-existing-option} e\n        set e\n    } {*syntax*}\n\n    test {Extended SET NX option} {\n        r del foo\n        set v1 [r set foo 1 nx]\n        set v2 [r set foo 2 nx]\n        list $v1 $v2 [r get foo]\n    } {OK {} 1}\n\n    test {Extended SET XX option} {\n        r del foo\n        set v1 [r set foo 1 xx]\n        r set foo bar\n        set v2 [r set foo 2 xx]\n        list $v1 $v2 [r get foo]\n    } {{} OK 2}\n\n    test {Extended SET EX option} {\n        r del foo\n        r set foo bar ex 10\n        set ttl [r ttl foo]\n        assert {$ttl <= 10 && $ttl > 5}\n    }\n\n    test {Extended SET PX option} {\n        r del foo\n        r set foo bar px 10000\n        set ttl [r ttl foo]\n        assert {$ttl <= 10 && $ttl > 5}\n    }\n\n    test {Extended SET using multiple options at once} {\n        r set foo val\n        assert {[r set foo bar xx px 10000] eq {OK}}\n        set ttl [r ttl foo]\n        assert {$ttl <= 10 && $ttl > 5}\n    }\n\n    test {KEYS * two times with long key, Github issue #1208} {\n        r flushdb\n        r set dlskeriewrioeuwqoirueioqwrueoqwrueqw test\n        r keys *\n        r keys *\n    } {dlskeriewrioeuwqoirueioqwrueoqwrueqw}\n}\n"
  },
  {
    "path": "tests/unit/bitops.tcl",
    "content": "# Compare Redis commadns against Tcl implementations of the same commands.\nproc count_bits s {\n    binary scan $s b* bits\n    string length [regsub -all {0} $bits {}]\n}\n\nproc simulate_bit_op {op args} {\n    set maxlen 0\n    set j 0\n    set count [llength $args]\n    foreach a $args {\n        binary scan $a b* bits\n        set b($j) $bits\n        if {[string length $bits] > $maxlen} {\n            set maxlen [string length $bits]\n        }\n        incr j\n    }\n    for {set j 0} {$j < $count} {incr j} {\n        if {[string length $b($j)] < $maxlen} {\n            append b($j) [string repeat 0 [expr $maxlen-[string length $b($j)]]]\n        }\n    }\n    set out {}\n    for {set x 0} {$x < $maxlen} {incr x} {\n        set bit [string range $b(0) $x $x]\n        if {$op eq {not}} {set bit [expr {!$bit}]}\n        for {set j 1} {$j < $count} {incr j} {\n            set bit2 [string range $b($j) $x $x]\n            switch $op {\n                and {set bit [expr {$bit & $bit2}]}\n                or  {set bit [expr {$bit | $bit2}]}\n                xor {set bit [expr {$bit ^ $bit2}]}\n            }\n        }\n        append out $bit\n    }\n    binary format b* $out\n}\n\nstart_server {tags {\"bitops\"}} {\n    test {BITCOUNT returns 0 against non existing key} {\n        r bitcount no-key\n    } 0\n\n    catch {unset num}\n    foreach vec [list \"\" \"\\xaa\" \"\\x00\\x00\\xff\" \"foobar\" \"123\"] {\n        incr num\n        test \"BITCOUNT against test vector #$num\" {\n            r set str $vec\n            assert {[r bitcount str] == [count_bits $vec]}\n        }\n    }\n\n    test {BITCOUNT fuzzing without start/end} {\n        for {set j 0} {$j < 100} {incr j} {\n            set str [randstring 0 3000]\n            r set str $str\n            assert {[r bitcount str] == [count_bits $str]}\n        }\n    }\n\n    test {BITCOUNT fuzzing with start/end} {\n        for {set j 0} {$j < 100} {incr j} {\n            set str [randstring 0 3000]\n            r set str $str\n            set l [string length $str]\n            set start [randomInt $l]\n            set end [randomInt $l]\n            if {$start > $end} {\n                lassign [list $end $start] start end\n            }\n            assert {[r bitcount str $start $end] == [count_bits [string range $str $start $end]]}\n        }\n    }\n\n    test {BITCOUNT with start, end} {\n        r set s \"foobar\"\n        assert_equal [r bitcount s 0 -1] [count_bits \"foobar\"]\n        assert_equal [r bitcount s 1 -2] [count_bits \"ooba\"]\n        assert_equal [r bitcount s -2 1] [count_bits \"\"]\n        assert_equal [r bitcount s 0 1000] [count_bits \"foobar\"]\n    }\n\n    test {BITCOUNT syntax error #1} {\n        catch {r bitcount s 0} e\n        set e\n    } {ERR*syntax*}\n\n    test {BITCOUNT regression test for github issue #582} {\n        r del str\n        r setbit foo 0 1\n        if {[catch {r bitcount foo 0 4294967296} e]} {\n            assert_match {*ERR*out of range*} $e\n            set _ 1\n        } else {\n            set e\n        }\n    } {1}\n\n    test {BITCOUNT misaligned prefix} {\n        r del str\n        r set str ab\n        r bitcount str 1 -1\n    } {3}\n\n    test {BITCOUNT misaligned prefix + full words + remainder} {\n        r del str\n        r set str __PPxxxxxxxxxxxxxxxxRR__\n        r bitcount str 2 -3\n    } {74}\n\n    test {BITOP NOT (empty string)} {\n        r set s \"\"\n        r bitop not dest s\n        r get dest\n    } {}\n\n    test {BITOP NOT (known string)} {\n        r set s \"\\xaa\\x00\\xff\\x55\"\n        r bitop not dest s\n        r get dest\n    } \"\\x55\\xff\\x00\\xaa\"\n\n    test {BITOP where dest and target are the same key} {\n        r set s \"\\xaa\\x00\\xff\\x55\"\n        r bitop not s s\n        r get s \n    } \"\\x55\\xff\\x00\\xaa\"\n\n    test {BITOP AND|OR|XOR don't change the string with single input key} {\n        r set a \"\\x01\\x02\\xff\"\n        r bitop and res1 a\n        r bitop or  res2 a\n        r bitop xor res3 a\n        list [r get res1] [r get res2] [r get res3]\n    } [list \"\\x01\\x02\\xff\" \"\\x01\\x02\\xff\" \"\\x01\\x02\\xff\"]\n\n    test {BITOP missing key is considered a stream of zero} {\n        r set a \"\\x01\\x02\\xff\"\n        r bitop and res1 no-suck-key a\n        r bitop or  res2 no-suck-key a no-such-key\n        r bitop xor res3 no-such-key a\n        list [r get res1] [r get res2] [r get res3]\n    } [list \"\\x00\\x00\\x00\" \"\\x01\\x02\\xff\" \"\\x01\\x02\\xff\"]\n\n    test {BITOP shorter keys are zero-padded to the key with max length} {\n        r set a \"\\x01\\x02\\xff\\xff\"\n        r set b \"\\x01\\x02\\xff\"\n        r bitop and res1 a b\n        r bitop or  res2 a b\n        r bitop xor res3 a b\n        list [r get res1] [r get res2] [r get res3]\n    } [list \"\\x01\\x02\\xff\\x00\" \"\\x01\\x02\\xff\\xff\" \"\\x00\\x00\\x00\\xff\"]\n\n    foreach op {and or xor} {\n        test \"BITOP $op fuzzing\" {\n            for {set i 0} {$i < 10} {incr i} {\n                r flushall\n                set vec {}\n                set veckeys {}\n                set numvec [expr {[randomInt 10]+1}]\n                for {set j 0} {$j < $numvec} {incr j} {\n                    set str [randstring 0 1000]\n                    lappend vec $str\n                    lappend veckeys vector_$j\n                    r set vector_$j $str\n                }\n                r bitop $op target {*}$veckeys\n                assert_equal [r get target] [simulate_bit_op $op {*}$vec]\n            }\n        }\n    }\n\n    test {BITOP NOT fuzzing} {\n        for {set i 0} {$i < 10} {incr i} {\n            r flushall\n            set str [randstring 0 1000]\n            r set str $str\n            r bitop not target str\n            assert_equal [r get target] [simulate_bit_op not $str]\n        }\n    }\n\n    test {BITOP with integer encoded source objects} {\n        r set a 1\n        r set b 2\n        r bitop xor dest a b a\n        r get dest\n    } {2}\n\n    test {BITOP with non string source key} {\n        r del c\n        r set a 1\n        r set b 2\n        r lpush c foo\n        catch {r bitop xor dest a b c d} e\n        set e\n    } {WRONGTYPE*}\n\n    test {BITOP with empty string after non empty string (issue #529)} {\n        r flushdb\n        r set a \"\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n        r bitop or x a b\n    } {32}\n\n    test {BITPOS bit=0 with empty key returns 0} {\n        r del str\n        r bitpos str 0\n    } {0}\n\n    test {BITPOS bit=1 with empty key returns -1} {\n        r del str\n        r bitpos str 1\n    } {-1}\n\n    test {BITPOS bit=0 with string less than 1 word works} {\n        r set str \"\\xff\\xf0\\x00\"\n        r bitpos str 0\n    } {12}\n\n    test {BITPOS bit=1 with string less than 1 word works} {\n        r set str \"\\x00\\x0f\\x00\"\n        r bitpos str 1\n    } {12}\n\n    test {BITPOS bit=0 starting at unaligned address} {\n        r set str \"\\xff\\xf0\\x00\"\n        r bitpos str 0 1\n    } {12}\n\n    test {BITPOS bit=1 starting at unaligned address} {\n        r set str \"\\x00\\x0f\\xff\"\n        r bitpos str 1 1\n    } {12}\n\n    test {BITPOS bit=0 unaligned+full word+reminder} {\n        r del str\n        r set str \"\\xff\\xff\\xff\" ; # Prefix\n        # Followed by two (or four in 32 bit systems) full words\n        r append str \"\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\"\n        r append str \"\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\"\n        r append str \"\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\"\n        # First zero bit.\n        r append str \"\\x0f\"\n        assert {[r bitpos str 0] == 216}\n        assert {[r bitpos str 0 1] == 216}\n        assert {[r bitpos str 0 2] == 216}\n        assert {[r bitpos str 0 3] == 216}\n        assert {[r bitpos str 0 4] == 216}\n        assert {[r bitpos str 0 5] == 216}\n        assert {[r bitpos str 0 6] == 216}\n        assert {[r bitpos str 0 7] == 216}\n        assert {[r bitpos str 0 8] == 216}\n    }\n\n    test {BITPOS bit=1 unaligned+full word+reminder} {\n        r del str\n        r set str \"\\x00\\x00\\x00\" ; # Prefix\n        # Followed by two (or four in 32 bit systems) full words\n        r append str \"\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n        r append str \"\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n        r append str \"\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n        # First zero bit.\n        r append str \"\\xf0\"\n        assert {[r bitpos str 1] == 216}\n        assert {[r bitpos str 1 1] == 216}\n        assert {[r bitpos str 1 2] == 216}\n        assert {[r bitpos str 1 3] == 216}\n        assert {[r bitpos str 1 4] == 216}\n        assert {[r bitpos str 1 5] == 216}\n        assert {[r bitpos str 1 6] == 216}\n        assert {[r bitpos str 1 7] == 216}\n        assert {[r bitpos str 1 8] == 216}\n    }\n\n    test {BITPOS bit=1 returns -1 if string is all 0 bits} {\n        r set str \"\"\n        for {set j 0} {$j < 20} {incr j} {\n            assert {[r bitpos str 1] == -1}\n            r append str \"\\x00\"\n        }\n    }\n\n    test {BITPOS bit=0 works with intervals} {\n        r set str \"\\x00\\xff\\x00\"\n        assert {[r bitpos str 0 0 -1] == 0}\n        assert {[r bitpos str 0 1 -1] == 16}\n        assert {[r bitpos str 0 2 -1] == 16}\n        assert {[r bitpos str 0 2 200] == 16}\n        assert {[r bitpos str 0 1 1] == -1}\n    }\n\n    test {BITPOS bit=1 works with intervals} {\n        r set str \"\\x00\\xff\\x00\"\n        assert {[r bitpos str 1 0 -1] == 8}\n        assert {[r bitpos str 1 1 -1] == 8}\n        assert {[r bitpos str 1 2 -1] == -1}\n        assert {[r bitpos str 1 2 200] == -1}\n        assert {[r bitpos str 1 1 1] == 8}\n    }\n\n    test {BITPOS bit=0 changes behavior if end is given} {\n        r set str \"\\xff\\xff\\xff\"\n        assert {[r bitpos str 0] == 24}\n        assert {[r bitpos str 0 0] == 24}\n        assert {[r bitpos str 0 0 -1] == -1}\n    }\n\n    test {BITPOS bit=1 fuzzy testing using SETBIT} {\n        r del str\n        set max 524288; # 64k\n        set first_one_pos -1\n        for {set j 0} {$j < 1000} {incr j} {\n            assert {[r bitpos str 1] == $first_one_pos}\n            set pos [randomInt $max]\n            r setbit str $pos 1\n            if {$first_one_pos == -1 || $first_one_pos > $pos} {\n                # Update the position of the first 1 bit in the array\n                # if the bit we set is on the left of the previous one.\n                set first_one_pos $pos\n            }\n        }\n    }\n\n    test {BITPOS bit=0 fuzzy testing using SETBIT} {\n        set max 524288; # 64k\n        set first_zero_pos $max\n        r set str [string repeat \"\\xff\" [expr $max/8]]\n        for {set j 0} {$j < 1000} {incr j} {\n            assert {[r bitpos str 0] == $first_zero_pos}\n            set pos [randomInt $max]\n            r setbit str $pos 0\n            if {$first_zero_pos > $pos} {\n                # Update the position of the first 0 bit in the array\n                # if the bit we clear is on the left of the previous one.\n                set first_zero_pos $pos\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "tests/unit/dump.tcl",
    "content": "start_server {tags {\"dump\"}} {\n    test {DUMP / RESTORE are able to serialize / unserialize a simple key} {\n        r set foo bar\n        set encoded [r dump foo]\n        r del foo\n        list [r exists foo] [r restore foo 0 $encoded] [r ttl foo] [r get foo]\n    } {0 OK -1 bar}\n\n    test {RESTORE can set an arbitrary expire to the materialized key} {\n        r set foo bar\n        set encoded [r dump foo]\n        r del foo\n        r restore foo 5000 $encoded\n        set ttl [r pttl foo]\n        assert {$ttl >= 3000 && $ttl <= 5000}\n        r get foo\n    } {bar}\n\n    test {RESTORE can set an expire that overflows a 32 bit integer} {\n        r set foo bar\n        set encoded [r dump foo]\n        r del foo\n        r restore foo 2569591501 $encoded\n        set ttl [r pttl foo]\n        assert {$ttl >= (2569591501-3000) && $ttl <= 2569591501}\n        r get foo\n    } {bar}\n\n    test {RESTORE returns an error of the key already exists} {\n        r set foo bar\n        set e {}\n        catch {r restore foo 0 \"...\"} e\n        set e\n    } {*BUSYKEY*}\n\n    test {RESTORE can overwrite an existing key with REPLACE} {\n        r set foo bar1\n        set encoded1 [r dump foo]\n        r set foo bar2\n        set encoded2 [r dump foo]\n        r del foo\n        r restore foo 0 $encoded1\n        r restore foo 0 $encoded2 replace\n        r get foo\n    } {bar2}\n\n    test {RESTORE can detect a syntax error for unrecongized options} {\n        catch {r restore foo 0 \"...\" invalid-option} e\n        set e\n    } {*syntax*}\n\n    test {DUMP of non existing key returns nil} {\n        r dump nonexisting_key\n    } {}\n\n    test {MIGRATE is caching connections} {\n        # Note, we run this as first test so that the connection cache\n        # is empty.\n        set first [srv 0 client]\n        r set key \"Some Value\"\n        start_server {tags {\"repl\"}} {\n            set second [srv 0 client]\n            set second_host [srv 0 host]\n            set second_port [srv 0 port]\n\n            assert_match {*migrate_cached_sockets:0*} [r -1 info]\n            r -1 migrate $second_host $second_port key 9 1000\n            assert_match {*migrate_cached_sockets:1*} [r -1 info]\n        }\n    }\n\n    test {MIGRATE cached connections are released after some time} {\n        after 15000\n        assert_match {*migrate_cached_sockets:0*} [r info]\n    }\n\n    test {MIGRATE is able to migrate a key between two instances} {\n        set first [srv 0 client]\n        r set key \"Some Value\"\n        start_server {tags {\"repl\"}} {\n            set second [srv 0 client]\n            set second_host [srv 0 host]\n            set second_port [srv 0 port]\n\n            assert {[$first exists key] == 1}\n            assert {[$second exists key] == 0}\n            set ret [r -1 migrate $second_host $second_port key 9 5000]\n            assert {$ret eq {OK}}\n            assert {[$first exists key] == 0}\n            assert {[$second exists key] == 1}\n            assert {[$second get key] eq {Some Value}}\n            assert {[$second ttl key] == -1}\n        }\n    }\n\n    test {MIGRATE is able to copy a key between two instances} {\n        set first [srv 0 client]\n        r del list\n        r lpush list a b c d\n        start_server {tags {\"repl\"}} {\n            set second [srv 0 client]\n            set second_host [srv 0 host]\n            set second_port [srv 0 port]\n\n            assert {[$first exists list] == 1}\n            assert {[$second exists list] == 0}\n            set ret [r -1 migrate $second_host $second_port list 9 5000 copy]\n            assert {$ret eq {OK}}\n            assert {[$first exists list] == 1}\n            assert {[$second exists list] == 1}\n            assert {[$first lrange list 0 -1] eq [$second lrange list 0 -1]}\n        }\n    }\n\n    test {MIGRATE will not overwrite existing keys, unless REPLACE is used} {\n        set first [srv 0 client]\n        r del list\n        r lpush list a b c d\n        start_server {tags {\"repl\"}} {\n            set second [srv 0 client]\n            set second_host [srv 0 host]\n            set second_port [srv 0 port]\n\n            assert {[$first exists list] == 1}\n            assert {[$second exists list] == 0}\n            $second set list somevalue\n            catch {r -1 migrate $second_host $second_port list 9 5000 copy} e\n            assert_match {ERR*} $e\n            set res [r -1 migrate $second_host $second_port list 9 5000 copy replace]\n            assert {$ret eq {OK}}\n            assert {[$first exists list] == 1}\n            assert {[$second exists list] == 1}\n            assert {[$first lrange list 0 -1] eq [$second lrange list 0 -1]}\n        }\n    }\n\n    test {MIGRATE propagates TTL correctly} {\n        set first [srv 0 client]\n        r set key \"Some Value\"\n        start_server {tags {\"repl\"}} {\n            set second [srv 0 client]\n            set second_host [srv 0 host]\n            set second_port [srv 0 port]\n\n            assert {[$first exists key] == 1}\n            assert {[$second exists key] == 0}\n            $first expire key 10\n            set ret [r -1 migrate $second_host $second_port key 9 5000]\n            assert {$ret eq {OK}}\n            assert {[$first exists key] == 0}\n            assert {[$second exists key] == 1}\n            assert {[$second get key] eq {Some Value}}\n            assert {[$second ttl key] >= 7 && [$second ttl key] <= 10}\n        }\n    }\n\n    test {MIGRATE can correctly transfer large values} {\n        set first [srv 0 client]\n        r del key\n        for {set j 0} {$j < 5000} {incr j} {\n            r rpush key 1 2 3 4 5 6 7 8 9 10\n            r rpush key \"item 1\" \"item 2\" \"item 3\" \"item 4\" \"item 5\" \\\n                        \"item 6\" \"item 7\" \"item 8\" \"item 9\" \"item 10\"\n        }\n        assert {[string length [r dump key]] > (1024*64)}\n        start_server {tags {\"repl\"}} {\n            set second [srv 0 client]\n            set second_host [srv 0 host]\n            set second_port [srv 0 port]\n\n            assert {[$first exists key] == 1}\n            assert {[$second exists key] == 0}\n            set ret [r -1 migrate $second_host $second_port key 9 10000]\n            assert {$ret eq {OK}}\n            assert {[$first exists key] == 0}\n            assert {[$second exists key] == 1}\n            assert {[$second ttl key] == -1}\n            assert {[$second llen key] == 5000*20}\n        }\n    }\n\n    test {MIGRATE can correctly transfer hashes} {\n        set first [srv 0 client]\n        r del key\n        r hmset key field1 \"item 1\" field2 \"item 2\" field3 \"item 3\" \\\n                    field4 \"item 4\" field5 \"item 5\" field6 \"item 6\"\n        start_server {tags {\"repl\"}} {\n            set second [srv 0 client]\n            set second_host [srv 0 host]\n            set second_port [srv 0 port]\n\n            assert {[$first exists key] == 1}\n            assert {[$second exists key] == 0}\n            set ret [r -1 migrate $second_host $second_port key 9 10000]\n            assert {$ret eq {OK}}\n            assert {[$first exists key] == 0}\n            assert {[$second exists key] == 1}\n            assert {[$second ttl key] == -1}\n        }\n    }\n\n    test {MIGRATE timeout actually works} {\n        set first [srv 0 client]\n        r set key \"Some Value\"\n        start_server {tags {\"repl\"}} {\n            set second [srv 0 client]\n            set second_host [srv 0 host]\n            set second_port [srv 0 port]\n\n            assert {[$first exists key] == 1}\n            assert {[$second exists key] == 0}\n\n            set rd [redis_deferring_client]\n            $rd debug sleep 1.0 ; # Make second server unable to reply.\n            set e {}\n            catch {r -1 migrate $second_host $second_port key 9 500} e\n            assert_match {IOERR*} $e\n        }\n    }\n}\n"
  },
  {
    "path": "tests/unit/expire.tcl",
    "content": "start_server {tags {\"expire\"}} {\n    test {EXPIRE - set timeouts multiple times} {\n        r set x foobar\n        set v1 [r expire x 5]\n        set v2 [r ttl x]\n        set v3 [r expire x 10]\n        set v4 [r ttl x]\n        r expire x 2\n        list $v1 $v2 $v3 $v4\n    } {1 [45] 1 10}\n\n    test {EXPIRE - It should be still possible to read 'x'} {\n        r get x\n    } {foobar}\n\n    tags {\"slow\"} {\n        test {EXPIRE - After 2.1 seconds the key should no longer be here} {\n            after 2100\n            list [r get x] [r exists x]\n        } {{} 0}\n    }\n\n    test {EXPIRE - write on expire should work} {\n        r del x\n        r lpush x foo\n        r expire x 1000\n        r lpush x bar\n        r lrange x 0 -1\n    } {bar foo}\n\n    test {EXPIREAT - Check for EXPIRE alike behavior} {\n        r del x\n        r set x foo\n        r expireat x [expr [clock seconds]+15]\n        r ttl x\n    } {1[345]}\n\n    test {SETEX - Set + Expire combo operation. Check for TTL} {\n        r setex x 12 test\n        r ttl x\n    } {1[012]}\n\n    test {SETEX - Check value} {\n        r get x\n    } {test}\n\n    test {SETEX - Overwrite old key} {\n        r setex y 1 foo\n        r get y\n    } {foo}\n\n    tags {\"slow\"} {\n        test {SETEX - Wait for the key to expire} {\n            after 1100\n            r get y\n        } {}\n    }\n\n    test {SETEX - Wrong time parameter} {\n        catch {r setex z -10 foo} e\n        set _ $e\n    } {*invalid expire*}\n\n    test {PERSIST can undo an EXPIRE} {\n        r set x foo\n        r expire x 50\n        list [r ttl x] [r persist x] [r ttl x] [r get x]\n    } {50 1 -1 foo}\n\n    test {PERSIST returns 0 against non existing or non volatile keys} {\n        r set x foo\n        list [r persist foo] [r persist nokeyatall]\n    } {0 0}\n\n    test {EXPIRE pricision is now the millisecond} {\n        # This test is very likely to do a false positive if the\n        # server is under pressure, so if it does not work give it a few more\n        # chances.\n        for {set j 0} {$j < 3} {incr j} {\n            r del x\n            r setex x 1 somevalue\n            after 900\n            set a [r get x]\n            after 1100\n            set b [r get x]\n            if {$a eq {somevalue} && $b eq {}} break\n        }\n        list $a $b\n    } {somevalue {}}\n\n    test {PEXPIRE/PSETEX/PEXPIREAT can set sub-second expires} {\n        # This test is very likely to do a false positive if the\n        # server is under pressure, so if it does not work give it a few more\n        # chances.\n        for {set j 0} {$j < 3} {incr j} {\n            r del x y z\n            r psetex x 100 somevalue\n            after 80\n            set a [r get x]\n            after 120\n            set b [r get x]\n\n            r set x somevalue\n            r pexpire x 100\n            after 80\n            set c [r get x]\n            after 120\n            set d [r get x]\n\n            r set x somevalue\n            r pexpireat x [expr ([clock seconds]*1000)+100]\n            after 80\n            set e [r get x]\n            after 120\n            set f [r get x]\n\n            if {$a eq {somevalue} && $b eq {} &&\n                $c eq {somevalue} && $d eq {} &&\n                $e eq {somevalue} && $f eq {}} break\n        }\n        list $a $b\n    } {somevalue {}}\n\n    test {TTL returns tiem to live in seconds} {\n        r del x\n        r setex x 10 somevalue\n        set ttl [r ttl x]\n        assert {$ttl > 8 && $ttl <= 10}\n    }\n\n    test {PTTL returns time to live in milliseconds} {\n        r del x\n        r setex x 1 somevalue\n        set ttl [r pttl x]\n        assert {$ttl > 900 && $ttl <= 1000}\n    }\n\n    test {TTL / PTTL return -1 if key has no expire} {\n        r del x\n        r set x hello\n        list [r ttl x] [r pttl x]\n    } {-1 -1}\n\n    test {TTL / PTTL return -2 if key does not exit} {\n        r del x\n        list [r ttl x] [r pttl x]\n    } {-2 -2}\n\n    test {Redis should actively expire keys incrementally} {\n        r flushdb\n        r psetex key1 500 a\n        r psetex key2 500 a\n        r psetex key3 500 a\n        set size1 [r dbsize]\n        # Redis expires random keys ten times every second so we are\n        # fairly sure that all the three keys should be evicted after\n        # one second.\n        after 1000\n        set size2 [r dbsize]\n        list $size1 $size2\n    } {3 0}\n\n    test {Redis should lazy expire keys} {\n        r flushdb\n        r debug set-active-expire 0\n        r psetex key1 500 a\n        r psetex key2 500 a\n        r psetex key3 500 a\n        set size1 [r dbsize]\n        # Redis expires random keys ten times every second so we are\n        # fairly sure that all the three keys should be evicted after\n        # one second.\n        after 1000\n        set size2 [r dbsize]\n        r mget key1 key2 key3\n        set size3 [r dbsize]\n        r debug set-active-expire 1\n        list $size1 $size2 $size3\n    } {3 3 0}\n\n    test {EXPIRE should not resurrect keys (issue #1026)} {\n        r debug set-active-expire 0\n        r set foo bar\n        r pexpire foo 500\n        after 1000\n        r expire foo 10\n        r debug set-active-expire 1\n        r exists foo\n    } {0}\n\n    test {5 keys in, 5 keys out} {\n        r flushdb\n        r set a c\n        r expire a 5\n        r set t c\n        r set e c\n        r set s c\n        r set foo b\n        lsort [r keys *]\n    } {a e foo s t}\n}\n"
  },
  {
    "path": "tests/unit/hyperloglog.tcl",
    "content": "start_server {tags {\"hll\"}} {\n    test {HyperLogLog self test passes} {\n        catch {r pfselftest} e\n        set e\n    } {OK}\n\n    test {PFADD without arguments creates an HLL value} {\n        r pfadd hll\n        r exists hll\n    } {1}\n\n    test {Approximated cardinality after creation is zero} {\n        r pfcount hll\n    } {0}\n\n    test {PFADD returns 1 when at least 1 reg was modified} {\n        r pfadd hll a b c\n    } {1}\n\n    test {PFADD returns 0 when no reg was modified} {\n        r pfadd hll a b c\n    } {0}\n\n    test {PFADD works with empty string (regression)} {\n        r pfadd hll \"\"\n    }\n\n    # Note that the self test stresses much better the\n    # cardinality estimation error. We are testing just the\n    # command implementation itself here.\n    test {PFCOUNT returns approximated cardinality of set} {\n        r del hll\n        set res {}\n        r pfadd hll 1 2 3 4 5\n        lappend res [r pfcount hll]\n        # Call it again to test cached value invalidation.\n        r pfadd hll 6 7 8 8 9 10\n        lappend res [r pfcount hll]\n        set res\n    } {5 10}\n\n    test {HyperLogLogs are promote from sparse to dense} {\n        r del hll\n        r config set hll-sparse-max-bytes 3000\n        set n 0\n        while {$n < 100000} {\n            set elements {}\n            for {set j 0} {$j < 100} {incr j} {lappend elements [expr rand()]}\n            incr n 100\n            r pfadd hll {*}$elements\n            set card [r pfcount hll]\n            set err [expr {abs($card-$n)}]\n            assert {$err < (double($card)/100)*5}\n            if {$n < 1000} {\n                assert {[r pfdebug encoding hll] eq {sparse}}\n            } elseif {$n > 10000} {\n                assert {[r pfdebug encoding hll] eq {dense}}\n            }\n        }\n    }\n\n    test {HyperLogLog sparse encoding stress test} {\n        for {set x 0} {$x < 1000} {incr x} {\n            r del hll1 hll2\n            set numele [randomInt 100]\n            set elements {}\n            for {set j 0} {$j < $numele} {incr j} {\n                lappend elements [expr rand()]\n            }\n            # Force dense representation of hll2\n            r pfadd hll2\n            r pfdebug todense hll2\n            r pfadd hll1 {*}$elements\n            r pfadd hll2 {*}$elements\n            assert {[r pfdebug encoding hll1] eq {sparse}}\n            assert {[r pfdebug encoding hll2] eq {dense}}\n            # Cardinality estimated should match exactly.\n            assert {[r pfcount hll1] eq [r pfcount hll2]}\n        }\n    }\n\n    test {Corrupted sparse HyperLogLogs are detected: Additionl at tail} {\n        r del hll\n        r pfadd hll a b c\n        r append hll \"hello\"\n        set e {}\n        catch {r pfcount hll} e\n        set e\n    } {*INVALIDOBJ*}\n\n    test {Corrupted sparse HyperLogLogs are detected: Broken magic} {\n        r del hll\n        r pfadd hll a b c\n        r setrange hll 0 \"0123\"\n        set e {}\n        catch {r pfcount hll} e\n        set e\n    } {*WRONGTYPE*}\n\n    test {Corrupted sparse HyperLogLogs are detected: Invalid encoding} {\n        r del hll\n        r pfadd hll a b c\n        r setrange hll 4 \"x\"\n        set e {}\n        catch {r pfcount hll} e\n        set e\n    } {*WRONGTYPE*}\n\n    test {Corrupted dense HyperLogLogs are detected: Wrong length} {\n        r del hll\n        r pfadd hll a b c\n        r setrange hll 4 \"\\x00\"\n        set e {}\n        catch {r pfcount hll} e\n        set e\n    } {*WRONGTYPE*}\n\n    test {PFADD, PFCOUNT, PFMERGE type checking works} {\n        r set foo bar\n        catch {r pfadd foo 1} e\n        assert_match {*WRONGTYPE*} $e\n        catch {r pfcount foo} e\n        assert_match {*WRONGTYPE*} $e\n        catch {r pfmerge bar foo} e\n        assert_match {*WRONGTYPE*} $e\n        catch {r pfmerge foo bar} e\n        assert_match {*WRONGTYPE*} $e\n    }\n\n    test {PFMERGE results on the cardinality of union of sets} {\n        r del hll hll1 hll2 hll3\n        r pfadd hll1 a b c\n        r pfadd hll2 b c d\n        r pfadd hll3 c d e\n        r pfmerge hll hll1 hll2 hll3\n        r pfcount hll\n    } {5}\n\n    test {PFCOUNT multiple-keys merge returns cardinality of union} {\n        r del hll1 hll2 hll3\n        for {set x 1} {$x < 10000} {incr x} {\n            # Force dense representation of hll2\n            r pfadd hll1 \"foo-$x\"\n            r pfadd hll2 \"bar-$x\"\n            r pfadd hll3 \"zap-$x\"\n\n            set card [r pfcount hll1 hll2 hll3]\n            set realcard [expr {$x*3}]\n            set err [expr {abs($card-$realcard)}]\n            assert {$err < (double($card)/100)*5}\n        }\n    }\n\n    test {PFDEBUG GETREG returns the HyperLogLog raw registers} {\n        r del hll\n        r pfadd hll 1 2 3\n        llength [r pfdebug getreg hll]\n    } {16384}\n}\n"
  },
  {
    "path": "tests/unit/introspection.tcl",
    "content": "start_server {tags {\"introspection\"}} {\n    test {CLIENT LIST} {\n        r client list\n    } {*addr=*:* fd=* age=* idle=* flags=N db=9 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=* obl=0 oll=0 omem=0 events=r cmd=client*}\n\n    test {MONITOR can log executed commands} {\n        set rd [redis_deferring_client]\n        $rd monitor\n        r set foo bar\n        r get foo\n        list [$rd read] [$rd read] [$rd read]\n    } {*OK*\"set\" \"foo\"*\"get\" \"foo\"*}\n\n    test {MONITOR can log commands issued by the scripting engine} {\n        set rd [redis_deferring_client]\n        $rd monitor\n        r eval {redis.call('set',KEYS[1],ARGV[1])} 1 foo bar\n        $rd read ;# Discard the OK\n        assert_match {*eval*} [$rd read]\n        assert_match {*lua*\"set\"*\"foo\"*\"bar\"*} [$rd read]\n    }\n\n    test {CLIENT GETNAME should return NIL if name is not assigned} {\n        r client getname\n    } {}\n\n    test {CLIENT LIST shows empty fields for unassigned names} {\n        r client list\n    } {*name= *}\n    \n    test {CLIENT SETNAME does not accept spaces} {\n        catch {r client setname \"foo bar\"} e\n        set e\n    } {ERR*}\n\n    test {CLIENT SETNAME can assign a name to this connection} {\n        assert_equal [r client setname myname] {OK}\n        r client list\n    } {*name=myname*}\n\n    test {CLIENT SETNAME can change the name of an existing connection} {\n        assert_equal [r client setname someothername] {OK}\n        r client list\n    } {*name=someothername*}\n\n    test {After CLIENT SETNAME, connection can still be closed} {\n        set rd [redis_deferring_client]\n        $rd client setname foobar\n        assert_equal [$rd read] \"OK\"\n        assert_match {*foobar*} [r client list]\n        $rd close\n        # Now the client should no longer be listed\n        wait_for_condition 50 100 {\n            [string match {*foobar*} [r client list]] == 0\n        } else {\n            fail \"Client still listed in CLIENT LIST after SETNAME.\"\n        }\n    }\n}\n"
  },
  {
    "path": "tests/unit/limits.tcl",
    "content": "start_server {tags {\"limits\"} overrides {maxclients 10}} {\n    test {Check if maxclients works refusing connections} {\n        set c 0\n        catch {\n            while {$c < 50} {\n                incr c\n                set rd [redis_deferring_client]\n                $rd ping\n                $rd read\n                after 100\n            }\n        } e\n        assert {$c > 8 && $c <= 10}\n        set e\n    } {*ERR max*reached*}\n}\n"
  },
  {
    "path": "tests/unit/maxmemory.tcl",
    "content": "start_server {tags {\"maxmemory\"}} {\n    foreach policy {\n        allkeys-random allkeys-lru volatile-lru volatile-random volatile-ttl\n    } {\n        test \"maxmemory - is the memory limit honoured? (policy $policy)\" {\n            # make sure to start with a blank instance\n            r flushall \n            # Get the current memory limit and calculate a new limit.\n            # We just add 100k to the current memory size so that it is\n            # fast for us to reach that limit.\n            set used [s used_memory]\n            set limit [expr {$used+100*1024}]\n            r config set maxmemory $limit\n            r config set maxmemory-policy $policy\n            # Now add keys until the limit is almost reached.\n            set numkeys 0\n            while 1 {\n                r setex [randomKey] 10000 x\n                incr numkeys\n                if {[s used_memory]+4096 > $limit} {\n                    assert {$numkeys > 10}\n                    break\n                }\n            }\n            # If we add the same number of keys already added again, we\n            # should still be under the limit.\n            for {set j 0} {$j < $numkeys} {incr j} {\n                r setex [randomKey] 10000 x\n            }\n            assert {[s used_memory] < ($limit+4096)}\n        }\n    }\n\n    foreach policy {\n        allkeys-random allkeys-lru volatile-lru volatile-random volatile-ttl\n    } {\n        test \"maxmemory - only allkeys-* should remove non-volatile keys ($policy)\" {\n            # make sure to start with a blank instance\n            r flushall \n            # Get the current memory limit and calculate a new limit.\n            # We just add 100k to the current memory size so that it is\n            # fast for us to reach that limit.\n            set used [s used_memory]\n            set limit [expr {$used+100*1024}]\n            r config set maxmemory $limit\n            r config set maxmemory-policy $policy\n            # Now add keys until the limit is almost reached.\n            set numkeys 0\n            while 1 {\n                r set [randomKey] x\n                incr numkeys\n                if {[s used_memory]+4096 > $limit} {\n                    assert {$numkeys > 10}\n                    break\n                }\n            }\n            # If we add the same number of keys already added again and\n            # the policy is allkeys-* we should still be under the limit.\n            # Otherwise we should see an error reported by Redis.\n            set err 0\n            for {set j 0} {$j < $numkeys} {incr j} {\n                if {[catch {r set [randomKey] x} e]} {\n                    if {[string match {*used memory*} $e]} {\n                        set err 1\n                    }\n                }\n            }\n            if {[string match allkeys-* $policy]} {\n                assert {[s used_memory] < ($limit+4096)}\n            } else {\n                assert {$err == 1}\n            }\n        }\n    }\n\n    foreach policy {\n        volatile-lru volatile-random volatile-ttl\n    } {\n        test \"maxmemory - policy $policy should only remove volatile keys.\" {\n            # make sure to start with a blank instance\n            r flushall \n            # Get the current memory limit and calculate a new limit.\n            # We just add 100k to the current memory size so that it is\n            # fast for us to reach that limit.\n            set used [s used_memory]\n            set limit [expr {$used+100*1024}]\n            r config set maxmemory $limit\n            r config set maxmemory-policy $policy\n            # Now add keys until the limit is almost reached.\n            set numkeys 0\n            while 1 {\n                # Odd keys are volatile\n                # Even keys are non volatile\n                if {$numkeys % 2} {\n                    r setex \"key:$numkeys\" 10000 x\n                } else {\n                    r set \"key:$numkeys\" x\n                }\n                if {[s used_memory]+4096 > $limit} {\n                    assert {$numkeys > 10}\n                    break\n                }\n                incr numkeys\n            }\n            # Now we add the same number of volatile keys already added.\n            # We expect Redis to evict only volatile keys in order to make\n            # space.\n            set err 0\n            for {set j 0} {$j < $numkeys} {incr j} {\n                catch {r setex \"foo:$j\" 10000 x}\n            }\n            # We should still be under the limit.\n            assert {[s used_memory] < ($limit+4096)}\n            # However all our non volatile keys should be here.\n            for {set j 0} {$j < $numkeys} {incr j 2} {\n                assert {[r exists \"key:$j\"]}\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "tests/unit/memefficiency.tcl",
    "content": "proc test_memory_efficiency {range} {\n    r flushall\n    set base_mem [s used_memory]\n    set written 0\n    for {set j 0} {$j < 10000} {incr j} {\n        set key key:$j\n        set val [string repeat A [expr {int(rand()*$range)}]]\n        r set $key $val\n        incr written [string length $key]\n        incr written [string length $val]\n        incr written 2 ;# A separator is the minimum to store key-value data.\n    }\n    set current_mem [s used_memory]\n    set used [expr {$current_mem-$base_mem}]\n    set efficiency [expr {double($written)/$used}]\n    return $efficiency\n}\n\nstart_server {tags {\"memefficiency\"}} {\n    foreach {size_range expected_min_efficiency} {\n        32    0.15\n        64    0.25\n        128   0.35\n        1024  0.75\n        16384 0.82\n    } {\n        test \"Memory efficiency with values in range $size_range\" {\n            set efficiency [test_memory_efficiency $size_range]\n            assert {$efficiency >= $expected_min_efficiency}\n        }\n    }\n}\n"
  },
  {
    "path": "tests/unit/multi.tcl",
    "content": "start_server {tags {\"multi\"}} {\n    test {MUTLI / EXEC basics} {\n        r del mylist\n        r rpush mylist a\n        r rpush mylist b\n        r rpush mylist c\n        r multi\n        set v1 [r lrange mylist 0 -1]\n        set v2 [r ping]\n        set v3 [r exec]\n        list $v1 $v2 $v3\n    } {QUEUED QUEUED {{a b c} PONG}}\n\n    test {DISCARD} {\n        r del mylist\n        r rpush mylist a\n        r rpush mylist b\n        r rpush mylist c\n        r multi\n        set v1 [r del mylist]\n        set v2 [r discard]\n        set v3 [r lrange mylist 0 -1]\n        list $v1 $v2 $v3\n    } {QUEUED OK {a b c}}\n\n    test {Nested MULTI are not allowed} {\n        set err {}\n        r multi\n        catch {[r multi]} err\n        r exec\n        set _ $err\n    } {*ERR MULTI*}\n\n    test {MULTI where commands alter argc/argv} {\n        r sadd myset a\n        r multi\n        r spop myset\n        list [r exec] [r exists myset]\n    } {a 0}\n\n    test {WATCH inside MULTI is not allowed} {\n        set err {}\n        r multi\n        catch {[r watch x]} err\n        r exec\n        set _ $err\n    } {*ERR WATCH*}\n\n    test {EXEC fails if there are errors while queueing commands #1} {\n        r del foo1 foo2\n        r multi\n        r set foo1 bar1\n        catch {r non-existing-command}\n        r set foo2 bar2\n        catch {r exec} e\n        assert_match {EXECABORT*} $e\n        list [r exists foo1] [r exists foo2]\n    } {0 0}\n\n    test {EXEC fails if there are errors while queueing commands #2} {\n        set rd [redis_deferring_client]\n        r del foo1 foo2\n        r multi\n        r set foo1 bar1\n        $rd config set maxmemory 1\n        assert  {[$rd read] eq {OK}}\n        catch {r lpush mylist myvalue}\n        $rd config set maxmemory 0\n        assert  {[$rd read] eq {OK}}\n        r set foo2 bar2\n        catch {r exec} e\n        assert_match {EXECABORT*} $e\n        $rd close\n        list [r exists foo1] [r exists foo2]\n    } {0 0}\n\n    test {If EXEC aborts, the client MULTI state is cleared} {\n        r del foo1 foo2\n        r multi\n        r set foo1 bar1\n        catch {r non-existing-command}\n        r set foo2 bar2\n        catch {r exec} e\n        assert_match {EXECABORT*} $e\n        r ping\n    } {PONG}\n\n    test {EXEC works on WATCHed key not modified} {\n        r watch x y z\n        r watch k\n        r multi\n        r ping\n        r exec\n    } {PONG}\n\n    test {EXEC fail on WATCHed key modified (1 key of 1 watched)} {\n        r set x 30\n        r watch x\n        r set x 40\n        r multi\n        r ping\n        r exec\n    } {}\n\n    test {EXEC fail on WATCHed key modified (1 key of 5 watched)} {\n        r set x 30\n        r watch a b x k z\n        r set x 40\n        r multi\n        r ping\n        r exec\n    } {}\n\n    test {EXEC fail on WATCHed key modified by SORT with STORE even if the result is empty} {\n        r flushdb\n        r lpush foo bar\n        r watch foo\n        r sort emptylist store foo\n        r multi\n        r ping\n        r exec\n    } {}\n\n    test {After successful EXEC key is no longer watched} {\n        r set x 30\n        r watch x\n        r multi\n        r ping\n        r exec\n        r set x 40\n        r multi\n        r ping\n        r exec\n    } {PONG}\n\n    test {After failed EXEC key is no longer watched} {\n        r set x 30\n        r watch x\n        r set x 40\n        r multi\n        r ping\n        r exec\n        r set x 40\n        r multi\n        r ping\n        r exec\n    } {PONG}\n\n    test {It is possible to UNWATCH} {\n        r set x 30\n        r watch x\n        r set x 40\n        r unwatch\n        r multi\n        r ping\n        r exec\n    } {PONG}\n\n    test {UNWATCH when there is nothing watched works as expected} {\n        r unwatch\n    } {OK}\n\n    test {FLUSHALL is able to touch the watched keys} {\n        r set x 30\n        r watch x\n        r flushall\n        r multi\n        r ping\n        r exec\n    } {}\n\n    test {FLUSHALL does not touch non affected keys} {\n        r del x\n        r watch x\n        r flushall\n        r multi\n        r ping\n        r exec\n    } {PONG}\n\n    test {FLUSHDB is able to touch the watched keys} {\n        r set x 30\n        r watch x\n        r flushdb\n        r multi\n        r ping\n        r exec\n    } {}\n\n    test {FLUSHDB does not touch non affected keys} {\n        r del x\n        r watch x\n        r flushdb\n        r multi\n        r ping\n        r exec\n    } {PONG}\n\n    test {WATCH is able to remember the DB a key belongs to} {\n        r select 5\n        r set x 30\n        r watch x\n        r select 1\n        r set x 10\n        r select 5\n        r multi\n        r ping\n        set res [r exec]\n        # Restore original DB\n        r select 9\n        set res\n    } {PONG}\n\n    test {WATCH will consider touched keys target of EXPIRE} {\n        r del x\n        r set x foo\n        r watch x\n        r expire x 10\n        r multi\n        r ping\n        r exec\n    } {}\n\n    test {WATCH will not consider touched expired keys} {\n        r del x\n        r set x foo\n        r expire x 1\n        r watch x\n        after 1100\n        r multi\n        r ping\n        r exec\n    } {PONG}\n\n    test {DISCARD should clear the WATCH dirty flag on the client} {\n        r watch x\n        r set x 10\n        r multi\n        r discard\n        r multi\n        r incr x\n        r exec\n    } {11}\n\n    test {DISCARD should UNWATCH all the keys} {\n        r watch x\n        r set x 10\n        r multi\n        r discard\n        r set x 10\n        r multi\n        r incr x\n        r exec\n    } {11}\n\n    test {MULTI / EXEC is propagated correctly (single write command)} {\n        set repl [attach_to_replication_stream]\n        r multi\n        r set foo bar\n        r exec\n        assert_replication_stream $repl {\n            {select *}\n            {multi}\n            {set foo bar}\n            {exec}\n        }\n        close_replication_stream $repl\n    }\n\n    test {MULTI / EXEC is propagated correctly (empty transaction)} {\n        set repl [attach_to_replication_stream]\n        r multi\n        r exec\n        r set foo bar\n        assert_replication_stream $repl {\n            {select *}\n            {set foo bar}\n        }\n        close_replication_stream $repl\n    }\n\n    test {MULTI / EXEC is propagated correctly (read-only commands)} {\n        r set foo value1\n        set repl [attach_to_replication_stream]\n        r multi\n        r get foo\n        r exec\n        r set foo value2\n        assert_replication_stream $repl {\n            {select *}\n            {set foo value2}\n        }\n        close_replication_stream $repl\n    }\n\n    test {MULTI / EXEC is propagated correctly (write command, no effect)} {\n        r del bar foo bar\n        set repl [attach_to_replication_stream]\n        r multi\n        r del foo\n        r exec\n        assert_replication_stream $repl {\n            {select *}\n            {multi}\n            {exec}\n        }\n        close_replication_stream $repl\n    }\n}\n"
  },
  {
    "path": "tests/unit/obuf-limits.tcl",
    "content": "start_server {tags {\"obuf-limits\"}} {\n    test {Client output buffer hard limit is enforced} {\n        r config set client-output-buffer-limit {pubsub 100000 0 0}\n        set rd1 [redis_deferring_client]\n\n        $rd1 subscribe foo\n        set reply [$rd1 read]\n        assert {$reply eq \"subscribe foo 1\"}\n\n        set omem 0\n        while 1 {\n            r publish foo bar\n            set clients [split [r client list] \"\\r\\n\"]\n            set c [split [lindex $clients 1] \" \"]\n            if {![regexp {omem=([0-9]+)} $c - omem]} break\n            if {$omem > 200000} break\n        }\n        assert {$omem >= 90000 && $omem < 200000}\n        $rd1 close\n    }\n\n    test {Client output buffer soft limit is not enforced if time is not overreached} {\n        r config set client-output-buffer-limit {pubsub 0 100000 10}\n        set rd1 [redis_deferring_client]\n\n        $rd1 subscribe foo\n        set reply [$rd1 read]\n        assert {$reply eq \"subscribe foo 1\"}\n\n        set omem 0\n        set start_time 0\n        set time_elapsed 0\n        while 1 {\n            r publish foo bar\n            set clients [split [r client list] \"\\r\\n\"]\n            set c [split [lindex $clients 1] \" \"]\n            if {![regexp {omem=([0-9]+)} $c - omem]} break\n            if {$omem > 100000} {\n                if {$start_time == 0} {set start_time [clock seconds]}\n                set time_elapsed [expr {[clock seconds]-$start_time}]\n                if {$time_elapsed >= 5} break\n            }\n        }\n        assert {$omem >= 100000 && $time_elapsed >= 5 && $time_elapsed <= 10}\n        $rd1 close\n    }\n\n    test {Client output buffer soft limit is enforced if time is overreached} {\n        r config set client-output-buffer-limit {pubsub 0 100000 3}\n        set rd1 [redis_deferring_client]\n\n        $rd1 subscribe foo\n        set reply [$rd1 read]\n        assert {$reply eq \"subscribe foo 1\"}\n\n        set omem 0\n        set start_time 0\n        set time_elapsed 0\n        while 1 {\n            r publish foo bar\n            set clients [split [r client list] \"\\r\\n\"]\n            set c [split [lindex $clients 1] \" \"]\n            if {![regexp {omem=([0-9]+)} $c - omem]} break\n            if {$omem > 100000} {\n                if {$start_time == 0} {set start_time [clock seconds]}\n                set time_elapsed [expr {[clock seconds]-$start_time}]\n                if {$time_elapsed >= 10} break\n            }\n        }\n        assert {$omem >= 100000 && $time_elapsed < 6}\n        $rd1 close\n    }\n}\n"
  },
  {
    "path": "tests/unit/other.tcl",
    "content": "start_server {tags {\"other\"}} {\n    if {$::force_failure} {\n        # This is used just for test suite development purposes.\n        test {Failing test} {\n            format err\n        } {ok}\n    }\n\n    test {SAVE - make sure there are all the types as values} {\n        # Wait for a background saving in progress to terminate\n        waitForBgsave r\n        r lpush mysavelist hello\n        r lpush mysavelist world\n        r set myemptykey {}\n        r set mynormalkey {blablablba}\n        r zadd mytestzset 10 a\n        r zadd mytestzset 20 b\n        r zadd mytestzset 30 c\n        r save\n    } {OK}\n\n    tags {slow} {\n        if {$::accurate} {set iterations 10000} else {set iterations 1000}\n        foreach fuzztype {binary alpha compr} {\n            test \"FUZZ stresser with data model $fuzztype\" {\n                set err 0\n                for {set i 0} {$i < $iterations} {incr i} {\n                    set fuzz [randstring 0 512 $fuzztype]\n                    r set foo $fuzz\n                    set got [r get foo]\n                    if {$got ne $fuzz} {\n                        set err [list $fuzz $got]\n                        break\n                    }\n                }\n                set _ $err\n            } {0}\n        }\n    }\n\n    test {BGSAVE} {\n        waitForBgsave r\n        r flushdb\n        r save\n        r set x 10\n        r bgsave\n        waitForBgsave r\n        r debug reload\n        r get x\n    } {10}\n\n    test {SELECT an out of range DB} {\n        catch {r select 1000000} err\n        set _ $err\n    } {*invalid*}\n\n    tags {consistency} {\n        if {![catch {package require sha1}]} {\n            if {$::accurate} {set numops 10000} else {set numops 1000}\n            test {Check consistency of different data types after a reload} {\n                r flushdb\n                createComplexDataset r $numops\n                set dump [csvdump r]\n                set sha1 [r debug digest]\n                r debug reload\n                set sha1_after [r debug digest]\n                if {$sha1 eq $sha1_after} {\n                    set _ 1\n                } else {\n                    set newdump [csvdump r]\n                    puts \"Consistency test failed!\"\n                    puts \"You can inspect the two dumps in /tmp/repldump*.txt\"\n\n                    set fd [open /tmp/repldump1.txt w]\n                    puts $fd $dump\n                    close $fd\n                    set fd [open /tmp/repldump2.txt w]\n                    puts $fd $newdump\n                    close $fd\n\n                    set _ 0\n                }\n            } {1}\n\n            test {Same dataset digest if saving/reloading as AOF?} {\n                r bgrewriteaof\n                waitForBgrewriteaof r\n                r debug loadaof\n                set sha1_after [r debug digest]\n                if {$sha1 eq $sha1_after} {\n                    set _ 1\n                } else {\n                    set newdump [csvdump r]\n                    puts \"Consistency test failed!\"\n                    puts \"You can inspect the two dumps in /tmp/aofdump*.txt\"\n\n                    set fd [open /tmp/aofdump1.txt w]\n                    puts $fd $dump\n                    close $fd\n                    set fd [open /tmp/aofdump2.txt w]\n                    puts $fd $newdump\n                    close $fd\n\n                    set _ 0\n                }\n            } {1}\n        }\n    }\n\n    test {EXPIRES after a reload (snapshot + append only file rewrite)} {\n        r flushdb\n        r set x 10\n        r expire x 1000\n        r save\n        r debug reload\n        set ttl [r ttl x]\n        set e1 [expr {$ttl > 900 && $ttl <= 1000}]\n        r bgrewriteaof\n        waitForBgrewriteaof r\n        r debug loadaof\n        set ttl [r ttl x]\n        set e2 [expr {$ttl > 900 && $ttl <= 1000}]\n        list $e1 $e2\n    } {1 1}\n\n    test {EXPIRES after AOF reload (without rewrite)} {\n        r flushdb\n        r config set appendonly yes\n        r set x somevalue\n        r expire x 1000\n        r setex y 2000 somevalue\n        r set z somevalue\n        r expireat z [expr {[clock seconds]+3000}]\n\n        # Milliseconds variants\n        r set px somevalue\n        r pexpire px 1000000\n        r psetex py 2000000 somevalue\n        r set pz somevalue\n        r pexpireat pz [expr {([clock seconds]+3000)*1000}]\n\n        # Reload and check\n        waitForBgrewriteaof r\n        # We need to wait two seconds to avoid false positives here, otherwise\n        # the DEBUG LOADAOF command may read a partial file.\n        # Another solution would be to set the fsync policy to no, since this\n        # prevents write() to be delayed by the completion of fsync().\n        after 2000\n        r debug loadaof\n        set ttl [r ttl x]\n        assert {$ttl > 900 && $ttl <= 1000}\n        set ttl [r ttl y]\n        assert {$ttl > 1900 && $ttl <= 2000}\n        set ttl [r ttl z]\n        assert {$ttl > 2900 && $ttl <= 3000}\n        set ttl [r ttl px]\n        assert {$ttl > 900 && $ttl <= 1000}\n        set ttl [r ttl py]\n        assert {$ttl > 1900 && $ttl <= 2000}\n        set ttl [r ttl pz]\n        assert {$ttl > 2900 && $ttl <= 3000}\n        r config set appendonly no\n    }\n\n    tags {protocol} {\n        test {PIPELINING stresser (also a regression for the old epoll bug)} {\n            set fd2 [socket $::host $::port]\n            fconfigure $fd2 -encoding binary -translation binary\n            puts -nonewline $fd2 \"SELECT 9\\r\\n\"\n            flush $fd2\n            gets $fd2\n\n            for {set i 0} {$i < 100000} {incr i} {\n                set q {}\n                set val \"0000${i}0000\"\n                append q \"SET key:$i $val\\r\\n\"\n                puts -nonewline $fd2 $q\n                set q {}\n                append q \"GET key:$i\\r\\n\"\n                puts -nonewline $fd2 $q\n            }\n            flush $fd2\n\n            for {set i 0} {$i < 100000} {incr i} {\n                gets $fd2 line\n                gets $fd2 count\n                set count [string range $count 1 end]\n                set val [read $fd2 $count]\n                read $fd2 2\n            }\n            close $fd2\n            set _ 1\n        } {1}\n    }\n\n    test {APPEND basics} {\n        list [r append foo bar] [r get foo] \\\n             [r append foo 100] [r get foo]\n    } {3 bar 6 bar100}\n\n    test {APPEND basics, integer encoded values} {\n        set res {}\n        r del foo\n        r append foo 1\n        r append foo 2\n        lappend res [r get foo]\n        r set foo 1\n        r append foo 2\n        lappend res [r get foo]\n    } {12 12}\n\n    test {APPEND fuzzing} {\n        set err {}\n        foreach type {binary alpha compr} {\n            set buf {}\n            r del x\n            for {set i 0} {$i < 1000} {incr i} {\n                set bin [randstring 0 10 $type]\n                append buf $bin\n                r append x $bin\n            }\n            if {$buf != [r get x]} {\n                set err \"Expected '$buf' found '[r get x]'\"\n                break\n            }\n        }\n        set _ $err\n    } {}\n\n    # Leave the user with a clean DB before to exit\n    test {FLUSHDB} {\n        set aux {}\n        r select 9\n        r flushdb\n        lappend aux [r dbsize]\n        r select 10\n        r flushdb\n        lappend aux [r dbsize]\n    } {0 0}\n\n    test {Perform a final SAVE to leave a clean DB on disk} {\n        waitForBgsave r\n        r save\n    } {OK}\n}\n"
  },
  {
    "path": "tests/unit/printver.tcl",
    "content": "start_server {} {\n    set i [r info]\n    regexp {redis_version:(.*?)\\r\\n} $i - version\n    regexp {redis_git_sha1:(.*?)\\r\\n} $i - sha1\n    puts \"Testing Redis version $version ($sha1)\"\n}\n"
  },
  {
    "path": "tests/unit/protocol.tcl",
    "content": "start_server {tags {\"protocol\"}} {\n    test \"Handle an empty query\" {\n        reconnect\n        r write \"\\r\\n\"\n        r flush\n        assert_equal \"PONG\" [r ping]\n    }\n\n    test \"Negative multibulk length\" {\n        reconnect\n        r write \"*-10\\r\\n\"\n        r flush\n        assert_equal PONG [r ping]\n    }\n\n    test \"Out of range multibulk length\" {\n        reconnect\n        r write \"*20000000\\r\\n\"\n        r flush\n        assert_error \"*invalid multibulk length*\" {r read}\n    }\n\n    test \"Wrong multibulk payload header\" {\n        reconnect\n        r write \"*3\\r\\n\\$3\\r\\nSET\\r\\n\\$1\\r\\nx\\r\\nfooz\\r\\n\"\n        r flush\n        assert_error \"*expected '$', got 'f'*\" {r read}\n    }\n\n    test \"Negative multibulk payload length\" {\n        reconnect\n        r write \"*3\\r\\n\\$3\\r\\nSET\\r\\n\\$1\\r\\nx\\r\\n\\$-10\\r\\n\"\n        r flush\n        assert_error \"*invalid bulk length*\" {r read}\n    }\n\n    test \"Out of range multibulk payload length\" {\n        reconnect\n        r write \"*3\\r\\n\\$3\\r\\nSET\\r\\n\\$1\\r\\nx\\r\\n\\$2000000000\\r\\n\"\n        r flush\n        assert_error \"*invalid bulk length*\" {r read}\n    }\n\n    test \"Non-number multibulk payload length\" {\n        reconnect\n        r write \"*3\\r\\n\\$3\\r\\nSET\\r\\n\\$1\\r\\nx\\r\\n\\$blabla\\r\\n\"\n        r flush\n        assert_error \"*invalid bulk length*\" {r read}\n    }\n\n    test \"Multi bulk request not followed by bulk arguments\" {\n        reconnect\n        r write \"*1\\r\\nfoo\\r\\n\"\n        r flush\n        assert_error \"*expected '$', got 'f'*\" {r read}\n    }\n\n    test \"Generic wrong number of args\" {\n        reconnect\n        assert_error \"*wrong*arguments*ping*\" {r ping x y z}\n    }\n\n    test \"Unbalanced number of quotes\" {\n        reconnect\n        r write \"set \\\"\\\"\\\"test-key\\\"\\\"\\\" test-value\\r\\n\"\n        r write \"ping\\r\\n\"\n        r flush\n        assert_error \"*unbalanced*\" {r read}\n    }\n\n    set c 0\n    foreach seq [list \"\\x00\" \"*\\x00\" \"$\\x00\"] {\n        incr c\n        test \"Protocol desync regression test #$c\" {\n            set s [socket [srv 0 host] [srv 0 port]]\n            puts -nonewline $s $seq\n            set payload [string repeat A 1024]\"\\n\"\n            set test_start [clock seconds]\n            set test_time_limit 30\n            while 1 {\n                if {[catch {\n                    puts -nonewline $s payload\n                    flush $s\n                    incr payload_size [string length $payload]\n                }]} {\n                    set retval [gets $s]\n                    close $s\n                    break\n                } else {\n                    set elapsed [expr {[clock seconds]-$test_start}]\n                    if {$elapsed > $test_time_limit} {\n                        close $s\n                        error \"assertion:Redis did not closed connection after protocol desync\"\n                    }\n                }\n            }\n            set retval\n        } {*Protocol error*}\n    }\n    unset c\n}\n\nstart_server {tags {\"regression\"}} {\n    test \"Regression for a crash with blocking ops and pipelining\" {\n        set rd [redis_deferring_client]\n        set fd [r channel]\n        set proto \"*3\\r\\n\\$5\\r\\nBLPOP\\r\\n\\$6\\r\\nnolist\\r\\n\\$1\\r\\n0\\r\\n\"\n        puts -nonewline $fd $proto$proto\n        flush $fd\n        set res {}\n\n        $rd rpush nolist a\n        $rd read\n        $rd rpush nolist a\n        $rd read\n    }\n}\n"
  },
  {
    "path": "tests/unit/pubsub.tcl",
    "content": "start_server {tags {\"pubsub\"}} {\n    proc __consume_subscribe_messages {client type channels} {\n        set numsub -1\n        set counts {}\n\n        for {set i [llength $channels]} {$i > 0} {incr i -1} {\n            set msg [$client read]\n            assert_equal $type [lindex $msg 0]\n\n            # when receiving subscribe messages the channels names\n            # are ordered. when receiving unsubscribe messages\n            # they are unordered\n            set idx [lsearch -exact $channels [lindex $msg 1]]\n            if {[string match \"*unsubscribe\" $type]} {\n                assert {$idx >= 0}\n            } else {\n                assert {$idx == 0}\n            }\n            set channels [lreplace $channels $idx $idx]\n\n            # aggregate the subscription count to return to the caller\n            lappend counts [lindex $msg 2]\n        }\n\n        # we should have received messages for channels\n        assert {[llength $channels] == 0}\n        return $counts\n    }\n\n    proc subscribe {client channels} {\n        $client subscribe {*}$channels\n        __consume_subscribe_messages $client subscribe $channels\n    }\n\n    proc unsubscribe {client {channels {}}} {\n        $client unsubscribe {*}$channels\n        __consume_subscribe_messages $client unsubscribe $channels\n    }\n\n    proc psubscribe {client channels} {\n        $client psubscribe {*}$channels\n        __consume_subscribe_messages $client psubscribe $channels\n    }\n\n    proc punsubscribe {client {channels {}}} {\n        $client punsubscribe {*}$channels\n        __consume_subscribe_messages $client punsubscribe $channels\n    }\n\n    test \"PUBLISH/SUBSCRIBE basics\" {\n        set rd1 [redis_deferring_client]\n\n        # subscribe to two channels\n        assert_equal {1 2} [subscribe $rd1 {chan1 chan2}]\n        assert_equal 1 [r publish chan1 hello]\n        assert_equal 1 [r publish chan2 world]\n        assert_equal {message chan1 hello} [$rd1 read]\n        assert_equal {message chan2 world} [$rd1 read]\n\n        # unsubscribe from one of the channels\n        unsubscribe $rd1 {chan1}\n        assert_equal 0 [r publish chan1 hello]\n        assert_equal 1 [r publish chan2 world]\n        assert_equal {message chan2 world} [$rd1 read]\n\n        # unsubscribe from the remaining channel\n        unsubscribe $rd1 {chan2}\n        assert_equal 0 [r publish chan1 hello]\n        assert_equal 0 [r publish chan2 world]\n\n        # clean up clients\n        $rd1 close\n    }\n\n    test \"PUBLISH/SUBSCRIBE with two clients\" {\n        set rd1 [redis_deferring_client]\n        set rd2 [redis_deferring_client]\n\n        assert_equal {1} [subscribe $rd1 {chan1}]\n        assert_equal {1} [subscribe $rd2 {chan1}]\n        assert_equal 2 [r publish chan1 hello]\n        assert_equal {message chan1 hello} [$rd1 read]\n        assert_equal {message chan1 hello} [$rd2 read]\n\n        # clean up clients\n        $rd1 close\n        $rd2 close\n    }\n\n    test \"PUBLISH/SUBSCRIBE after UNSUBSCRIBE without arguments\" {\n        set rd1 [redis_deferring_client]\n        assert_equal {1 2 3} [subscribe $rd1 {chan1 chan2 chan3}]\n        unsubscribe $rd1\n        assert_equal 0 [r publish chan1 hello]\n        assert_equal 0 [r publish chan2 hello]\n        assert_equal 0 [r publish chan3 hello]\n\n        # clean up clients\n        $rd1 close\n    }\n\n    test \"SUBSCRIBE to one channel more than once\" {\n        set rd1 [redis_deferring_client]\n        assert_equal {1 1 1} [subscribe $rd1 {chan1 chan1 chan1}]\n        assert_equal 1 [r publish chan1 hello]\n        assert_equal {message chan1 hello} [$rd1 read]\n\n        # clean up clients\n        $rd1 close\n    }\n\n    test \"UNSUBSCRIBE from non-subscribed channels\" {\n        set rd1 [redis_deferring_client]\n        assert_equal {0 0 0} [unsubscribe $rd1 {foo bar quux}]\n\n        # clean up clients\n        $rd1 close\n    }\n\n    test \"PUBLISH/PSUBSCRIBE basics\" {\n        set rd1 [redis_deferring_client]\n\n        # subscribe to two patterns\n        assert_equal {1 2} [psubscribe $rd1 {foo.* bar.*}]\n        assert_equal 1 [r publish foo.1 hello]\n        assert_equal 1 [r publish bar.1 hello]\n        assert_equal 0 [r publish foo1 hello]\n        assert_equal 0 [r publish barfoo.1 hello]\n        assert_equal 0 [r publish qux.1 hello]\n        assert_equal {pmessage foo.* foo.1 hello} [$rd1 read]\n        assert_equal {pmessage bar.* bar.1 hello} [$rd1 read]\n\n        # unsubscribe from one of the patterns\n        assert_equal {1} [punsubscribe $rd1 {foo.*}]\n        assert_equal 0 [r publish foo.1 hello]\n        assert_equal 1 [r publish bar.1 hello]\n        assert_equal {pmessage bar.* bar.1 hello} [$rd1 read]\n\n        # unsubscribe from the remaining pattern\n        assert_equal {0} [punsubscribe $rd1 {bar.*}]\n        assert_equal 0 [r publish foo.1 hello]\n        assert_equal 0 [r publish bar.1 hello]\n\n        # clean up clients\n        $rd1 close\n    }\n\n    test \"PUBLISH/PSUBSCRIBE with two clients\" {\n        set rd1 [redis_deferring_client]\n        set rd2 [redis_deferring_client]\n\n        assert_equal {1} [psubscribe $rd1 {chan.*}]\n        assert_equal {1} [psubscribe $rd2 {chan.*}]\n        assert_equal 2 [r publish chan.foo hello]\n        assert_equal {pmessage chan.* chan.foo hello} [$rd1 read]\n        assert_equal {pmessage chan.* chan.foo hello} [$rd2 read]\n\n        # clean up clients\n        $rd1 close\n        $rd2 close\n    }\n\n    test \"PUBLISH/PSUBSCRIBE after PUNSUBSCRIBE without arguments\" {\n        set rd1 [redis_deferring_client]\n        assert_equal {1 2 3} [psubscribe $rd1 {chan1.* chan2.* chan3.*}]\n        punsubscribe $rd1\n        assert_equal 0 [r publish chan1.hi hello]\n        assert_equal 0 [r publish chan2.hi hello]\n        assert_equal 0 [r publish chan3.hi hello]\n\n        # clean up clients\n        $rd1 close\n    }\n\n    test \"PUNSUBSCRIBE from non-subscribed channels\" {\n        set rd1 [redis_deferring_client]\n        assert_equal {0 0 0} [punsubscribe $rd1 {foo.* bar.* quux.*}]\n\n        # clean up clients\n        $rd1 close\n    }\n\n    test \"Mix SUBSCRIBE and PSUBSCRIBE\" {\n        set rd1 [redis_deferring_client]\n        assert_equal {1} [subscribe $rd1 {foo.bar}]\n        assert_equal {2} [psubscribe $rd1 {foo.*}]\n\n        assert_equal 2 [r publish foo.bar hello]\n        assert_equal {message foo.bar hello} [$rd1 read]\n        assert_equal {pmessage foo.* foo.bar hello} [$rd1 read]\n\n        # clean up clients\n        $rd1 close\n    }\n\n    test \"PUNSUBSCRIBE and UNSUBSCRIBE should always reply\" {\n        # Make sure we are not subscribed to any channel at all.\n        r punsubscribe\n        r unsubscribe\n        # Now check if the commands still reply correctly.\n        set reply1 [r punsubscribe]\n        set reply2 [r unsubscribe]\n        concat $reply1 $reply2\n    } {punsubscribe {} 0 unsubscribe {} 0}\n\n    ### Keyspace events notification tests\n\n    test \"Keyspace notifications: we receive keyspace notifications\" {\n        r config set notify-keyspace-events KA\n        set rd1 [redis_deferring_client]\n        assert_equal {1} [psubscribe $rd1 *]\n        r set foo bar\n        assert_equal {pmessage * __keyspace@9__:foo set} [$rd1 read]\n        $rd1 close\n    }\n\n    test \"Keyspace notifications: we receive keyevent notifications\" {\n        r config set notify-keyspace-events EA\n        set rd1 [redis_deferring_client]\n        assert_equal {1} [psubscribe $rd1 *]\n        r set foo bar\n        assert_equal {pmessage * __keyevent@9__:set foo} [$rd1 read]\n        $rd1 close\n    }\n\n    test \"Keyspace notifications: we can receive both kind of events\" {\n        r config set notify-keyspace-events KEA\n        set rd1 [redis_deferring_client]\n        assert_equal {1} [psubscribe $rd1 *]\n        r set foo bar\n        assert_equal {pmessage * __keyspace@9__:foo set} [$rd1 read]\n        assert_equal {pmessage * __keyevent@9__:set foo} [$rd1 read]\n        $rd1 close\n    }\n\n    test \"Keyspace notifications: we are able to mask events\" {\n        r config set notify-keyspace-events KEl\n        r del mylist\n        set rd1 [redis_deferring_client]\n        assert_equal {1} [psubscribe $rd1 *]\n        r set foo bar\n        r lpush mylist a\n        # No notification for set, because only list commands are enabled.\n        assert_equal {pmessage * __keyspace@9__:mylist lpush} [$rd1 read]\n        assert_equal {pmessage * __keyevent@9__:lpush mylist} [$rd1 read]\n        $rd1 close\n    }\n\n    test \"Keyspace notifications: general events test\" {\n        r config set notify-keyspace-events KEg\n        set rd1 [redis_deferring_client]\n        assert_equal {1} [psubscribe $rd1 *]\n        r set foo bar\n        r expire foo 1\n        r del foo\n        assert_equal {pmessage * __keyspace@9__:foo expire} [$rd1 read]\n        assert_equal {pmessage * __keyevent@9__:expire foo} [$rd1 read]\n        assert_equal {pmessage * __keyspace@9__:foo del} [$rd1 read]\n        assert_equal {pmessage * __keyevent@9__:del foo} [$rd1 read]\n        $rd1 close\n    }\n\n    test \"Keyspace notifications: list events test\" {\n        r config set notify-keyspace-events KEl\n        r del mylist\n        set rd1 [redis_deferring_client]\n        assert_equal {1} [psubscribe $rd1 *]\n        r lpush mylist a\n        r rpush mylist a\n        r rpop mylist\n        assert_equal {pmessage * __keyspace@9__:mylist lpush} [$rd1 read]\n        assert_equal {pmessage * __keyevent@9__:lpush mylist} [$rd1 read]\n        assert_equal {pmessage * __keyspace@9__:mylist rpush} [$rd1 read]\n        assert_equal {pmessage * __keyevent@9__:rpush mylist} [$rd1 read]\n        assert_equal {pmessage * __keyspace@9__:mylist rpop} [$rd1 read]\n        assert_equal {pmessage * __keyevent@9__:rpop mylist} [$rd1 read]\n        $rd1 close\n    }\n\n    test \"Keyspace notifications: set events test\" {\n        r config set notify-keyspace-events Ks\n        r del myset\n        set rd1 [redis_deferring_client]\n        assert_equal {1} [psubscribe $rd1 *]\n        r sadd myset a b c d\n        r srem myset x\n        r sadd myset x y z\n        r srem myset x\n        assert_equal {pmessage * __keyspace@9__:myset sadd} [$rd1 read]\n        assert_equal {pmessage * __keyspace@9__:myset sadd} [$rd1 read]\n        assert_equal {pmessage * __keyspace@9__:myset srem} [$rd1 read]\n        $rd1 close\n    }\n\n    test \"Keyspace notifications: zset events test\" {\n        r config set notify-keyspace-events Kz\n        r del myzset\n        set rd1 [redis_deferring_client]\n        assert_equal {1} [psubscribe $rd1 *]\n        r zadd myzset 1 a 2 b\n        r zrem myzset x\n        r zadd myzset 3 x 4 y 5 z\n        r zrem myzset x\n        assert_equal {pmessage * __keyspace@9__:myzset zadd} [$rd1 read]\n        assert_equal {pmessage * __keyspace@9__:myzset zadd} [$rd1 read]\n        assert_equal {pmessage * __keyspace@9__:myzset zrem} [$rd1 read]\n        $rd1 close\n    }\n\n    test \"Keyspace notifications: hash events test\" {\n        r config set notify-keyspace-events Kh\n        r del myhash\n        set rd1 [redis_deferring_client]\n        assert_equal {1} [psubscribe $rd1 *]\n        r hmset myhash yes 1 no 0\n        r hincrby myhash yes 10\n        assert_equal {pmessage * __keyspace@9__:myhash hset} [$rd1 read]\n        assert_equal {pmessage * __keyspace@9__:myhash hincrby} [$rd1 read]\n        $rd1 close\n    }\n\n    test \"Keyspace notifications: expired events (triggered expire)\" {\n        r config set notify-keyspace-events Ex\n        r del foo\n        set rd1 [redis_deferring_client]\n        assert_equal {1} [psubscribe $rd1 *]\n        r psetex foo 100 1\n        wait_for_condition 50 100 {\n            [r exists foo] == 0\n        } else {\n            fail \"Key does not expire?!\"\n        }\n        assert_equal {pmessage * __keyevent@9__:expired foo} [$rd1 read]\n        $rd1 close\n    }\n\n    test \"Keyspace notifications: expired events (background expire)\" {\n        r config set notify-keyspace-events Ex\n        r del foo\n        set rd1 [redis_deferring_client]\n        assert_equal {1} [psubscribe $rd1 *]\n        r psetex foo 100 1\n        assert_equal {pmessage * __keyevent@9__:expired foo} [$rd1 read]\n        $rd1 close\n    }\n\n    test \"Keyspace notifications: evicted events\" {\n        r config set notify-keyspace-events Ee\n        r config set maxmemory-policy allkeys-lru\n        r flushdb\n        set rd1 [redis_deferring_client]\n        assert_equal {1} [psubscribe $rd1 *]\n        r set foo bar\n        r config set maxmemory 1\n        assert_equal {pmessage * __keyevent@9__:evicted foo} [$rd1 read]\n        r config set maxmemory 0\n        $rd1 close\n    }\n\n    test \"Keyspace notifications: test CONFIG GET/SET of event flags\" {\n        r config set notify-keyspace-events gKE\n        assert_equal {gKE} [lindex [r config get notify-keyspace-events] 1]\n        r config set notify-keyspace-events {$lshzxeKE}\n        assert_equal {$lshzxeKE} [lindex [r config get notify-keyspace-events] 1]\n        r config set notify-keyspace-events KA\n        assert_equal {AK} [lindex [r config get notify-keyspace-events] 1]\n        r config set notify-keyspace-events EA\n        assert_equal {AE} [lindex [r config get notify-keyspace-events] 1]\n    }\n}\n"
  },
  {
    "path": "tests/unit/quit.tcl",
    "content": "start_server {tags {\"quit\"}} {\n    proc format_command {args} {\n        set cmd \"*[llength $args]\\r\\n\"\n        foreach a $args {\n            append cmd \"$[string length $a]\\r\\n$a\\r\\n\"\n        }\n        set _ $cmd\n    }\n\n    test \"QUIT returns OK\" {\n        reconnect\n        assert_equal OK [r quit]\n        assert_error * {r ping}\n    }\n\n    test \"Pipelined commands after QUIT must not be executed\" {\n        reconnect\n        r write [format_command quit]\n        r write [format_command set foo bar]\n        r flush\n        assert_equal OK [r read]\n        assert_error * {r read}\n\n        reconnect\n        assert_equal {} [r get foo]\n    }\n\n    test \"Pipelined commands after QUIT that exceed read buffer size\" {\n        reconnect\n        r write [format_command quit]\n        r write [format_command set foo [string repeat \"x\" 1024]]\n        r flush\n        assert_equal OK [r read]\n        assert_error * {r read}\n\n        reconnect\n        assert_equal {} [r get foo]\n\n    }\n}\n"
  },
  {
    "path": "tests/unit/scan.tcl",
    "content": "start_server {tags {\"scan\"}} {\n    test \"SCAN basic\" {\n        r flushdb\n        r debug populate 1000\n\n        set cur 0\n        set keys {}\n        while 1 {\n            set res [r scan $cur]\n            set cur [lindex $res 0]\n            set k [lindex $res 1]\n            lappend keys {*}$k\n            if {$cur == 0} break\n        }\n\n        set keys [lsort -unique $keys]\n        assert_equal 1000 [llength $keys]\n    }\n\n    test \"SCAN COUNT\" {\n        r flushdb\n        r debug populate 1000\n\n        set cur 0\n        set keys {}\n        while 1 {\n            set res [r scan $cur count 5]\n            set cur [lindex $res 0]\n            set k [lindex $res 1]\n            lappend keys {*}$k\n            if {$cur == 0} break\n        }\n\n        set keys [lsort -unique $keys]\n        assert_equal 1000 [llength $keys]\n    }\n\n    test \"SCAN MATCH\" {\n        r flushdb\n        r debug populate 1000\n\n        set cur 0\n        set keys {}\n        while 1 {\n            set res [r scan $cur match \"key:1??\"]\n            set cur [lindex $res 0]\n            set k [lindex $res 1]\n            lappend keys {*}$k\n            if {$cur == 0} break\n        }\n\n        set keys [lsort -unique $keys]\n        assert_equal 100 [llength $keys]\n    }\n\n    foreach enc {intset hashtable} {\n        test \"SSCAN with encoding $enc\" {\n            # Create the Set\n            r del set\n            if {$enc eq {intset}} {\n                set prefix \"\"\n            } else {\n                set prefix \"ele:\"\n            }\n            set elements {}\n            for {set j 0} {$j < 100} {incr j} {\n                lappend elements ${prefix}${j}\n            }\n            r sadd set {*}$elements\n\n            # Verify that the encoding matches.\n            assert {[r object encoding set] eq $enc}\n\n            # Test SSCAN\n            set cur 0\n            set keys {}\n            while 1 {\n                set res [r sscan set $cur]\n                set cur [lindex $res 0]\n                set k [lindex $res 1]\n                lappend keys {*}$k\n                if {$cur == 0} break\n            }\n\n            set keys [lsort -unique $keys]\n            assert_equal 100 [llength $keys]\n        }\n    }\n\n    foreach enc {ziplist hashtable} {\n        test \"HSCAN with encoding $enc\" {\n            # Create the Hash\n            r del hash\n            if {$enc eq {ziplist}} {\n                set count 30\n            } else {\n                set count 1000\n            }\n            set elements {}\n            for {set j 0} {$j < $count} {incr j} {\n                lappend elements key:$j $j\n            }\n            r hmset hash {*}$elements\n\n            # Verify that the encoding matches.\n            assert {[r object encoding hash] eq $enc}\n\n            # Test HSCAN\n            set cur 0\n            set keys {}\n            while 1 {\n                set res [r hscan hash $cur]\n                set cur [lindex $res 0]\n                set k [lindex $res 1]\n                lappend keys {*}$k\n                if {$cur == 0} break\n            }\n\n            set keys2 {}\n            foreach {k v} $keys {\n                assert {$k eq \"key:$v\"}\n                lappend keys2 $k\n            }\n\n            set keys2 [lsort -unique $keys2]\n            assert_equal $count [llength $keys2]\n        }\n    }\n\n    foreach enc {ziplist skiplist} {\n        test \"ZSCAN with encoding $enc\" {\n            # Create the Sorted Set\n            r del zset\n            if {$enc eq {ziplist}} {\n                set count 30\n            } else {\n                set count 1000\n            }\n            set elements {}\n            for {set j 0} {$j < $count} {incr j} {\n                lappend elements $j key:$j\n            }\n            r zadd zset {*}$elements\n\n            # Verify that the encoding matches.\n            assert {[r object encoding zset] eq $enc}\n\n            # Test ZSCAN\n            set cur 0\n            set keys {}\n            while 1 {\n                set res [r zscan zset $cur]\n                set cur [lindex $res 0]\n                set k [lindex $res 1]\n                lappend keys {*}$k\n                if {$cur == 0} break\n            }\n\n            set keys2 {}\n            foreach {k v} $keys {\n                assert {$k eq \"key:$v\"}\n                lappend keys2 $k\n            }\n\n            set keys2 [lsort -unique $keys2]\n            assert_equal $count [llength $keys2]\n        }\n    }\n\n    test \"SCAN guarantees check under write load\" {\n        r flushdb\n        r debug populate 100\n\n        # We start scanning here, so keys from 0 to 99 should all be\n        # reported at the end of the iteration.\n        set keys {}\n        while 1 {\n            set res [r scan $cur]\n            set cur [lindex $res 0]\n            set k [lindex $res 1]\n            lappend keys {*}$k\n            if {$cur == 0} break\n            # Write 10 random keys at every SCAN iteration.\n            for {set j 0} {$j < 10} {incr j} {\n                r set addedkey:[randomInt 1000] foo\n            }\n        }\n\n        set keys2 {}\n        foreach k $keys {\n            if {[string length $k] > 6} continue\n            lappend keys2 $k\n        }\n\n        set keys2 [lsort -unique $keys2]\n        assert_equal 100 [llength $keys2]\n    }\n\n    test \"SSCAN with integer encoded object (issue #1345)\" {\n        set objects {1 a}\n        r del set\n        r sadd set {*}$objects\n        set res [r sscan set 0 MATCH *a* COUNT 100]\n        assert_equal [lsort -unique [lindex $res 1]] {a}\n        set res [r sscan set 0 MATCH *1* COUNT 100]\n        assert_equal [lsort -unique [lindex $res 1]] {1}\n    }\n\n    test \"SSCAN with PATTERN\" {\n        r del mykey\n        r sadd mykey foo fab fiz foobar 1 2 3 4\n        set res [r sscan mykey 0 MATCH foo* COUNT 10000]\n        lsort -unique [lindex $res 1]\n    } {foo foobar}\n\n    test \"HSCAN with PATTERN\" {\n        r del mykey\n        r hmset mykey foo 1 fab 2 fiz 3 foobar 10 1 a 2 b 3 c 4 d\n        set res [r hscan mykey 0 MATCH foo* COUNT 10000]\n        lsort -unique [lindex $res 1]\n    } {1 10 foo foobar}\n\n    test \"ZSCAN with PATTERN\" {\n        r del mykey\n        r zadd mykey 1 foo 2 fab 3 fiz 10 foobar\n        set res [r zscan mykey 0 MATCH foo* COUNT 10000]\n        lsort -unique [lindex $res 1]\n    }\n}\n"
  },
  {
    "path": "tests/unit/scripting.tcl",
    "content": "start_server {tags {\"scripting\"}} {\n    test {EVAL - Does Lua interpreter replies to our requests?} {\n        r eval {return 'hello'} 0\n    } {hello}\n\n    test {EVAL - Lua integer -> Redis protocol type conversion} {\n        r eval {return 100.5} 0\n    } {100}\n\n    test {EVAL - Lua string -> Redis protocol type conversion} {\n        r eval {return 'hello world'} 0\n    } {hello world}\n\n    test {EVAL - Lua true boolean -> Redis protocol type conversion} {\n        r eval {return true} 0\n    } {1}\n\n    test {EVAL - Lua false boolean -> Redis protocol type conversion} {\n        r eval {return false} 0\n    } {}\n\n    test {EVAL - Lua status code reply -> Redis protocol type conversion} {\n        r eval {return {ok='fine'}} 0\n    } {fine}\n\n    test {EVAL - Lua error reply -> Redis protocol type conversion} {\n        catch {\n            r eval {return {err='this is an error'}} 0\n        } e\n        set _ $e\n    } {this is an error}\n\n    test {EVAL - Lua table -> Redis protocol type conversion} {\n        r eval {return {1,2,3,'ciao',{1,2}}} 0\n    } {1 2 3 ciao {1 2}}\n\n    test {EVAL - Are the KEYS and ARGV arrays populated correctly?} {\n        r eval {return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}} 2 a b c d\n    } {a b c d}\n\n    test {EVAL - is Lua able to call Redis API?} {\n        r set mykey myval\n        r eval {return redis.call('get','mykey')} 0\n    } {myval}\n\n    test {EVALSHA - Can we call a SHA1 if already defined?} {\n        r evalsha 9bd632c7d33e571e9f24556ebed26c3479a87129 0\n    } {myval}\n\n    test {EVALSHA - Can we call a SHA1 in uppercase?} {\n        r evalsha 9BD632C7D33E571E9F24556EBED26C3479A87129 0\n    } {myval}\n\n    test {EVALSHA - Do we get an error on invalid SHA1?} {\n        catch {r evalsha NotValidShaSUM 0} e\n        set _ $e\n    } {NOSCRIPT*}\n\n    test {EVALSHA - Do we get an error on non defined SHA1?} {\n        catch {r evalsha ffd632c7d33e571e9f24556ebed26c3479a87130 0} e\n        set _ $e\n    } {NOSCRIPT*}\n\n    test {EVAL - Redis integer -> Lua type conversion} {\n        r eval {\n            local foo = redis.pcall('incr','x')\n            return {type(foo),foo}\n        } 0\n    } {number 1}\n\n    test {EVAL - Redis bulk -> Lua type conversion} {\n        r set mykey myval\n        r eval {\n            local foo = redis.pcall('get','mykey')\n            return {type(foo),foo}\n        } 0\n    } {string myval}\n\n    test {EVAL - Redis multi bulk -> Lua type conversion} {\n        r del mylist\n        r rpush mylist a\n        r rpush mylist b\n        r rpush mylist c\n        r eval {\n            local foo = redis.pcall('lrange','mylist',0,-1)\n            return {type(foo),foo[1],foo[2],foo[3],# foo}\n        } 0\n    } {table a b c 3}\n\n    test {EVAL - Redis status reply -> Lua type conversion} {\n        r eval {\n            local foo = redis.pcall('set','mykey','myval')\n            return {type(foo),foo['ok']}\n        } 0\n    } {table OK}\n\n    test {EVAL - Redis error reply -> Lua type conversion} {\n        r set mykey myval\n        r eval {\n            local foo = redis.pcall('incr','mykey')\n            return {type(foo),foo['err']}\n        } 0\n    } {table {ERR value is not an integer or out of range}}\n\n    test {EVAL - Redis nil bulk reply -> Lua type conversion} {\n        r del mykey\n        r eval {\n            local foo = redis.pcall('get','mykey')\n            return {type(foo),foo == false}\n        } 0\n    } {boolean 1}\n\n    test {EVAL - Is Lua affecting the currently selected DB?} {\n        r set mykey \"this is DB 9\"\n        r select 10\n        r set mykey \"this is DB 10\"\n        r eval {return redis.pcall('get','mykey')} 0\n    } {this is DB 10}\n\n    test {EVAL - Is Lua seleced DB retained?} {\n        r eval {return redis.pcall('select','9')} 0\n        r get mykey\n    } {this is DB 9}\n\n    if 0 {\n        test {EVAL - Script can't run more than configured time limit} {\n            r config set lua-time-limit 1\n            catch {\n                r eval {\n                    local i = 0\n                    while true do i=i+1 end\n                } 0\n            } e\n            set _ $e\n        } {*execution time*}\n    }\n\n    test {EVAL - Scripts can't run certain commands} {\n        set e {}\n        catch {r eval {return redis.pcall('spop','x')} 0} e\n        set e\n    } {*not allowed*}\n\n    test {EVAL - Scripts can't run certain commands} {\n        set e {}\n        catch {\n            r eval \"redis.pcall('randomkey'); return redis.pcall('set','x','ciao')\" 0\n        } e\n        set e\n    } {*not allowed after*}\n\n    test {EVAL - No arguments to redis.call/pcall is considered an error} {\n        set e {}\n        catch {r eval {return redis.call()} 0} e\n        set e\n    } {*one argument*}\n\n    test {EVAL - redis.call variant raises a Lua error on Redis cmd error (1)} {\n        set e {}\n        catch {\n            r eval \"redis.call('nosuchcommand')\" 0\n        } e\n        set e\n    } {*Unknown Redis*}\n\n    test {EVAL - redis.call variant raises a Lua error on Redis cmd error (1)} {\n        set e {}\n        catch {\n            r eval \"redis.call('get','a','b','c')\" 0\n        } e\n        set e\n    } {*number of args*}\n\n    test {EVAL - redis.call variant raises a Lua error on Redis cmd error (1)} {\n        set e {}\n        r set foo bar\n        catch {\n            r eval \"redis.call('lpush','foo','val')\" 0\n        } e\n        set e\n    } {*against a key*}\n\n    test {SCRIPTING FLUSH - is able to clear the scripts cache?} {\n        r set mykey myval\n        set v [r evalsha 9bd632c7d33e571e9f24556ebed26c3479a87129 0]\n        assert_equal $v myval\n        set e \"\"\n        r script flush\n        catch {r evalsha 9bd632c7d33e571e9f24556ebed26c3479a87129 0} e\n        set e\n    } {NOSCRIPT*}\n\n    test {SCRIPT EXISTS - can detect already defined scripts?} {\n        r eval \"return 1+1\" 0\n        r script exists a27e7e8a43702b7046d4f6a7ccf5b60cef6b9bd9 a27e7e8a43702b7046d4f6a7ccf5b60cef6b9bda\n    } {1 0}\n\n    test {SCRIPT LOAD - is able to register scripts in the scripting cache} {\n        list \\\n            [r script load \"return 'loaded'\"] \\\n            [r evalsha b534286061d4b9e4026607613b95c06c06015ae8 0]\n    } {b534286061d4b9e4026607613b95c06c06015ae8 loaded}\n\n    test \"In the context of Lua the output of random commands gets ordered\" {\n        r del myset\n        r sadd myset a b c d e f g h i l m n o p q r s t u v z aa aaa azz\n        r eval {return redis.call('smembers','myset')} 0\n    } {a aa aaa azz b c d e f g h i l m n o p q r s t u v z}\n\n    test \"SORT is normally not alpha re-ordered for the scripting engine\" {\n        r del myset\n        r sadd myset 1 2 3 4 10\n        r eval {return redis.call('sort','myset','desc')} 0\n    } {10 4 3 2 1}\n\n    test \"SORT BY <constant> output gets ordered for scripting\" {\n        r del myset\n        r sadd myset a b c d e f g h i l m n o p q r s t u v z aa aaa azz\n        r eval {return redis.call('sort','myset','by','_')} 0\n    } {a aa aaa azz b c d e f g h i l m n o p q r s t u v z}\n\n    test \"SORT BY <constant> with GET gets ordered for scripting\" {\n        r del myset\n        r sadd myset a b c\n        r eval {return redis.call('sort','myset','by','_','get','#','get','_:*')} 0\n    } {a {} b {} c {}}\n\n    test \"redis.sha1hex() implementation\" {\n        list [r eval {return redis.sha1hex('')} 0] \\\n             [r eval {return redis.sha1hex('Pizza & Mandolino')} 0]\n    } {da39a3ee5e6b4b0d3255bfef95601890afd80709 74822d82031af7493c20eefa13bd07ec4fada82f}\n\n    test {Globals protection reading an undeclared global variable} {\n        catch {r eval {return a} 0} e\n        set e\n    } {*ERR*attempted to access unexisting global*}\n\n    test {Globals protection setting an undeclared global*} {\n        catch {r eval {a=10} 0} e\n        set e\n    } {*ERR*attempted to create global*}\n\n    test {Test an example script DECR_IF_GT} {\n        set decr_if_gt {\n            local current\n\n            current = redis.call('get',KEYS[1])\n            if not current then return nil end\n            if current > ARGV[1] then\n                return redis.call('decr',KEYS[1])\n            else\n                return redis.call('get',KEYS[1])\n            end\n        }\n        r set foo 5\n        set res {}\n        lappend res [r eval $decr_if_gt 1 foo 2]\n        lappend res [r eval $decr_if_gt 1 foo 2]\n        lappend res [r eval $decr_if_gt 1 foo 2]\n        lappend res [r eval $decr_if_gt 1 foo 2]\n        lappend res [r eval $decr_if_gt 1 foo 2]\n        set res\n    } {4 3 2 2 2}\n\n    test {Scripting engine resets PRNG at every script execution} {\n        set rand1 [r eval {return tostring(math.random())} 0]\n        set rand2 [r eval {return tostring(math.random())} 0]\n        assert_equal $rand1 $rand2\n    }\n\n    test {Scripting engine PRNG can be seeded correctly} {\n        set rand1 [r eval {\n            math.randomseed(ARGV[1]); return tostring(math.random())\n        } 0 10]\n        set rand2 [r eval {\n            math.randomseed(ARGV[1]); return tostring(math.random())\n        } 0 10]\n        set rand3 [r eval {\n            math.randomseed(ARGV[1]); return tostring(math.random())\n        } 0 20]\n        assert_equal $rand1 $rand2\n        assert {$rand2 ne $rand3}\n    }\n\n    test {EVAL does not leak in the Lua stack} {\n        r set x 0\n        # Use a non blocking client to speedup the loop.\n        set rd [redis_deferring_client]\n        for {set j 0} {$j < 10000} {incr j} {\n            $rd eval {return redis.call(\"incr\",KEYS[1])} 1 x\n        }\n        for {set j 0} {$j < 10000} {incr j} {\n            $rd read\n        }\n        assert {[s used_memory_lua] < 1024*100}\n        $rd close\n        r get x\n    } {10000}\n\n    test {EVAL processes writes from AOF in read-only slaves} {\n        r flushall\n        r config set appendonly yes\n        r eval {redis.call(\"set\",\"foo\",\"100\")} 0\n        r eval {redis.call(\"incr\",\"foo\")} 0\n        r eval {redis.call(\"incr\",\"foo\")} 0\n        wait_for_condition 50 100 {\n            [s aof_rewrite_in_progress] == 0\n        } else {\n            fail \"AOF rewrite can't complete after CONFIG SET appendonly yes.\"\n        }\n        r config set slave-read-only yes\n        r slaveof 127.0.0.1 0\n        r debug loadaof\n        set res [r get foo]\n        r slaveof no one\n        set res\n    } {102}\n\n    test {We can call scripts rewriting client->argv from Lua} {\n        r del myset\n        r sadd myset a b c\n        r mset a 1 b 2 c 3 d 4\n        assert {[r spop myset] ne {}}\n        assert {[r spop myset] ne {}}\n        assert {[r spop myset] ne {}}\n        assert {[r mget a b c d] eq {1 2 3 4}}\n        assert {[r spop myset] eq {}}\n    }\n}\n\n# Start a new server since the last test in this stanza will kill the\n# instance at all.\nstart_server {tags {\"scripting\"}} {\n    test {Timedout read-only scripts can be killed by SCRIPT KILL} {\n        set rd [redis_deferring_client]\n        r config set lua-time-limit 10\n        $rd eval {while true do end} 0\n        after 200\n        catch {r ping} e\n        assert_match {BUSY*} $e\n        r script kill\n        after 200 ; # Give some time to Lua to call the hook again...\n        assert_equal [r ping] \"PONG\"\n    }\n\n    test {Timedout script link is still usable after Lua returns} {\n        r config set lua-time-limit 10\n        r eval {for i=1,100000 do redis.call('ping') end return 'ok'} 0\n        r ping\n    } {PONG}\n\n    test {Timedout scripts that modified data can't be killed by SCRIPT KILL} {\n        set rd [redis_deferring_client]\n        r config set lua-time-limit 10\n        $rd eval {redis.call('set','x','y'); while true do end} 0\n        after 200\n        catch {r ping} e\n        assert_match {BUSY*} $e\n        catch {r script kill} e\n        assert_match {UNKILLABLE*} $e\n        catch {r ping} e\n        assert_match {BUSY*} $e\n    }\n\n    # Note: keep this test at the end of this server stanza because it\n    # kills the server.\n    test {SHUTDOWN NOSAVE can kill a timedout script anyway} {\n        # The server sould be still unresponding to normal commands.\n        catch {r ping} e\n        assert_match {BUSY*} $e\n        catch {r shutdown nosave}\n        # Make sure the server was killed\n        catch {set rd [redis_deferring_client]} e\n        assert_match {*connection refused*} $e\n    }\n}\n\nstart_server {tags {\"scripting repl\"}} {\n    start_server {} {\n        test {Before the slave connects we issue two EVAL commands} {\n            # One with an error, but still executing a command.\n            # SHA is: 6e8bd6bdccbe78899e3cc06b31b6dbf4324c2e56\n            catch {\n                r eval {redis.call('incr','x'); redis.call('nonexisting')} 0\n            }\n            # One command is correct:\n            # SHA is: ae3477e27be955de7e1bc9adfdca626b478d3cb2\n            r eval {return redis.call('incr','x')} 0\n        } {2}\n\n        test {Connect a slave to the main instance} {\n            r -1 slaveof [srv 0 host] [srv 0 port]\n            wait_for_condition 50 100 {\n                [s -1 role] eq {slave} &&\n                [string match {*master_link_status:up*} [r -1 info replication]]\n            } else {\n                fail \"Can't turn the instance into a slave\"\n            }\n        }\n\n        test {Now use EVALSHA against the master, with both SHAs} {\n            # The server should replicate successful and unsuccessful\n            # commands as EVAL instead of EVALSHA.\n            catch {\n                r evalsha 6e8bd6bdccbe78899e3cc06b31b6dbf4324c2e56 0\n            }\n            r evalsha ae3477e27be955de7e1bc9adfdca626b478d3cb2 0\n        } {4}\n\n        test {If EVALSHA was replicated as EVAL, 'x' should be '4'} {\n            wait_for_condition 50 100 {\n                [r -1 get x] eq {4}\n            } else {\n                fail \"Expected 4 in x, but value is '[r -1 get x]'\"\n            }\n        }\n\n        test {Replication of script multiple pushes to list with BLPOP} {\n            set rd [redis_deferring_client]\n            $rd brpop a 0\n            r eval {\n                redis.call(\"lpush\",\"a\",\"1\");\n                redis.call(\"lpush\",\"a\",\"2\");\n            } 0\n            set res [$rd read]\n            $rd close\n            wait_for_condition 50 100 {\n                [r -1 lrange a 0 -1] eq [r lrange a 0 -1]\n            } else {\n                fail \"Expected list 'a' in slave and master to be the same, but they are respectively '[r -1 lrange a 0 -1]' and '[r lrange a 0 -1]'\"\n            }\n            set res\n        } {a 1}\n\n        test {EVALSHA replication when first call is readonly} {\n            r del x\n            r eval {if tonumber(KEYS[1]) > 0 then redis.call('incr', 'x') end} 1 0\n            r evalsha 38fe3ddf5284a1d48f37f824b4c4e826879f3cb9 1 0\n            r evalsha 38fe3ddf5284a1d48f37f824b4c4e826879f3cb9 1 1\n            wait_for_condition 50 100 {\n                [r -1 get x] eq {1}\n            } else {\n                fail \"Expected 1 in x, but value is '[r -1 get x]'\"\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "tests/unit/slowlog.tcl",
    "content": "start_server {tags {\"slowlog\"} overrides {slowlog-log-slower-than 1000000}} {\n    test {SLOWLOG - check that it starts with an empty log} {\n        r slowlog len\n    } {0}\n\n    test {SLOWLOG - only logs commands taking more time than specified} {\n        r config set slowlog-log-slower-than 100000\n        r ping\n        assert_equal [r slowlog len] 0\n        r debug sleep 0.2\n        assert_equal [r slowlog len] 1\n    }\n\n    test {SLOWLOG - max entries is correctly handled} {\n        r config set slowlog-log-slower-than 0\n        r config set slowlog-max-len 10\n        for {set i 0} {$i < 100} {incr i} {\n            r ping\n        }\n        r slowlog len\n    } {10}\n\n    test {SLOWLOG - GET optional argument to limit output len works} {\n        llength [r slowlog get 5]\n    } {5}\n\n    test {SLOWLOG - RESET subcommand works} {\n        r config set slowlog-log-slower-than 100000\n        r slowlog reset\n        r slowlog len\n    } {0}\n\n    test {SLOWLOG - logged entry sanity check} {\n        r debug sleep 0.2\n        set e [lindex [r slowlog get] 0]\n        assert_equal [llength $e] 4\n        assert_equal [lindex $e 0] 105\n        assert_equal [expr {[lindex $e 2] > 100000}] 1\n        assert_equal [lindex $e 3] {debug sleep 0.2}\n    }\n\n    test {SLOWLOG - commands with too many arguments are trimmed} {\n        r config set slowlog-log-slower-than 0\n        r slowlog reset\n        r sadd set 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33\n        set e [lindex [r slowlog get] 0]\n        lindex $e 3\n    } {sadd set 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 {... (2 more arguments)}}\n\n    test {SLOWLOG - too long arguments are trimmed} {\n        r config set slowlog-log-slower-than 0\n        r slowlog reset\n        set arg [string repeat A 129]\n        r sadd set foo $arg\n        set e [lindex [r slowlog get] 0]\n        lindex $e 3\n    } {sadd set foo {AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA... (1 more bytes)}}\n\n    test {SLOWLOG - EXEC is not logged, just executed commands} {\n        r config set slowlog-log-slower-than 100000\n        r slowlog reset\n        assert_equal [r slowlog len] 0\n        r multi\n        r debug sleep 0.2\n        r exec\n        assert_equal [r slowlog len] 1\n        set e [lindex [r slowlog get] 0]\n        assert_equal [lindex $e 3] {debug sleep 0.2}\n    }\n}\n"
  },
  {
    "path": "tests/unit/sort.tcl",
    "content": "start_server {\n    tags {\"sort\"}\n    overrides {\n        \"list-max-ziplist-value\" 16\n        \"list-max-ziplist-entries\" 32\n        \"set-max-intset-entries\" 32\n    }\n} {\n    proc create_random_dataset {num cmd} {\n        set tosort {}\n        set result {}\n        array set seenrand {}\n        r del tosort\n        for {set i 0} {$i < $num} {incr i} {\n            # Make sure all the weights are different because\n            # Redis does not use a stable sort but Tcl does.\n            while 1 {\n                randpath {\n                    set rint [expr int(rand()*1000000)]\n                } {\n                    set rint [expr rand()]\n                }\n                if {![info exists seenrand($rint)]} break\n            }\n            set seenrand($rint) x\n            r $cmd tosort $i\n            r set weight_$i $rint\n            r hset wobj_$i weight $rint\n            lappend tosort [list $i $rint]\n        }\n        set sorted [lsort -index 1 -real $tosort]\n        for {set i 0} {$i < $num} {incr i} {\n            lappend result [lindex $sorted $i 0]\n        }\n        set _ $result\n    }\n\n    foreach {num cmd enc title} {\n        16 lpush ziplist \"Ziplist\"\n        1000 lpush linkedlist \"Linked list\"\n        10000 lpush linkedlist \"Big Linked list\"\n        16 sadd intset \"Intset\"\n        1000 sadd hashtable \"Hash table\"\n        10000 sadd hashtable \"Big Hash table\"\n    } {\n        set result [create_random_dataset $num $cmd]\n        assert_encoding $enc tosort\n\n        test \"$title: SORT BY key\" {\n            assert_equal $result [r sort tosort BY weight_*]\n        }\n\n        test \"$title: SORT BY key with limit\" {\n            assert_equal [lrange $result 5 9] [r sort tosort BY weight_* LIMIT 5 5]\n        }\n\n        test \"$title: SORT BY hash field\" {\n            assert_equal $result [r sort tosort BY wobj_*->weight]\n        }\n    }\n\n    set result [create_random_dataset 16 lpush]\n    test \"SORT GET #\" {\n        assert_equal [lsort -integer $result] [r sort tosort GET #]\n    }\n\n    test \"SORT GET <const>\" {\n        r del foo\n        set res [r sort tosort GET foo]\n        assert_equal 16 [llength $res]\n        foreach item $res { assert_equal {} $item }\n    }\n\n    test \"SORT GET (key and hash) with sanity check\" {\n        set l1 [r sort tosort GET # GET weight_*]\n        set l2 [r sort tosort GET # GET wobj_*->weight]\n        foreach {id1 w1} $l1 {id2 w2} $l2 {\n            assert_equal $id1 $id2\n            assert_equal $w1 [r get weight_$id1]\n            assert_equal $w2 [r get weight_$id1]\n        }\n    }\n\n    test \"SORT BY key STORE\" {\n        r sort tosort BY weight_* store sort-res\n        assert_equal $result [r lrange sort-res 0 -1]\n        assert_equal 16 [r llen sort-res]\n        assert_encoding ziplist sort-res\n    }\n\n    test \"SORT BY hash field STORE\" {\n        r sort tosort BY wobj_*->weight store sort-res\n        assert_equal $result [r lrange sort-res 0 -1]\n        assert_equal 16 [r llen sort-res]\n        assert_encoding ziplist sort-res\n    }\n\n    test \"SORT DESC\" {\n        assert_equal [lsort -decreasing -integer $result] [r sort tosort DESC]\n    }\n\n    test \"SORT ALPHA against integer encoded strings\" {\n        r del mylist\n        r lpush mylist 2\n        r lpush mylist 1\n        r lpush mylist 3\n        r lpush mylist 10\n        r sort mylist alpha\n    } {1 10 2 3}\n\n    test \"SORT sorted set\" {\n        r del zset\n        r zadd zset 1 a\n        r zadd zset 5 b\n        r zadd zset 2 c\n        r zadd zset 10 d\n        r zadd zset 3 e\n        r sort zset alpha desc\n    } {e d c b a}\n\n    test \"SORT sorted set BY nosort should retain ordering\" {\n        r del zset\n        r zadd zset 1 a\n        r zadd zset 5 b\n        r zadd zset 2 c\n        r zadd zset 10 d\n        r zadd zset 3 e\n        r multi\n        r sort zset by nosort asc\n        r sort zset by nosort desc\n        r exec\n    } {{a c e b d} {d b e c a}}\n\n    test \"SORT sorted set BY nosort + LIMIT\" {\n        r del zset\n        r zadd zset 1 a\n        r zadd zset 5 b\n        r zadd zset 2 c\n        r zadd zset 10 d\n        r zadd zset 3 e\n        assert_equal [r sort zset by nosort asc limit 0 1] {a}\n        assert_equal [r sort zset by nosort desc limit 0 1] {d}\n        assert_equal [r sort zset by nosort asc limit 0 2] {a c}\n        assert_equal [r sort zset by nosort desc limit 0 2] {d b}\n        assert_equal [r sort zset by nosort limit 5 10] {}\n        assert_equal [r sort zset by nosort limit -10 100] {a c e b d}\n    }\n\n    test \"SORT sorted set BY nosort works as expected from scripts\" {\n        r del zset\n        r zadd zset 1 a\n        r zadd zset 5 b\n        r zadd zset 2 c\n        r zadd zset 10 d\n        r zadd zset 3 e\n        r eval {\n            return {redis.call('sort','zset','by','nosort','asc'),\n                    redis.call('sort','zset','by','nosort','desc')}\n        } 0\n    } {{a c e b d} {d b e c a}}\n\n    test \"SORT sorted set: +inf and -inf handling\" {\n        r del zset\n        r zadd zset -100 a\n        r zadd zset 200 b\n        r zadd zset -300 c\n        r zadd zset 1000000 d\n        r zadd zset +inf max\n        r zadd zset -inf min\n        r zrange zset 0 -1\n    } {min c a b d max}\n\n    test \"SORT regression for issue #19, sorting floats\" {\n        r flushdb\n        set floats {1.1 5.10 3.10 7.44 2.1 5.75 6.12 0.25 1.15}\n        foreach x $floats {\n            r lpush mylist $x\n        }\n        assert_equal [lsort -real $floats] [r sort mylist]\n    }\n\n    test \"SORT with STORE returns zero if result is empty (github isse 224)\" {\n        r flushdb\n        r sort foo store bar\n    } {0}\n\n    test \"SORT with STORE does not create empty lists (github issue 224)\" {\n        r flushdb\n        r lpush foo bar\n        r sort foo alpha limit 10 10 store zap\n        r exists zap\n    } {0}\n\n    test \"SORT with STORE removes key if result is empty (github issue 227)\" {\n        r flushdb\n        r lpush foo bar\n        r sort emptylist store foo\n        r exists foo\n    } {0}\n\n    test \"SORT with BY <constant> and STORE should still order output\" {\n        r del myset mylist\n        r sadd myset a b c d e f g h i l m n o p q r s t u v z aa aaa azz\n        r sort myset alpha by _ store mylist\n        r lrange mylist 0 -1\n    } {a aa aaa azz b c d e f g h i l m n o p q r s t u v z}\n\n    test \"SORT will complain with numerical sorting and bad doubles (1)\" {\n        r del myset\n        r sadd myset 1 2 3 4 not-a-double\n        set e {}\n        catch {r sort myset} e\n        set e\n    } {*ERR*double*}\n\n    test \"SORT will complain with numerical sorting and bad doubles (2)\" {\n        r del myset\n        r sadd myset 1 2 3 4\n        r mset score:1 10 score:2 20 score:3 30 score:4 not-a-double\n        set e {}\n        catch {r sort myset by score:*} e\n        set e\n    } {*ERR*double*}\n\n    test \"SORT BY sub-sorts lexicographically if score is the same\" {\n        r del myset\n        r sadd myset a b c d e f g h i l m n o p q r s t u v z aa aaa azz\n        foreach ele {a aa aaa azz b c d e f g h i l m n o p q r s t u v z} {\n            set score:$ele 100\n        }\n        r sort myset by score:*\n    } {a aa aaa azz b c d e f g h i l m n o p q r s t u v z}\n\n    test \"SORT GET with pattern ending with just -> does not get hash field\" {\n        r del mylist\n        r lpush mylist a\n        r set x:a-> 100\n        r sort mylist by num get x:*->\n    } {100}\n\n    tags {\"slow\"} {\n        set num 100\n        set res [create_random_dataset $num lpush]\n\n        test \"SORT speed, $num element list BY key, 100 times\" {\n            set start [clock clicks -milliseconds]\n            for {set i 0} {$i < 100} {incr i} {\n                set sorted [r sort tosort BY weight_* LIMIT 0 10]\n            }\n            set elapsed [expr [clock clicks -milliseconds]-$start]\n            if {$::verbose} {\n                puts -nonewline \"\\n  Average time to sort: [expr double($elapsed)/100] milliseconds \"\n                flush stdout\n            }\n        }\n\n        test \"SORT speed, $num element list BY hash field, 100 times\" {\n            set start [clock clicks -milliseconds]\n            for {set i 0} {$i < 100} {incr i} {\n                set sorted [r sort tosort BY wobj_*->weight LIMIT 0 10]\n            }\n            set elapsed [expr [clock clicks -milliseconds]-$start]\n            if {$::verbose} {\n                puts -nonewline \"\\n  Average time to sort: [expr double($elapsed)/100] milliseconds \"\n                flush stdout\n            }\n        }\n\n        test \"SORT speed, $num element list directly, 100 times\" {\n            set start [clock clicks -milliseconds]\n            for {set i 0} {$i < 100} {incr i} {\n                set sorted [r sort tosort LIMIT 0 10]\n            }\n            set elapsed [expr [clock clicks -milliseconds]-$start]\n            if {$::verbose} {\n                puts -nonewline \"\\n  Average time to sort: [expr double($elapsed)/100] milliseconds \"\n                flush stdout\n            }\n        }\n\n        test \"SORT speed, $num element list BY <const>, 100 times\" {\n            set start [clock clicks -milliseconds]\n            for {set i 0} {$i < 100} {incr i} {\n                set sorted [r sort tosort BY nokey LIMIT 0 10]\n            }\n            set elapsed [expr [clock clicks -milliseconds]-$start]\n            if {$::verbose} {\n                puts -nonewline \"\\n  Average time to sort: [expr double($elapsed)/100] milliseconds \"\n                flush stdout\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "tests/unit/type/hash.tcl",
    "content": "start_server {tags {\"hash\"}} {\n    test {HSET/HLEN - Small hash creation} {\n        array set smallhash {}\n        for {set i 0} {$i < 8} {incr i} {\n            set key [randstring 0 8 alpha]\n            set val [randstring 0 8 alpha]\n            if {[info exists smallhash($key)]} {\n                incr i -1\n                continue\n            }\n            r hset smallhash $key $val\n            set smallhash($key) $val\n        }\n        list [r hlen smallhash]\n    } {8}\n\n    test {Is the small hash encoded with a ziplist?} {\n        assert_encoding ziplist smallhash\n    }\n\n    test {HSET/HLEN - Big hash creation} {\n        array set bighash {}\n        for {set i 0} {$i < 1024} {incr i} {\n            set key [randstring 0 8 alpha]\n            set val [randstring 0 8 alpha]\n            if {[info exists bighash($key)]} {\n                incr i -1\n                continue\n            }\n            r hset bighash $key $val\n            set bighash($key) $val\n        }\n        list [r hlen bighash]\n    } {1024}\n\n    test {Is the big hash encoded with a ziplist?} {\n        assert_encoding hashtable bighash\n    }\n\n    test {HGET against the small hash} {\n        set err {}\n        foreach k [array names smallhash *] {\n            if {$smallhash($k) ne [r hget smallhash $k]} {\n                set err \"$smallhash($k) != [r hget smallhash $k]\"\n                break\n            }\n        }\n        set _ $err\n    } {}\n\n    test {HGET against the big hash} {\n        set err {}\n        foreach k [array names bighash *] {\n            if {$bighash($k) ne [r hget bighash $k]} {\n                set err \"$bighash($k) != [r hget bighash $k]\"\n                break\n            }\n        }\n        set _ $err\n    } {}\n\n    test {HGET against non existing key} {\n        set rv {}\n        lappend rv [r hget smallhash __123123123__]\n        lappend rv [r hget bighash __123123123__]\n        set _ $rv\n    } {{} {}}\n\n    test {HSET in update and insert mode} {\n        set rv {}\n        set k [lindex [array names smallhash *] 0]\n        lappend rv [r hset smallhash $k newval1]\n        set smallhash($k) newval1\n        lappend rv [r hget smallhash $k]\n        lappend rv [r hset smallhash __foobar123__ newval]\n        set k [lindex [array names bighash *] 0]\n        lappend rv [r hset bighash $k newval2]\n        set bighash($k) newval2\n        lappend rv [r hget bighash $k]\n        lappend rv [r hset bighash __foobar123__ newval]\n        lappend rv [r hdel smallhash __foobar123__]\n        lappend rv [r hdel bighash __foobar123__]\n        set _ $rv\n    } {0 newval1 1 0 newval2 1 1 1}\n\n    test {HSETNX target key missing - small hash} {\n        r hsetnx smallhash __123123123__ foo\n        r hget smallhash __123123123__\n    } {foo}\n\n    test {HSETNX target key exists - small hash} {\n        r hsetnx smallhash __123123123__ bar\n        set result [r hget smallhash __123123123__]\n        r hdel smallhash __123123123__\n        set _ $result\n    } {foo}\n\n    test {HSETNX target key missing - big hash} {\n        r hsetnx bighash __123123123__ foo\n        r hget bighash __123123123__\n    } {foo}\n\n    test {HSETNX target key exists - big hash} {\n        r hsetnx bighash __123123123__ bar\n        set result [r hget bighash __123123123__]\n        r hdel bighash __123123123__\n        set _ $result\n    } {foo}\n\n    test {HMSET wrong number of args} {\n        catch {r hmset smallhash key1 val1 key2} err\n        format $err\n    } {*wrong number*}\n\n    test {HMSET - small hash} {\n        set args {}\n        foreach {k v} [array get smallhash] {\n            set newval [randstring 0 8 alpha]\n            set smallhash($k) $newval\n            lappend args $k $newval\n        }\n        r hmset smallhash {*}$args\n    } {OK}\n\n    test {HMSET - big hash} {\n        set args {}\n        foreach {k v} [array get bighash] {\n            set newval [randstring 0 8 alpha]\n            set bighash($k) $newval\n            lappend args $k $newval\n        }\n        r hmset bighash {*}$args\n    } {OK}\n\n    test {HMGET against non existing key and fields} {\n        set rv {}\n        lappend rv [r hmget doesntexist __123123123__ __456456456__]\n        lappend rv [r hmget smallhash __123123123__ __456456456__]\n        lappend rv [r hmget bighash __123123123__ __456456456__]\n        set _ $rv\n    } {{{} {}} {{} {}} {{} {}}}\n\n    test {HMGET against wrong type} {\n        r set wrongtype somevalue\n        assert_error \"*wrong*\" {r hmget wrongtype field1 field2}\n    }\n\n    test {HMGET - small hash} {\n        set keys {}\n        set vals {}\n        foreach {k v} [array get smallhash] {\n            lappend keys $k\n            lappend vals $v\n        }\n        set err {}\n        set result [r hmget smallhash {*}$keys]\n        if {$vals ne $result} {\n            set err \"$vals != $result\"\n            break\n        }\n        set _ $err\n    } {}\n\n    test {HMGET - big hash} {\n        set keys {}\n        set vals {}\n        foreach {k v} [array get bighash] {\n            lappend keys $k\n            lappend vals $v\n        }\n        set err {}\n        set result [r hmget bighash {*}$keys]\n        if {$vals ne $result} {\n            set err \"$vals != $result\"\n            break\n        }\n        set _ $err\n    } {}\n\n    test {HKEYS - small hash} {\n        lsort [r hkeys smallhash]\n    } [lsort [array names smallhash *]]\n\n    test {HKEYS - big hash} {\n        lsort [r hkeys bighash]\n    } [lsort [array names bighash *]]\n\n    test {HVALS - small hash} {\n        set vals {}\n        foreach {k v} [array get smallhash] {\n            lappend vals $v\n        }\n        set _ [lsort $vals]\n    } [lsort [r hvals smallhash]]\n\n    test {HVALS - big hash} {\n        set vals {}\n        foreach {k v} [array get bighash] {\n            lappend vals $v\n        }\n        set _ [lsort $vals]\n    } [lsort [r hvals bighash]]\n\n    test {HGETALL - small hash} {\n        lsort [r hgetall smallhash]\n    } [lsort [array get smallhash]]\n\n    test {HGETALL - big hash} {\n        lsort [r hgetall bighash]\n    } [lsort [array get bighash]]\n\n    test {HDEL and return value} {\n        set rv {}\n        lappend rv [r hdel smallhash nokey]\n        lappend rv [r hdel bighash nokey]\n        set k [lindex [array names smallhash *] 0]\n        lappend rv [r hdel smallhash $k]\n        lappend rv [r hdel smallhash $k]\n        lappend rv [r hget smallhash $k]\n        unset smallhash($k)\n        set k [lindex [array names bighash *] 0]\n        lappend rv [r hdel bighash $k]\n        lappend rv [r hdel bighash $k]\n        lappend rv [r hget bighash $k]\n        unset bighash($k)\n        set _ $rv\n    } {0 0 1 0 {} 1 0 {}}\n\n    test {HDEL - more than a single value} {\n        set rv {}\n        r del myhash\n        r hmset myhash a 1 b 2 c 3\n        assert_equal 0 [r hdel myhash x y]\n        assert_equal 2 [r hdel myhash a c f]\n        r hgetall myhash\n    } {b 2}\n\n    test {HDEL - hash becomes empty before deleting all specified fields} {\n        r del myhash\n        r hmset myhash a 1 b 2 c 3\n        assert_equal 3 [r hdel myhash a b c d e]\n        assert_equal 0 [r exists myhash]\n    }\n\n    test {HEXISTS} {\n        set rv {}\n        set k [lindex [array names smallhash *] 0]\n        lappend rv [r hexists smallhash $k]\n        lappend rv [r hexists smallhash nokey]\n        set k [lindex [array names bighash *] 0]\n        lappend rv [r hexists bighash $k]\n        lappend rv [r hexists bighash nokey]\n    } {1 0 1 0}\n\n    test {Is a ziplist encoded Hash promoted on big payload?} {\n        r hset smallhash foo [string repeat a 1024]\n        r debug object smallhash\n    } {*hashtable*}\n\n    test {HINCRBY against non existing database key} {\n        r del htest\n        list [r hincrby htest foo 2]\n    } {2}\n\n    test {HINCRBY against non existing hash key} {\n        set rv {}\n        r hdel smallhash tmp\n        r hdel bighash tmp\n        lappend rv [r hincrby smallhash tmp 2]\n        lappend rv [r hget smallhash tmp]\n        lappend rv [r hincrby bighash tmp 2]\n        lappend rv [r hget bighash tmp]\n    } {2 2 2 2}\n\n    test {HINCRBY against hash key created by hincrby itself} {\n        set rv {}\n        lappend rv [r hincrby smallhash tmp 3]\n        lappend rv [r hget smallhash tmp]\n        lappend rv [r hincrby bighash tmp 3]\n        lappend rv [r hget bighash tmp]\n    } {5 5 5 5}\n\n    test {HINCRBY against hash key originally set with HSET} {\n        r hset smallhash tmp 100\n        r hset bighash tmp 100\n        list [r hincrby smallhash tmp 2] [r hincrby bighash tmp 2]\n    } {102 102}\n\n    test {HINCRBY over 32bit value} {\n        r hset smallhash tmp 17179869184\n        r hset bighash tmp 17179869184\n        list [r hincrby smallhash tmp 1] [r hincrby bighash tmp 1]\n    } {17179869185 17179869185}\n\n    test {HINCRBY over 32bit value with over 32bit increment} {\n        r hset smallhash tmp 17179869184\n        r hset bighash tmp 17179869184\n        list [r hincrby smallhash tmp 17179869184] [r hincrby bighash tmp 17179869184]\n    } {34359738368 34359738368}\n\n    test {HINCRBY fails against hash value with spaces (left)} {\n        r hset smallhash str \" 11\"\n        r hset bighash str \" 11\"\n        catch {r hincrby smallhash str 1} smallerr\n        catch {r hincrby smallhash str 1} bigerr\n        set rv {}\n        lappend rv [string match \"ERR*not an integer*\" $smallerr]\n        lappend rv [string match \"ERR*not an integer*\" $bigerr]\n    } {1 1}\n\n    test {HINCRBY fails against hash value with spaces (right)} {\n        r hset smallhash str \"11 \"\n        r hset bighash str \"11 \"\n        catch {r hincrby smallhash str 1} smallerr\n        catch {r hincrby smallhash str 1} bigerr\n        set rv {}\n        lappend rv [string match \"ERR*not an integer*\" $smallerr]\n        lappend rv [string match \"ERR*not an integer*\" $bigerr]\n    } {1 1}\n\n    test {HINCRBY can detect overflows} {\n        set e {}\n        r hset hash n -9223372036854775484\n        assert {[r hincrby hash n -1] == -9223372036854775485}\n        catch {r hincrby hash n -10000} e\n        set e\n    } {*overflow*}\n\n    test {HINCRBYFLOAT against non existing database key} {\n        r del htest\n        list [r hincrbyfloat htest foo 2.5]\n    } {2.5}\n\n    test {HINCRBYFLOAT against non existing hash key} {\n        set rv {}\n        r hdel smallhash tmp\n        r hdel bighash tmp\n        lappend rv [roundFloat [r hincrbyfloat smallhash tmp 2.5]]\n        lappend rv [roundFloat [r hget smallhash tmp]]\n        lappend rv [roundFloat [r hincrbyfloat bighash tmp 2.5]]\n        lappend rv [roundFloat [r hget bighash tmp]]\n    } {2.5 2.5 2.5 2.5}\n\n    test {HINCRBYFLOAT against hash key created by hincrby itself} {\n        set rv {}\n        lappend rv [roundFloat [r hincrbyfloat smallhash tmp 3.5]]\n        lappend rv [roundFloat [r hget smallhash tmp]]\n        lappend rv [roundFloat [r hincrbyfloat bighash tmp 3.5]]\n        lappend rv [roundFloat [r hget bighash tmp]]\n    } {6 6 6 6}\n\n    test {HINCRBYFLOAT against hash key originally set with HSET} {\n        r hset smallhash tmp 100\n        r hset bighash tmp 100\n        list [roundFloat [r hincrbyfloat smallhash tmp 2.5]] \\\n             [roundFloat [r hincrbyfloat bighash tmp 2.5]]\n    } {102.5 102.5}\n\n    test {HINCRBYFLOAT over 32bit value} {\n        r hset smallhash tmp 17179869184\n        r hset bighash tmp 17179869184\n        list [r hincrbyfloat smallhash tmp 1] \\\n             [r hincrbyfloat bighash tmp 1]\n    } {17179869185 17179869185}\n\n    test {HINCRBYFLOAT over 32bit value with over 32bit increment} {\n        r hset smallhash tmp 17179869184\n        r hset bighash tmp 17179869184\n        list [r hincrbyfloat smallhash tmp 17179869184] \\\n             [r hincrbyfloat bighash tmp 17179869184]\n    } {34359738368 34359738368}\n\n    test {HINCRBYFLOAT fails against hash value with spaces (left)} {\n        r hset smallhash str \" 11\"\n        r hset bighash str \" 11\"\n        catch {r hincrbyfloat smallhash str 1} smallerr\n        catch {r hincrbyfloat smallhash str 1} bigerr\n        set rv {}\n        lappend rv [string match \"ERR*not*float*\" $smallerr]\n        lappend rv [string match \"ERR*not*float*\" $bigerr]\n    } {1 1}\n\n    test {HINCRBYFLOAT fails against hash value with spaces (right)} {\n        r hset smallhash str \"11 \"\n        r hset bighash str \"11 \"\n        catch {r hincrbyfloat smallhash str 1} smallerr\n        catch {r hincrbyfloat smallhash str 1} bigerr\n        set rv {}\n        lappend rv [string match \"ERR*not*float*\" $smallerr]\n        lappend rv [string match \"ERR*not*float*\" $bigerr]\n    } {1 1}\n\n    test {Hash ziplist regression test for large keys} {\n        r hset hash kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk a\n        r hset hash kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk b\n        r hget hash kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk\n    } {b}\n\n    foreach size {10 512} {\n        test \"Hash fuzzing #1 - $size fields\" {\n            for {set times 0} {$times < 10} {incr times} {\n                catch {unset hash}\n                array set hash {}\n                r del hash\n\n                # Create\n                for {set j 0} {$j < $size} {incr j} {\n                    set field [randomValue]\n                    set value [randomValue]\n                    r hset hash $field $value\n                    set hash($field) $value\n                }\n\n                # Verify\n                foreach {k v} [array get hash] {\n                    assert_equal $v [r hget hash $k]\n                }\n                assert_equal [array size hash] [r hlen hash]\n            }\n        }\n\n        test \"Hash fuzzing #2 - $size fields\" {\n            for {set times 0} {$times < 10} {incr times} {\n                catch {unset hash}\n                array set hash {}\n                r del hash\n\n                # Create\n                for {set j 0} {$j < $size} {incr j} {\n                    randpath {\n                        set field [randomValue]\n                        set value [randomValue]\n                        r hset hash $field $value\n                        set hash($field) $value\n                    } {\n                        set field [randomSignedInt 512]\n                        set value [randomSignedInt 512]\n                        r hset hash $field $value\n                        set hash($field) $value\n                    } {\n                        randpath {\n                            set field [randomValue]\n                        } {\n                            set field [randomSignedInt 512]\n                        }\n                        r hdel hash $field\n                        unset -nocomplain hash($field)\n                    }\n                }\n\n                # Verify\n                foreach {k v} [array get hash] {\n                    assert_equal $v [r hget hash $k]\n                }\n                assert_equal [array size hash] [r hlen hash]\n            }\n        }\n    }\n\n    test {Stress test the hash ziplist -> hashtable encoding conversion} {\n        r config set hash-max-ziplist-entries 32\n        for {set j 0} {$j < 100} {incr j} {\n            r del myhash\n            for {set i 0} {$i < 64} {incr i} {\n                r hset myhash [randomValue] [randomValue]\n            }\n            assert {[r object encoding myhash] eq {hashtable}}\n        }\n    }\n}\n"
  },
  {
    "path": "tests/unit/type/list-2.tcl",
    "content": "start_server {\n    tags {\"list\"}\n    overrides {\n        \"list-max-ziplist-value\" 16\n        \"list-max-ziplist-entries\" 256\n    }\n} {\n    source \"tests/unit/type/list-common.tcl\"\n\n    foreach {type large} [array get largevalue] {\n        tags {\"slow\"} {\n            test \"LTRIM stress testing - $type\" {\n                set mylist {}\n                set startlen 32\n                r del mylist\n\n                # Start with the large value to ensure the\n                # right encoding is used.\n                r rpush mylist $large\n                lappend mylist $large\n\n                for {set i 0} {$i < $startlen} {incr i} {\n                    set str [randomInt 9223372036854775807]\n                    r rpush mylist $str\n                    lappend mylist $str\n                }\n\n                for {set i 0} {$i < 1000} {incr i} {\n                    set min [expr {int(rand()*$startlen)}]\n                    set max [expr {$min+int(rand()*$startlen)}]\n                    set mylist [lrange $mylist $min $max]\n                    r ltrim mylist $min $max\n                    assert_equal $mylist [r lrange mylist 0 -1]\n\n                    for {set j [r llen mylist]} {$j < $startlen} {incr j} {\n                        set str [randomInt 9223372036854775807]\n                        r rpush mylist $str\n                        lappend mylist $str\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "tests/unit/type/list-3.tcl",
    "content": "start_server {\n    tags {list ziplist}\n    overrides {\n        \"list-max-ziplist-value\" 200000\n        \"list-max-ziplist-entries\" 256\n    }\n} {\n    test {Explicit regression for a list bug} {\n        set mylist {49376042582 {BkG2o\\pIC]4YYJa9cJ4GWZalG[4tin;1D2whSkCOW`mX;SFXGyS8sedcff3fQI^tgPCC@^Nu1J6o]meM@Lko]t_jRyo<xSJ1oObDYd`ppZuW6P@fS278YaOx=s6lvdFlMbP0[SbkI^Kr\\HBXtuFaA^mDx:yzS4a[skiiPWhT<nNfAf=aQVfclcuwDrfe;iVuKdNvB9kbfq>tK?tH[\\EvWqS]b`o2OCtjg:?nUTwdjpcUm]y:pg5q24q7LlCOwQE^}}\n        r del l\n        r rpush l [lindex $mylist 0]\n        r rpush l [lindex $mylist 1]\n        assert_equal [r lindex l 0] [lindex $mylist 0]\n        assert_equal [r lindex l 1] [lindex $mylist 1]\n    }\n\n    tags {slow} {\n        test {ziplist implementation: value encoding and backlink} {\n            if {$::accurate} {set iterations 100} else {set iterations 10}\n            for {set j 0} {$j < $iterations} {incr j} {\n                r del l\n                set l {}\n                for {set i 0} {$i < 200} {incr i} {\n                    randpath {\n                        set data [string repeat x [randomInt 100000]]\n                    } {\n                        set data [randomInt 65536]\n                    } {\n                        set data [randomInt 4294967296]\n                    } {\n                        set data [randomInt 18446744073709551616]\n                    } {\n                        set data -[randomInt 65536]\n                        if {$data eq {-0}} {set data 0}\n                    } {\n                        set data -[randomInt 4294967296]\n                        if {$data eq {-0}} {set data 0}\n                    } {\n                        set data -[randomInt 18446744073709551616]\n                        if {$data eq {-0}} {set data 0}\n                    }\n                    lappend l $data\n                    r rpush l $data\n                }\n                assert_equal [llength $l] [r llen l]\n                # Traverse backward\n                for {set i 199} {$i >= 0} {incr i -1} {\n                    if {[lindex $l $i] ne [r lindex l $i]} {\n                        assert_equal [lindex $l $i] [r lindex l $i]\n                    }\n                }\n            }\n        }\n\n        test {ziplist implementation: encoding stress testing} {\n            for {set j 0} {$j < 200} {incr j} {\n                r del l\n                set l {}\n                set len [randomInt 400]\n                for {set i 0} {$i < $len} {incr i} {\n                    set rv [randomValue]\n                    randpath {\n                        lappend l $rv\n                        r rpush l $rv\n                    } {\n                        set l [concat [list $rv] $l]\n                        r lpush l $rv\n                    }\n                }\n                assert_equal [llength $l] [r llen l]\n                for {set i 0} {$i < $len} {incr i} {\n                    if {[lindex $l $i] ne [r lindex l $i]} {\n                        assert_equal [lindex $l $i] [r lindex l $i]\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "tests/unit/type/list-common.tcl",
    "content": "# We need a value larger than list-max-ziplist-value to make sure\n# the list has the right encoding when it is swapped in again.\narray set largevalue {}\nset largevalue(ziplist) \"hello\"\nset largevalue(linkedlist) [string repeat \"hello\" 4]\n"
  },
  {
    "path": "tests/unit/type/list.tcl",
    "content": "start_server {\n    tags {\"list\"}\n    overrides {\n        \"list-max-ziplist-value\" 16\n        \"list-max-ziplist-entries\" 256\n    }\n} {\n    source \"tests/unit/type/list-common.tcl\"\n\n    test {LPUSH, RPUSH, LLENGTH, LINDEX, LPOP - ziplist} {\n        # first lpush then rpush\n        assert_equal 1 [r lpush myziplist1 a]\n        assert_equal 2 [r rpush myziplist1 b]\n        assert_equal 3 [r rpush myziplist1 c]\n        assert_equal 3 [r llen myziplist1]\n        assert_equal a [r lindex myziplist1 0]\n        assert_equal b [r lindex myziplist1 1]\n        assert_equal c [r lindex myziplist1 2]\n        assert_equal {} [r lindex myziplist2 3]\n        assert_equal c [r rpop myziplist1]\n        assert_equal a [r lpop myziplist1]\n        assert_encoding ziplist myziplist1\n\n        # first rpush then lpush\n        assert_equal 1 [r rpush myziplist2 a]\n        assert_equal 2 [r lpush myziplist2 b]\n        assert_equal 3 [r lpush myziplist2 c]\n        assert_equal 3 [r llen myziplist2]\n        assert_equal c [r lindex myziplist2 0]\n        assert_equal b [r lindex myziplist2 1]\n        assert_equal a [r lindex myziplist2 2]\n        assert_equal {} [r lindex myziplist2 3]\n        assert_equal a [r rpop myziplist2]\n        assert_equal c [r lpop myziplist2]\n        assert_encoding ziplist myziplist2\n    }\n\n    test {LPUSH, RPUSH, LLENGTH, LINDEX, LPOP - regular list} {\n        # first lpush then rpush\n        assert_equal 1 [r lpush mylist1 $largevalue(linkedlist)]\n        assert_encoding linkedlist mylist1\n        assert_equal 2 [r rpush mylist1 b]\n        assert_equal 3 [r rpush mylist1 c]\n        assert_equal 3 [r llen mylist1]\n        assert_equal $largevalue(linkedlist) [r lindex mylist1 0]\n        assert_equal b [r lindex mylist1 1]\n        assert_equal c [r lindex mylist1 2]\n        assert_equal {} [r lindex mylist1 3]\n        assert_equal c [r rpop mylist1]\n        assert_equal $largevalue(linkedlist) [r lpop mylist1]\n\n        # first rpush then lpush\n        assert_equal 1 [r rpush mylist2 $largevalue(linkedlist)]\n        assert_encoding linkedlist mylist2\n        assert_equal 2 [r lpush mylist2 b]\n        assert_equal 3 [r lpush mylist2 c]\n        assert_equal 3 [r llen mylist2]\n        assert_equal c [r lindex mylist2 0]\n        assert_equal b [r lindex mylist2 1]\n        assert_equal $largevalue(linkedlist) [r lindex mylist2 2]\n        assert_equal {} [r lindex mylist2 3]\n        assert_equal $largevalue(linkedlist) [r rpop mylist2]\n        assert_equal c [r lpop mylist2]\n    }\n\n    test {R/LPOP against empty list} {\n        r lpop non-existing-list\n    } {}\n\n    test {Variadic RPUSH/LPUSH} {\n        r del mylist\n        assert_equal 4 [r lpush mylist a b c d]\n        assert_equal 8 [r rpush mylist 0 1 2 3]\n        assert_equal {d c b a 0 1 2 3} [r lrange mylist 0 -1]\n    }\n\n    test {DEL a list - ziplist} {\n        assert_equal 1 [r del myziplist2]\n        assert_equal 0 [r exists myziplist2]\n        assert_equal 0 [r llen myziplist2]\n    }\n\n    test {DEL a list - regular list} {\n        assert_equal 1 [r del mylist2]\n        assert_equal 0 [r exists mylist2]\n        assert_equal 0 [r llen mylist2]\n    }\n\n    proc create_ziplist {key entries} {\n        r del $key\n        foreach entry $entries { r rpush $key $entry }\n        assert_encoding ziplist $key\n    }\n\n    proc create_linkedlist {key entries} {\n        r del $key\n        foreach entry $entries { r rpush $key $entry }\n        assert_encoding linkedlist $key\n    }\n\n    foreach {type large} [array get largevalue] {\n        test \"BLPOP, BRPOP: single existing list - $type\" {\n            set rd [redis_deferring_client]\n            create_$type blist \"a b $large c d\"\n\n            $rd blpop blist 1\n            assert_equal {blist a} [$rd read]\n            $rd brpop blist 1\n            assert_equal {blist d} [$rd read]\n\n            $rd blpop blist 1\n            assert_equal {blist b} [$rd read]\n            $rd brpop blist 1\n            assert_equal {blist c} [$rd read]\n        }\n\n        test \"BLPOP, BRPOP: multiple existing lists - $type\" {\n            set rd [redis_deferring_client]\n            create_$type blist1 \"a $large c\"\n            create_$type blist2 \"d $large f\"\n\n            $rd blpop blist1 blist2 1\n            assert_equal {blist1 a} [$rd read]\n            $rd brpop blist1 blist2 1\n            assert_equal {blist1 c} [$rd read]\n            assert_equal 1 [r llen blist1]\n            assert_equal 3 [r llen blist2]\n\n            $rd blpop blist2 blist1 1\n            assert_equal {blist2 d} [$rd read]\n            $rd brpop blist2 blist1 1\n            assert_equal {blist2 f} [$rd read]\n            assert_equal 1 [r llen blist1]\n            assert_equal 1 [r llen blist2]\n        }\n\n        test \"BLPOP, BRPOP: second list has an entry - $type\" {\n            set rd [redis_deferring_client]\n            r del blist1\n            create_$type blist2 \"d $large f\"\n\n            $rd blpop blist1 blist2 1\n            assert_equal {blist2 d} [$rd read]\n            $rd brpop blist1 blist2 1\n            assert_equal {blist2 f} [$rd read]\n            assert_equal 0 [r llen blist1]\n            assert_equal 1 [r llen blist2]\n        }\n\n        test \"BRPOPLPUSH - $type\" {\n            r del target\n\n            set rd [redis_deferring_client]\n            create_$type blist \"a b $large c d\"\n\n            $rd brpoplpush blist target 1\n            assert_equal d [$rd read]\n\n            assert_equal d [r rpop target]\n            assert_equal \"a b $large c\" [r lrange blist 0 -1]\n        }\n    }\n\n    test \"BLPOP, LPUSH + DEL should not awake blocked client\" {\n        set rd [redis_deferring_client]\n        r del list\n\n        $rd blpop list 0\n        r multi\n        r lpush list a\n        r del list\n        r exec\n        r del list\n        r lpush list b\n        $rd read\n    } {list b}\n\n    test \"BLPOP, LPUSH + DEL + SET should not awake blocked client\" {\n        set rd [redis_deferring_client]\n        r del list\n\n        $rd blpop list 0\n        r multi\n        r lpush list a\n        r del list\n        r set list foo\n        r exec\n        r del list\n        r lpush list b\n        $rd read\n    } {list b}\n\n    test \"BLPOP with same key multiple times should work (issue #801)\" {\n        set rd [redis_deferring_client]\n        r del list1 list2\n\n        # Data arriving after the BLPOP.\n        $rd blpop list1 list2 list2 list1 0\n        r lpush list1 a\n        assert_equal [$rd read] {list1 a}\n        $rd blpop list1 list2 list2 list1 0\n        r lpush list2 b\n        assert_equal [$rd read] {list2 b}\n\n        # Data already there.\n        r lpush list1 a\n        r lpush list2 b\n        $rd blpop list1 list2 list2 list1 0\n        assert_equal [$rd read] {list1 a}\n        $rd blpop list1 list2 list2 list1 0\n        assert_equal [$rd read] {list2 b}\n    }\n\n    test \"MULTI/EXEC is isolated from the point of view of BLPOP\" {\n        set rd [redis_deferring_client]\n        r del list\n        $rd blpop list 0\n        r multi\n        r lpush list a\n        r lpush list b\n        r lpush list c\n        r exec\n        $rd read\n    } {list c}\n\n    test \"BLPOP with variadic LPUSH\" {\n        set rd [redis_deferring_client]\n        r del blist target\n        if {$::valgrind} {after 100}\n        $rd blpop blist 0\n        if {$::valgrind} {after 100}\n        assert_equal 2 [r lpush blist foo bar]\n        if {$::valgrind} {after 100}\n        assert_equal {blist bar} [$rd read]\n        assert_equal foo [lindex [r lrange blist 0 -1] 0]\n    }\n\n    test \"BRPOPLPUSH with zero timeout should block indefinitely\" {\n        set rd [redis_deferring_client]\n        r del blist target\n        $rd brpoplpush blist target 0\n        after 1000\n        r rpush blist foo\n        assert_equal foo [$rd read]\n        assert_equal {foo} [r lrange target 0 -1]\n    }\n\n    test \"BRPOPLPUSH with a client BLPOPing the target list\" {\n        set rd [redis_deferring_client]\n        set rd2 [redis_deferring_client]\n        r del blist target\n        $rd2 blpop target 0\n        $rd brpoplpush blist target 0\n        after 1000\n        r rpush blist foo\n        assert_equal foo [$rd read]\n        assert_equal {target foo} [$rd2 read]\n        assert_equal 0 [r exists target]\n    }\n\n    test \"BRPOPLPUSH with wrong source type\" {\n        set rd [redis_deferring_client]\n        r del blist target\n        r set blist nolist\n        $rd brpoplpush blist target 1\n        assert_error \"WRONGTYPE*\" {$rd read}\n    }\n\n    test \"BRPOPLPUSH with wrong destination type\" {\n        set rd [redis_deferring_client]\n        r del blist target\n        r set target nolist\n        r lpush blist foo\n        $rd brpoplpush blist target 1\n        assert_error \"WRONGTYPE*\" {$rd read}\n\n        set rd [redis_deferring_client]\n        r del blist target\n        r set target nolist\n        $rd brpoplpush blist target 0\n        after 1000\n        r rpush blist foo\n        assert_error \"WRONGTYPE*\" {$rd read}\n        assert_equal {foo} [r lrange blist 0 -1]\n    }\n\n    test \"BRPOPLPUSH maintains order of elements after failure\" {\n        set rd [redis_deferring_client]\n        r del blist target\n        r set target nolist\n        $rd brpoplpush blist target 0\n        r rpush blist a b c\n        assert_error \"WRONGTYPE*\" {$rd read}\n        r lrange blist 0 -1\n    } {a b c}\n\n    test \"BRPOPLPUSH with multiple blocked clients\" {\n        set rd1 [redis_deferring_client]\n        set rd2 [redis_deferring_client]\n        r del blist target1 target2\n        r set target1 nolist\n        $rd1 brpoplpush blist target1 0\n        $rd2 brpoplpush blist target2 0\n        r lpush blist foo\n\n        assert_error \"WRONGTYPE*\" {$rd1 read}\n        assert_equal {foo} [$rd2 read]\n        assert_equal {foo} [r lrange target2 0 -1]\n    }\n\n    test \"Linked BRPOPLPUSH\" {\n      set rd1 [redis_deferring_client]\n      set rd2 [redis_deferring_client]\n\n      r del list1 list2 list3\n\n      $rd1 brpoplpush list1 list2 0\n      $rd2 brpoplpush list2 list3 0\n\n      r rpush list1 foo\n\n      assert_equal {} [r lrange list1 0 -1]\n      assert_equal {} [r lrange list2 0 -1]\n      assert_equal {foo} [r lrange list3 0 -1]\n    }\n\n    test \"Circular BRPOPLPUSH\" {\n      set rd1 [redis_deferring_client]\n      set rd2 [redis_deferring_client]\n\n      r del list1 list2\n\n      $rd1 brpoplpush list1 list2 0\n      $rd2 brpoplpush list2 list1 0\n\n      r rpush list1 foo\n\n      assert_equal {foo} [r lrange list1 0 -1]\n      assert_equal {} [r lrange list2 0 -1]\n    }\n\n    test \"Self-referential BRPOPLPUSH\" {\n      set rd [redis_deferring_client]\n\n      r del blist\n\n      $rd brpoplpush blist blist 0\n\n      r rpush blist foo\n\n      assert_equal {foo} [r lrange blist 0 -1]\n    }\n\n    test \"BRPOPLPUSH inside a transaction\" {\n        r del xlist target\n        r lpush xlist foo\n        r lpush xlist bar\n\n        r multi\n        r brpoplpush xlist target 0\n        r brpoplpush xlist target 0\n        r brpoplpush xlist target 0\n        r lrange xlist 0 -1\n        r lrange target 0 -1\n        r exec\n    } {foo bar {} {} {bar foo}}\n\n    test \"PUSH resulting from BRPOPLPUSH affect WATCH\" {\n        set blocked_client [redis_deferring_client]\n        set watching_client [redis_deferring_client]\n        r del srclist dstlist somekey\n        r set somekey somevalue\n        $blocked_client brpoplpush srclist dstlist 0\n        $watching_client watch dstlist\n        $watching_client read\n        $watching_client multi\n        $watching_client read\n        $watching_client get somekey\n        $watching_client read\n        r lpush srclist element\n        $watching_client exec\n        $watching_client read\n    } {}\n\n    test \"BRPOPLPUSH does not affect WATCH while still blocked\" {\n        set blocked_client [redis_deferring_client]\n        set watching_client [redis_deferring_client]\n        r del srclist dstlist somekey\n        r set somekey somevalue\n        $blocked_client brpoplpush srclist dstlist 0\n        $watching_client watch dstlist\n        $watching_client read\n        $watching_client multi\n        $watching_client read\n        $watching_client get somekey\n        $watching_client read\n        $watching_client exec\n        # Blocked BLPOPLPUSH may create problems, unblock it.\n        r lpush srclist element\n        $watching_client read\n    } {somevalue}\n\n    test {BRPOPLPUSH timeout} {\n      set rd [redis_deferring_client]\n\n      $rd brpoplpush foo_list bar_list 1\n      after 2000\n      $rd read\n    } {}\n\n    foreach {pop} {BLPOP BRPOP} {\n        test \"$pop: with single empty list argument\" {\n            set rd [redis_deferring_client]\n            r del blist1\n            $rd $pop blist1 1\n            r rpush blist1 foo\n            assert_equal {blist1 foo} [$rd read]\n            assert_equal 0 [r exists blist1]\n        }\n\n        test \"$pop: with negative timeout\" {\n            set rd [redis_deferring_client]\n            $rd $pop blist1 -1\n            assert_error \"ERR*is negative*\" {$rd read}\n        }\n\n        test \"$pop: with non-integer timeout\" {\n            set rd [redis_deferring_client]\n            $rd $pop blist1 1.1\n            assert_error \"ERR*not an integer*\" {$rd read}\n        }\n\n        test \"$pop: with zero timeout should block indefinitely\" {\n            # To test this, use a timeout of 0 and wait a second.\n            # The blocking pop should still be waiting for a push.\n            set rd [redis_deferring_client]\n            $rd $pop blist1 0\n            after 1000\n            r rpush blist1 foo\n            assert_equal {blist1 foo} [$rd read]\n        }\n\n        test \"$pop: second argument is not a list\" {\n            set rd [redis_deferring_client]\n            r del blist1 blist2\n            r set blist2 nolist\n            $rd $pop blist1 blist2 1\n            assert_error \"WRONGTYPE*\" {$rd read}\n        }\n\n        test \"$pop: timeout\" {\n            set rd [redis_deferring_client]\n            r del blist1 blist2\n            $rd $pop blist1 blist2 1\n            assert_equal {} [$rd read]\n        }\n\n        test \"$pop: arguments are empty\" {\n            set rd [redis_deferring_client]\n            r del blist1 blist2\n\n            $rd $pop blist1 blist2 1\n            r rpush blist1 foo\n            assert_equal {blist1 foo} [$rd read]\n            assert_equal 0 [r exists blist1]\n            assert_equal 0 [r exists blist2]\n\n            $rd $pop blist1 blist2 1\n            r rpush blist2 foo\n            assert_equal {blist2 foo} [$rd read]\n            assert_equal 0 [r exists blist1]\n            assert_equal 0 [r exists blist2]\n        }\n    }\n\n    test {BLPOP inside a transaction} {\n        r del xlist\n        r lpush xlist foo\n        r lpush xlist bar\n        r multi\n        r blpop xlist 0\n        r blpop xlist 0\n        r blpop xlist 0\n        r exec\n    } {{xlist bar} {xlist foo} {}}\n\n    test {LPUSHX, RPUSHX - generic} {\n        r del xlist\n        assert_equal 0 [r lpushx xlist a]\n        assert_equal 0 [r llen xlist]\n        assert_equal 0 [r rpushx xlist a]\n        assert_equal 0 [r llen xlist]\n    }\n\n    foreach {type large} [array get largevalue] {\n        test \"LPUSHX, RPUSHX - $type\" {\n            create_$type xlist \"$large c\"\n            assert_equal 3 [r rpushx xlist d]\n            assert_equal 4 [r lpushx xlist a]\n            assert_equal \"a $large c d\" [r lrange xlist 0 -1]\n        }\n\n        test \"LINSERT - $type\" {\n            create_$type xlist \"a $large c d\"\n            assert_equal 5 [r linsert xlist before c zz]\n            assert_equal \"a $large zz c d\" [r lrange xlist 0 10]\n            assert_equal 6 [r linsert xlist after c yy]\n            assert_equal \"a $large zz c yy d\" [r lrange xlist 0 10]\n            assert_equal 7 [r linsert xlist after d dd]\n            assert_equal -1 [r linsert xlist after bad ddd]\n            assert_equal \"a $large zz c yy d dd\" [r lrange xlist 0 10]\n            assert_equal 8 [r linsert xlist before a aa]\n            assert_equal -1 [r linsert xlist before bad aaa]\n            assert_equal \"aa a $large zz c yy d dd\" [r lrange xlist 0 10]\n\n            # check inserting integer encoded value\n            assert_equal 9 [r linsert xlist before aa 42]\n            assert_equal 42 [r lrange xlist 0 0]\n        }\n    }\n\n    test {LINSERT raise error on bad syntax} {\n        catch {[r linsert xlist aft3r aa 42]} e\n        set e\n    } {*ERR*syntax*error*}\n\n    test {LPUSHX, RPUSHX convert from ziplist to list} {\n        set large $largevalue(linkedlist)\n\n        # convert when a large value is pushed\n        create_ziplist xlist a\n        assert_equal 2 [r rpushx xlist $large]\n        assert_encoding linkedlist xlist\n        create_ziplist xlist a\n        assert_equal 2 [r lpushx xlist $large]\n        assert_encoding linkedlist xlist\n\n        # convert when the length threshold is exceeded\n        create_ziplist xlist [lrepeat 256 a]\n        assert_equal 257 [r rpushx xlist b]\n        assert_encoding linkedlist xlist\n        create_ziplist xlist [lrepeat 256 a]\n        assert_equal 257 [r lpushx xlist b]\n        assert_encoding linkedlist xlist\n    }\n\n    test {LINSERT convert from ziplist to list} {\n        set large $largevalue(linkedlist)\n\n        # convert when a large value is inserted\n        create_ziplist xlist a\n        assert_equal 2 [r linsert xlist before a $large]\n        assert_encoding linkedlist xlist\n        create_ziplist xlist a\n        assert_equal 2 [r linsert xlist after a $large]\n        assert_encoding linkedlist xlist\n\n        # convert when the length threshold is exceeded\n        create_ziplist xlist [lrepeat 256 a]\n        assert_equal 257 [r linsert xlist before a a]\n        assert_encoding linkedlist xlist\n        create_ziplist xlist [lrepeat 256 a]\n        assert_equal 257 [r linsert xlist after a a]\n        assert_encoding linkedlist xlist\n\n        # don't convert when the value could not be inserted\n        create_ziplist xlist [lrepeat 256 a]\n        assert_equal -1 [r linsert xlist before foo a]\n        assert_encoding ziplist xlist\n        create_ziplist xlist [lrepeat 256 a]\n        assert_equal -1 [r linsert xlist after foo a]\n        assert_encoding ziplist xlist\n    }\n\n    foreach {type num} {ziplist 250 linkedlist 500} {\n        proc check_numbered_list_consistency {key} {\n            set len [r llen $key]\n            for {set i 0} {$i < $len} {incr i} {\n                assert_equal $i [r lindex $key $i]\n                assert_equal [expr $len-1-$i] [r lindex $key [expr (-$i)-1]]\n            }\n        }\n\n        proc check_random_access_consistency {key} {\n            set len [r llen $key]\n            for {set i 0} {$i < $len} {incr i} {\n                set rint [expr int(rand()*$len)]\n                assert_equal $rint [r lindex $key $rint]\n                assert_equal [expr $len-1-$rint] [r lindex $key [expr (-$rint)-1]]\n            }\n        }\n\n        test \"LINDEX consistency test - $type\" {\n            r del mylist\n            for {set i 0} {$i < $num} {incr i} {\n                r rpush mylist $i\n            }\n            assert_encoding $type mylist\n            check_numbered_list_consistency mylist\n        }\n\n        test \"LINDEX random access - $type\" {\n            assert_encoding $type mylist\n            check_random_access_consistency mylist\n        }\n\n        test \"Check if list is still ok after a DEBUG RELOAD - $type\" {\n            r debug reload\n            assert_encoding $type mylist\n            check_numbered_list_consistency mylist\n            check_random_access_consistency mylist\n        }\n    }\n\n    test {LLEN against non-list value error} {\n        r del mylist\n        r set mylist foobar\n        assert_error WRONGTYPE* {r llen mylist}\n    }\n\n    test {LLEN against non existing key} {\n        assert_equal 0 [r llen not-a-key]\n    }\n\n    test {LINDEX against non-list value error} {\n        assert_error WRONGTYPE* {r lindex mylist 0}\n    }\n\n    test {LINDEX against non existing key} {\n        assert_equal \"\" [r lindex not-a-key 10]\n    }\n\n    test {LPUSH against non-list value error} {\n        assert_error WRONGTYPE* {r lpush mylist 0}\n    }\n\n    test {RPUSH against non-list value error} {\n        assert_error WRONGTYPE* {r rpush mylist 0}\n    }\n\n    foreach {type large} [array get largevalue] {\n        test \"RPOPLPUSH base case - $type\" {\n            r del mylist1 mylist2\n            create_$type mylist1 \"a $large c d\"\n            assert_equal d [r rpoplpush mylist1 mylist2]\n            assert_equal c [r rpoplpush mylist1 mylist2]\n            assert_equal \"a $large\" [r lrange mylist1 0 -1]\n            assert_equal \"c d\" [r lrange mylist2 0 -1]\n            assert_encoding ziplist mylist2\n        }\n\n        test \"RPOPLPUSH with the same list as src and dst - $type\" {\n            create_$type mylist \"a $large c\"\n            assert_equal \"a $large c\" [r lrange mylist 0 -1]\n            assert_equal c [r rpoplpush mylist mylist]\n            assert_equal \"c a $large\" [r lrange mylist 0 -1]\n        }\n\n        foreach {othertype otherlarge} [array get largevalue] {\n            test \"RPOPLPUSH with $type source and existing target $othertype\" {\n                create_$type srclist \"a b c $large\"\n                create_$othertype dstlist \"$otherlarge\"\n                assert_equal $large [r rpoplpush srclist dstlist]\n                assert_equal c [r rpoplpush srclist dstlist]\n                assert_equal \"a b\" [r lrange srclist 0 -1]\n                assert_equal \"c $large $otherlarge\" [r lrange dstlist 0 -1]\n\n                # When we rpoplpush'ed a large value, dstlist should be\n                # converted to the same encoding as srclist.\n                if {$type eq \"linkedlist\"} {\n                    assert_encoding linkedlist dstlist\n                }\n            }\n        }\n    }\n\n    test {RPOPLPUSH against non existing key} {\n        r del srclist dstlist\n        assert_equal {} [r rpoplpush srclist dstlist]\n        assert_equal 0 [r exists srclist]\n        assert_equal 0 [r exists dstlist]\n    }\n\n    test {RPOPLPUSH against non list src key} {\n        r del srclist dstlist\n        r set srclist x\n        assert_error WRONGTYPE* {r rpoplpush srclist dstlist}\n        assert_type string srclist\n        assert_equal 0 [r exists newlist]\n    }\n\n    test {RPOPLPUSH against non list dst key} {\n        create_ziplist srclist {a b c d}\n        r set dstlist x\n        assert_error WRONGTYPE* {r rpoplpush srclist dstlist}\n        assert_type string dstlist\n        assert_equal {a b c d} [r lrange srclist 0 -1]\n    }\n\n    test {RPOPLPUSH against non existing src key} {\n        r del srclist dstlist\n        assert_equal {} [r rpoplpush srclist dstlist]\n    } {}\n\n    foreach {type large} [array get largevalue] {\n        test \"Basic LPOP/RPOP - $type\" {\n            create_$type mylist \"$large 1 2\"\n            assert_equal $large [r lpop mylist]\n            assert_equal 2 [r rpop mylist]\n            assert_equal 1 [r lpop mylist]\n            assert_equal 0 [r llen mylist]\n\n            # pop on empty list\n            assert_equal {} [r lpop mylist]\n            assert_equal {} [r rpop mylist]\n        }\n    }\n\n    test {LPOP/RPOP against non list value} {\n        r set notalist foo\n        assert_error WRONGTYPE* {r lpop notalist}\n        assert_error WRONGTYPE* {r rpop notalist}\n    }\n\n    foreach {type num} {ziplist 250 linkedlist 500} {\n        test \"Mass RPOP/LPOP - $type\" {\n            r del mylist\n            set sum1 0\n            for {set i 0} {$i < $num} {incr i} {\n                r lpush mylist $i\n                incr sum1 $i\n            }\n            assert_encoding $type mylist\n            set sum2 0\n            for {set i 0} {$i < [expr $num/2]} {incr i} {\n                incr sum2 [r lpop mylist]\n                incr sum2 [r rpop mylist]\n            }\n            assert_equal $sum1 $sum2\n        }\n    }\n\n    foreach {type large} [array get largevalue] {\n        test \"LRANGE basics - $type\" {\n            create_$type mylist \"$large 1 2 3 4 5 6 7 8 9\"\n            assert_equal {1 2 3 4 5 6 7 8} [r lrange mylist 1 -2]\n            assert_equal {7 8 9} [r lrange mylist -3 -1]\n            assert_equal {4} [r lrange mylist 4 4]\n        }\n\n        test \"LRANGE inverted indexes - $type\" {\n            create_$type mylist \"$large 1 2 3 4 5 6 7 8 9\"\n            assert_equal {} [r lrange mylist 6 2]\n        }\n\n        test \"LRANGE out of range indexes including the full list - $type\" {\n            create_$type mylist \"$large 1 2 3\"\n            assert_equal \"$large 1 2 3\" [r lrange mylist -1000 1000]\n        }\n\n        test \"LRANGE out of range negative end index - $type\" {\n            create_$type mylist \"$large 1 2 3\"\n            assert_equal $large [r lrange mylist 0 -4]\n            assert_equal {} [r lrange mylist 0 -5]\n        }\n    }\n\n    test {LRANGE against non existing key} {\n        assert_equal {} [r lrange nosuchkey 0 1]\n    }\n\n    foreach {type large} [array get largevalue] {\n        proc trim_list {type min max} {\n            upvar 1 large large\n            r del mylist\n            create_$type mylist \"1 2 3 4 $large\"\n            r ltrim mylist $min $max\n            r lrange mylist 0 -1\n        }\n\n        test \"LTRIM basics - $type\" {\n            assert_equal \"1\" [trim_list $type 0 0]\n            assert_equal \"1 2\" [trim_list $type 0 1]\n            assert_equal \"1 2 3\" [trim_list $type 0 2]\n            assert_equal \"2 3\" [trim_list $type 1 2]\n            assert_equal \"2 3 4 $large\" [trim_list $type 1 -1]\n            assert_equal \"2 3 4\" [trim_list $type 1 -2]\n            assert_equal \"4 $large\" [trim_list $type -2 -1]\n            assert_equal \"$large\" [trim_list $type -1 -1]\n            assert_equal \"1 2 3 4 $large\" [trim_list $type -5 -1]\n            assert_equal \"1 2 3 4 $large\" [trim_list $type -10 10]\n            assert_equal \"1 2 3 4 $large\" [trim_list $type 0 5]\n            assert_equal \"1 2 3 4 $large\" [trim_list $type 0 10]\n        }\n\n        test \"LTRIM out of range negative end index - $type\" {\n            assert_equal {1} [trim_list $type 0 -5]\n            assert_equal {} [trim_list $type 0 -6]\n        }\n\n    }\n\n    foreach {type large} [array get largevalue] {\n        test \"LSET - $type\" {\n            create_$type mylist \"99 98 $large 96 95\"\n            r lset mylist 1 foo\n            r lset mylist -1 bar\n            assert_equal \"99 foo $large 96 bar\" [r lrange mylist 0 -1]\n        }\n\n        test \"LSET out of range index - $type\" {\n            assert_error ERR*range* {r lset mylist 10 foo}\n        }\n    }\n\n    test {LSET against non existing key} {\n        assert_error ERR*key* {r lset nosuchkey 10 foo}\n    }\n\n    test {LSET against non list value} {\n        r set nolist foobar\n        assert_error WRONGTYPE* {r lset nolist 0 foo}\n    }\n\n    foreach {type e} [array get largevalue] {\n        test \"LREM remove all the occurrences - $type\" {\n            create_$type mylist \"$e foo bar foobar foobared zap bar test foo\"\n            assert_equal 2 [r lrem mylist 0 bar]\n            assert_equal \"$e foo foobar foobared zap test foo\" [r lrange mylist 0 -1]\n        }\n\n        test \"LREM remove the first occurrence - $type\" {\n            assert_equal 1 [r lrem mylist 1 foo]\n            assert_equal \"$e foobar foobared zap test foo\" [r lrange mylist 0 -1]\n        }\n\n        test \"LREM remove non existing element - $type\" {\n            assert_equal 0 [r lrem mylist 1 nosuchelement]\n            assert_equal \"$e foobar foobared zap test foo\" [r lrange mylist 0 -1]\n        }\n\n        test \"LREM starting from tail with negative count - $type\" {\n            create_$type mylist \"$e foo bar foobar foobared zap bar test foo foo\"\n            assert_equal 1 [r lrem mylist -1 bar]\n            assert_equal \"$e foo bar foobar foobared zap test foo foo\" [r lrange mylist 0 -1]\n        }\n\n        test \"LREM starting from tail with negative count (2) - $type\" {\n            assert_equal 2 [r lrem mylist -2 foo]\n            assert_equal \"$e foo bar foobar foobared zap test\" [r lrange mylist 0 -1]\n        }\n\n        test \"LREM deleting objects that may be int encoded - $type\" {\n            create_$type myotherlist \"$e 1 2 3\"\n            assert_equal 1 [r lrem myotherlist 1 2]\n            assert_equal 3 [r llen myotherlist]\n        }\n    }\n\n    test \"Regression for bug 593 - chaining BRPOPLPUSH with other blocking cmds\" {\n        set rd1 [redis_deferring_client]\n        set rd2 [redis_deferring_client]\n\n        $rd1 brpoplpush a b 0\n        $rd1 brpoplpush a b 0\n        $rd2 brpoplpush b c 0\n        after 1000\n        r lpush a data\n        $rd1 close\n        $rd2 close\n        r ping\n    } {PONG}\n}\n"
  },
  {
    "path": "tests/unit/type/set.tcl",
    "content": "start_server {\n    tags {\"set\"}\n    overrides {\n        \"set-max-intset-entries\" 512\n    }\n} {\n    proc create_set {key entries} {\n        r del $key\n        foreach entry $entries { r sadd $key $entry }\n    }\n\n    test {SADD, SCARD, SISMEMBER, SMEMBERS basics - regular set} {\n        create_set myset {foo}\n        assert_encoding hashtable myset\n        assert_equal 1 [r sadd myset bar]\n        assert_equal 0 [r sadd myset bar]\n        assert_equal 2 [r scard myset]\n        assert_equal 1 [r sismember myset foo]\n        assert_equal 1 [r sismember myset bar]\n        assert_equal 0 [r sismember myset bla]\n        assert_equal {bar foo} [lsort [r smembers myset]]\n    }\n\n    test {SADD, SCARD, SISMEMBER, SMEMBERS basics - intset} {\n        create_set myset {17}\n        assert_encoding intset myset\n        assert_equal 1 [r sadd myset 16]\n        assert_equal 0 [r sadd myset 16]\n        assert_equal 2 [r scard myset]\n        assert_equal 1 [r sismember myset 16]\n        assert_equal 1 [r sismember myset 17]\n        assert_equal 0 [r sismember myset 18]\n        assert_equal {16 17} [lsort [r smembers myset]]\n    }\n\n    test {SADD against non set} {\n        r lpush mylist foo\n        assert_error WRONGTYPE* {r sadd mylist bar}\n    }\n\n    test \"SADD a non-integer against an intset\" {\n        create_set myset {1 2 3}\n        assert_encoding intset myset\n        assert_equal 1 [r sadd myset a]\n        assert_encoding hashtable myset\n    }\n\n    test \"SADD an integer larger than 64 bits\" {\n        create_set myset {213244124402402314402033402}\n        assert_encoding hashtable myset\n        assert_equal 1 [r sismember myset 213244124402402314402033402]\n    }\n\n    test \"SADD overflows the maximum allowed integers in an intset\" {\n        r del myset\n        for {set i 0} {$i < 512} {incr i} { r sadd myset $i }\n        assert_encoding intset myset\n        assert_equal 1 [r sadd myset 512]\n        assert_encoding hashtable myset\n    }\n\n    test {Variadic SADD} {\n        r del myset\n        assert_equal 3 [r sadd myset a b c]\n        assert_equal 2 [r sadd myset A a b c B]\n        assert_equal [lsort {A a b c B}] [lsort [r smembers myset]]\n    }\n\n    test \"Set encoding after DEBUG RELOAD\" {\n        r del myintset myhashset mylargeintset\n        for {set i 0} {$i <  100} {incr i} { r sadd myintset $i }\n        for {set i 0} {$i < 1280} {incr i} { r sadd mylargeintset $i }\n        for {set i 0} {$i <  256} {incr i} { r sadd myhashset [format \"i%03d\" $i] }\n        assert_encoding intset myintset\n        assert_encoding hashtable mylargeintset\n        assert_encoding hashtable myhashset\n\n        r debug reload\n        assert_encoding intset myintset\n        assert_encoding hashtable mylargeintset\n        assert_encoding hashtable myhashset\n    }\n\n    test {SREM basics - regular set} {\n        create_set myset {foo bar ciao}\n        assert_encoding hashtable myset\n        assert_equal 0 [r srem myset qux]\n        assert_equal 1 [r srem myset foo]\n        assert_equal {bar ciao} [lsort [r smembers myset]]\n    }\n\n    test {SREM basics - intset} {\n        create_set myset {3 4 5}\n        assert_encoding intset myset\n        assert_equal 0 [r srem myset 6]\n        assert_equal 1 [r srem myset 4]\n        assert_equal {3 5} [lsort [r smembers myset]]\n    }\n\n    test {SREM with multiple arguments} {\n        r del myset\n        r sadd myset a b c d\n        assert_equal 0 [r srem myset k k k]\n        assert_equal 2 [r srem myset b d x y]\n        lsort [r smembers myset]\n    } {a c}\n\n    test {SREM variadic version with more args needed to destroy the key} {\n        r del myset\n        r sadd myset 1 2 3\n        r srem myset 1 2 3 4 5 6 7 8\n    } {3}\n\n    foreach {type} {hashtable intset} {\n        for {set i 1} {$i <= 5} {incr i} {\n            r del [format \"set%d\" $i]\n        }\n        for {set i 0} {$i < 200} {incr i} {\n            r sadd set1 $i\n            r sadd set2 [expr $i+195]\n        }\n        foreach i {199 195 1000 2000} {\n            r sadd set3 $i\n        }\n        for {set i 5} {$i < 200} {incr i} {\n            r sadd set4 $i\n        }\n        r sadd set5 0\n\n        # To make sure the sets are encoded as the type we are testing -- also\n        # when the VM is enabled and the values may be swapped in and out\n        # while the tests are running -- an extra element is added to every\n        # set that determines its encoding.\n        set large 200\n        if {$type eq \"hashtable\"} {\n            set large foo\n        }\n\n        for {set i 1} {$i <= 5} {incr i} {\n            r sadd [format \"set%d\" $i] $large\n        }\n\n        test \"Generated sets must be encoded as $type\" {\n            for {set i 1} {$i <= 5} {incr i} {\n                assert_encoding $type [format \"set%d\" $i]\n            }\n        }\n\n        test \"SINTER with two sets - $type\" {\n            assert_equal [list 195 196 197 198 199 $large] [lsort [r sinter set1 set2]]\n        }\n\n        test \"SINTERSTORE with two sets - $type\" {\n            r sinterstore setres set1 set2\n            assert_encoding $type setres\n            assert_equal [list 195 196 197 198 199 $large] [lsort [r smembers setres]]\n        }\n\n        test \"SINTERSTORE with two sets, after a DEBUG RELOAD - $type\" {\n            r debug reload\n            r sinterstore setres set1 set2\n            assert_encoding $type setres\n            assert_equal [list 195 196 197 198 199 $large] [lsort [r smembers setres]]\n        }\n\n        test \"SUNION with two sets - $type\" {\n            set expected [lsort -uniq \"[r smembers set1] [r smembers set2]\"]\n            assert_equal $expected [lsort [r sunion set1 set2]]\n        }\n\n        test \"SUNIONSTORE with two sets - $type\" {\n            r sunionstore setres set1 set2\n            assert_encoding $type setres\n            set expected [lsort -uniq \"[r smembers set1] [r smembers set2]\"]\n            assert_equal $expected [lsort [r smembers setres]]\n        }\n\n        test \"SINTER against three sets - $type\" {\n            assert_equal [list 195 199 $large] [lsort [r sinter set1 set2 set3]]\n        }\n\n        test \"SINTERSTORE with three sets - $type\" {\n            r sinterstore setres set1 set2 set3\n            assert_equal [list 195 199 $large] [lsort [r smembers setres]]\n        }\n\n        test \"SUNION with non existing keys - $type\" {\n            set expected [lsort -uniq \"[r smembers set1] [r smembers set2]\"]\n            assert_equal $expected [lsort [r sunion nokey1 set1 set2 nokey2]]\n        }\n\n        test \"SDIFF with two sets - $type\" {\n            assert_equal {0 1 2 3 4} [lsort [r sdiff set1 set4]]\n        }\n\n        test \"SDIFF with three sets - $type\" {\n            assert_equal {1 2 3 4} [lsort [r sdiff set1 set4 set5]]\n        }\n\n        test \"SDIFFSTORE with three sets - $type\" {\n            r sdiffstore setres set1 set4 set5\n            # When we start with intsets, we should always end with intsets.\n            if {$type eq {intset}} {\n                assert_encoding intset setres\n            }\n            assert_equal {1 2 3 4} [lsort [r smembers setres]]\n        }\n    }\n\n    test \"SDIFF with first set empty\" {\n        r del set1 set2 set3\n        r sadd set2 1 2 3 4\n        r sadd set3 a b c d\n        r sdiff set1 set2 set3\n    } {}\n\n    test \"SDIFF with same set two times\" {\n        r del set1\n        r sadd set1 a b c 1 2 3 4 5 6\n        r sdiff set1 set1\n    } {}\n\n    test \"SDIFF fuzzing\" {\n        for {set j 0} {$j < 100} {incr j} {\n            unset -nocomplain s\n            array set s {}\n            set args {}\n            set num_sets [expr {[randomInt 10]+1}]\n            for {set i 0} {$i < $num_sets} {incr i} {\n                set num_elements [randomInt 100]\n                r del set_$i\n                lappend args set_$i\n                while {$num_elements} {\n                    set ele [randomValue]\n                    r sadd set_$i $ele\n                    if {$i == 0} {\n                        set s($ele) x\n                    } else {\n                        unset -nocomplain s($ele)\n                    }\n                    incr num_elements -1\n                }\n            }\n            set result [lsort [r sdiff {*}$args]]\n            assert_equal $result [lsort [array names s]]\n        }\n    }\n\n    test \"SINTER against non-set should throw error\" {\n        r set key1 x\n        assert_error \"WRONGTYPE*\" {r sinter key1 noset}\n    }\n\n    test \"SUNION against non-set should throw error\" {\n        r set key1 x\n        assert_error \"WRONGTYPE*\" {r sunion key1 noset}\n    }\n\n    test \"SINTER should handle non existing key as empty\" {\n        r del set1 set2 set3\n        r sadd set1 a b c\n        r sadd set2 b c d\n        r sinter set1 set2 set3\n    } {}\n\n    test \"SINTER with same integer elements but different encoding\" {\n        r del set1 set2\n        r sadd set1 1 2 3\n        r sadd set2 1 2 3 a\n        r srem set2 a\n        assert_encoding intset set1\n        assert_encoding hashtable set2\n        lsort [r sinter set1 set2]\n    } {1 2 3}\n\n    test \"SINTERSTORE against non existing keys should delete dstkey\" {\n        r set setres xxx\n        assert_equal 0 [r sinterstore setres foo111 bar222]\n        assert_equal 0 [r exists setres]\n    }\n\n    test \"SUNIONSTORE against non existing keys should delete dstkey\" {\n        r set setres xxx\n        assert_equal 0 [r sunionstore setres foo111 bar222]\n        assert_equal 0 [r exists setres]\n    }\n\n    foreach {type contents} {hashtable {a b c} intset {1 2 3}} {\n        test \"SPOP basics - $type\" {\n            create_set myset $contents\n            assert_encoding $type myset\n            assert_equal $contents [lsort [list [r spop myset] [r spop myset] [r spop myset]]]\n            assert_equal 0 [r scard myset]\n        }\n\n        test \"SRANDMEMBER - $type\" {\n            create_set myset $contents\n            unset -nocomplain myset\n            array set myset {}\n            for {set i 0} {$i < 100} {incr i} {\n                set myset([r srandmember myset]) 1\n            }\n            assert_equal $contents [lsort [array names myset]]\n        }\n    }\n\n    test \"SRANDMEMBER with <count> against non existing key\" {\n        r srandmember nonexisting_key 100\n    } {}\n\n    foreach {type contents} {\n        hashtable {\n            1 5 10 50 125 50000 33959417 4775547 65434162\n            12098459 427716 483706 2726473884 72615637475\n            MARY PATRICIA LINDA BARBARA ELIZABETH JENNIFER MARIA\n            SUSAN MARGARET DOROTHY LISA NANCY KAREN BETTY HELEN\n            SANDRA DONNA CAROL RUTH SHARON MICHELLE LAURA SARAH\n            KIMBERLY DEBORAH JESSICA SHIRLEY CYNTHIA ANGELA MELISSA\n            BRENDA AMY ANNA REBECCA VIRGINIA KATHLEEN\n        }\n        intset {\n            0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19\n            20 21 22 23 24 25 26 27 28 29\n            30 31 32 33 34 35 36 37 38 39\n            40 41 42 43 44 45 46 47 48 49\n        }\n    } {\n        test \"SRANDMEMBER with <count> - $type\" {\n            create_set myset $contents\n            unset -nocomplain myset\n            array set myset {}\n            foreach ele [r smembers myset] {\n                set myset($ele) 1\n            }\n            assert_equal [lsort $contents] [lsort [array names myset]]\n\n            # Make sure that a count of 0 is handled correctly.\n            assert_equal [r srandmember myset 0] {}\n\n            # We'll stress different parts of the code, see the implementation\n            # of SRANDMEMBER for more information, but basically there are\n            # four different code paths.\n            #\n            # PATH 1: Use negative count.\n            #\n            # 1) Check that it returns repeated elements.\n            set res [r srandmember myset -100]\n            assert_equal [llength $res] 100\n\n            # 2) Check that all the elements actually belong to the\n            # original set.\n            foreach ele $res {\n                assert {[info exists myset($ele)]}\n            }\n\n            # 3) Check that eventually all the elements are returned.\n            unset -nocomplain auxset\n            set iterations 1000\n            while {$iterations != 0} {\n                incr iterations -1\n                set res [r srandmember myset -10]\n                foreach ele $res {\n                    set auxset($ele) 1\n                }\n                if {[lsort [array names myset]] eq\n                    [lsort [array names auxset]]} {\n                    break;\n                }\n            }\n            assert {$iterations != 0}\n\n            # PATH 2: positive count (unique behavior) with requested size\n            # equal or greater than set size.\n            foreach size {50 100} {\n                set res [r srandmember myset $size]\n                assert_equal [llength $res] 50\n                assert_equal [lsort $res] [lsort [array names myset]]\n            }\n\n            # PATH 3: Ask almost as elements as there are in the set.\n            # In this case the implementation will duplicate the original\n            # set and will remove random elements up to the requested size.\n            #\n            # PATH 4: Ask a number of elements definitely smaller than\n            # the set size.\n            #\n            # We can test both the code paths just changing the size but\n            # using the same code.\n\n            foreach size {45 5} {\n                set res [r srandmember myset $size]\n                assert_equal [llength $res] $size\n\n                # 1) Check that all the elements actually belong to the\n                # original set.\n                foreach ele $res {\n                    assert {[info exists myset($ele)]}\n                }\n\n                # 2) Check that eventually all the elements are returned.\n                unset -nocomplain auxset\n                set iterations 1000\n                while {$iterations != 0} {\n                    incr iterations -1\n                    set res [r srandmember myset -10]\n                    foreach ele $res {\n                        set auxset($ele) 1\n                    }\n                    if {[lsort [array names myset]] eq\n                        [lsort [array names auxset]]} {\n                        break;\n                    }\n                }\n                assert {$iterations != 0}\n            }\n        }\n    }\n\n    proc setup_move {} {\n        r del myset3 myset4\n        create_set myset1 {1 a b}\n        create_set myset2 {2 3 4}\n        assert_encoding hashtable myset1\n        assert_encoding intset myset2\n    }\n\n    test \"SMOVE basics - from regular set to intset\" {\n        # move a non-integer element to an intset should convert encoding\n        setup_move\n        assert_equal 1 [r smove myset1 myset2 a]\n        assert_equal {1 b} [lsort [r smembers myset1]]\n        assert_equal {2 3 4 a} [lsort [r smembers myset2]]\n        assert_encoding hashtable myset2\n\n        # move an integer element should not convert the encoding\n        setup_move\n        assert_equal 1 [r smove myset1 myset2 1]\n        assert_equal {a b} [lsort [r smembers myset1]]\n        assert_equal {1 2 3 4} [lsort [r smembers myset2]]\n        assert_encoding intset myset2\n    }\n\n    test \"SMOVE basics - from intset to regular set\" {\n        setup_move\n        assert_equal 1 [r smove myset2 myset1 2]\n        assert_equal {1 2 a b} [lsort [r smembers myset1]]\n        assert_equal {3 4} [lsort [r smembers myset2]]\n    }\n\n    test \"SMOVE non existing key\" {\n        setup_move\n        assert_equal 0 [r smove myset1 myset2 foo]\n        assert_equal {1 a b} [lsort [r smembers myset1]]\n        assert_equal {2 3 4} [lsort [r smembers myset2]]\n    }\n\n    test \"SMOVE non existing src set\" {\n        setup_move\n        assert_equal 0 [r smove noset myset2 foo]\n        assert_equal {2 3 4} [lsort [r smembers myset2]]\n    }\n\n    test \"SMOVE from regular set to non existing destination set\" {\n        setup_move\n        assert_equal 1 [r smove myset1 myset3 a]\n        assert_equal {1 b} [lsort [r smembers myset1]]\n        assert_equal {a} [lsort [r smembers myset3]]\n        assert_encoding hashtable myset3\n    }\n\n    test \"SMOVE from intset to non existing destination set\" {\n        setup_move\n        assert_equal 1 [r smove myset2 myset3 2]\n        assert_equal {3 4} [lsort [r smembers myset2]]\n        assert_equal {2} [lsort [r smembers myset3]]\n        assert_encoding intset myset3\n    }\n\n    test \"SMOVE wrong src key type\" {\n        r set x 10\n        assert_error \"WRONGTYPE*\" {r smove x myset2 foo}\n    }\n\n    test \"SMOVE wrong dst key type\" {\n        r set x 10\n        assert_error \"WRONGTYPE*\" {r smove myset2 x foo}\n    }\n\n    test \"SMOVE with identical source and destination\" {\n        r del set\n        r sadd set a b c\n        r smove set set b\n        lsort [r smembers set]\n    } {a b c}\n\n    tags {slow} {\n        test {intsets implementation stress testing} {\n            for {set j 0} {$j < 20} {incr j} {\n                unset -nocomplain s\n                array set s {}\n                r del s\n                set len [randomInt 1024]\n                for {set i 0} {$i < $len} {incr i} {\n                    randpath {\n                        set data [randomInt 65536]\n                    } {\n                        set data [randomInt 4294967296]\n                    } {\n                        set data [randomInt 18446744073709551616]\n                    }\n                    set s($data) {}\n                    r sadd s $data\n                }\n                assert_equal [lsort [r smembers s]] [lsort [array names s]]\n                set len [array size s]\n                for {set i 0} {$i < $len} {incr i} {\n                    set e [r spop s]\n                    if {![info exists s($e)]} {\n                        puts \"Can't find '$e' on local array\"\n                        puts \"Local array: [lsort [r smembers s]]\"\n                        puts \"Remote array: [lsort [array names s]]\"\n                        error \"exception\"\n                    }\n                    array unset s $e\n                }\n                assert_equal [r scard s] 0\n                assert_equal [array size s] 0\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "tests/unit/type/zset.tcl",
    "content": "start_server {tags {\"zset\"}} {\n    proc create_zset {key items} {\n        r del $key\n        foreach {score entry} $items {\n            r zadd $key $score $entry\n        }\n    }\n\n    proc basics {encoding} {\n        if {$encoding == \"ziplist\"} {\n            r config set zset-max-ziplist-entries 128\n            r config set zset-max-ziplist-value 64\n        } elseif {$encoding == \"skiplist\"} {\n            r config set zset-max-ziplist-entries 0\n            r config set zset-max-ziplist-value 0\n        } else {\n            puts \"Unknown sorted set encoding\"\n            exit\n        }\n\n        test \"Check encoding - $encoding\" {\n            r del ztmp\n            r zadd ztmp 10 x\n            assert_encoding $encoding ztmp\n        }\n\n        test \"ZSET basic ZADD and score update - $encoding\" {\n            r del ztmp\n            r zadd ztmp 10 x\n            r zadd ztmp 20 y\n            r zadd ztmp 30 z\n            assert_equal {x y z} [r zrange ztmp 0 -1]\n\n            r zadd ztmp 1 y\n            assert_equal {y x z} [r zrange ztmp 0 -1]\n        }\n\n        test \"ZSET element can't be set to NaN with ZADD - $encoding\" {\n            assert_error \"*not*float*\" {r zadd myzset nan abc}\n        }\n\n        test \"ZSET element can't be set to NaN with ZINCRBY\" {\n            assert_error \"*not*float*\" {r zadd myzset nan abc}\n        }\n\n        test \"ZINCRBY calls leading to NaN result in error\" {\n            r zincrby myzset +inf abc\n            assert_error \"*NaN*\" {r zincrby myzset -inf abc}\n        }\n\n        test {ZADD - Variadic version base case} {\n            r del myzset\n            list [r zadd myzset 10 a 20 b 30 c] [r zrange myzset 0 -1 withscores]\n        } {3 {a 10 b 20 c 30}}\n\n        test {ZADD - Return value is the number of actually added items} {\n            list [r zadd myzset 5 x 20 b 30 c] [r zrange myzset 0 -1 withscores]\n        } {1 {x 5 a 10 b 20 c 30}}\n\n        test {ZADD - Variadic version does not add nothing on single parsing err} {\n            r del myzset\n            catch {r zadd myzset 10 a 20 b 30.badscore c} e\n            assert_match {*ERR*not*float*} $e\n            r exists myzset\n        } {0}\n\n        test {ZADD - Variadic version will raise error on missing arg} {\n            r del myzset\n            catch {r zadd myzset 10 a 20 b 30 c 40} e\n            assert_match {*ERR*syntax*} $e\n        }\n\n        test {ZINCRBY does not work variadic even if shares ZADD implementation} {\n            r del myzset\n            catch {r zincrby myzset 10 a 20 b 30 c} e\n            assert_match {*ERR*wrong*number*arg*} $e\n        }\n\n        test \"ZCARD basics - $encoding\" {\n            assert_equal 3 [r zcard ztmp]\n            assert_equal 0 [r zcard zdoesntexist]\n        }\n\n        test \"ZREM removes key after last element is removed\" {\n            r del ztmp\n            r zadd ztmp 10 x\n            r zadd ztmp 20 y\n\n            assert_equal 1 [r exists ztmp]\n            assert_equal 0 [r zrem ztmp z]\n            assert_equal 1 [r zrem ztmp y]\n            assert_equal 1 [r zrem ztmp x]\n            assert_equal 0 [r exists ztmp]\n        }\n\n        test \"ZREM variadic version\" {\n            r del ztmp\n            r zadd ztmp 10 a 20 b 30 c\n            assert_equal 2 [r zrem ztmp x y a b k]\n            assert_equal 0 [r zrem ztmp foo bar]\n            assert_equal 1 [r zrem ztmp c]\n            r exists ztmp\n        } {0}\n\n        test \"ZREM variadic version -- remove elements after key deletion\" {\n            r del ztmp\n            r zadd ztmp 10 a 20 b 30 c\n            r zrem ztmp a b c d e f g\n        } {3}\n\n        test \"ZRANGE basics - $encoding\" {\n            r del ztmp\n            r zadd ztmp 1 a\n            r zadd ztmp 2 b\n            r zadd ztmp 3 c\n            r zadd ztmp 4 d\n\n            assert_equal {a b c d} [r zrange ztmp 0 -1]\n            assert_equal {a b c} [r zrange ztmp 0 -2]\n            assert_equal {b c d} [r zrange ztmp 1 -1]\n            assert_equal {b c} [r zrange ztmp 1 -2]\n            assert_equal {c d} [r zrange ztmp -2 -1]\n            assert_equal {c} [r zrange ztmp -2 -2]\n\n            # out of range start index\n            assert_equal {a b c} [r zrange ztmp -5 2]\n            assert_equal {a b} [r zrange ztmp -5 1]\n            assert_equal {} [r zrange ztmp 5 -1]\n            assert_equal {} [r zrange ztmp 5 -2]\n\n            # out of range end index\n            assert_equal {a b c d} [r zrange ztmp 0 5]\n            assert_equal {b c d} [r zrange ztmp 1 5]\n            assert_equal {} [r zrange ztmp 0 -5]\n            assert_equal {} [r zrange ztmp 1 -5]\n\n            # withscores\n            assert_equal {a 1 b 2 c 3 d 4} [r zrange ztmp 0 -1 withscores]\n        }\n\n        test \"ZREVRANGE basics - $encoding\" {\n            r del ztmp\n            r zadd ztmp 1 a\n            r zadd ztmp 2 b\n            r zadd ztmp 3 c\n            r zadd ztmp 4 d\n\n            assert_equal {d c b a} [r zrevrange ztmp 0 -1]\n            assert_equal {d c b} [r zrevrange ztmp 0 -2]\n            assert_equal {c b a} [r zrevrange ztmp 1 -1]\n            assert_equal {c b} [r zrevrange ztmp 1 -2]\n            assert_equal {b a} [r zrevrange ztmp -2 -1]\n            assert_equal {b} [r zrevrange ztmp -2 -2]\n\n            # out of range start index\n            assert_equal {d c b} [r zrevrange ztmp -5 2]\n            assert_equal {d c} [r zrevrange ztmp -5 1]\n            assert_equal {} [r zrevrange ztmp 5 -1]\n            assert_equal {} [r zrevrange ztmp 5 -2]\n\n            # out of range end index\n            assert_equal {d c b a} [r zrevrange ztmp 0 5]\n            assert_equal {c b a} [r zrevrange ztmp 1 5]\n            assert_equal {} [r zrevrange ztmp 0 -5]\n            assert_equal {} [r zrevrange ztmp 1 -5]\n\n            # withscores\n            assert_equal {d 4 c 3 b 2 a 1} [r zrevrange ztmp 0 -1 withscores]\n        }\n\n        test \"ZRANK/ZREVRANK basics - $encoding\" {\n            r del zranktmp\n            r zadd zranktmp 10 x\n            r zadd zranktmp 20 y\n            r zadd zranktmp 30 z\n            assert_equal 0 [r zrank zranktmp x]\n            assert_equal 1 [r zrank zranktmp y]\n            assert_equal 2 [r zrank zranktmp z]\n            assert_equal \"\" [r zrank zranktmp foo]\n            assert_equal 2 [r zrevrank zranktmp x]\n            assert_equal 1 [r zrevrank zranktmp y]\n            assert_equal 0 [r zrevrank zranktmp z]\n            assert_equal \"\" [r zrevrank zranktmp foo]\n        }\n\n        test \"ZRANK - after deletion - $encoding\" {\n            r zrem zranktmp y\n            assert_equal 0 [r zrank zranktmp x]\n            assert_equal 1 [r zrank zranktmp z]\n        }\n\n        test \"ZINCRBY - can create a new sorted set - $encoding\" {\n            r del zset\n            r zincrby zset 1 foo\n            assert_equal {foo} [r zrange zset 0 -1]\n            assert_equal 1 [r zscore zset foo]\n        }\n\n        test \"ZINCRBY - increment and decrement - $encoding\" {\n            r zincrby zset 2 foo\n            r zincrby zset 1 bar\n            assert_equal {bar foo} [r zrange zset 0 -1]\n\n            r zincrby zset 10 bar\n            r zincrby zset -5 foo\n            r zincrby zset -5 bar\n            assert_equal {foo bar} [r zrange zset 0 -1]\n\n            assert_equal -2 [r zscore zset foo]\n            assert_equal  6 [r zscore zset bar]\n        }\n\n        proc create_default_zset {} {\n            create_zset zset {-inf a 1 b 2 c 3 d 4 e 5 f +inf g}\n        }\n\n        test \"ZRANGEBYSCORE/ZREVRANGEBYSCORE/ZCOUNT basics\" {\n            create_default_zset\n\n            # inclusive range\n            assert_equal {a b c} [r zrangebyscore zset -inf 2]\n            assert_equal {b c d} [r zrangebyscore zset 0 3]\n            assert_equal {d e f} [r zrangebyscore zset 3 6]\n            assert_equal {e f g} [r zrangebyscore zset 4 +inf]\n            assert_equal {c b a} [r zrevrangebyscore zset 2 -inf]\n            assert_equal {d c b} [r zrevrangebyscore zset 3 0]\n            assert_equal {f e d} [r zrevrangebyscore zset 6 3]\n            assert_equal {g f e} [r zrevrangebyscore zset +inf 4]\n            assert_equal 3 [r zcount zset 0 3]\n\n            # exclusive range\n            assert_equal {b}   [r zrangebyscore zset (-inf (2]\n            assert_equal {b c} [r zrangebyscore zset (0 (3]\n            assert_equal {e f} [r zrangebyscore zset (3 (6]\n            assert_equal {f}   [r zrangebyscore zset (4 (+inf]\n            assert_equal {b}   [r zrevrangebyscore zset (2 (-inf]\n            assert_equal {c b} [r zrevrangebyscore zset (3 (0]\n            assert_equal {f e} [r zrevrangebyscore zset (6 (3]\n            assert_equal {f}   [r zrevrangebyscore zset (+inf (4]\n            assert_equal 2 [r zcount zset (0 (3]\n\n            # test empty ranges\n            r zrem zset a\n            r zrem zset g\n\n            # inclusive\n            assert_equal {} [r zrangebyscore zset 4 2]\n            assert_equal {} [r zrangebyscore zset 6 +inf]\n            assert_equal {} [r zrangebyscore zset -inf -6]\n            assert_equal {} [r zrevrangebyscore zset +inf 6]\n            assert_equal {} [r zrevrangebyscore zset -6 -inf]\n\n            # exclusive\n            assert_equal {} [r zrangebyscore zset (4 (2]\n            assert_equal {} [r zrangebyscore zset 2 (2]\n            assert_equal {} [r zrangebyscore zset (2 2]\n            assert_equal {} [r zrangebyscore zset (6 (+inf]\n            assert_equal {} [r zrangebyscore zset (-inf (-6]\n            assert_equal {} [r zrevrangebyscore zset (+inf (6]\n            assert_equal {} [r zrevrangebyscore zset (-6 (-inf]\n\n            # empty inner range\n            assert_equal {} [r zrangebyscore zset 2.4 2.6]\n            assert_equal {} [r zrangebyscore zset (2.4 2.6]\n            assert_equal {} [r zrangebyscore zset 2.4 (2.6]\n            assert_equal {} [r zrangebyscore zset (2.4 (2.6]\n        }\n\n        test \"ZRANGEBYSCORE with WITHSCORES\" {\n            create_default_zset\n            assert_equal {b 1 c 2 d 3} [r zrangebyscore zset 0 3 withscores]\n            assert_equal {d 3 c 2 b 1} [r zrevrangebyscore zset 3 0 withscores]\n        }\n\n        test \"ZRANGEBYSCORE with LIMIT\" {\n            create_default_zset\n            assert_equal {b c}   [r zrangebyscore zset 0 10 LIMIT 0 2]\n            assert_equal {d e f} [r zrangebyscore zset 0 10 LIMIT 2 3]\n            assert_equal {d e f} [r zrangebyscore zset 0 10 LIMIT 2 10]\n            assert_equal {}      [r zrangebyscore zset 0 10 LIMIT 20 10]\n            assert_equal {f e}   [r zrevrangebyscore zset 10 0 LIMIT 0 2]\n            assert_equal {d c b} [r zrevrangebyscore zset 10 0 LIMIT 2 3]\n            assert_equal {d c b} [r zrevrangebyscore zset 10 0 LIMIT 2 10]\n            assert_equal {}      [r zrevrangebyscore zset 10 0 LIMIT 20 10]\n        }\n\n        test \"ZRANGEBYSCORE with LIMIT and WITHSCORES\" {\n            create_default_zset\n            assert_equal {e 4 f 5} [r zrangebyscore zset 2 5 LIMIT 2 3 WITHSCORES]\n            assert_equal {d 3 c 2} [r zrevrangebyscore zset 5 2 LIMIT 2 3 WITHSCORES]\n        }\n\n        test \"ZRANGEBYSCORE with non-value min or max\" {\n            assert_error \"*not*float*\" {r zrangebyscore fooz str 1}\n            assert_error \"*not*float*\" {r zrangebyscore fooz 1 str}\n            assert_error \"*not*float*\" {r zrangebyscore fooz 1 NaN}\n        }\n\n        proc create_default_lex_zset {} {\n            create_zset zset {0 alpha 0 bar 0 cool 0 down\n                              0 elephant 0 foo 0 great 0 hill\n                              0 omega}\n        }\n\n        test \"ZRANGEBYLEX/ZREVRANGEBYLEX/ZCOUNT basics\" {\n            create_default_lex_zset\n\n            # inclusive range\n            assert_equal {alpha bar cool} [r zrangebylex zset - \\[cool]\n            assert_equal {bar cool down} [r zrangebylex zset \\[bar \\[down]\n            assert_equal {great hill omega} [r zrangebylex zset \\[g +]\n            assert_equal {cool bar alpha} [r zrevrangebylex zset \\[cool -]\n            assert_equal {down cool bar} [r zrevrangebylex zset \\[down \\[bar]\n            assert_equal {omega hill great foo elephant down} [r zrevrangebylex zset + \\[d]\n            assert_equal 3 [r zlexcount zset \\[ele \\[h]\n\n            # exclusive range\n            assert_equal {alpha bar} [r zrangebylex zset - (cool]\n            assert_equal {cool} [r zrangebylex zset (bar (down]\n            assert_equal {hill omega} [r zrangebylex zset (great +]\n            assert_equal {bar alpha} [r zrevrangebylex zset (cool -]\n            assert_equal {cool} [r zrevrangebylex zset (down (bar]\n            assert_equal {omega hill} [r zrevrangebylex zset + (great]\n            assert_equal 2 [r zlexcount zset (ele (great]\n\n            # inclusive and exclusive\n            assert_equal {} [r zrangebylex zset (az (b]\n            assert_equal {} [r zrangebylex zset (z +]\n            assert_equal {} [r zrangebylex zset - \\[aaaa]\n            assert_equal {} [r zrevrangebylex zset \\[elez \\[elex]\n            assert_equal {} [r zrevrangebylex zset (hill (omega]\n        }\n\n        test \"ZRANGEBYSLEX with LIMIT\" {\n            create_default_lex_zset\n            assert_equal {alpha bar} [r zrangebylex zset - \\[cool LIMIT 0 2]\n            assert_equal {bar cool} [r zrangebylex zset - \\[cool LIMIT 1 2]\n            assert_equal {} [r zrangebylex zset \\[bar \\[down LIMIT 0 0]\n            assert_equal {} [r zrangebylex zset \\[bar \\[down LIMIT 2 0]\n            assert_equal {bar} [r zrangebylex zset \\[bar \\[down LIMIT 0 1]\n            assert_equal {cool} [r zrangebylex zset \\[bar \\[down LIMIT 1 1]\n            assert_equal {bar cool down} [r zrangebylex zset \\[bar \\[down LIMIT 0 100]\n            assert_equal {omega hill great foo elephant} [r zrevrangebylex zset + \\[d LIMIT 0 5]\n            assert_equal {omega hill great foo} [r zrevrangebylex zset + \\[d LIMIT 0 4]\n        }\n\n        test \"ZRANGEBYLEX with invalid lex range specifiers\" {\n            assert_error \"*not*string*\" {r zrangebylex fooz foo bar}\n            assert_error \"*not*string*\" {r zrangebylex fooz \\[foo bar}\n            assert_error \"*not*string*\" {r zrangebylex fooz foo \\[bar}\n            assert_error \"*not*string*\" {r zrangebylex fooz +x \\[bar}\n            assert_error \"*not*string*\" {r zrangebylex fooz -x \\[bar}\n        }\n\n        test \"ZREMRANGEBYSCORE basics\" {\n            proc remrangebyscore {min max} {\n                create_zset zset {1 a 2 b 3 c 4 d 5 e}\n                assert_equal 1 [r exists zset]\n                r zremrangebyscore zset $min $max\n            }\n\n            # inner range\n            assert_equal 3 [remrangebyscore 2 4]\n            assert_equal {a e} [r zrange zset 0 -1]\n\n            # start underflow\n            assert_equal 1 [remrangebyscore -10 1]\n            assert_equal {b c d e} [r zrange zset 0 -1]\n\n            # end overflow\n            assert_equal 1 [remrangebyscore 5 10]\n            assert_equal {a b c d} [r zrange zset 0 -1]\n\n            # switch min and max\n            assert_equal 0 [remrangebyscore 4 2]\n            assert_equal {a b c d e} [r zrange zset 0 -1]\n\n            # -inf to mid\n            assert_equal 3 [remrangebyscore -inf 3]\n            assert_equal {d e} [r zrange zset 0 -1]\n\n            # mid to +inf\n            assert_equal 3 [remrangebyscore 3 +inf]\n            assert_equal {a b} [r zrange zset 0 -1]\n\n            # -inf to +inf\n            assert_equal 5 [remrangebyscore -inf +inf]\n            assert_equal {} [r zrange zset 0 -1]\n\n            # exclusive min\n            assert_equal 4 [remrangebyscore (1 5]\n            assert_equal {a} [r zrange zset 0 -1]\n            assert_equal 3 [remrangebyscore (2 5]\n            assert_equal {a b} [r zrange zset 0 -1]\n\n            # exclusive max\n            assert_equal 4 [remrangebyscore 1 (5]\n            assert_equal {e} [r zrange zset 0 -1]\n            assert_equal 3 [remrangebyscore 1 (4]\n            assert_equal {d e} [r zrange zset 0 -1]\n\n            # exclusive min and max\n            assert_equal 3 [remrangebyscore (1 (5]\n            assert_equal {a e} [r zrange zset 0 -1]\n\n            # destroy when empty\n            assert_equal 5 [remrangebyscore 1 5]\n            assert_equal 0 [r exists zset]\n        }\n\n        test \"ZREMRANGEBYSCORE with non-value min or max\" {\n            assert_error \"*not*float*\" {r zremrangebyscore fooz str 1}\n            assert_error \"*not*float*\" {r zremrangebyscore fooz 1 str}\n            assert_error \"*not*float*\" {r zremrangebyscore fooz 1 NaN}\n        }\n\n        test \"ZREMRANGEBYRANK basics\" {\n            proc remrangebyrank {min max} {\n                create_zset zset {1 a 2 b 3 c 4 d 5 e}\n                assert_equal 1 [r exists zset]\n                r zremrangebyrank zset $min $max\n            }\n\n            # inner range\n            assert_equal 3 [remrangebyrank 1 3]\n            assert_equal {a e} [r zrange zset 0 -1]\n\n            # start underflow\n            assert_equal 1 [remrangebyrank -10 0]\n            assert_equal {b c d e} [r zrange zset 0 -1]\n\n            # start overflow\n            assert_equal 0 [remrangebyrank 10 -1]\n            assert_equal {a b c d e} [r zrange zset 0 -1]\n\n            # end underflow\n            assert_equal 0 [remrangebyrank 0 -10]\n            assert_equal {a b c d e} [r zrange zset 0 -1]\n\n            # end overflow\n            assert_equal 5 [remrangebyrank 0 10]\n            assert_equal {} [r zrange zset 0 -1]\n\n            # destroy when empty\n            assert_equal 5 [remrangebyrank 0 4]\n            assert_equal 0 [r exists zset]\n        }\n\n        test \"ZUNIONSTORE against non-existing key doesn't set destination - $encoding\" {\n            r del zseta\n            assert_equal 0 [r zunionstore dst_key 1 zseta]\n            assert_equal 0 [r exists dst_key]\n        }\n\n        test \"ZUNIONSTORE with empty set - $encoding\" {\n            r del zseta zsetb\n            r zadd zseta 1 a\n            r zadd zseta 2 b\n            r zunionstore zsetc 2 zseta zsetb\n            r zrange zsetc 0 -1 withscores\n        } {a 1 b 2}\n\n        test \"ZUNIONSTORE basics - $encoding\" {\n            r del zseta zsetb zsetc\n            r zadd zseta 1 a\n            r zadd zseta 2 b\n            r zadd zseta 3 c\n            r zadd zsetb 1 b\n            r zadd zsetb 2 c\n            r zadd zsetb 3 d\n\n            assert_equal 4 [r zunionstore zsetc 2 zseta zsetb]\n            assert_equal {a 1 b 3 d 3 c 5} [r zrange zsetc 0 -1 withscores]\n        }\n\n        test \"ZUNIONSTORE with weights - $encoding\" {\n            assert_equal 4 [r zunionstore zsetc 2 zseta zsetb weights 2 3]\n            assert_equal {a 2 b 7 d 9 c 12} [r zrange zsetc 0 -1 withscores]\n        }\n\n        test \"ZUNIONSTORE with a regular set and weights - $encoding\" {\n            r del seta\n            r sadd seta a\n            r sadd seta b\n            r sadd seta c\n\n            assert_equal 4 [r zunionstore zsetc 2 seta zsetb weights 2 3]\n            assert_equal {a 2 b 5 c 8 d 9} [r zrange zsetc 0 -1 withscores]\n        }\n\n        test \"ZUNIONSTORE with AGGREGATE MIN - $encoding\" {\n            assert_equal 4 [r zunionstore zsetc 2 zseta zsetb aggregate min]\n            assert_equal {a 1 b 1 c 2 d 3} [r zrange zsetc 0 -1 withscores]\n        }\n\n        test \"ZUNIONSTORE with AGGREGATE MAX - $encoding\" {\n            assert_equal 4 [r zunionstore zsetc 2 zseta zsetb aggregate max]\n            assert_equal {a 1 b 2 c 3 d 3} [r zrange zsetc 0 -1 withscores]\n        }\n\n        test \"ZINTERSTORE basics - $encoding\" {\n            assert_equal 2 [r zinterstore zsetc 2 zseta zsetb]\n            assert_equal {b 3 c 5} [r zrange zsetc 0 -1 withscores]\n        }\n\n        test \"ZINTERSTORE with weights - $encoding\" {\n            assert_equal 2 [r zinterstore zsetc 2 zseta zsetb weights 2 3]\n            assert_equal {b 7 c 12} [r zrange zsetc 0 -1 withscores]\n        }\n\n        test \"ZINTERSTORE with a regular set and weights - $encoding\" {\n            r del seta\n            r sadd seta a\n            r sadd seta b\n            r sadd seta c\n            assert_equal 2 [r zinterstore zsetc 2 seta zsetb weights 2 3]\n            assert_equal {b 5 c 8} [r zrange zsetc 0 -1 withscores]\n        }\n\n        test \"ZINTERSTORE with AGGREGATE MIN - $encoding\" {\n            assert_equal 2 [r zinterstore zsetc 2 zseta zsetb aggregate min]\n            assert_equal {b 1 c 2} [r zrange zsetc 0 -1 withscores]\n        }\n\n        test \"ZINTERSTORE with AGGREGATE MAX - $encoding\" {\n            assert_equal 2 [r zinterstore zsetc 2 zseta zsetb aggregate max]\n            assert_equal {b 2 c 3} [r zrange zsetc 0 -1 withscores]\n        }\n\n        foreach cmd {ZUNIONSTORE ZINTERSTORE} {\n            test \"$cmd with +inf/-inf scores - $encoding\" {\n                r del zsetinf1 zsetinf2\n\n                r zadd zsetinf1 +inf key\n                r zadd zsetinf2 +inf key\n                r $cmd zsetinf3 2 zsetinf1 zsetinf2\n                assert_equal inf [r zscore zsetinf3 key]\n\n                r zadd zsetinf1 -inf key\n                r zadd zsetinf2 +inf key\n                r $cmd zsetinf3 2 zsetinf1 zsetinf2\n                assert_equal 0 [r zscore zsetinf3 key]\n\n                r zadd zsetinf1 +inf key\n                r zadd zsetinf2 -inf key\n                r $cmd zsetinf3 2 zsetinf1 zsetinf2\n                assert_equal 0 [r zscore zsetinf3 key]\n\n                r zadd zsetinf1 -inf key\n                r zadd zsetinf2 -inf key\n                r $cmd zsetinf3 2 zsetinf1 zsetinf2\n                assert_equal -inf [r zscore zsetinf3 key]\n            }\n\n            test \"$cmd with NaN weights $encoding\" {\n                r del zsetinf1 zsetinf2\n\n                r zadd zsetinf1 1.0 key\n                r zadd zsetinf2 1.0 key\n                assert_error \"*weight*not*float*\" {\n                    r $cmd zsetinf3 2 zsetinf1 zsetinf2 weights nan nan\n                }\n            }\n        }\n    }\n\n    basics ziplist\n    basics skiplist\n\n    test {ZINTERSTORE regression with two sets, intset+hashtable} {\n        r del seta setb setc\n        r sadd set1 a\n        r sadd set2 10\n        r zinterstore set3 2 set1 set2\n    } {0}\n\n    test {ZUNIONSTORE regression, should not create NaN in scores} {\n        r zadd z -inf neginf\n        r zunionstore out 1 z weights 0\n        r zrange out 0 -1 withscores\n    } {neginf 0}\n\n    test {ZINTERSTORE #516 regression, mixed sets and ziplist zsets} {\n        r sadd one 100 101 102 103\n        r sadd two 100 200 201 202\n        r zadd three 1 500 1 501 1 502 1 503 1 100\n        r zinterstore to_here 3 one two three WEIGHTS 0 0 1\n        r zrange to_here 0 -1\n    } {100}\n\n    proc stressers {encoding} {\n        if {$encoding == \"ziplist\"} {\n            # Little extra to allow proper fuzzing in the sorting stresser\n            r config set zset-max-ziplist-entries 256\n            r config set zset-max-ziplist-value 64\n            set elements 128\n        } elseif {$encoding == \"skiplist\"} {\n            r config set zset-max-ziplist-entries 0\n            r config set zset-max-ziplist-value 0\n            if {$::accurate} {set elements 1000} else {set elements 100}\n        } else {\n            puts \"Unknown sorted set encoding\"\n            exit\n        }\n\n        test \"ZSCORE - $encoding\" {\n            r del zscoretest\n            set aux {}\n            for {set i 0} {$i < $elements} {incr i} {\n                set score [expr rand()]\n                lappend aux $score\n                r zadd zscoretest $score $i\n            }\n\n            assert_encoding $encoding zscoretest\n            for {set i 0} {$i < $elements} {incr i} {\n                assert_equal [lindex $aux $i] [r zscore zscoretest $i]\n            }\n        }\n\n        test \"ZSCORE after a DEBUG RELOAD - $encoding\" {\n            r del zscoretest\n            set aux {}\n            for {set i 0} {$i < $elements} {incr i} {\n                set score [expr rand()]\n                lappend aux $score\n                r zadd zscoretest $score $i\n            }\n\n            r debug reload\n            assert_encoding $encoding zscoretest\n            for {set i 0} {$i < $elements} {incr i} {\n                assert_equal [lindex $aux $i] [r zscore zscoretest $i]\n            }\n        }\n\n        test \"ZSET sorting stresser - $encoding\" {\n            set delta 0\n            for {set test 0} {$test < 2} {incr test} {\n                unset -nocomplain auxarray\n                array set auxarray {}\n                set auxlist {}\n                r del myzset\n                for {set i 0} {$i < $elements} {incr i} {\n                    if {$test == 0} {\n                        set score [expr rand()]\n                    } else {\n                        set score [expr int(rand()*10)]\n                    }\n                    set auxarray($i) $score\n                    r zadd myzset $score $i\n                    # Random update\n                    if {[expr rand()] < .2} {\n                        set j [expr int(rand()*1000)]\n                        if {$test == 0} {\n                            set score [expr rand()]\n                        } else {\n                            set score [expr int(rand()*10)]\n                        }\n                        set auxarray($j) $score\n                        r zadd myzset $score $j\n                    }\n                }\n                foreach {item score} [array get auxarray] {\n                    lappend auxlist [list $score $item]\n                }\n                set sorted [lsort -command zlistAlikeSort $auxlist]\n                set auxlist {}\n                foreach x $sorted {\n                    lappend auxlist [lindex $x 1]\n                }\n\n                assert_encoding $encoding myzset\n                set fromredis [r zrange myzset 0 -1]\n                set delta 0\n                for {set i 0} {$i < [llength $fromredis]} {incr i} {\n                    if {[lindex $fromredis $i] != [lindex $auxlist $i]} {\n                        incr delta\n                    }\n                }\n            }\n            assert_equal 0 $delta\n        }\n\n        test \"ZRANGEBYSCORE fuzzy test, 100 ranges in $elements element sorted set - $encoding\" {\n            set err {}\n            r del zset\n            for {set i 0} {$i < $elements} {incr i} {\n                r zadd zset [expr rand()] $i\n            }\n\n            assert_encoding $encoding zset\n            for {set i 0} {$i < 100} {incr i} {\n                set min [expr rand()]\n                set max [expr rand()]\n                if {$min > $max} {\n                    set aux $min\n                    set min $max\n                    set max $aux\n                }\n                set low [r zrangebyscore zset -inf $min]\n                set ok [r zrangebyscore zset $min $max]\n                set high [r zrangebyscore zset $max +inf]\n                set lowx [r zrangebyscore zset -inf ($min]\n                set okx [r zrangebyscore zset ($min ($max]\n                set highx [r zrangebyscore zset ($max +inf]\n\n                if {[r zcount zset -inf $min] != [llength $low]} {\n                    append err \"Error, len does not match zcount\\n\"\n                }\n                if {[r zcount zset $min $max] != [llength $ok]} {\n                    append err \"Error, len does not match zcount\\n\"\n                }\n                if {[r zcount zset $max +inf] != [llength $high]} {\n                    append err \"Error, len does not match zcount\\n\"\n                }\n                if {[r zcount zset -inf ($min] != [llength $lowx]} {\n                    append err \"Error, len does not match zcount\\n\"\n                }\n                if {[r zcount zset ($min ($max] != [llength $okx]} {\n                    append err \"Error, len does not match zcount\\n\"\n                }\n                if {[r zcount zset ($max +inf] != [llength $highx]} {\n                    append err \"Error, len does not match zcount\\n\"\n                }\n\n                foreach x $low {\n                    set score [r zscore zset $x]\n                    if {$score > $min} {\n                        append err \"Error, score for $x is $score > $min\\n\"\n                    }\n                }\n                foreach x $lowx {\n                    set score [r zscore zset $x]\n                    if {$score >= $min} {\n                        append err \"Error, score for $x is $score >= $min\\n\"\n                    }\n                }\n                foreach x $ok {\n                    set score [r zscore zset $x]\n                    if {$score < $min || $score > $max} {\n                        append err \"Error, score for $x is $score outside $min-$max range\\n\"\n                    }\n                }\n                foreach x $okx {\n                    set score [r zscore zset $x]\n                    if {$score <= $min || $score >= $max} {\n                        append err \"Error, score for $x is $score outside $min-$max open range\\n\"\n                    }\n                }\n                foreach x $high {\n                    set score [r zscore zset $x]\n                    if {$score < $max} {\n                        append err \"Error, score for $x is $score < $max\\n\"\n                    }\n                }\n                foreach x $highx {\n                    set score [r zscore zset $x]\n                    if {$score <= $max} {\n                        append err \"Error, score for $x is $score <= $max\\n\"\n                    }\n                }\n            }\n            assert_equal {} $err\n        }\n\n        test \"ZRANGEBYLEX fuzzy test, 100 ranges in $elements element sorted set - $encoding\" {\n            set lexset {}\n            r del zset\n            for {set j 0} {$j < $elements} {incr j} {\n                set e [randstring 0 30 alpha]\n                lappend lexset $e\n                r zadd zset 0 $e\n            }\n            set lexset [lsort -unique $lexset]\n            for {set j 0} {$j < 100} {incr j} {\n                set min [randstring 0 30 alpha]\n                set max [randstring 0 30 alpha]\n                set mininc [randomInt 2]\n                set maxinc [randomInt 2]\n                if {$mininc} {set cmin \"\\[$min\"} else {set cmin \"($min\"}\n                if {$maxinc} {set cmax \"\\[$max\"} else {set cmax \"($max\"}\n                set rev [randomInt 2]\n                if {$rev} {\n                    set cmd zrevrangebylex\n                } else {\n                    set cmd zrangebylex\n                }\n\n                # Make sure data is the same in both sides\n                assert {[r zrange zset 0 -1] eq $lexset}\n\n                # Get the Redis output\n                set output [r $cmd zset $cmin $cmax]\n                if {$rev} {\n                    set outlen [r zlexcount zset $cmax $cmin]\n                } else {\n                    set outlen [r zlexcount zset $cmin $cmax]\n                }\n\n                # Compute the same output via Tcl\n                set o {}\n                set copy $lexset\n                if {(!$rev && [string compare $min $max] > 0) ||\n                    ($rev && [string compare $max $min] > 0)} {\n                    # Empty output when ranges are inverted.\n                } else {\n                    if {$rev} {\n                        # Invert the Tcl array using Redis itself.\n                        set copy [r zrevrange zset 0 -1]\n                        # Invert min / max as well\n                        lassign [list $min $max $mininc $maxinc] \\\n                            max min maxinc mininc\n                    }\n                    foreach e $copy {\n                        set mincmp [string compare $e $min]\n                        set maxcmp [string compare $e $max]\n                        if {\n                             ($mininc && $mincmp >= 0 || !$mininc && $mincmp > 0)\n                             &&\n                             ($maxinc && $maxcmp <= 0 || !$maxinc && $maxcmp < 0)\n                        } {\n                            lappend o $e\n                        }\n                    }\n                }\n                assert {$o eq $output}\n                assert {$outlen eq [llength $output]}\n            }\n        }\n\n        test \"ZREMRANGEBYLEX fuzzy test, 100 ranges in $elements element sorted set - $encoding\" {\n            set lexset {}\n            r del zset zsetcopy\n            for {set j 0} {$j < $elements} {incr j} {\n                set e [randstring 0 30 alpha]\n                lappend lexset $e\n                r zadd zset 0 $e\n            }\n            set lexset [lsort -unique $lexset]\n            for {set j 0} {$j < 100} {incr j} {\n                # Copy...\n                r zunionstore zsetcopy 1 zset\n                set lexsetcopy $lexset\n\n                set min [randstring 0 30 alpha]\n                set max [randstring 0 30 alpha]\n                set mininc [randomInt 2]\n                set maxinc [randomInt 2]\n                if {$mininc} {set cmin \"\\[$min\"} else {set cmin \"($min\"}\n                if {$maxinc} {set cmax \"\\[$max\"} else {set cmax \"($max\"}\n\n                # Make sure data is the same in both sides\n                assert {[r zrange zset 0 -1] eq $lexset}\n\n                # Get the range we are going to remove\n                set torem [r zrangebylex zset $cmin $cmax]\n                set toremlen [r zlexcount zset $cmin $cmax]\n                r zremrangebylex zsetcopy $cmin $cmax\n                set output [r zrange zsetcopy 0 -1]\n\n                # Remove the range with Tcl from the original list\n                if {$toremlen} {\n                    set first [lsearch -exact $lexsetcopy [lindex $torem 0]]\n                    set last [expr {$first+$toremlen-1}]\n                    set lexsetcopy [lreplace $lexsetcopy $first $last]\n                }\n                assert {$lexsetcopy eq $output}\n            }\n        }\n\n        test \"ZSETs skiplist implementation backlink consistency test - $encoding\" {\n            set diff 0\n            for {set j 0} {$j < $elements} {incr j} {\n                r zadd myzset [expr rand()] \"Element-$j\"\n                r zrem myzset \"Element-[expr int(rand()*$elements)]\"\n            }\n\n            assert_encoding $encoding myzset\n            set l1 [r zrange myzset 0 -1]\n            set l2 [r zrevrange myzset 0 -1]\n            for {set j 0} {$j < [llength $l1]} {incr j} {\n                if {[lindex $l1 $j] ne [lindex $l2 end-$j]} {\n                    incr diff\n                }\n            }\n            assert_equal 0 $diff\n        }\n\n        test \"ZSETs ZRANK augmented skip list stress testing - $encoding\" {\n            set err {}\n            r del myzset\n            for {set k 0} {$k < 2000} {incr k} {\n                set i [expr {$k % $elements}]\n                if {[expr rand()] < .2} {\n                    r zrem myzset $i\n                } else {\n                    set score [expr rand()]\n                    r zadd myzset $score $i\n                    assert_encoding $encoding myzset\n                }\n\n                set card [r zcard myzset]\n                if {$card > 0} {\n                    set index [randomInt $card]\n                    set ele [lindex [r zrange myzset $index $index] 0]\n                    set rank [r zrank myzset $ele]\n                    if {$rank != $index} {\n                        set err \"$ele RANK is wrong! ($rank != $index)\"\n                        break\n                    }\n                }\n            }\n            assert_equal {} $err\n        }\n    }\n\n    tags {\"slow\"} {\n        stressers ziplist\n        stressers skiplist\n    }\n}\n"
  },
  {
    "path": "utils/build-static-symbols.tcl",
    "content": "# Build a symbol table for static symbols of redis.c\n# Useful to get stack traces on segfault without a debugger. See redis.c\n# for more information.\n#\n# Copyright(C) 2009 Salvatore Sanfilippo, under the BSD license.\n\nset fd [open redis.c]\nset symlist {}\nwhile {[gets $fd line] != -1} {\n    if {[regexp {^static +[A-z0-9]+[ *]+([A-z0-9]*)\\(} $line - sym]} {\n        lappend symlist $sym\n    }\n}\nset symlist [lsort -unique $symlist]\nputs \"static struct redisFunctionSym symsTable\\[\\] = {\"\nforeach sym $symlist {\n    puts \"{\\\"$sym\\\",(unsigned long)$sym},\"\n}\nputs \"{NULL,0}\"\nputs \"};\"\n\nclose $fd\n"
  },
  {
    "path": "utils/generate-command-help.rb",
    "content": "#!/usr/bin/env ruby\n\nGROUPS = [\n  \"generic\",\n  \"string\",\n  \"list\",\n  \"set\",\n  \"sorted_set\",\n  \"hash\",\n  \"pubsub\",\n  \"transactions\",\n  \"connection\",\n  \"server\",\n  \"scripting\",\n  \"hyperloglog\"\n].freeze\n\nGROUPS_BY_NAME = Hash[*\n  GROUPS.each_with_index.map do |n,i|\n    [n,i]\n  end.flatten\n].freeze\n\ndef argument arg\n  name = arg[\"name\"].is_a?(Array) ? arg[\"name\"].join(\" \") : arg[\"name\"]\n  name = arg[\"enum\"].join \"|\" if \"enum\" == arg[\"type\"]\n  name = arg[\"command\"] + \" \" + name if arg[\"command\"]\n  if arg[\"multiple\"]\n    name = \"#{name} [#{name} ...]\"\n  end\n  if arg[\"optional\"]\n    name = \"[#{name}]\"\n  end\n  name\nend\n\ndef arguments command\n  return \"-\" unless command[\"arguments\"]\n  command[\"arguments\"].map do |arg|\n    argument arg\n  end.join \" \"\nend\n\ndef commands\n  return @commands if @commands\n\n  require \"rubygems\"\n  require \"net/http\"\n  require \"net/https\"\n  require \"json\"\n  require \"uri\"\n\n  url = URI.parse \"https://raw.github.com/antirez/redis-doc/master/commands.json\"\n  client = Net::HTTP.new url.host, url.port\n  client.use_ssl = true\n  response = client.get url.path\n  if response.is_a?(Net::HTTPSuccess)\n    @commands = JSON.parse(response.body)\n  else\n    response.error!\n  end\nend\n\ndef generate_groups\n  GROUPS.map do |n|\n    \"\\\"#{n}\\\"\"\n  end.join(\",\\n    \");\nend\n\ndef generate_commands\n  commands.to_a.sort do |x,y|\n    x[0] <=> y[0]\n  end.map do |key, command|\n    group = GROUPS_BY_NAME[command[\"group\"]]\n    if group.nil?\n      STDERR.puts \"Please update groups array in #{__FILE__}\"\n      raise \"Unknown group #{command[\"group\"]}\"\n    end\n\n    ret = <<-SPEC\n{ \"#{key}\",\n    \"#{arguments(command)}\",\n    \"#{command[\"summary\"]}\",\n    #{group},\n    \"#{command[\"since\"]}\" }\n    SPEC\n    ret.strip\n  end.join(\",\\n    \")\nend\n\n# Write to stdout\nputs <<-HELP_H\n/* Automatically generated by #{__FILE__}, do not edit. */\n\n#ifndef __REDIS_HELP_H\n#define __REDIS_HELP_H\n\nstatic char *commandGroups[] = {\n    #{generate_groups}\n};\n\nstruct commandHelp {\n  char *name;\n  char *params;\n  char *summary;\n  int group;\n  char *since;\n} commandHelp[] = {\n    #{generate_commands}\n};\n\n#endif\nHELP_H\n\n"
  },
  {
    "path": "utils/hyperloglog/.gitignore",
    "content": "*.txt\n"
  },
  {
    "path": "utils/hyperloglog/hll-err.rb",
    "content": "# hll-err.rb - Copyright (C) 2014 Salvatore Sanfilippo\n# BSD license, See the COPYING file for more information.\n#\n# Check error of HyperLogLog Redis implementation for different set sizes.\n\nrequire 'rubygems'\nrequire 'redis'\nrequire 'digest/sha1'\n\nr = Redis.new\nr.del('hll')\ni = 0\nwhile true do\n    100.times {\n        elements = []\n        1000.times {\n            ele = Digest::SHA1.hexdigest(i.to_s)\n            elements << ele\n            i += 1\n        }\n        r.pfadd('hll',*elements)\n    }\n    approx = r.pfcount('hll')\n    abs_err = (approx-i).abs\n    rel_err = 100.to_f*abs_err/i\n    puts \"#{i} vs #{approx}: #{rel_err}%\"\nend\n"
  },
  {
    "path": "utils/hyperloglog/hll-gnuplot-graph.rb",
    "content": "# hll-err.rb - Copyright (C) 2014 Salvatore Sanfilippo\n# BSD license, See the COPYING file for more information.\n#\n# This program is suited to output average and maximum errors of\n# the Redis HyperLogLog implementation in a format suitable to print\n# graphs using gnuplot.\n\nrequire 'rubygems'\nrequire 'redis'\nrequire 'digest/sha1'\n\n# Generate an array of [cardinality,relative_error] pairs\n# in the 0 - max range, with the specified step.\n#\n# 'r' is the Redis object used to perform the queries.\n# 'seed' must be different every time you want a test performed\n# with a different set. The function guarantees that if 'seed' is the\n# same, exactly the same dataset is used, and when it is different,\n# a totally unrelated different data set is used (without any common\n# element in practice).\ndef run_experiment(r,seed,max,step)\n    r.del('hll')\n    i = 0\n    samples = []\n    step = 1000 if step > 1000\n    while i < max do\n        elements = []\n        step.times {\n            ele = Digest::SHA1.hexdigest(i.to_s+seed.to_s)\n            elements << ele\n            i += 1\n        }\n        r.pfadd('hll',*elements)\n        approx = r.pfcount('hll')\n        err = approx-i\n        rel_err = 100.to_f*err/i\n        samples << [i,rel_err]\n    end\n    samples\nend\n\ndef filter_samples(numsets,max,step,filter)\n    r = Redis.new\n    dataset = {}\n    (0...numsets).each{|i|\n        dataset[i] = run_experiment(r,i,max,step)\n        STDERR.puts \"Set #{i}\"\n    }\n    dataset[0].each_with_index{|ele,index|\n        if filter == :max\n            card=ele[0]\n            err=ele[1].abs\n            (1...numsets).each{|i|\n                err = dataset[i][index][1] if err < dataset[i][index][1]\n            }\n            puts \"#{card} #{err}\"\n        elsif filter == :avg\n            card=ele[0]\n            err = 0\n            (0...numsets).each{|i|\n                err += dataset[i][index][1]\n            }\n            err /= numsets\n            puts \"#{card} #{err}\"\n        elsif filter == :absavg\n            card=ele[0]\n            err = 0\n            (0...numsets).each{|i|\n                err += dataset[i][index][1].abs\n            }\n            err /= numsets\n            puts \"#{card} #{err}\"\n        elsif filter == :all\n            (0...numsets).each{|i|\n                card,err = dataset[i][index]\n                puts \"#{card} #{err}\"\n            }\n        else\n            raise \"Unknown filter #{filter}\"\n        end\n    }\nend\n\nif ARGV.length != 4\n    puts \"Usage: hll-gnuplot-graph <samples> <max> <step> (max|avg|absavg|all)\"\n    exit 1\nend\nfilter_samples(ARGV[0].to_i,ARGV[1].to_i,ARGV[2].to_i,ARGV[3].to_sym)\n"
  },
  {
    "path": "utils/install_server.sh",
    "content": "#!/bin/sh\n\n# Copyright 2011 Dvir Volk <dvirsk at gmail dot com>. 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#   1. Redistributions of source code must retain the above copyright notice,\n#   this list of conditions and the following disclaimer.\n#\n#   2. 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#\n# THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED\n# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\n# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO\n# EVENT SHALL Dvir Volk OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,\n# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,\n# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\n# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,\n# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n#\n################################################################################\n#\n# Interactive service installer for redis server\n# this generates a redis config file and an /etc/init.d script, and installs them\n# this scripts should be run as root\n\ndie () {\n\techo \"ERROR: $1. Aborting!\"\n\texit 1\n}\n\n\n#Absolute path to this script\nSCRIPT=$(readlink -f $0)\n#Absolute path this script is in\nSCRIPTPATH=$(dirname $SCRIPT)\n\n#Initial defaults\n_REDIS_PORT=6379\n\necho \"Welcome to the redis service installer\"\necho \"This script will help you easily set up a running redis server\"\necho\n\n#check for root user\nif [ \"$(id -u)\" -ne 0 ] ; then\n\techo \"You must run this script as root. Sorry!\"\n\texit 1\nfi\n\n#Read the redis port\nread  -p \"Please select the redis port for this instance: [$_REDIS_PORT] \" REDIS_PORT\nif ! echo $REDIS_PORT | egrep -q '^[0-9]+$' ; then\n\techo \"Selecting default: $_REDIS_PORT\"\n\tREDIS_PORT=$_REDIS_PORT\nfi\n\n#read the redis config file\n_REDIS_CONFIG_FILE=\"/etc/redis/$REDIS_PORT.conf\"\nread -p \"Please select the redis config file name [$_REDIS_CONFIG_FILE] \" REDIS_CONFIG_FILE\nif [ -z \"$REDIS_CONFIG_FILE\" ] ; then\n\tREDIS_CONFIG_FILE=$_REDIS_CONFIG_FILE\n\techo \"Selected default - $REDIS_CONFIG_FILE\"\nfi\n\n#read the redis log file path\n_REDIS_LOG_FILE=\"/var/log/redis_$REDIS_PORT.log\"\nread -p \"Please select the redis log file name [$_REDIS_LOG_FILE] \" REDIS_LOG_FILE\nif [ -z \"$REDIS_LOG_FILE\" ] ; then\n\tREDIS_LOG_FILE=$_REDIS_LOG_FILE\n\techo \"Selected default - $REDIS_LOG_FILE\"\nfi\n\n\n#get the redis data directory\n_REDIS_DATA_DIR=\"/var/lib/redis/$REDIS_PORT\"\nread -p \"Please select the data directory for this instance [$_REDIS_DATA_DIR] \" REDIS_DATA_DIR\nif [ -z \"$REDIS_DATA_DIR\" ] ; then\n\tREDIS_DATA_DIR=$_REDIS_DATA_DIR\n\techo \"Selected default - $REDIS_DATA_DIR\"\nfi\n\n#get the redis executable path\n_REDIS_EXECUTABLE=`command -v redis-server`\nread -p \"Please select the redis executable path [$_REDIS_EXECUTABLE] \" REDIS_EXECUTABLE\nif [ ! -x \"$REDIS_EXECUTABLE\" ] ; then\n\tREDIS_EXECUTABLE=$_REDIS_EXECUTABLE\n\n\tif [ ! -x \"$REDIS_EXECUTABLE\" ] ; then\n\t\techo \"Mmmmm...  it seems like you don't have a redis executable. Did you run make install yet?\"\n\t\texit 1\n\tfi\nfi\n\n#check the default for redis cli\nCLI_EXEC=`command -v redis-cli`\nif [ -z \"$CLI_EXEC\" ] ; then\n\tCLI_EXEC=`dirname $REDIS_EXECUTABLE`\"/redis-cli\"\nfi\n\necho \"Selected config:\"\n\necho \"Port           : $REDIS_PORT\"\necho \"Config file    : $REDIS_CONFIG_FILE\"\necho \"Log file       : $REDIS_LOG_FILE\"\necho \"Data dir       : $REDIS_DATA_DIR\"\necho \"Executable     : $REDIS_EXECUTABLE\"\necho \"Cli Executable : $CLI_EXEC\"\n\nread -p \"Is this ok? Then press ENTER to go on or Ctrl-C to abort.\" _UNUSED_\n\nmkdir -p `dirname \"$REDIS_CONFIG_FILE\"` || die \"Could not create redis config directory\"\nmkdir -p `dirname \"$REDIS_LOG_FILE\"` || die \"Could not create redis log dir\"\nmkdir -p \"$REDIS_DATA_DIR\" || die \"Could not create redis data directory\"\n\n#render the templates\nTMP_FILE=\"/tmp/${REDIS_PORT}.conf\"\nDEFAULT_CONFIG=\"${SCRIPTPATH}/../redis.conf\"\nINIT_TPL_FILE=\"${SCRIPTPATH}/redis_init_script.tpl\"\nINIT_SCRIPT_DEST=\"/etc/init.d/redis_${REDIS_PORT}\"\nPIDFILE=\"/var/run/redis_${REDIS_PORT}.pid\"\n\nif [ ! -f \"$DEFAULT_CONFIG\" ]; then\n\techo \"Mmmmm... the default config is missing. Did you switch to the utils directory?\"\n\texit 1\nfi\n\n#Generate config file from the default config file as template\n#changing only the stuff we're controlling from this script\necho \"## Generated by install_server.sh ##\" > $TMP_FILE\n\nread -r SED_EXPR <<-EOF\ns#^port [0-9]{4}\\$#port ${REDIS_PORT}#; \\\ns#^logfile .+\\$#logfile ${REDIS_LOG_FILE}#; \\\ns#^dir .+\\$#dir ${REDIS_DATA_DIR}#; \\\ns#^pidfile .+\\$#pidfile ${PIDFILE}#; \\\ns#^daemonize no\\$#daemonize yes#;\nEOF\nsed -r \"$SED_EXPR\" $DEFAULT_CONFIG  >> $TMP_FILE\n\n#cat $TPL_FILE | while read line; do eval \"echo \\\"$line\\\"\" >> $TMP_FILE; done\ncp $TMP_FILE $REDIS_CONFIG_FILE || die \"Could not write redis config file $REDIS_CONFIG_FILE\"\n\n#Generate sample script from template file\nrm -f $TMP_FILE\n\n#we hard code the configs here to avoid issues with templates containing env vars\n#kinda lame but works!\nREDIS_INIT_HEADER=\\\n\"#/bin/sh\\n\n#Configurations injected by install_server below....\\n\\n\nEXEC=$REDIS_EXECUTABLE\\n\nCLIEXEC=$CLI_EXEC\\n\nPIDFILE=\\\"$PIDFILE\\\"\\n\nCONF=\\\"$REDIS_CONFIG_FILE\\\"\\n\\n\nREDISPORT=\\\"$REDIS_PORT\\\"\\n\\n\n###############\\n\\n\"\n\nREDIS_CHKCONFIG_INFO=\\\n\"# REDHAT chkconfig header\\n\\n\n# chkconfig: - 58 74\\n\n# description: redis_${REDIS_PORT} is the redis daemon.\\n\n### BEGIN INIT INFO\\n\n# Provides: redis_6379\\n\n# Required-Start: \\$network \\$local_fs \\$remote_fs\\n\n# Required-Stop: \\$network \\$local_fs \\$remote_fs\\n\n# Default-Start: 2 3 4 5\\n\n# Default-Stop: 0 1 6\\n\n# Should-Start: \\$syslog \\$named\\n\n# Should-Stop: \\$syslog \\$named\\n\n# Short-Description: start and stop redis_${REDIS_PORT}\\n\n# Description: Redis daemon\\n\n### END INIT INFO\\n\\n\"\n\nif command -v chkconfig >/dev/null; then\n\t#if we're a box with chkconfig on it we want to include info for chkconfig\n\techo \"$REDIS_INIT_HEADER\" \"$REDIS_CHKCONFIG_INFO\" > $TMP_FILE && cat $INIT_TPL_FILE >> $TMP_FILE || die \"Could not write init script to $TMP_FILE\"\nelse\n\t#combine the header and the template (which is actually a static footer)\n\techo \"$REDIS_INIT_HEADER\" > $TMP_FILE && cat $INIT_TPL_FILE >> $TMP_FILE || die \"Could not write init script to $TMP_FILE\"\nfi\n\n###\n# Generate sample script from template file\n# - No need to check which system we are on. The init info are comments and\n#   do not interfere with update_rc.d systems. Additionally:\n#     Ubuntu/debian by default does not come with chkconfig, but does issue a\n#     warning if init info is not available.\n\ncat > ${TMP_FILE} <<EOT\n#/bin/sh\n#Configurations injected by install_server below....\n\nEXEC=$REDIS_EXECUTABLE\nCLIEXEC=$CLI_EXEC\nPIDFILE=$PIDFILE\nCONF=\"$REDIS_CONFIG_FILE\"\nREDISPORT=\"$REDIS_PORT\"\n###############\n# SysV Init Information\n# chkconfig: - 58 74\n# description: redis_${REDIS_PORT} is the redis daemon.\n### BEGIN INIT INFO\n# Provides: redis_${REDIS_PORT}\n# Required-Start: \\$network \\$local_fs \\$remote_fs\n# Required-Stop: \\$network \\$local_fs \\$remote_fs\n# Default-Start: 2 3 4 5\n# Default-Stop: 0 1 6\n# Should-Start: \\$syslog \\$named\n# Should-Stop: \\$syslog \\$named\n# Short-Description: start and stop redis_${REDIS_PORT}\n# Description: Redis daemon\n### END INIT INFO\n\nEOT\ncat ${INIT_TPL_FILE} >> ${TMP_FILE}\n\n#copy to /etc/init.d\ncp $TMP_FILE $INIT_SCRIPT_DEST && \\\n\tchmod +x $INIT_SCRIPT_DEST || die \"Could not copy redis init script to  $INIT_SCRIPT_DEST\"\necho \"Copied $TMP_FILE => $INIT_SCRIPT_DEST\"\n\n#Install the service\necho \"Installing service...\"\nif command -v chkconfig >/dev/null 2>&1; then\n\t# we're chkconfig, so lets add to chkconfig and put in runlevel 345\n\tchkconfig --add redis_${REDIS_PORT} && echo \"Successfully added to chkconfig!\"\n\tchkconfig --level 345 redis_${REDIS_PORT} on && echo \"Successfully added to runlevels 345!\"\nelif command -v update-rc.d >/dev/null 2>&1; then\n\t#if we're not a chkconfig box assume we're able to use update-rc.d\n\tupdate-rc.d redis_${REDIS_PORT} defaults && echo \"Success!\"\nelse\n\techo \"No supported init tool found.\"\nfi\n\n/etc/init.d/redis_$REDIS_PORT start || die \"Failed starting service...\"\n\n#tada\necho \"Installation successful!\"\nexit 0\n"
  },
  {
    "path": "utils/lru/README",
    "content": "The test-lru.rb program can be used in order to check the behavior of the\nRedis approximated LRU algorithm against the theoretical output of true\nLRU algorithm.\n\nIn order to use the program you need to recompile Redis setting the define\nREDIS_LRU_CLOCK_RESOLUTION to 1, by editing redis.h.\nThis allows to execute the program in a fast way since the 1 ms resolution\nis enough for all the objects to have a different enough time stamp during\nthe test.\n\nThe program is executed like this:\n\n    ruby test-lru.rb > /tmp/lru.html\n"
  },
  {
    "path": "utils/lru/test-lru.rb",
    "content": "require 'rubygems'\nrequire 'redis'\n\nr = Redis.new\nr.config(\"SET\",\"maxmemory\",\"2000000\")\nr.config(\"SET\",\"maxmemory-policy\",\"allkeys-lru\")\nr.config(\"SET\",\"maxmemory-samples\",5)\nr.config(\"RESETSTAT\")\nr.flushall\n\nputs <<EOF\n<html>\n<body>\n<style>\n.box {\n    width:5px;\n    height:5px;\n    float:left;\n    margin: 1px;\n}\n\n.old {\n    border: 1px black solid;\n}\n\n.new {\n    border: 1px green solid;\n}\n\n.ex {\n    background-color: #666;\n}\n</style>\n<pre>\nEOF\n\n# Fill\noldsize = r.dbsize\nid = 0\nwhile true\n    id += 1\n    r.set(id,\"foo\")\n    newsize = r.dbsize\n    break if newsize == oldsize\n    oldsize = newsize\nend\n\ninserted = r.dbsize\nfirst_set_max_id = id\nputs \"#{r.dbsize} keys inserted\"\n\n# Access keys sequencially\n\nputs \"Access keys sequencially\"\n(1..first_set_max_id).each{|id|\n    r.get(id)\n#    sleep 0.001\n}\n\n# Insert more 50% keys. We expect that the new keys\nhalf = inserted/2\nputs \"Insert enough keys to evict half the keys we inserted\"\nadd = 0\nwhile true\n    add += 1\n    id += 1\n    r.set(id,\"foo\")\n    break if r.info['evicted_keys'].to_i >= half\nend\n\nputs \"#{add} additional keys added.\"\nputs \"#{r.dbsize} keys in DB\"\n\n# Check if evicted keys respect LRU\n# We consider errors from 1 to N progressively more serious as they violate\n# more the access pattern.\n\nerrors = 0\ne = 1\nedecr = 1.0/(first_set_max_id/2)\n(1..(first_set_max_id/2)).each{|id|\n    e -= edecr if e > 0\n    e = 0 if e < 0\n    if r.exists(id)\n        errors += e\n    end\n}\n\nputs \"#{errors} errors!\"\nputs \"</pre>\"\n\n# Generate the graphical representation\n(1..id).each{|id|\n    # Mark first set and added items in a different way.\n    c = \"box\"\n    if id <= first_set_max_id\n        c << \" old\"\n    else\n        c << \" new\"\n    end\n\n    # Add class if exists\n    c << \" ex\" if r.exists(id)\n    puts \"<div class=\\\"#{c}\\\"></div>\"\n}\n\n# Close HTML page\n\nputs <<EOF\n</body>\n</html>\nEOF\n"
  },
  {
    "path": "utils/mkrelease.sh",
    "content": "#!/bin/sh\nif [ $# != \"1\" ]\nthen\n    echo \"Usage: ./mkrelease.sh <git-ref>\"\n    exit 1\nfi\n\nTAG=$1\nTARNAME=\"redis-${TAG}.tar\"\necho \"Generating /tmp/${TARNAME}\"\ngit archive $TAG --prefix redis-${TAG}/ > /tmp/$TARNAME || exit 1\necho \"Gizipping the archive\"\nrm -f /tmp/$TARNAME.gz\ngzip -9 /tmp/$TARNAME\n"
  },
  {
    "path": "utils/redis-copy.rb",
    "content": "# redis-copy.rb - Copyright (C) 2009-2010 Salvatore Sanfilippo\n# BSD license, See the COPYING file for more information.\n#\n# Copy the whole dataset from one Redis instance to another one\n#\n# WARNING: this utility is deprecated and serves as a legacy adapter\n#          for the more-robust redis-copy gem.\n\nrequire 'shellwords'\n\ndef redisCopy(opts={})\n  src = \"#{opts[:srchost]}:#{opts[:srcport]}\"\n  dst = \"#{opts[:dsthost]}:#{opts[:dstport]}\"\n  `redis-copy #{src.shellescape} #{dst.shellescape}`\nrescue Errno::ENOENT\n  $stderr.puts 'This utility requires the redis-copy executable',\n               'from the redis-copy gem on https://rubygems.org',\n               'To install it, run `gem install redis-copy`.'\n  exit 1\nend\n\n$stderr.puts \"This utility is deprecated. Use the redis-copy gem instead.\"\nif ARGV.length != 4\n    puts \"Usage: redis-copy.rb <srchost> <srcport> <dsthost> <dstport>\"\n    exit 1\nend\nputs \"WARNING: it's up to you to FLUSHDB the destination host before to continue, press any key when ready.\"\nSTDIN.gets\nsrchost = ARGV[0]\nsrcport = ARGV[1]\ndsthost = ARGV[2]\ndstport = ARGV[3]\nputs \"Copying #{srchost}:#{srcport} into #{dsthost}:#{dstport}\"\nredisCopy(:srchost => srchost, :srcport => srcport.to_i,\n          :dsthost => dsthost, :dstport => dstport.to_i)\n"
  },
  {
    "path": "utils/redis-sha1.rb",
    "content": "# redis-sha1.rb - Copyright (C) 2009 Salvatore Sanfilippo\n# BSD license, See the COPYING file for more information.\n#\n# Performs the SHA1 sum of the whole datset.\n# This is useful to spot bugs in persistence related code and to make sure\n# Slaves and Masters are in SYNC.\n#\n# If you hack this code make sure to sort keys and set elements as this are\n# unsorted elements. Otherwise the sum may differ with equal dataset.\n\nrequire 'rubygems'\nrequire 'redis'\nrequire 'digest/sha1'\n\ndef redisSha1(opts={})\n    sha1=\"\"\n    r = Redis.new(opts)\n    r.keys('*').sort.each{|k|\n        vtype = r.type?(k)\n        if vtype == \"string\"\n            len = 1\n            sha1 = Digest::SHA1.hexdigest(sha1+k)\n            sha1 = Digest::SHA1.hexdigest(sha1+r.get(k))\n        elsif vtype == \"list\"\n            len = r.llen(k)\n            if len != 0\n                sha1 = Digest::SHA1.hexdigest(sha1+k)\n                sha1 = Digest::SHA1.hexdigest(sha1+r.list_range(k,0,-1).join(\"\\x01\"))\n            end\n        elsif vtype == \"set\"\n            len = r.scard(k)\n            if len != 0\n                sha1 = Digest::SHA1.hexdigest(sha1+k)\n                sha1 = Digest::SHA1.hexdigest(sha1+r.set_members(k).to_a.sort.join(\"\\x02\"))\n            end\n        elsif vtype == \"zset\"\n            len = r.zcard(k)\n            if len != 0\n                sha1 = Digest::SHA1.hexdigest(sha1+k)\n                sha1 = Digest::SHA1.hexdigest(sha1+r.zrange(k,0,-1).join(\"\\x01\"))\n            end\n        end\n        # puts \"#{k} => #{sha1}\" if len != 0\n    }\n    sha1\nend\n\nhost = ARGV[0] || \"127.0.0.1\"\nport = ARGV[1] || \"6379\"\ndb = ARGV[2] || \"0\"\nputs \"Performing SHA1 of Redis server #{host} #{port} DB: #{db}\"\np \"Dataset SHA1: #{redisSha1(:host => host, :port => port.to_i, :db => db)}\"\n"
  },
  {
    "path": "utils/redis_init_script",
    "content": "#!/bin/sh\n#\n# Simple Redis init.d script conceived to work on Linux systems\n# as it does use of the /proc filesystem.\n\nREDISPORT=6379\nEXEC=/usr/local/bin/redis-server\nCLIEXEC=/usr/local/bin/redis-cli\n\nPIDFILE=/var/run/redis_${REDISPORT}.pid\nCONF=\"/etc/redis/${REDISPORT}.conf\"\n\ncase \"$1\" in\n    start)\n        if [ -f $PIDFILE ]\n        then\n                echo \"$PIDFILE exists, process is already running or crashed\"\n        else\n                echo \"Starting Redis server...\"\n                $EXEC $CONF\n        fi\n        ;;\n    stop)\n        if [ ! -f $PIDFILE ]\n        then\n                echo \"$PIDFILE does not exist, process is not running\"\n        else\n                PID=$(cat $PIDFILE)\n                echo \"Stopping ...\"\n                $CLIEXEC -p $REDISPORT shutdown\n                while [ -x /proc/${PID} ]\n                do\n                    echo \"Waiting for Redis to shutdown ...\"\n                    sleep 1\n                done\n                echo \"Redis stopped\"\n        fi\n        ;;\n    *)\n        echo \"Please use start or stop as first argument\"\n        ;;\nesac\n"
  },
  {
    "path": "utils/redis_init_script.tpl",
    "content": "\ncase \"$1\" in\n    start)\n        if [ -f $PIDFILE ]\n        then\n            echo \"$PIDFILE exists, process is already running or crashed\"\n        else\n            echo \"Starting Redis server...\"\n            $EXEC $CONF\n        fi\n        ;;\n    stop)\n        if [ ! -f $PIDFILE ]\n        then\n            echo \"$PIDFILE does not exist, process is not running\"\n        else\n            PID=$(cat $PIDFILE)\n            echo \"Stopping ...\"\n            $CLIEXEC -p $REDISPORT shutdown\n            while [ -x /proc/${PID} ]\n            do\n                echo \"Waiting for Redis to shutdown ...\"\n                sleep 1\n            done\n            echo \"Redis stopped\"\n        fi\n        ;;\n    status)\n        if [ ! -f $PIDFILE ]\n        then\n            echo 'Redis is not running'\n        else\n            echo \"Redis is running ($(<$PIDFILE))\"\n        fi\n        ;;\n    restart)\n        $0 stop\n        $0 start\n        ;;\n    *)\n        echo \"Please use start, stop, restart or status as first argument\"\n        ;;\nesac\n"
  },
  {
    "path": "utils/speed-regression.tcl",
    "content": "#!/usr/bin/env tclsh8.5\n# Copyright (C) 2011 Salvatore Sanfilippo\n# Released under the BSD license like Redis itself\n\nsource ../tests/support/redis.tcl\nset ::port 12123\nset ::tests {PING,SET,GET,INCR,LPUSH,LPOP,SADD,SPOP,LRANGE_100,LRANGE_600,MSET}\nset ::datasize 16\nset ::requests 100000\n\nproc run-tests branches {\n    set runs {}\n    set branch_id 0\n    foreach b $branches {\n        cd ../src\n        puts \"Benchmarking $b\"\n        exec -ignorestderr git checkout $b 2> /dev/null\n        exec -ignorestderr make clean 2> /dev/null\n        puts \"  compiling...\"\n        exec -ignorestderr make 2> /dev/null\n\n        if {$branch_id == 0} {\n            puts \"  copy redis-benchmark from unstable to /tmp...\"\n            exec -ignorestderr cp ./redis-benchmark /tmp\n            incr branch_id\n            continue\n        }\n\n        # Start the Redis server\n        puts \"  starting the server... [exec ./redis-server -v]\"\n        set pids [exec echo \"port $::port\\nloglevel warning\\n\" | ./redis-server - > /dev/null 2> /dev/null &]\n        puts \"  pids: $pids\"\n        after 1000\n        puts \"  running the benchmark\"\n\n        set r [redis 127.0.0.1 $::port]\n        set i [$r info]\n        puts \"  redis INFO shows version: [lindex [split $i] 0]\"\n        $r close\n\n        set output [exec /tmp/redis-benchmark -n $::requests -t $::tests -d $::datasize --csv -p $::port]\n        lappend runs $b $output\n        puts \"  killing server...\"\n        catch {exec kill -9 [lindex $pids 0]}\n        catch {exec kill -9 [lindex $pids 1]}\n        incr branch_id\n    }\n    return $runs\n}\n\nproc get-result-with-name {output name} {\n    foreach line [split $output \"\\n\"] {\n        lassign [split $line \",\"] key value\n        set key [string tolower [string range $key 1 end-1]]\n        set value [string range $value 1 end-1]\n        if {$key eq [string tolower $name]} {\n            return $value\n        }\n    }\n    return \"n/a\"\n}\n\nproc get-test-names output {\n    set names {}\n    foreach line [split $output \"\\n\"] {\n        lassign [split $line \",\"] key value\n        set key [string tolower [string range $key 1 end-1]]\n        lappend names $key\n    }\n    return $names\n}\n\nproc combine-results {results} {\n    set tests [get-test-names [lindex $results 1]]\n    foreach test $tests {\n        puts $test\n        foreach {branch output} $results {\n            puts [format \"%-20s %s\" \\\n                $branch [get-result-with-name $output $test]]\n        }\n        puts {}\n    }\n}\n\nproc main {} {\n    # Note: the first branch is only used in order to get the redis-benchmark\n    # executable. Tests are performed starting from the second branch.\n    set branches {\n        slowset 2.2.0 2.4.0 unstable slowset\n    }\n    set results [run-tests $branches]\n    puts \"\\n\"\n    puts \"# Test results: datasize=$::datasize requests=$::requests\"\n    puts [combine-results $results]\n}\n\n# Force the user to run the script from the 'utils' directory.\nif {![file exists speed-regression.tcl]} {\n    puts \"Please make sure to run speed-regression.tcl while inside /utils.\"\n    puts \"Example: cd utils; ./speed-regression.tcl\"\n    exit 1\n}\n\n# Make sure there is not already a server runnign on port 12123\nset is_not_running [catch {set r [redis 127.0.0.1 $::port]}]\nif {!$is_not_running} {\n    puts \"Sorry, you have a running server on port $::port\"\n    exit 1\n}\n\n# parse arguments\nfor {set j 0} {$j < [llength $argv]} {incr j} {\n    set opt [lindex $argv $j]\n    set arg [lindex $argv [expr $j+1]]\n    if {$opt eq {--tests}} {\n        set ::tests $arg\n        incr j\n    } elseif {$opt eq {--datasize}} {\n        set ::datasize $arg\n        incr j\n    } elseif {$opt eq {--requests}} {\n        set ::requests $arg\n        incr j\n    } else {\n        puts \"Wrong argument: $opt\"\n        exit 1\n    }\n}\n\nmain\n"
  },
  {
    "path": "utils/whatisdoing.sh",
    "content": "# This script is from http://poormansprofiler.org/\n\n#!/bin/bash\nnsamples=1\nsleeptime=0\npid=$(pidof redis-server)\n\nfor x in $(seq 1 $nsamples)\n  do\n    gdb -ex \"set pagination 0\" -ex \"thread apply all bt\" -batch -p $pid\n    sleep $sleeptime\n  done | \\\nawk '\n  BEGIN { s = \"\"; } \n  /Thread/ { print s; s = \"\"; } \n  /^\\#/ { if (s != \"\" ) { s = s \",\" $4} else { s = $4 } } \n  END { print s }' | \\\nsort | uniq -c | sort -r -n -k 1,1\n"
  }
]